1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * This class represent one XMLDB Field
19 *
20 * @package    core_xmldb
21 * @copyright  1999 onwards Martin Dougiamas     http://dougiamas.com
22 *             2001-3001 Eloy Lafuente (stronk7) http://contiento.com
23 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28
29class xmldb_field extends xmldb_object {
30
31    /** @var int XMLDB_TYPE_ constants */
32    protected $type;
33
34    /** @var int size of field */
35    protected $length;
36
37    /** @var bool is null forbidden? XMLDB_NOTNULL */
38    protected $notnull;
39
40    /** @var mixed default value */
41    protected $default;
42
43    /** @var bool use automatic counter */
44    protected $sequence;
45
46    /** @var int number of decimals */
47    protected $decimals;
48
49    /**
50     * Note:
51     *  - Oracle: VARCHAR2 has a limit of 4000 bytes
52     *  - SQL Server: NVARCHAR has a limit of 40000 chars
53     *  - MySQL: VARCHAR 65,535 chars
54     *  - PostgreSQL: no limit
55     *
56     * @const maximum length of text field
57     */
58    const CHAR_MAX_LENGTH = 1333;
59
60
61    /**
62     * @const maximum number of digits of integers
63     */
64    const INTEGER_MAX_LENGTH = 20;
65
66    /**
67     * @const max length (precision, the total number of digits) of decimals
68     */
69    const NUMBER_MAX_LENGTH = 38;
70
71    /**
72     * @const max length of floats
73     */
74    const FLOAT_MAX_LENGTH = 20;
75
76    /**
77     * Note:
78     *  - Oracle has 30 chars limit for all names
79     *
80     * @const maximumn length of field names
81     */
82    const NAME_MAX_LENGTH = 30;
83
84    /**
85     * Creates one new xmldb_field
86     * @param string $name of field
87     * @param int $type XMLDB_TYPE_INTEGER, XMLDB_TYPE_NUMBER, XMLDB_TYPE_CHAR, XMLDB_TYPE_TEXT, XMLDB_TYPE_BINARY
88     * @param string $precision length for integers and chars, two-comma separated numbers for numbers
89     * @param bool $unsigned XMLDB_UNSIGNED or null (or false)
90     * @param bool $notnull XMLDB_NOTNULL or null (or false)
91     * @param bool $sequence XMLDB_SEQUENCE or null (or false)
92     * @param mixed $default meaningful default o null (or false)
93     * @param xmldb_object $previous
94     */
95    public function __construct($name, $type=null, $precision=null, $unsigned=null, $notnull=null, $sequence=null, $default=null, $previous=null) {
96        $this->type = null;
97        $this->length = null;
98        $this->notnull = false;
99        $this->default = null;
100        $this->sequence = false;
101        $this->decimals = null;
102        parent::__construct($name);
103        $this->set_attributes($type, $precision, $unsigned, $notnull, $sequence, $default, $previous);
104    }
105
106    /**
107     * Set all the attributes of one xmldb_field
108     *
109     * @param int $type XMLDB_TYPE_INTEGER, XMLDB_TYPE_NUMBER, XMLDB_TYPE_CHAR, XMLDB_TYPE_TEXT, XMLDB_TYPE_BINARY
110     * @param string $precision length for integers and chars, two-comma separated numbers for numbers
111     * @param bool $unsigned XMLDB_UNSIGNED or null (or false)
112     * @param bool $notnull XMLDB_NOTNULL or null (or false)
113     * @param bool $sequence XMLDB_SEQUENCE or null (or false)
114     * @param mixed $default meaningful default o null (or false)
115     * @param xmldb_object $previous
116     */
117    public function set_attributes($type, $precision=null, $unsigned=null, $notnull=null, $sequence=null, $default=null, $previous=null) {
118        $this->type = $type;
119    /// Try to split the precision into length and decimals and apply
120    /// each one as needed
121        $precisionarr = explode(',', $precision);
122        if (isset($precisionarr[0])) {
123            $this->length = trim($precisionarr[0]);
124        }
125        if (isset($precisionarr[1])) {
126            $this->decimals = trim($precisionarr[1]);
127        }
128        $this->precision = $type;
129        $this->notnull = !empty($notnull) ? true : false;
130        $this->sequence = !empty($sequence) ? true : false;
131        $this->setDefault($default);
132
133        if ($this->type == XMLDB_TYPE_BINARY || $this->type == XMLDB_TYPE_TEXT) {
134            $this->length = null;
135            $this->decimals = null;
136        }
137
138        $this->previous = $previous;
139    }
140
141    /**
142     * Get the type
143     * @return int
144     */
145    public function getType() {
146        return $this->type;
147    }
148
149    /**
150     * Get the length
151     * @return int
152     */
153    public function getLength() {
154        return $this->length;
155    }
156
157    /**
158     * Get the decimals
159     * @return string
160     */
161    public function getDecimals() {
162        return $this->decimals;
163    }
164
165    /**
166     * Get the notnull
167     * @return bool
168     */
169    public function getNotNull() {
170        return $this->notnull;
171    }
172
173    /**
174     * Get the unsigned
175     * @deprecated since moodle 2.3
176     * @return bool
177     */
178    public function getUnsigned() {
179        return false;
180    }
181
182    /**
183     * Get the sequence
184     * @return bool
185     */
186    public function getSequence() {
187        return $this->sequence;
188    }
189
190    /**
191     * Get the default
192     * @return mixed
193     */
194    public function getDefault() {
195        return $this->default;
196    }
197
198    /**
199     * Set the field type
200     * @param int $type
201     */
202    public function setType($type) {
203        $this->type = $type;
204    }
205
206    /**
207     * Set the field length
208     * @param int $length
209     */
210    public function setLength($length) {
211        $this->length = $length;
212    }
213
214    /**
215     * Set the field decimals
216     * @param string
217     */
218    public function setDecimals($decimals) {
219        $this->decimals = $decimals;
220    }
221
222    /**
223     * Set the field unsigned
224     * @deprecated since moodle 2.3
225     * @param bool $unsigned
226     */
227    public function setUnsigned($unsigned=true) {
228    }
229
230    /**
231     * Set the field notnull
232     * @param bool $notnull
233     */
234    public function setNotNull($notnull=true) {
235        $this->notnull = $notnull;
236    }
237
238    /**
239     * Set the field sequence
240     * @param bool $sequence
241     */
242    public function setSequence($sequence=true) {
243        $this->sequence = $sequence;
244    }
245
246    /**
247     * Set the field default
248     * @param mixed $default
249     */
250    public function setDefault($default) {
251        // Check, warn and auto-fix '' (empty) defaults for CHAR NOT NULL columns, changing them
252        // to NULL so XMLDB will apply the proper default
253        if ($this->type == XMLDB_TYPE_CHAR && $this->notnull && $default === '') {
254            $this->errormsg = 'XMLDB has detected one CHAR NOT NULL column (' . $this->name . ") with '' (empty string) as DEFAULT value. This type of columns must have one meaningful DEFAULT declared or none (NULL). XMLDB have fixed it automatically changing it to none (NULL). The process will continue ok and proper defaults will be created accordingly with each DB requirements. Please fix it in source (XML and/or upgrade script) to avoid this message to be displayed.";
255            $this->debug($this->errormsg);
256            $default = null;
257        }
258        // Check, warn and autofix TEXT|BINARY columns having a default clause (only null is allowed)
259        if (($this->type == XMLDB_TYPE_TEXT || $this->type == XMLDB_TYPE_BINARY) && $default !== null) {
260            $this->errormsg = 'XMLDB has detected one TEXT/BINARY column (' . $this->name . ") with some DEFAULT defined. This type of columns cannot have any default value. Please fix it in source (XML and/or upgrade script) to avoid this message to be displayed.";
261            $this->debug($this->errormsg);
262            $default = null;
263        }
264        $this->default = $default;
265    }
266
267    /**
268     * Load data from XML to the table
269     * @param array $xmlarr
270     * @return mixed
271     */
272    public function arr2xmldb_field($xmlarr) {
273
274        $result = true;
275
276        // Debug the table
277        // traverse_xmlize($xmlarr);                   //Debug
278        // print_object ($GLOBALS['traverse_array']);  //Debug
279        // $GLOBALS['traverse_array']="";              //Debug
280
281        // Process table attributes (name, type, length
282        // notnull, sequence, decimals, comment, previous, next)
283        if (isset($xmlarr['@']['NAME'])) {
284            $this->name = trim($xmlarr['@']['NAME']);
285        } else {
286            $this->errormsg = 'Missing NAME attribute';
287            $this->debug($this->errormsg);
288            $result = false;
289        }
290
291        if (isset($xmlarr['@']['TYPE'])) {
292            // Check for valid type
293            $type = $this->getXMLDBFieldType(trim($xmlarr['@']['TYPE']));
294            if ($type) {
295                $this->type = $type;
296            } else {
297                $this->errormsg = 'Invalid TYPE attribute';
298                $this->debug($this->errormsg);
299                $result = false;
300            }
301        } else {
302            $this->errormsg = 'Missing TYPE attribute';
303            $this->debug($this->errormsg);
304            $result = false;
305        }
306
307        if (isset($xmlarr['@']['LENGTH'])) {
308            $length = trim($xmlarr['@']['LENGTH']);
309            // Check for integer values
310            if ($this->type == XMLDB_TYPE_INTEGER ||
311                $this->type == XMLDB_TYPE_NUMBER ||
312                $this->type == XMLDB_TYPE_CHAR) {
313                if (!(is_numeric($length)&&(intval($length)==floatval($length)))) {
314                    $this->errormsg = 'Incorrect LENGTH attribute for int, number or char fields';
315                    $this->debug($this->errormsg);
316                    $result = false;
317                } else if (!$length) {
318                    $this->errormsg = 'Zero LENGTH attribute';
319                    $this->debug($this->errormsg);
320                    $result = false;
321                }
322            }
323            // Remove length from text and binary
324            if ($this->type == XMLDB_TYPE_TEXT ||
325                $this->type == XMLDB_TYPE_BINARY) {
326                $length = null;
327            }
328            // Finally, set the length
329            $this->length = $length;
330        }
331
332        if (isset($xmlarr['@']['NOTNULL'])) {
333            $notnull = strtolower(trim($xmlarr['@']['NOTNULL']));
334            if ($notnull == 'true') {
335                $this->notnull = true;
336            } else if ($notnull == 'false') {
337                $this->notnull = false;
338            } else {
339                $this->errormsg = 'Incorrect NOTNULL attribute (true/false allowed)';
340                $this->debug($this->errormsg);
341                $result = false;
342            }
343        }
344
345        if (isset($xmlarr['@']['SEQUENCE'])) {
346            $sequence = strtolower(trim($xmlarr['@']['SEQUENCE']));
347            if ($sequence == 'true') {
348                $this->sequence = true;
349            } else if ($sequence == 'false') {
350                $this->sequence = false;
351            } else {
352                $this->errormsg = 'Incorrect SEQUENCE attribute (true/false allowed)';
353                $this->debug($this->errormsg);
354                $result = false;
355            }
356        }
357
358        if (isset($xmlarr['@']['DEFAULT'])) {
359            $this->setDefault(trim($xmlarr['@']['DEFAULT']));
360        }
361
362        $decimals = null;
363        if (isset($xmlarr['@']['DECIMALS'])) {
364            $decimals = trim($xmlarr['@']['DECIMALS']);
365            // Check for integer values
366            if ($this->type == XMLDB_TYPE_NUMBER ||
367                $this->type == XMLDB_TYPE_FLOAT) {
368                if (!(is_numeric($decimals)&&(intval($decimals)==floatval($decimals)))) {
369                    $this->errormsg = 'Incorrect DECIMALS attribute for number field';
370                    $this->debug($this->errormsg);
371                    $result = false;
372                } else if ($this->length <= $decimals){
373                    $this->errormsg = 'Incorrect DECIMALS attribute (bigget than length)';
374                    $this->debug($this->errormsg);
375                    $result = false;
376                }
377            } else {
378                $this->errormsg = 'Incorrect DECIMALS attribute for non-number field';
379                $this->debug($this->errormsg);
380                $result = false;
381            }
382        } else {
383            if ($this->type == XMLDB_TYPE_NUMBER) {
384                $decimals = 0;
385            }
386        }
387        // Finally, set the decimals
388        if ($this->type == XMLDB_TYPE_NUMBER ||
389            $this->type == XMLDB_TYPE_FLOAT) {
390            $this->decimals = $decimals;
391        }
392
393        if (isset($xmlarr['@']['COMMENT'])) {
394            $this->comment = trim($xmlarr['@']['COMMENT']);
395        }
396
397        // Set some attributes
398        if ($result) {
399            $this->loaded = true;
400        }
401        $this->calculateHash();
402        return $result;
403    }
404
405    /**
406     * This function returns the correct XMLDB_TYPE_XXX value for the
407     * string passed as argument
408     * @param string $type
409     * @return int
410     */
411    public function getXMLDBFieldType($type) {
412
413        $result = XMLDB_TYPE_INCORRECT;
414
415        switch (strtolower($type)) {
416            case 'int':
417                $result = XMLDB_TYPE_INTEGER;
418                break;
419            case 'number':
420                $result = XMLDB_TYPE_NUMBER;
421                break;
422            case 'float':
423                $result = XMLDB_TYPE_FLOAT;
424                break;
425            case 'char':
426                $result = XMLDB_TYPE_CHAR;
427                break;
428            case 'text':
429                $result = XMLDB_TYPE_TEXT;
430                break;
431            case 'binary':
432                $result = XMLDB_TYPE_BINARY;
433                break;
434            case 'datetime':
435                $result = XMLDB_TYPE_DATETIME;
436                break;
437        }
438        // Return the normalized XMLDB_TYPE
439        return $result;
440    }
441
442    /**
443     * This function returns the correct name value for the
444     * XMLDB_TYPE_XXX passed as argument
445     * @param int $type
446     * @return string
447     */
448    public function getXMLDBTypeName($type) {
449
450        $result = "";
451
452        switch (strtolower($type)) {
453            case XMLDB_TYPE_INTEGER:
454                $result = 'int';
455                break;
456            case XMLDB_TYPE_NUMBER:
457                $result = 'number';
458                break;
459            case XMLDB_TYPE_FLOAT:
460                $result = 'float';
461                break;
462            case XMLDB_TYPE_CHAR:
463                $result = 'char';
464                break;
465            case XMLDB_TYPE_TEXT:
466                $result = 'text';
467                break;
468            case XMLDB_TYPE_BINARY:
469                $result = 'binary';
470                break;
471            case XMLDB_TYPE_DATETIME:
472                $result = 'datetime';
473                break;
474        }
475        // Return the normalized name
476        return $result;
477    }
478
479    /**
480     * This function calculate and set the hash of one xmldb_field
481     * @param bool $recursive
482     * @return void, modifies $this->hash
483     */
484     public function calculateHash($recursive = false) {
485        if (!$this->loaded) {
486            $this->hash = null;
487        } else {
488            $defaulthash = is_null($this->default) ? '' : sha1($this->default);
489            $key = $this->name . $this->type . $this->length .
490                   $this->notnull . $this->sequence .
491                   $this->decimals . $this->comment . $defaulthash;
492            $this->hash = md5($key);
493        }
494    }
495
496    /**
497     * This function will output the XML text for one field
498     * @return string
499     */
500    public function xmlOutput() {
501        $o = '';
502        $o.= '        <FIELD NAME="' . $this->name . '"';
503        $o.= ' TYPE="' . $this->getXMLDBTypeName($this->type) . '"';
504        if ($this->length) {
505            $o.= ' LENGTH="' . $this->length . '"';
506        }
507        if ($this->notnull) {
508            $notnull = 'true';
509        } else {
510            $notnull = 'false';
511        }
512        $o.= ' NOTNULL="' . $notnull . '"';
513        if (!$this->sequence && $this->default !== null) {
514            $o.= ' DEFAULT="' . $this->default . '"';
515        }
516        if ($this->sequence) {
517            $sequence = 'true';
518        } else {
519            $sequence = 'false';
520        }
521        $o.= ' SEQUENCE="' . $sequence . '"';
522        if ($this->decimals !== null) {
523            $o.= ' DECIMALS="' . $this->decimals . '"';
524        }
525        if ($this->comment) {
526            $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
527        }
528        $o.= '/>' . "\n";
529
530        return $o;
531    }
532
533    /**
534     * This function will set all the attributes of the xmldb_field object
535     * based on information passed in one ADOField
536     * @param string $adofield
537     * @return void, sets $this->type
538     */
539    public function setFromADOField($adofield) {
540
541        // Calculate the XMLDB_TYPE
542        switch (strtolower($adofield->type)) {
543            case 'int':
544            case 'tinyint':
545            case 'smallint':
546            case 'bigint':
547            case 'integer':
548                $this->type = XMLDB_TYPE_INTEGER;
549                break;
550            case 'number':
551            case 'decimal':
552            case 'dec':
553            case 'numeric':
554                $this->type = XMLDB_TYPE_NUMBER;
555                break;
556            case 'float':
557            case 'double':
558                $this->type = XMLDB_TYPE_FLOAT;
559                break;
560            case 'char':
561            case 'varchar':
562            case 'enum':
563                $this->type = XMLDB_TYPE_CHAR;
564                break;
565            case 'text':
566            case 'tinytext':
567            case 'mediumtext':
568            case 'longtext':
569                $this->type = XMLDB_TYPE_TEXT;
570                break;
571            case 'blob':
572            case 'tinyblob':
573            case 'mediumblob':
574            case 'longblob':
575                $this->type = XMLDB_TYPE_BINARY;
576                break;
577            case 'datetime':
578            case 'timestamp':
579                $this->type = XMLDB_TYPE_DATETIME;
580                break;
581            default:
582                $this->type = XMLDB_TYPE_TEXT;
583        }
584        // Calculate the length of the field
585        if ($adofield->max_length > 0 &&
586               ($this->type == XMLDB_TYPE_INTEGER ||
587                $this->type == XMLDB_TYPE_NUMBER  ||
588                $this->type == XMLDB_TYPE_FLOAT   ||
589                $this->type == XMLDB_TYPE_CHAR)) {
590            $this->length = $adofield->max_length;
591        }
592        if ($this->type == XMLDB_TYPE_TEXT) {
593            $this->length = null;
594        }
595        if ($this->type == XMLDB_TYPE_BINARY) {
596            $this->length = null;
597        }
598        // Calculate the decimals of the field
599        if ($adofield->max_length > 0 &&
600            $adofield->scale &&
601               ($this->type == XMLDB_TYPE_NUMBER ||
602                $this->type == XMLDB_TYPE_FLOAT)) {
603            $this->decimals = $adofield->scale;
604        }
605        // Calculate the notnull field
606        if ($adofield->not_null) {
607            $this->notnull = true;
608        }
609        // Calculate the default field
610        if ($adofield->has_default) {
611            $this->default = $adofield->default_value;
612        }
613        // Calculate the sequence field
614        if ($adofield->auto_increment) {
615            $this->sequence = true;
616        }
617        // Some more fields
618        $this->loaded = true;
619        $this->changed = true;
620    }
621
622    /**
623     * Returns the PHP code needed to define one xmldb_field
624     * @param bool $includeprevious
625     * @return string
626     */
627    public function getPHP($includeprevious=true) {
628
629        $result = '';
630
631        // The XMLDBTYPE
632        switch ($this->getType()) {
633            case XMLDB_TYPE_INTEGER:
634                $result .= 'XMLDB_TYPE_INTEGER' . ', ';
635                break;
636            case XMLDB_TYPE_NUMBER:
637                $result .= 'XMLDB_TYPE_NUMBER' . ', ';
638                break;
639            case XMLDB_TYPE_FLOAT:
640                $result .= 'XMLDB_TYPE_FLOAT' . ', ';
641                break;
642            case XMLDB_TYPE_CHAR:
643                $result .= 'XMLDB_TYPE_CHAR' . ', ';
644                break;
645            case XMLDB_TYPE_TEXT:
646                $result .= 'XMLDB_TYPE_TEXT' . ', ';
647                break;
648            case XMLDB_TYPE_BINARY:
649                $result .= 'XMLDB_TYPE_BINARY' . ', ';
650                break;
651            case XMLDB_TYPE_DATETIME:
652                $result .= 'XMLDB_TYPE_DATETIME' . ', ';
653                break;
654            case XMLDB_TYPE_TIMESTAMP:
655                $result .= 'XMLDB_TYPE_TIMESTAMP' . ', ';
656                break;
657        }
658        // The length
659        $length = $this->getLength();
660        $decimals = $this->getDecimals();
661        if (!empty($length)) {
662            $result .= "'" . $length;
663            if (!empty($decimals)) {
664                $result .= ', ' . $decimals;
665            }
666            $result .= "', ";
667        } else {
668            $result .= 'null, ';
669        }
670        // Unsigned is not used any more since Moodle 2.3
671        $result .= 'null, ';
672        // Not Null
673        $notnull = $this->getNotnull();
674        if (!empty($notnull)) {
675            $result .= 'XMLDB_NOTNULL' . ', ';
676        } else {
677            $result .= 'null, ';
678        }
679        // Sequence
680        $sequence = $this->getSequence();
681        if (!empty($sequence)) {
682            $result .= 'XMLDB_SEQUENCE' . ', ';
683        } else {
684            $result .= 'null, ';
685        }
686        // Default
687        $default =  $this->getDefault();
688        if ($default !== null && !$this->getSequence()) {
689            $result .= "'" . $default . "'";
690        } else {
691            $result .= 'null';
692        }
693        // Previous (decided by parameter)
694        if ($includeprevious) {
695            $previous = $this->getPrevious();
696            if (!empty($previous)) {
697                $result .= ", '" . $previous . "'";
698            } else {
699                $result .= ', null';
700            }
701        }
702        // Return result
703        return $result;
704    }
705
706    /**
707     * Shows info in a readable format
708     * @return string
709     */
710    public function readableInfo() {
711        $o = '';
712        // type
713        $o .= $this->getXMLDBTypeName($this->type);
714        // length
715        if ($this->type == XMLDB_TYPE_INTEGER ||
716            $this->type == XMLDB_TYPE_NUMBER  ||
717            $this->type == XMLDB_TYPE_FLOAT   ||
718            $this->type == XMLDB_TYPE_CHAR) {
719            if ($this->length) {
720                $o .= ' (' . $this->length;
721                if ($this->type == XMLDB_TYPE_NUMBER  ||
722                    $this->type == XMLDB_TYPE_FLOAT) {
723                    if ($this->decimals !== null) {
724                        $o .= ', ' . $this->decimals;
725                    }
726                }
727                $o .= ')';
728            }
729        }
730        // not null
731        if ($this->notnull) {
732            $o .= ' not null';
733        }
734        // default
735        if ($this->default !== null) {
736            $o .= ' default ';
737            if ($this->type == XMLDB_TYPE_CHAR ||
738                $this->type == XMLDB_TYPE_TEXT) {
739                    $o .= "'" . $this->default . "'";
740            } else {
741                $o .= $this->default;
742            }
743        }
744        // sequence
745        if ($this->sequence) {
746            $o .= ' auto-numbered';
747        }
748
749        return $o;
750    }
751
752    /**
753     * Validates the field restrictions.
754     *
755     * The error message should not be localised because it is intended for developers,
756     * end users and admins should never see these problems!
757     *
758     * @param xmldb_table $xmldb_table optional when object is table
759     * @return string null if ok, error message if problem found
760     */
761    public function validateDefinition(xmldb_table $xmldb_table=null) {
762        if (!$xmldb_table) {
763            return 'Invalid xmldb_field->validateDefinition() call, $xmldb_table is required.';
764        }
765
766        $name = $this->getName();
767        if (strlen($name) > self::NAME_MAX_LENGTH) {
768            return 'Invalid field name in table {'.$xmldb_table->getName().'}: field "'.$this->getName().'" name is too long.'
769                .' Limit is '.self::NAME_MAX_LENGTH.' chars.';
770        }
771        if (!preg_match('/^[a-z][a-z0-9_]*$/', $name)) {
772            return 'Invalid field name in table {'.$xmldb_table->getName().'}: field "'.$this->getName().'" name includes invalid characters.';
773        }
774
775        switch ($this->getType()) {
776            case XMLDB_TYPE_INTEGER:
777                $length = $this->getLength();
778                if (!is_number($length) or $length <= 0 or $length > self::INTEGER_MAX_LENGTH) {
779                    return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_INTEGER field "'.$this->getName().'" has invalid length';
780                }
781                $default = $this->getDefault();
782                if (!empty($default) and !is_number($default)) {
783                    return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_INTEGER field "'.$this->getName().'" has invalid default';
784                }
785                break;
786
787            case XMLDB_TYPE_NUMBER:
788                $maxlength = self::NUMBER_MAX_LENGTH;
789                $length = $this->getLength();
790                if (!is_number($length) or $length <= 0 or $length > $maxlength) {
791                    return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_NUMBER field "'.$this->getName().'" has invalid length';
792                }
793                $decimals = $this->getDecimals();
794                $decimals = empty($decimals) ? 0 : $decimals; // fix missing decimals
795                if (!is_number($decimals) or $decimals < 0 or $decimals > $length) {
796                    return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_NUMBER field "'.$this->getName().'" has invalid decimals';
797                }
798                if ($length - $decimals > self::INTEGER_MAX_LENGTH) {
799                    return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_NUMBER field "'.
800                        $this->getName().'" has too big whole number part';
801                }
802                $default = $this->getDefault();
803                if (!empty($default) and !is_numeric($default)) {
804                    return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_NUMBER field "'.$this->getName().'" has invalid default';
805                }
806                break;
807
808            case XMLDB_TYPE_FLOAT:
809                $length = $this->getLength();
810                $length = empty($length) ? 6 : $length; // weird, it might be better to require something here...
811                if (!is_number($length) or $length <= 0 or $length > self::FLOAT_MAX_LENGTH) {
812                    return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_FLOAT field "'.$this->getName().'" has invalid length';
813                }
814                $decimals = $this->getDecimals();
815                $decimals = empty($decimals) ? 0 : $decimals; // fix missing decimals
816                if (!is_number($decimals) or $decimals < 0 or $decimals > $length) {
817                    return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_FLOAT field "'.$this->getName().'" has invalid decimals';
818                }
819                $default = $this->getDefault();
820                if (!empty($default) and !is_numeric($default)) {
821                    return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_FLOAT field "'.$this->getName().'" has invalid default';
822                }
823                break;
824
825            case XMLDB_TYPE_CHAR:
826                if ($this->getLength() > self::CHAR_MAX_LENGTH) {
827                    return 'Invalid field definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_CHAR field "'.$this->getName().'" is too long.'
828                           .' Limit is '.self::CHAR_MAX_LENGTH.' chars.';
829                }
830                break;
831
832            case XMLDB_TYPE_TEXT:
833                break;
834
835            case XMLDB_TYPE_BINARY:
836                break;
837
838            case XMLDB_TYPE_DATETIME:
839                break;
840
841            case XMLDB_TYPE_TIMESTAMP:
842                break;
843        }
844
845        return null;
846    }
847}
848