Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 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  
 120          // LOBs (BINARY OR TEXT) don't support any precision (neither length or decimals).
 121          if ($type == XMLDB_TYPE_BINARY || $this->type == XMLDB_TYPE_TEXT) {
 122              $this->length = null;
 123              $this->decimals = null;
 124  
 125          } else if (!is_null($precision)) {
 126              // Try to split the not null precision into length and decimals and apply each one as needed.
 127              $precisionarr = explode(',', $precision);
 128              if (isset($precisionarr[0])) {
 129                  $this->length = trim($precisionarr[0]);
 130              }
 131              if (isset($precisionarr[1])) {
 132                  $this->decimals = trim($precisionarr[1]);
 133              }
 134          }
 135  
 136          $this->notnull = !empty($notnull) ? true : false;
 137          $this->sequence = !empty($sequence) ? true : false;
 138          $this->setDefault($default);
 139  
 140          $this->previous = $previous;
 141      }
 142  
 143      /**
 144       * Get the type
 145       * @return int
 146       */
 147      public function getType() {
 148          return $this->type;
 149      }
 150  
 151      /**
 152       * Get the length
 153       * @return int
 154       */
 155      public function getLength() {
 156          return $this->length;
 157      }
 158  
 159      /**
 160       * Get the decimals
 161       * @return string
 162       */
 163      public function getDecimals() {
 164          return $this->decimals;
 165      }
 166  
 167      /**
 168       * Get the notnull
 169       * @return bool
 170       */
 171      public function getNotNull() {
 172          return $this->notnull;
 173      }
 174  
 175      /**
 176       * Get the unsigned
 177       * @deprecated since moodle 2.3
 178       * @return bool
 179       */
 180      public function getUnsigned() {
 181          return false;
 182      }
 183  
 184      /**
 185       * Get the sequence
 186       * @return bool
 187       */
 188      public function getSequence() {
 189          return $this->sequence;
 190      }
 191  
 192      /**
 193       * Get the default
 194       * @return mixed
 195       */
 196      public function getDefault() {
 197          return $this->default;
 198      }
 199  
 200      /**
 201       * Set the field type
 202       * @param int $type
 203       */
 204      public function setType($type) {
 205          $this->type = $type;
 206      }
 207  
 208      /**
 209       * Set the field length
 210       * @param int $length
 211       */
 212      public function setLength($length) {
 213          $this->length = $length;
 214      }
 215  
 216      /**
 217       * Set the field decimals
 218       * @param string
 219       */
 220      public function setDecimals($decimals) {
 221          $this->decimals = $decimals;
 222      }
 223  
 224      /**
 225       * Set the field unsigned
 226       * @deprecated since moodle 2.3
 227       * @param bool $unsigned
 228       */
 229      public function setUnsigned($unsigned=true) {
 230      }
 231  
 232      /**
 233       * Set the field notnull
 234       * @param bool $notnull
 235       */
 236      public function setNotNull($notnull=true) {
 237          $this->notnull = $notnull;
 238      }
 239  
 240      /**
 241       * Set the field sequence
 242       * @param bool $sequence
 243       */
 244      public function setSequence($sequence=true) {
 245          $this->sequence = $sequence;
 246      }
 247  
 248      /**
 249       * Set the field default
 250       * @param mixed $default
 251       */
 252      public function setDefault($default) {
 253          // Check, warn and auto-fix '' (empty) defaults for CHAR NOT NULL columns, changing them
 254          // to NULL so XMLDB will apply the proper default
 255          if ($this->type == XMLDB_TYPE_CHAR && $this->notnull && $default === '') {
 256              $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.";
 257              $this->debug($this->errormsg);
 258              $default = null;
 259          }
 260          // Check, warn and autofix TEXT|BINARY columns having a default clause (only null is allowed)
 261          if (($this->type == XMLDB_TYPE_TEXT || $this->type == XMLDB_TYPE_BINARY) && $default !== null) {
 262              $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.";
 263              $this->debug($this->errormsg);
 264              $default = null;
 265          }
 266          $this->default = $default;
 267      }
 268  
 269      /**
 270       * Load data from XML to the table
 271       * @param array $xmlarr
 272       * @return mixed
 273       */
 274      public function arr2xmldb_field($xmlarr) {
 275  
 276          $result = true;
 277  
 278          // Debug the table
 279          // traverse_xmlize($xmlarr);                   //Debug
 280          // print_object ($GLOBALS['traverse_array']);  //Debug
 281          // $GLOBALS['traverse_array']="";              //Debug
 282  
 283          // Process table attributes (name, type, length
 284          // notnull, sequence, decimals, comment, previous, next)
 285          if (isset($xmlarr['@']['NAME'])) {
 286              $this->name = trim($xmlarr['@']['NAME']);
 287          } else {
 288              $this->errormsg = 'Missing NAME attribute';
 289              $this->debug($this->errormsg);
 290              $result = false;
 291          }
 292  
 293          if (isset($xmlarr['@']['TYPE'])) {
 294              // Check for valid type
 295              $type = $this->getXMLDBFieldType(trim($xmlarr['@']['TYPE']));
 296              if ($type) {
 297                  $this->type = $type;
 298              } else {
 299                  $this->errormsg = 'Invalid TYPE attribute';
 300                  $this->debug($this->errormsg);
 301                  $result = false;
 302              }
 303          } else {
 304              $this->errormsg = 'Missing TYPE attribute';
 305              $this->debug($this->errormsg);
 306              $result = false;
 307          }
 308  
 309          if (isset($xmlarr['@']['LENGTH'])) {
 310              $length = trim($xmlarr['@']['LENGTH']);
 311              // Check for integer values
 312              if ($this->type == XMLDB_TYPE_INTEGER ||
 313                  $this->type == XMLDB_TYPE_NUMBER ||
 314                  $this->type == XMLDB_TYPE_CHAR) {
 315                  if (!(is_numeric($length)&&(intval($length)==floatval($length)))) {
 316                      $this->errormsg = 'Incorrect LENGTH attribute for int, number or char fields';
 317                      $this->debug($this->errormsg);
 318                      $result = false;
 319                  } else if (!$length) {
 320                      $this->errormsg = 'Zero LENGTH attribute';
 321                      $this->debug($this->errormsg);
 322                      $result = false;
 323                  }
 324              }
 325              // Remove length from text and binary
 326              if ($this->type == XMLDB_TYPE_TEXT ||
 327                  $this->type == XMLDB_TYPE_BINARY) {
 328                  $length = null;
 329              }
 330              // Finally, set the length
 331              $this->length = $length;
 332          }
 333  
 334          if (isset($xmlarr['@']['NOTNULL'])) {
 335              $notnull = strtolower(trim($xmlarr['@']['NOTNULL']));
 336              if ($notnull == 'true') {
 337                  $this->notnull = true;
 338              } else if ($notnull == 'false') {
 339                  $this->notnull = false;
 340              } else {
 341                  $this->errormsg = 'Incorrect NOTNULL attribute (true/false allowed)';
 342                  $this->debug($this->errormsg);
 343                  $result = false;
 344              }
 345          }
 346  
 347          if (isset($xmlarr['@']['SEQUENCE'])) {
 348              $sequence = strtolower(trim($xmlarr['@']['SEQUENCE']));
 349              if ($sequence == 'true') {
 350                  $this->sequence = true;
 351              } else if ($sequence == 'false') {
 352                  $this->sequence = false;
 353              } else {
 354                  $this->errormsg = 'Incorrect SEQUENCE attribute (true/false allowed)';
 355                  $this->debug($this->errormsg);
 356                  $result = false;
 357              }
 358          }
 359  
 360          if (isset($xmlarr['@']['DEFAULT'])) {
 361              $this->setDefault(trim($xmlarr['@']['DEFAULT']));
 362          }
 363  
 364          $decimals = null;
 365          if (isset($xmlarr['@']['DECIMALS'])) {
 366              $decimals = trim($xmlarr['@']['DECIMALS']);
 367              // Check for integer values
 368              if ($this->type == XMLDB_TYPE_NUMBER ||
 369                  $this->type == XMLDB_TYPE_FLOAT) {
 370                  if (!(is_numeric($decimals)&&(intval($decimals)==floatval($decimals)))) {
 371                      $this->errormsg = 'Incorrect DECIMALS attribute for number field';
 372                      $this->debug($this->errormsg);
 373                      $result = false;
 374                  } else if ($this->length <= $decimals){
 375                      $this->errormsg = 'Incorrect DECIMALS attribute (bigget than length)';
 376                      $this->debug($this->errormsg);
 377                      $result = false;
 378                  }
 379              } else {
 380                  $this->errormsg = 'Incorrect DECIMALS attribute for non-number field';
 381                  $this->debug($this->errormsg);
 382                  $result = false;
 383              }
 384          } else {
 385              if ($this->type == XMLDB_TYPE_NUMBER) {
 386                  $decimals = 0;
 387              }
 388          }
 389          // Finally, set the decimals
 390          if ($this->type == XMLDB_TYPE_NUMBER ||
 391              $this->type == XMLDB_TYPE_FLOAT) {
 392              $this->decimals = $decimals;
 393          }
 394  
 395          if (isset($xmlarr['@']['COMMENT'])) {
 396              $this->comment = trim($xmlarr['@']['COMMENT']);
 397          }
 398  
 399          // Set some attributes
 400          if ($result) {
 401              $this->loaded = true;
 402          }
 403          $this->calculateHash();
 404          return $result;
 405      }
 406  
 407      /**
 408       * This function returns the correct XMLDB_TYPE_XXX value for the
 409       * string passed as argument
 410       * @param string $type
 411       * @return int
 412       */
 413      public function getXMLDBFieldType($type) {
 414  
 415          $result = XMLDB_TYPE_INCORRECT;
 416  
 417          switch (strtolower($type)) {
 418              case 'int':
 419                  $result = XMLDB_TYPE_INTEGER;
 420                  break;
 421              case 'number':
 422                  $result = XMLDB_TYPE_NUMBER;
 423                  break;
 424              case 'float':
 425                  $result = XMLDB_TYPE_FLOAT;
 426                  break;
 427              case 'char':
 428                  $result = XMLDB_TYPE_CHAR;
 429                  break;
 430              case 'text':
 431                  $result = XMLDB_TYPE_TEXT;
 432                  break;
 433              case 'binary':
 434                  $result = XMLDB_TYPE_BINARY;
 435                  break;
 436              case 'datetime':
 437                  $result = XMLDB_TYPE_DATETIME;
 438                  break;
 439          }
 440          // Return the normalized XMLDB_TYPE
 441          return $result;
 442      }
 443  
 444      /**
 445       * This function returns the correct name value for the
 446       * XMLDB_TYPE_XXX passed as argument
 447       * @param int $type
 448       * @return string
 449       */
 450      public function getXMLDBTypeName($type) {
 451  
 452          $result = "";
 453  
 454          switch (strtolower($type)) {
 455              case XMLDB_TYPE_INTEGER:
 456                  $result = 'int';
 457                  break;
 458              case XMLDB_TYPE_NUMBER:
 459                  $result = 'number';
 460                  break;
 461              case XMLDB_TYPE_FLOAT:
 462                  $result = 'float';
 463                  break;
 464              case XMLDB_TYPE_CHAR:
 465                  $result = 'char';
 466                  break;
 467              case XMLDB_TYPE_TEXT:
 468                  $result = 'text';
 469                  break;
 470              case XMLDB_TYPE_BINARY:
 471                  $result = 'binary';
 472                  break;
 473              case XMLDB_TYPE_DATETIME:
 474                  $result = 'datetime';
 475                  break;
 476          }
 477          // Return the normalized name
 478          return $result;
 479      }
 480  
 481      /**
 482       * This function calculate and set the hash of one xmldb_field
 483       * @param bool $recursive
 484       * @return void, modifies $this->hash
 485       */
 486       public function calculateHash($recursive = false) {
 487          if (!$this->loaded) {
 488              $this->hash = null;
 489          } else {
 490              $defaulthash = is_null($this->default) ? '' : sha1($this->default);
 491              $key = $this->name . $this->type . $this->length .
 492                     $this->notnull . $this->sequence .
 493                     $this->decimals . $this->comment . $defaulthash;
 494              $this->hash = md5($key);
 495          }
 496      }
 497  
 498      /**
 499       * This function will output the XML text for one field
 500       * @return string
 501       */
 502      public function xmlOutput() {
 503          $o = '';
 504          $o.= '        <FIELD NAME="' . $this->name . '"';
 505          $o.= ' TYPE="' . $this->getXMLDBTypeName($this->type) . '"';
 506          if ($this->length) {
 507              $o.= ' LENGTH="' . $this->length . '"';
 508          }
 509          if ($this->notnull) {
 510              $notnull = 'true';
 511          } else {
 512              $notnull = 'false';
 513          }
 514          $o.= ' NOTNULL="' . $notnull . '"';
 515          if (!$this->sequence && $this->default !== null) {
 516              $o.= ' DEFAULT="' . $this->default . '"';
 517          }
 518          if ($this->sequence) {
 519              $sequence = 'true';
 520          } else {
 521              $sequence = 'false';
 522          }
 523          $o.= ' SEQUENCE="' . $sequence . '"';
 524          if ($this->decimals !== null) {
 525              $o.= ' DECIMALS="' . $this->decimals . '"';
 526          }
 527          if ($this->comment) {
 528              $o.= ' COMMENT="' . htmlspecialchars($this->comment, ENT_COMPAT) . '"';
 529          }
 530          $o.= '/>' . "\n";
 531  
 532          return $o;
 533      }
 534  
 535      /**
 536       * This function will set all the attributes of the xmldb_field object
 537       * based on information passed in one ADOField
 538       * @param string $adofield
 539       * @return void, sets $this->type
 540       */
 541      public function setFromADOField($adofield) {
 542  
 543          // Calculate the XMLDB_TYPE
 544          switch (strtolower($adofield->type)) {
 545              case 'int':
 546              case 'tinyint':
 547              case 'smallint':
 548              case 'bigint':
 549              case 'integer':
 550                  $this->type = XMLDB_TYPE_INTEGER;
 551                  break;
 552              case 'number':
 553              case 'decimal':
 554              case 'dec':
 555              case 'numeric':
 556                  $this->type = XMLDB_TYPE_NUMBER;
 557                  break;
 558              case 'float':
 559              case 'double':
 560                  $this->type = XMLDB_TYPE_FLOAT;
 561                  break;
 562              case 'char':
 563              case 'varchar':
 564              case 'enum':
 565                  $this->type = XMLDB_TYPE_CHAR;
 566                  break;
 567              case 'text':
 568              case 'tinytext':
 569              case 'mediumtext':
 570              case 'longtext':
 571                  $this->type = XMLDB_TYPE_TEXT;
 572                  break;
 573              case 'blob':
 574              case 'tinyblob':
 575              case 'mediumblob':
 576              case 'longblob':
 577                  $this->type = XMLDB_TYPE_BINARY;
 578                  break;
 579              case 'datetime':
 580              case 'timestamp':
 581                  $this->type = XMLDB_TYPE_DATETIME;
 582                  break;
 583              default:
 584                  $this->type = XMLDB_TYPE_TEXT;
 585          }
 586          // Calculate the length of the field
 587          if ($adofield->max_length > 0 &&
 588                 ($this->type == XMLDB_TYPE_INTEGER ||
 589                  $this->type == XMLDB_TYPE_NUMBER  ||
 590                  $this->type == XMLDB_TYPE_FLOAT   ||
 591                  $this->type == XMLDB_TYPE_CHAR)) {
 592              $this->length = $adofield->max_length;
 593          }
 594          if ($this->type == XMLDB_TYPE_TEXT) {
 595              $this->length = null;
 596          }
 597          if ($this->type == XMLDB_TYPE_BINARY) {
 598              $this->length = null;
 599          }
 600          // Calculate the decimals of the field
 601          if ($adofield->max_length > 0 &&
 602              $adofield->scale &&
 603                 ($this->type == XMLDB_TYPE_NUMBER ||
 604                  $this->type == XMLDB_TYPE_FLOAT)) {
 605              $this->decimals = $adofield->scale;
 606          }
 607          // Calculate the notnull field
 608          if ($adofield->not_null) {
 609              $this->notnull = true;
 610          }
 611          // Calculate the default field
 612          if ($adofield->has_default) {
 613              $this->default = $adofield->default_value;
 614          }
 615          // Calculate the sequence field
 616          if ($adofield->auto_increment) {
 617              $this->sequence = true;
 618          }
 619          // Some more fields
 620          $this->loaded = true;
 621          $this->changed = true;
 622      }
 623  
 624      /**
 625       * Returns the PHP code needed to define one xmldb_field
 626       * @param bool $includeprevious
 627       * @return string
 628       */
 629      public function getPHP($includeprevious=true) {
 630  
 631          $result = '';
 632  
 633          // The XMLDBTYPE
 634          switch ($this->getType()) {
 635              case XMLDB_TYPE_INTEGER:
 636                  $result .= 'XMLDB_TYPE_INTEGER' . ', ';
 637                  break;
 638              case XMLDB_TYPE_NUMBER:
 639                  $result .= 'XMLDB_TYPE_NUMBER' . ', ';
 640                  break;
 641              case XMLDB_TYPE_FLOAT:
 642                  $result .= 'XMLDB_TYPE_FLOAT' . ', ';
 643                  break;
 644              case XMLDB_TYPE_CHAR:
 645                  $result .= 'XMLDB_TYPE_CHAR' . ', ';
 646                  break;
 647              case XMLDB_TYPE_TEXT:
 648                  $result .= 'XMLDB_TYPE_TEXT' . ', ';
 649                  break;
 650              case XMLDB_TYPE_BINARY:
 651                  $result .= 'XMLDB_TYPE_BINARY' . ', ';
 652                  break;
 653              case XMLDB_TYPE_DATETIME:
 654                  $result .= 'XMLDB_TYPE_DATETIME' . ', ';
 655                  break;
 656              case XMLDB_TYPE_TIMESTAMP:
 657                  $result .= 'XMLDB_TYPE_TIMESTAMP' . ', ';
 658                  break;
 659          }
 660          // The length
 661          $length = $this->getLength();
 662          $decimals = $this->getDecimals();
 663          if (!empty($length)) {
 664              $result .= "'" . $length;
 665              if (!empty($decimals)) {
 666                  $result .= ', ' . $decimals;
 667              }
 668              $result .= "', ";
 669          } else {
 670              $result .= 'null, ';
 671          }
 672          // Unsigned is not used any more since Moodle 2.3
 673          $result .= 'null, ';
 674          // Not Null
 675          $notnull = $this->getNotnull();
 676          if (!empty($notnull)) {
 677              $result .= 'XMLDB_NOTNULL' . ', ';
 678          } else {
 679              $result .= 'null, ';
 680          }
 681          // Sequence
 682          $sequence = $this->getSequence();
 683          if (!empty($sequence)) {
 684              $result .= 'XMLDB_SEQUENCE' . ', ';
 685          } else {
 686              $result .= 'null, ';
 687          }
 688          // Default
 689          $default =  $this->getDefault();
 690          if ($default !== null && !$this->getSequence()) {
 691              $result .= "'" . $default . "'";
 692          } else {
 693              $result .= 'null';
 694          }
 695          // Previous (decided by parameter)
 696          if ($includeprevious) {
 697              $previous = $this->getPrevious();
 698              if (!empty($previous)) {
 699                  $result .= ", '" . $previous . "'";
 700              } else {
 701                  $result .= ', null';
 702              }
 703          }
 704          // Return result
 705          return $result;
 706      }
 707  
 708      /**
 709       * Shows info in a readable format
 710       * @return string
 711       */
 712      public function readableInfo() {
 713          $o = '';
 714          // type
 715          $o .= $this->getXMLDBTypeName($this->type);
 716          // length
 717          if ($this->type == XMLDB_TYPE_INTEGER ||
 718              $this->type == XMLDB_TYPE_NUMBER  ||
 719              $this->type == XMLDB_TYPE_FLOAT   ||
 720              $this->type == XMLDB_TYPE_CHAR) {
 721              if ($this->length) {
 722                  $o .= ' (' . $this->length;
 723                  if ($this->type == XMLDB_TYPE_NUMBER  ||
 724                      $this->type == XMLDB_TYPE_FLOAT) {
 725                      if ($this->decimals !== null) {
 726                          $o .= ', ' . $this->decimals;
 727                      }
 728                  }
 729                  $o .= ')';
 730              }
 731          }
 732          // not null
 733          if ($this->notnull) {
 734              $o .= ' not null';
 735          }
 736          // default
 737          if ($this->default !== null) {
 738              $o .= ' default ';
 739              if ($this->type == XMLDB_TYPE_CHAR ||
 740                  $this->type == XMLDB_TYPE_TEXT) {
 741                      $o .= "'" . $this->default . "'";
 742              } else {
 743                  $o .= $this->default;
 744              }
 745          }
 746          // sequence
 747          if ($this->sequence) {
 748              $o .= ' auto-numbered';
 749          }
 750  
 751          return $o;
 752      }
 753  
 754      /**
 755       * Validates the field restrictions.
 756       *
 757       * The error message should not be localised because it is intended for developers,
 758       * end users and admins should never see these problems!
 759       *
 760       * @param xmldb_table $xmldb_table optional when object is table
 761       * @return string null if ok, error message if problem found
 762       */
 763      public function validateDefinition(xmldb_table $xmldb_table=null) {
 764          if (!$xmldb_table) {
 765              return 'Invalid xmldb_field->validateDefinition() call, $xmldb_table is required.';
 766          }
 767  
 768          $name = $this->getName();
 769          if (strlen($name) > self::NAME_MAX_LENGTH) {
 770              return 'Invalid field name in table {'.$xmldb_table->getName().'}: field "'.$this->getName().'" name is too long.'
 771                  .' Limit is '.self::NAME_MAX_LENGTH.' chars.';
 772          }
 773          if (!preg_match('/^[a-z][a-z0-9_]*$/', $name)) {
 774              return 'Invalid field name in table {'.$xmldb_table->getName().'}: field "'.$this->getName().'" name includes invalid characters.';
 775          }
 776  
 777          switch ($this->getType()) {
 778              case XMLDB_TYPE_INTEGER:
 779                  $length = $this->getLength();
 780                  if (!is_number($length) or $length <= 0 or $length > self::INTEGER_MAX_LENGTH) {
 781                      return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_INTEGER field "'.$this->getName().'" has invalid length';
 782                  }
 783                  $default = $this->getDefault();
 784                  if (!empty($default) and !is_number($default)) {
 785                      return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_INTEGER field "'.$this->getName().'" has invalid default';
 786                  }
 787                  break;
 788  
 789              case XMLDB_TYPE_NUMBER:
 790                  $maxlength = self::NUMBER_MAX_LENGTH;
 791                  $length = $this->getLength();
 792                  if (!is_number($length) or $length <= 0 or $length > $maxlength) {
 793                      return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_NUMBER field "'.$this->getName().'" has invalid length';
 794                  }
 795                  $decimals = $this->getDecimals();
 796                  $decimals = empty($decimals) ? 0 : $decimals; // fix missing decimals
 797                  if (!is_number($decimals) or $decimals < 0 or $decimals > $length) {
 798                      return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_NUMBER field "'.$this->getName().'" has invalid decimals';
 799                  }
 800                  if ($length - $decimals > self::INTEGER_MAX_LENGTH) {
 801                      return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_NUMBER field "'.
 802                          $this->getName().'" has too big whole number part';
 803                  }
 804                  $default = $this->getDefault();
 805                  if (!empty($default) and !is_numeric($default)) {
 806                      return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_NUMBER field "'.$this->getName().'" has invalid default';
 807                  }
 808                  break;
 809  
 810              case XMLDB_TYPE_FLOAT:
 811                  $length = $this->getLength();
 812                  $length = empty($length) ? 6 : $length; // weird, it might be better to require something here...
 813                  if (!is_number($length) or $length <= 0 or $length > self::FLOAT_MAX_LENGTH) {
 814                      return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_FLOAT field "'.$this->getName().'" has invalid length';
 815                  }
 816                  $decimals = $this->getDecimals();
 817                  $decimals = empty($decimals) ? 0 : $decimals; // fix missing decimals
 818                  if (!is_number($decimals) or $decimals < 0 or $decimals > $length) {
 819                      return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_FLOAT field "'.$this->getName().'" has invalid decimals';
 820                  }
 821                  $default = $this->getDefault();
 822                  if (!empty($default) and !is_numeric($default)) {
 823                      return 'Invalid field definition in table {'.$xmldb_table->getName().'}: XMLDB_TYPE_FLOAT field "'.$this->getName().'" has invalid default';
 824                  }
 825                  break;
 826  
 827              case XMLDB_TYPE_CHAR:
 828                  if ($this->getLength() > self::CHAR_MAX_LENGTH) {
 829                      return 'Invalid field definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_CHAR field "'.$this->getName().'" is too long.'
 830                             .' Limit is '.self::CHAR_MAX_LENGTH.' chars.';
 831                  }
 832                  break;
 833  
 834              case XMLDB_TYPE_TEXT:
 835                  break;
 836  
 837              case XMLDB_TYPE_BINARY:
 838                  break;
 839  
 840              case XMLDB_TYPE_DATETIME:
 841                  break;
 842  
 843              case XMLDB_TYPE_TIMESTAMP:
 844                  break;
 845          }
 846  
 847          return null;
 848      }
 849  }