3 ///////////////////////////////////////////////////////////////////////////
5 // NOTICE OF COPYRIGHT //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
10 // Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
11 // (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
13 // This program is free software; you can redistribute it and/or modify //
14 // it under the terms of the GNU General Public License as published by //
15 // the Free Software Foundation; either version 2 of the License, or //
16 // (at your option) any later version. //
18 // This program is distributed in the hope that it will be useful, //
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
21 // GNU General Public License for more details: //
23 // http://www.gnu.org/copyleft/gpl.html //
25 ///////////////////////////////////////////////////////////////////////////
27 /// This class represent one XMLDB Field
29 class xmldb_field
extends xmldb_object
{
41 * - Oracle: VARCHAR2 has a limit of 4000 bytes
42 * - SQL Server: NVARCHAR has a limit of 40000 chars
43 * - MySQL: VARCHAR 65,535 chars
44 * - PostgreSQL: no limit
46 * @const maximum length of text field
48 const CHAR_MAX_LENGTH
= 1333;
51 * Creates one new xmldb_field
53 function __construct($name, $type=null, $precision=null, $unsigned=null, $notnull=null, $sequence=null, $default=null, $previous=null) {
56 $this->unsigned
= true;
57 $this->notnull
= false;
58 $this->default = NULL;
59 $this->sequence
= false;
60 $this->decimals
= NULL;
61 parent
::__construct($name);
62 $this->set_attributes($type, $precision, $unsigned, $notnull, $sequence, $default, $previous);
65 /// TODO: Delete for 2.1 (deprecated in 2.0).
66 /// Deprecated API starts here
68 function setAttributes($type, $precision=null, $unsigned=null, $notnull=null, $sequence=null, $enum=null, $enumvalues=null, $default=null, $previous=null) {
70 debugging('XMLDBField->setAttributes() has been deprecated in Moodle 2.0. Will be out in Moodle 2.1. Please use xmldb_field->set_attributes() instead.', DEBUG_DEVELOPER
);
72 debugging('Also, ENUMs support has been dropped in Moodle 2.0. Your fields specs are incorrect because you are trying to introduce one new ENUM. Created DB estructures will ignore that.');
75 return $this->set_attributes($type, $precision, $unsigned, $notnull, $sequence, $default, $previous);
77 /// Deprecated API ends here
80 * Set all the attributes of one xmldb_field
82 * @param string type XMLDB_TYPE_INTEGER, XMLDB_TYPE_NUMBER, XMLDB_TYPE_CHAR, XMLDB_TYPE_TEXT, XMLDB_TYPE_BINARY
83 * @param string precision length for integers and chars, two-comma separated numbers for numbers and 'small', 'medium', 'big' for texts and binaries
84 * @param string unsigned XMLDB_UNSIGNED or null (or false)
85 * @param string notnull XMLDB_NOTNULL or null (or false)
86 * @param string sequence XMLDB_SEQUENCE or null (or false)
87 * @param string default meaningful default o null (or false)
89 function set_attributes($type, $precision=null, $unsigned=null, $notnull=null, $sequence=null, $default=null, $previous=null) {
91 /// Try to split the precision into length and decimals and apply
92 /// each one as needed
93 $precisionarr = explode(',', $precision);
94 if (isset($precisionarr[0])) {
95 $this->length
= trim($precisionarr[0]);
97 if (isset($precisionarr[1])) {
98 $this->decimals
= trim($precisionarr[1]);
100 $this->precision
= $type;
101 $this->unsigned
= !empty($unsigned) ?
true : false;
102 $this->notnull
= !empty($notnull) ?
true : false;
103 $this->sequence
= !empty($sequence) ?
true : false;
104 $this->setDefault($default);
106 $this->previous
= $previous;
119 function getLength() {
120 return $this->length
;
126 function getDecimals() {
127 return $this->decimals
;
133 function getNotNull() {
134 return $this->notnull
;
140 function getUnsigned() {
141 return $this->unsigned
;
147 function getSequence() {
148 return $this->sequence
;
154 function getDefault() {
155 return $this->default;
161 function setType($type) {
166 * Set the field length
168 function setLength($length) {
169 $this->length
= $length;
173 * Set the field decimals
175 function setDecimals($decimals) {
176 $this->decimals
= $decimals;
180 * Set the field unsigned
182 function setUnsigned($unsigned=true) {
183 $this->unsigned
= $unsigned;
187 * Set the field notnull
189 function setNotNull($notnull=true) {
190 $this->notnull
= $notnull;
194 * Set the field sequence
196 function setSequence($sequence=true) {
197 $this->sequence
= $sequence;
201 * Set the field default
203 function setDefault($default) {
204 /// Check, warn and auto-fix '' (empty) defaults for CHAR NOT NULL columns, changing them
205 /// to NULL so XMLDB will apply the proper default
206 if ($this->type
== XMLDB_TYPE_CHAR
&& $this->notnull
&& $default === '') {
207 $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.";
208 $this->debug($this->errormsg
);
211 /// Check, warn and autofix TEXT|BINARY columns having a default clause (only null is allowed)
212 if (($this->type
== XMLDB_TYPE_TEXT ||
$this->type
== XMLDB_TYPE_BINARY
) && $default !== null) {
213 $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.";
214 $this->debug($this->errormsg
);
217 $this->default = $default;
221 * Load data from XML to the table
223 function arr2xmldb_field($xmlarr) {
228 /// traverse_xmlize($xmlarr); //Debug
229 /// print_object ($GLOBALS['traverse_array']); //Debug
230 /// $GLOBALS['traverse_array']=""; //Debug
232 /// Process table attributes (name, type, length, unsigned,
233 /// notnull, sequence, decimals, comment, previous, next)
234 if (isset($xmlarr['@']['NAME'])) {
235 $this->name
= trim($xmlarr['@']['NAME']);
237 $this->errormsg
= 'Missing NAME attribute';
238 $this->debug($this->errormsg
);
242 if (isset($xmlarr['@']['TYPE'])) {
243 /// Check for valid type
244 $type = $this->getXMLDBFieldType(trim($xmlarr['@']['TYPE']));
248 $this->errormsg
= 'Invalid TYPE attribute';
249 $this->debug($this->errormsg
);
253 $this->errormsg
= 'Missing TYPE attribute';
254 $this->debug($this->errormsg
);
258 if (isset($xmlarr['@']['LENGTH'])) {
259 $length = trim($xmlarr['@']['LENGTH']);
260 /// Check for integer values
261 if ($this->type
== XMLDB_TYPE_INTEGER ||
262 $this->type
== XMLDB_TYPE_NUMBER ||
263 $this->type
== XMLDB_TYPE_CHAR
) {
264 if (!(is_numeric($length)&&(intval($length)==floatval($length)))) {
265 $this->errormsg
= 'Incorrect LENGTH attribute for int, number or char fields';
266 $this->debug($this->errormsg
);
268 } else if (!$length) {
269 $this->errormsg
= 'Zero LENGTH attribute';
270 $this->debug($this->errormsg
);
274 /// Check for big, medium, small to be applied to text and binary
275 if ($this->type
== XMLDB_TYPE_TEXT ||
276 $this->type
== XMLDB_TYPE_BINARY
) {
280 if ($length != 'big' &&
281 $length != 'medium' &&
282 $length != 'small') {
283 $this->errormsg
= 'Incorrect LENGTH attribute for text and binary fields (only big, medium and small allowed)';
284 $this->debug($this->errormsg
);
288 /// Finally, set the length
289 $this->length
= $length;
292 if (isset($xmlarr['@']['UNSIGNED'])) {
293 $unsigned = strtolower(trim($xmlarr['@']['UNSIGNED']));
294 if ($unsigned == 'true') {
295 $this->unsigned
= true;
296 } else if ($unsigned == 'false') {
297 $this->unsigned
= false;
299 $this->errormsg
= 'Incorrect UNSIGNED attribute (true/false allowed)';
300 $this->debug($this->errormsg
);
305 if (isset($xmlarr['@']['NOTNULL'])) {
306 $notnull = strtolower(trim($xmlarr['@']['NOTNULL']));
307 if ($notnull == 'true') {
308 $this->notnull
= true;
309 } else if ($notnull == 'false') {
310 $this->notnull
= false;
312 $this->errormsg
= 'Incorrect NOTNULL attribute (true/false allowed)';
313 $this->debug($this->errormsg
);
318 if (isset($xmlarr['@']['SEQUENCE'])) {
319 $sequence = strtolower(trim($xmlarr['@']['SEQUENCE']));
320 if ($sequence == 'true') {
321 $this->sequence
= true;
322 } else if ($sequence == 'false') {
323 $this->sequence
= false;
325 $this->errormsg
= 'Incorrect SEQUENCE attribute (true/false allowed)';
326 $this->debug($this->errormsg
);
331 if (isset($xmlarr['@']['DEFAULT'])) {
332 $this->setDefault(trim($xmlarr['@']['DEFAULT']));
336 if (isset($xmlarr['@']['DECIMALS'])) {
337 $decimals = trim($xmlarr['@']['DECIMALS']);
338 /// Check for integer values
339 if ($this->type
== XMLDB_TYPE_NUMBER ||
340 $this->type
== XMLDB_TYPE_FLOAT
) {
341 if (!(is_numeric($decimals)&&(intval($decimals)==floatval($decimals)))) {
342 $this->errormsg
= 'Incorrect DECIMALS attribute for number field';
343 $this->debug($this->errormsg
);
345 } else if ($this->length
<= $decimals){
346 $this->errormsg
= 'Incorrect DECIMALS attribute (bigget than length)';
347 $this->debug($this->errormsg
);
351 $this->errormsg
= 'Incorrect DECIMALS attribute for non-number field';
352 $this->debug($this->errormsg
);
356 if ($this->type
== XMLDB_TYPE_NUMBER
) {
360 // Finally, set the decimals
361 if ($this->type
== XMLDB_TYPE_NUMBER ||
362 $this->type
== XMLDB_TYPE_FLOAT
) {
363 $this->decimals
= $decimals;
366 if (isset($xmlarr['@']['COMMENT'])) {
367 $this->comment
= trim($xmlarr['@']['COMMENT']);
370 if (isset($xmlarr['@']['PREVIOUS'])) {
371 $this->previous
= trim($xmlarr['@']['PREVIOUS']);
374 if (isset($xmlarr['@']['NEXT'])) {
375 $this->next
= trim($xmlarr['@']['NEXT']);
378 /// TODO: Drop this check in Moodle 2.1
379 /// Detect if there is old enum information in the XML file
380 if (isset($xmlarr['@']['ENUM']) && isset($xmlarr['@']['ENUMVALUES'])) {
381 $this->hasenums
= true;
382 if ($xmlarr['@']['ENUM'] == 'true') {
383 $this->hasenumsenabled
= true;
387 /// Set some attributes
389 $this->loaded
= true;
391 $this->calculateHash();
396 * This function returns the correct XMLDB_TYPE_XXX value for the
397 * string passed as argument
399 function getXMLDBFieldType($type) {
401 $result = XMLDB_TYPE_INCORRECT
;
403 switch (strtolower($type)) {
405 $result = XMLDB_TYPE_INTEGER
;
408 $result = XMLDB_TYPE_NUMBER
;
411 $result = XMLDB_TYPE_FLOAT
;
414 $result = XMLDB_TYPE_CHAR
;
417 $result = XMLDB_TYPE_TEXT
;
420 $result = XMLDB_TYPE_BINARY
;
423 $result = XMLDB_TYPE_DATETIME
;
426 /// Return the normalized XMLDB_TYPE
431 * This function returns the correct name value for the
432 * XMLDB_TYPE_XXX passed as argument
434 function getXMLDBTypeName($type) {
438 switch (strtolower($type)) {
439 case XMLDB_TYPE_INTEGER
:
442 case XMLDB_TYPE_NUMBER
:
445 case XMLDB_TYPE_FLOAT
:
448 case XMLDB_TYPE_CHAR
:
451 case XMLDB_TYPE_TEXT
:
454 case XMLDB_TYPE_BINARY
:
457 case XMLDB_TYPE_DATETIME
:
458 $result = 'datetime';
461 /// Return the normalized name
466 * This function calculate and set the hash of one xmldb_field
468 function calculateHash($recursive = false) {
469 if (!$this->loaded
) {
472 $key = $this->name
. $this->type
. $this->length
.
473 $this->unsigned
. $this->notnull
. $this->sequence
.
474 $this->decimals
. $this->comment
;
475 $this->hash
= md5($key);
480 *This function will output the XML text for one field
482 function xmlOutput() {
484 $o.= ' <FIELD NAME="' . $this->name
. '"';
485 $o.= ' TYPE="' . $this->getXMLDBTypeName($this->type
) . '"';
487 $o.= ' LENGTH="' . $this->length
. '"';
489 if ($this->notnull
) {
494 $o.= ' NOTNULL="' . $notnull . '"';
495 if ($this->type
== XMLDB_TYPE_INTEGER ||
496 $this->type
== XMLDB_TYPE_NUMBER ||
497 $this->type
== XMLDB_TYPE_FLOAT
) {
498 if ($this->unsigned
) {
503 $o.= ' UNSIGNED="' . $unsigned . '"';
505 if (!$this->sequence
&& $this->default !== NULL) {
506 $o.= ' DEFAULT="' . $this->default . '"';
508 if ($this->sequence
) {
513 $o.= ' SEQUENCE="' . $sequence . '"';
514 if ($this->decimals
!== NULL) {
515 $o.= ' DECIMALS="' . $this->decimals
. '"';
517 if ($this->comment
) {
518 $o.= ' COMMENT="' . htmlspecialchars($this->comment
) . '"';
520 if ($this->previous
) {
521 $o.= ' PREVIOUS="' . $this->previous
. '"';
524 $o.= ' NEXT="' . $this->next
. '"';
532 * This function will set all the attributes of the xmldb_field object
533 * based on information passed in one ADOField
535 function setFromADOField($adofield) {
537 /// Calculate the XMLDB_TYPE
538 switch (strtolower($adofield->type
)) {
544 $this->type
= XMLDB_TYPE_INTEGER
;
550 $this->type
= XMLDB_TYPE_NUMBER
;
554 $this->type
= XMLDB_TYPE_FLOAT
;
559 $this->type
= XMLDB_TYPE_CHAR
;
565 $this->type
= XMLDB_TYPE_TEXT
;
571 $this->type
= XMLDB_TYPE_BINARY
;
575 $this->type
= XMLDB_TYPE_DATETIME
;
578 $this->type
= XMLDB_TYPE_TEXT
;
580 /// Calculate the length of the field
581 if ($adofield->max_length
> 0 &&
582 ($this->type
== XMLDB_TYPE_INTEGER ||
583 $this->type
== XMLDB_TYPE_NUMBER ||
584 $this->type
== XMLDB_TYPE_FLOAT ||
585 $this->type
== XMLDB_TYPE_CHAR
)) {
586 $this->length
= $adofield->max_length
;
588 if ($this->type
== XMLDB_TYPE_TEXT
) {
589 switch (strtolower($adofield->type
)) {
592 $this->length
= 'small';
595 $this->length
= 'medium';
598 $this->length
= 'big';
601 $this->length
= 'small';
604 if ($this->type
== XMLDB_TYPE_BINARY
) {
605 switch (strtolower($adofield->type
)) {
608 $this->length
= 'small';
611 $this->length
= 'medium';
614 $this->length
= 'big';
617 $this->length
= 'small';
620 /// Calculate the decimals of the field
621 if ($adofield->max_length
> 0 &&
623 ($this->type
== XMLDB_TYPE_NUMBER ||
624 $this->type
== XMLDB_TYPE_FLOAT
)) {
625 $this->decimals
= $adofield->scale
;
627 /// Calculate the unsigned field
628 if ($adofield->unsigned
&&
629 ($this->type
== XMLDB_TYPE_INTEGER ||
630 $this->type
== XMLDB_TYPE_NUMBER ||
631 $this->type
== XMLDB_TYPE_FLOAT
)) {
632 $this->unsigned
= true;
634 /// Calculate the notnull field
635 if ($adofield->not_null
) {
636 $this->notnull
= true;
638 /// Calculate the default field
639 if ($adofield->has_default
) {
640 $this->default = $adofield->default_value
;
642 /// Calculate the sequence field
643 if ($adofield->auto_increment
) {
644 $this->sequence
= true;
645 /// Sequence fields are always unsigned
646 $this->unsigned
= true;
649 $this->loaded
= true;
650 $this->changed
= true;
654 * Returns the PHP code needed to define one xmldb_field
656 function getPHP($includeprevious=true) {
661 switch ($this->getType()) {
662 case XMLDB_TYPE_INTEGER
:
663 $result .= 'XMLDB_TYPE_INTEGER' . ', ';
665 case XMLDB_TYPE_NUMBER
:
666 $result .= 'XMLDB_TYPE_NUMBER' . ', ';
668 case XMLDB_TYPE_FLOAT
:
669 $result .= 'XMLDB_TYPE_FLOAT' . ', ';
671 case XMLDB_TYPE_CHAR
:
672 $result .= 'XMLDB_TYPE_CHAR' . ', ';
674 case XMLDB_TYPE_TEXT
:
675 $result .= 'XMLDB_TYPE_TEXT' . ', ';
677 case XMLDB_TYPE_BINARY
:
678 $result .= 'XMLDB_TYPE_BINARY' . ', ';
680 case XMLDB_TYPE_DATETIME
:
681 $result .= 'XMLDB_TYPE_DATETIME' . ', ';
683 case XMLDB_TYPE_TIMESTAMP
:
684 $result .= 'XMLDB_TYPE_TIMESTAMP' . ', ';
688 $length = $this->getLength();
689 $decimals = $this->getDecimals();
690 if (!empty($length)) {
691 $result .= "'" . $length;
692 if (!empty($decimals)) {
693 $result .= ', ' . $decimals;
699 /// Unsigned (only applicable to numbers)
700 $unsigned = $this->getUnsigned();
701 if (!empty($unsigned) &&
702 ($this->getType() == XMLDB_TYPE_INTEGER ||
$this->getType() == XMLDB_TYPE_NUMBER ||
$this->getType() == XMLDB_TYPE_FLOAT
)) {
703 $result .= 'XMLDB_UNSIGNED' . ', ';
708 $notnull = $this->getNotnull();
709 if (!empty($notnull)) {
710 $result .= 'XMLDB_NOTNULL' . ', ';
715 $sequence = $this->getSequence();
716 if (!empty($sequence)) {
717 $result .= 'XMLDB_SEQUENCE' . ', ';
722 $default = $this->getDefault();
723 if ($default !== null && !$this->getSequence()) {
724 $result .= "'" . $default . "'";
728 /// Previous (decided by parameter)
729 if ($includeprevious) {
730 $previous = $this->getPrevious();
731 if (!empty($previous)) {
732 $result .= ", '" . $previous . "'";
742 * Shows info in a readable format
744 function readableInfo() {
747 $o .= $this->getXMLDBTypeName($this->type
);
749 if ($this->type
== XMLDB_TYPE_INTEGER ||
750 $this->type
== XMLDB_TYPE_NUMBER ||
751 $this->type
== XMLDB_TYPE_FLOAT ||
752 $this->type
== XMLDB_TYPE_CHAR
) {
754 $o .= ' (' . $this->length
;
755 if ($this->type
== XMLDB_TYPE_NUMBER ||
756 $this->type
== XMLDB_TYPE_FLOAT
) {
757 if ($this->decimals
!== NULL) {
758 $o .= ', ' . $this->decimals
;
764 if ($this->type
== XMLDB_TYPE_TEXT ||
765 $this->type
== XMLDB_TYPE_BINARY
) {
766 $o .= ' (' . $this->length
. ')';
769 if ($this->type
== XMLDB_TYPE_INTEGER ||
770 $this->type
== XMLDB_TYPE_NUMBER ||
771 $this->type
== XMLDB_TYPE_FLOAT
) {
772 if ($this->unsigned
) {
779 if ($this->notnull
) {
783 if ($this->default !== NULL) {
785 if ($this->type
== XMLDB_TYPE_CHAR ||
786 $this->type
== XMLDB_TYPE_TEXT
) {
787 $o .= "'" . $this->default . "'";
789 $o .= $this->default;
793 if ($this->sequence
) {
794 $o .= ' auto-numbered';
801 * Validates the field restrictions.
803 * The error message should not be localised because it is intended for developers,
804 * end users and admins should never see these problems!
806 * @param xmldb_table $xmldb_table optional when object is table
807 * @return string null if ok, error message if problem found
809 function validateDefinition(xmldb_table
$xmldb_table=null) {
811 return 'Invalid xmldb_field->validateDefinition() call, $xmldb_table si required.';
814 switch ($this->getType()) {
815 case XMLDB_TYPE_INTEGER
:
818 case XMLDB_TYPE_NUMBER
:
821 case XMLDB_TYPE_FLOAT
:
824 case XMLDB_TYPE_CHAR
:
825 if ($this->getLength() > self
::CHAR_MAX_LENGTH
) {
826 return 'Invalid field definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_CHAR field "'.$this->getName().'" is too long.'
827 .' Limit is '.self
::CHAR_MAX_LENGTH
.' chars.';
831 case XMLDB_TYPE_TEXT
:
834 case XMLDB_TYPE_BINARY
:
837 case XMLDB_TYPE_DATETIME
:
840 case XMLDB_TYPE_TIMESTAMP
:
848 /// TODO: Delete for 2.1 (deprecated in 2.0).
849 /// Deprecated API starts here
850 class XMLDBField
extends xmldb_field
{
852 function __construct($name) {
853 parent
::__construct($name);
857 /// Deprecated API ends here