Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 402] [Versions 311 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 the XMLDB base class where all the common pieces are defined
  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_object {
  30  
  31      /** @var string name of obejct */
  32      protected $name;
  33  
  34      /** @var string comment on object */
  35      protected $comment;
  36  
  37      /** @var xmldb_object */
  38      protected $previous;
  39  
  40      /** @var xmldb_object */
  41      protected $next;
  42  
  43      /** @var string hash of object */
  44      protected $hash;
  45  
  46      /** @var bool is it loaded yet */
  47      protected $loaded;
  48  
  49      /** @var bool was object changed */
  50      protected $changed;
  51  
  52      /** @var string error message */
  53      protected $errormsg;
  54  
  55      /**
  56       * Creates one new xmldb_object
  57       * @param string $name
  58       */
  59      public function __construct($name) {
  60          $this->name = $name;
  61          $this->comment = null;
  62          $this->previous = null;
  63          $this->next = null;
  64          $this->hash = null;
  65          $this->loaded = false;
  66          $this->changed = false;
  67          $this->errormsg = null;
  68      }
  69  
  70      /**
  71       * This function returns true/false, if the xmldb_object has been loaded
  72       * @return bool
  73       */
  74      public function isLoaded() {
  75          return $this->loaded;
  76      }
  77  
  78      /**
  79       * This function returns true/false, if the xmldb_object has changed
  80       * @return bool
  81       */
  82      public function hasChanged() {
  83          return $this->changed;
  84      }
  85  
  86      /**
  87       * This function returns the comment of one xmldb_object
  88       * @return string
  89       */
  90      public function getComment() {
  91          return $this->comment;
  92      }
  93  
  94      /**
  95       * This function returns the hash of one xmldb_object
  96       * @return string
  97       */
  98      public function getHash() {
  99          return $this->hash;
 100      }
 101  
 102      /**
 103       * This function will return the name of the previous xmldb_object
 104       * @return xmldb_object
 105       */
 106      public function getPrevious() {
 107          return $this->previous;
 108      }
 109  
 110      /**
 111       * This function will return the name of the next xmldb_object
 112       * @return xmldb_object
 113       */
 114      public function getNext() {
 115          return $this->next;
 116      }
 117  
 118      /**
 119       * This function will return the name of the xmldb_object
 120       * @return string
 121       */
 122      public function getName() {
 123          return $this->name;
 124      }
 125  
 126      /**
 127       * This function will return the error detected in the object
 128       * @return string
 129       */
 130      public function getError() {
 131          return $this->errormsg;
 132      }
 133  
 134      /**
 135       * This function will set the comment of the xmldb_object
 136       * @param string $comment
 137       */
 138      public function setComment($comment) {
 139          $this->comment = $comment;
 140      }
 141  
 142      /**
 143       * This function will set the previous of the xmldb_object
 144       * @param xmldb_object $previous
 145       */
 146      public function setPrevious($previous) {
 147          $this->previous = $previous;
 148      }
 149  
 150      /**
 151       * This function will set the next of the xmldb_object
 152       * @param xmldb_object $next
 153       */
 154      public function setNext($next) {
 155          $this->next = $next;
 156      }
 157  
 158      /**
 159       * This function will set the hash of the xmldb_object
 160       * @param string $hash
 161       */
 162      public function setHash($hash) {
 163          $this->hash = $hash;
 164      }
 165  
 166      /**
 167       * This function will set the loaded field of the xmldb_object
 168       * @param bool $loaded
 169       */
 170      public function setLoaded($loaded = true) {
 171          $this->loaded = $loaded;
 172      }
 173  
 174      /**
 175       * This function will set the changed field of the xmldb_object
 176       * @param bool $changed
 177       */
 178      public function setChanged($changed = true) {
 179          $this->changed = $changed;
 180      }
 181      /**
 182       * This function will set the name field of the xmldb_object
 183       * @param string $name
 184       */
 185      public function setName($name) {
 186          $this->name = $name;
 187      }
 188  
 189  
 190      /**
 191       * This function will check if one key name is ok or no (true/false)
 192       * only lowercase a-z, 0-9 and _ are allowed
 193       * @return bool
 194       */
 195      public function checkName () {
 196          $result = true;
 197  
 198          if ($this->name != preg_replace('/[^a-z0-9_ -]/i', '', $this->name)) {
 199              $result = false;
 200          }
 201          return $result;
 202      }
 203  
 204      /**
 205       * This function will check that all the elements in one array
 206       * have a correct name [a-z0-9_]
 207       * @param array $arr
 208       * @return bool
 209       */
 210      public function checkNameValues($arr) {
 211          $result = true;
 212          // TODO: Perhaps, add support for reserved words
 213  
 214          // Check the name only contains valid chars
 215          if ($arr) {
 216              foreach($arr as $element) {
 217                  if (!$element->checkName()) {
 218                      $result = false;
 219                  }
 220              }
 221          }
 222          // Check there aren't duplicate names
 223          if ($arr) {
 224              $existing_fields = array();
 225              foreach($arr as $element) {
 226                  if (in_array($element->getName(), $existing_fields)) {
 227                      debugging('Object ' . $element->getName() . ' is duplicated!', DEBUG_DEVELOPER);
 228                      $result = false;
 229                  }
 230                  $existing_fields[] = $element->getName();
 231              }
 232          }
 233          return $result;
 234      }
 235  
 236      /**
 237       * Reconstruct previous/next attributes.
 238       * @param array $arr
 239       * @return bool true if $arr modified
 240       */
 241      public function fixPrevNext(&$arr) {
 242          $tweaked = false;
 243  
 244          $prev = null;
 245          foreach ($arr as $key=>$el) {
 246              $prev_value = $arr[$key]->previous;
 247              $next_value = $arr[$key]->next;
 248  
 249              $arr[$key]->next     = null;
 250              $arr[$key]->previous = null;
 251              if ($prev !== null) {
 252                  $arr[$prev]->next    = $arr[$key]->name;
 253                  $arr[$key]->previous = $arr[$prev]->name;
 254              }
 255              $prev = $key;
 256  
 257              if ($prev_value != $arr[$key]->previous or $next_value != $arr[$key]->next) {
 258                  $tweaked = true;
 259              }
 260          }
 261  
 262          return $tweaked;
 263      }
 264  
 265      /**
 266       * This function will order all the elements in one array, following
 267       * the previous/next rules
 268       * @param array $arr
 269       * @return array|bool
 270       */
 271      public function orderElements($arr) {
 272          $result = true;
 273  
 274          // Create a new array
 275          $newarr = array();
 276          if (!empty($arr)) {
 277              $currentelement = null;
 278              // Get the element without previous
 279              foreach($arr as $key => $element) {
 280                  if (!$element->getPrevious()) {
 281                      $currentelement = $arr[$key];
 282                      $newarr[0] = $arr[$key];
 283                  }
 284              }
 285              if (!$currentelement) {
 286                  $result = false;
 287              }
 288              // Follow the next rules
 289              $counter = 1;
 290              while ($result && $currentelement->getNext()) {
 291                  $i = $this->findObjectInArray($currentelement->getNext(), $arr);
 292                  $currentelement = $arr[$i];
 293                  $newarr[$counter] = $arr[$i];
 294                  $counter++;
 295              }
 296              // Compare number of elements between original and new array
 297              if ($result && count($arr) != count($newarr)) {
 298                  $result = false;
 299              } else if ($newarr) {
 300                  $result = $newarr;
 301              } else {
 302                  $result = false;
 303              }
 304          } else {
 305              $result = array();
 306          }
 307          return $result;
 308      }
 309  
 310      /**
 311       * Returns the position of one object in the array.
 312       * @param string $objectname
 313       * @param array $arr
 314       * @return mixed
 315       */
 316      public function findObjectInArray($objectname, $arr) {
 317          foreach ($arr as $i => $object) {
 318              if ($objectname == $object->getName()) {
 319                  return $i;
 320              }
 321          }
 322          return null;
 323      }
 324  
 325      /**
 326       * This function will display a readable info about the xmldb_object
 327       * (should be implemented inside each XMLDBxxx object)
 328       * @return string
 329       */
 330      public function readableInfo() {
 331          return get_class($this);
 332      }
 333  
 334      /**
 335       * This function will perform the central debug of all the XMLDB classes
 336       * being called automatically every time one error is found. Apart from
 337       * the main actions performed in it (XMLDB agnostic) it looks for one
 338       * function called xmldb_debug() and invokes it, passing both the
 339       * message code and the whole object.
 340       * So, to perform custom debugging just add such function to your libs.
 341       *
 342       * Call to the external hook function can be disabled by request by
 343       * defining XMLDB_SKIP_DEBUG_HOOK
 344       * @param string $message
 345       */
 346      public function debug($message) {
 347  
 348          // Check for xmldb_debug($message, $xmldb_object)
 349          $funcname = 'xmldb_debug';
 350          // If exists and XMLDB_SKIP_DEBUG_HOOK is undefined
 351          if (function_exists($funcname) && !defined('XMLDB_SKIP_DEBUG_HOOK')) {
 352              $funcname($message, $this);
 353          }
 354      }
 355  
 356      /**
 357       * Returns one array of elements from one comma separated string,
 358       * supporting quoted strings containing commas and concat function calls
 359       * @param string $string
 360       * @return array
 361       */
 362      public function comma2array($string) {
 363  
 364          $foundquotes  = array();
 365          $foundconcats = array();
 366  
 367          // Extract all the concat elements from the string
 368          preg_match_all("/(CONCAT\(.*?\))/is", $string, $matches);
 369          foreach (array_unique($matches[0]) as $key=>$value) {
 370              $foundconcats['<#'.$key.'#>'] = $value;
 371          }
 372          if (!empty($foundconcats)) {
 373              $string = str_replace($foundconcats,array_keys($foundconcats),$string);
 374          }
 375  
 376          // Extract all the quoted elements from the string (skipping
 377          // backslashed quotes that are part of the content.
 378          preg_match_all("/(''|'.*?[^\\\\]')/is", $string, $matches);
 379          foreach (array_unique($matches[0]) as $key=>$value) {
 380              $foundquotes['<%'.$key.'%>'] = $value;
 381          }
 382          if (!empty($foundquotes)) {
 383              $string = str_replace($foundquotes,array_keys($foundquotes),$string);
 384          }
 385  
 386          // Explode safely the string
 387          $arr = explode (',', $string);
 388  
 389          // Put the concat and quoted elements back again, trimming every element
 390          if ($arr) {
 391              foreach ($arr as $key => $element) {
 392                  // Clear some spaces
 393                  $element = trim($element);
 394                  // Replace the quoted elements if exists
 395                  if (!empty($foundquotes)) {
 396                      $element = str_replace(array_keys($foundquotes), $foundquotes, $element);
 397                  }
 398                  // Replace the concat elements if exists
 399                  if (!empty($foundconcats)) {
 400                      $element = str_replace(array_keys($foundconcats), $foundconcats, $element);
 401                  }
 402                  // Delete any backslash used for quotes. XMLDB stuff will add them before insert
 403                  $arr[$key] = str_replace("\\'", "'", $element);
 404              }
 405          }
 406  
 407          return $arr;
 408      }
 409  
 410      /**
 411       * Validates the definition of objects and returns error message.
 412       *
 413       * The error message should not be localised because it is intended for developers,
 414       * end users and admins should never see these problems!
 415       *
 416       * @param xmldb_table $xmldb_table optional when object is table
 417       * @return string null if ok, error message if problem found
 418       */
 419      public function validateDefinition(xmldb_table $xmldb_table=null) {
 420          return null;
 421      }
 422  }