Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   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  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  
  29  class 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  }