Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * @package    moodlecore
  20   * @subpackage backup-structure
  21   * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   *
  24   * TODO: Finish phpdocs
  25   */
  26  
  27  /**
  28   * Abstract class representing one final element atom (name/value/parent) piece of information
  29   */
  30  abstract class base_final_element extends base_atom {
  31  
  32      /** @var array base_attributes of the element (maps to XML attributes of the tag) */
  33          private $attributes;
  34  
  35      /** @var base_nested_element parent of this element (describes structure of the XML file) */
  36          private $parent;
  37  
  38      /**
  39       * Constructor - instantiates one base_final_element, specifying its basic info.
  40       *
  41       * @param string $name name of the element
  42       * @param array  $attributes attributes this element will handle (optional, defaults to null)
  43       */
  44      public function __construct($name, $attributes = null) {
  45          parent::__construct($name);
  46          $this->attributes = array();
  47          if (!empty($attributes)) {
  48              $this->add_attributes($attributes);
  49          }
  50          $this->parent = null;
  51      }
  52  
  53      /**
  54       * Destroy all circular references. It helps PHP 5.2 a lot!
  55       */
  56      public function destroy() {
  57          // No need to destroy anything recursively here, direct reset
  58          $this->attributes = array();
  59          $this->parent = null;
  60      }
  61  
  62      protected function set_parent($element) {
  63          if ($this->parent) {
  64              $info = new stdClass();
  65              $info->currparent= $this->parent->get_name();
  66              $info->newparent = $element->get_name();
  67              $info->element   = $this->get_name();
  68              throw new base_element_parent_exception('baseelementhasparent', $info);
  69          }
  70          $this->parent = $element;
  71      }
  72  
  73      protected function get_grandparent() {
  74          $parent = $this->parent;
  75          if ($parent instanceof base_nested_element) {
  76              return $parent->get_grandparent();
  77          } else {
  78              return $this;
  79          }
  80      }
  81  
  82      protected function get_grandoptigroupelement_or_grandparent() {
  83          $parent = $this->parent;
  84          if ($parent instanceof base_optigroup) {
  85              return $this; // Have found one parent optigroup, so I (first child of optigroup) am
  86          } else if ($parent instanceof base_nested_element) {
  87              return $parent->get_grandoptigroupelement_or_grandparent(); // Continue searching
  88          } else {
  89              return $this;
  90          }
  91      }
  92  
  93      protected function find_element_by_path($path) {
  94          $patharr = explode('/', trim($path, '/')); // Split the path trimming slashes
  95          if (substr($path, 0, 1) == '/') { // Absolute path, go to grandparent and process
  96              if (!$this->get_grandparent() instanceof base_nested_element) {
  97                  throw new base_element_struct_exception('baseelementincorrectgrandparent', $patharr[0]);
  98              } else if ($this->get_grandparent()->get_name() !== $patharr[0]) {
  99                  throw new base_element_struct_exception('baseelementincorrectgrandparent', $patharr[0]);
 100              } else {
 101                  $newpath = implode('/', array_slice($patharr, 1)); // Take out 1st element
 102                  return $this->get_grandparent()->find_element_by_path($newpath); // Process as relative in grandparent
 103              }
 104          } else {
 105              if ($patharr[0] == '..') { // Go to parent
 106                  if (!$this->get_parent() instanceof base_nested_element) {
 107                      throw new base_element_struct_exception('baseelementincorrectparent', $patharr[0]);
 108                  } else {
 109                      $newpath = implode('/', array_slice($patharr, 1)); // Take out 1st element
 110                      return $this->get_parent()->find_element_by_path($newpath); // Process as relative in parent
 111                  }
 112              } else if (count($patharr) > 1) { // Go to next child
 113                  if (!$this->get_child($patharr[0]) instanceof base_nested_element) {
 114                      throw new base_element_struct_exception('baseelementincorrectchild', $patharr[0]);
 115                  } else {
 116                      $newpath = implode('/', array_slice($patharr, 1)); // Take out 1st element
 117                      return $this->get_child($patharr[0])->find_element_by_path($newpath); // Process as relative in parent
 118                  }
 119              } else { // Return final element or attribute
 120                  if ($this->get_final_element($patharr[0]) instanceof base_final_element) {
 121                      return $this->get_final_element($patharr[0]);
 122                  } else if ($this->get_attribute($patharr[0]) instanceof base_attribute) {
 123                      return $this->get_attribute($patharr[0]);
 124                  } else {
 125                      throw new base_element_struct_exception('baseelementincorrectfinalorattribute', $patharr[0]);
 126                  }
 127              }
 128          }
 129      }
 130  
 131      protected function find_first_parent_by_name($name) {
 132          if ($parent = $this->get_parent()) { // If element has parent
 133              $element   = $parent->get_final_element($name); // Look for name into parent finals
 134              $attribute = $parent->get_attribute($name);     // Look for name into parent attrs
 135              if ($element instanceof base_final_element) {
 136                  return $element;
 137  
 138              } else if ($attribute instanceof base_attribute) {
 139                  return $attribute;
 140  
 141              } else { // Not found, go up 1 level and continue searching
 142                  return $parent->find_first_parent_by_name($name);
 143              }
 144          } else { // No more parents available, return the original backup::VAR_PARENTID, exception
 145              throw new base_element_struct_exception('cannotfindparentidforelement', $name);
 146          }
 147      }
 148  
 149  
 150  /// Public API starts here
 151  
 152      public function get_attributes() {
 153          return $this->attributes;
 154      }
 155  
 156      public function get_attribute($name) {
 157          if (array_key_exists($name, $this->attributes)) {
 158              return $this->attributes[$name];
 159          } else {
 160              return null;
 161          }
 162      }
 163  
 164      public function get_parent() {
 165          return $this->parent;
 166      }
 167  
 168      public function get_level() {
 169          return $this->parent == null ? 1 : $this->parent->get_level() + 1;
 170      }
 171  
 172      public function add_attributes($attributes) {
 173          if ($attributes instanceof base_attribute || is_string($attributes)) { // Accept 1 attribute, object or string
 174              $attributes = array($attributes);
 175          }
 176          if (is_array($attributes)) {
 177              foreach ($attributes as $attribute) {
 178                  if (is_string($attribute)) { // Accept string attributes
 179                      $attribute = $this->get_new_attribute($attribute);
 180                  }
 181                  if (!($attribute instanceof base_attribute)) {
 182                      throw new base_element_attribute_exception('baseelementnoattribute', get_class($attribute));
 183                  }
 184                  if (array_key_exists($attribute->get_name(), $this->attributes)) {
 185                      throw new base_element_attribute_exception('baseelementattributeexists', $attribute->get_name());
 186                  }
 187                  $this->attributes[$attribute->get_name()] = $attribute;
 188              }
 189          } else {
 190              throw new base_element_attribute_exception('baseelementattributeincorrect');
 191          }
 192      }
 193  
 194      public function clean_values() {
 195          parent::clean_value();
 196          if (!empty($this->attributes)) {
 197              foreach ($this->attributes as $attribute) {
 198                  $attribute->clean_value();
 199              }
 200          }
 201      }
 202  
 203      public function to_string($showvalue = false) {
 204          // Decide the correct prefix
 205          $prefix = '#'; // default
 206          if ($this->parent instanceof base_optigroup) {
 207              $prefix = '?';
 208          } else if ($this instanceof base_nested_element) {
 209              $prefix = '';
 210          }
 211          $indent = str_repeat('    ', $this->get_level()); // Indent output based in level (4cc)
 212          $output = $indent . $prefix . $this->get_name() . ' (level: ' . $this->get_level() . ')';
 213          if ($showvalue) {
 214              $value = $this->is_set() ? $this->get_value() : 'not set';
 215              $output .= ' => ' . $value;
 216          }
 217          if (!empty($this->attributes)) {
 218              foreach ($this->attributes as $attribute) {
 219                  $output .= PHP_EOL . $indent . '    ' . $attribute->to_string($showvalue);
 220              }
 221          }
 222          return $output;
 223      }
 224  
 225  // Implementable API
 226  
 227      /**
 228       * Returns one instace of the @base_attribute class to work with
 229       * when attributes are added simply by name
 230       */
 231      abstract protected function get_new_attribute($name);
 232  }
 233  
 234  /**
 235   * base_element exception to control all the errors related with parents handling
 236   */
 237  class base_element_parent_exception extends base_atom_exception {
 238  
 239      /**
 240       * Constructor - instantiates one base_element_parent_exception
 241       *
 242       * @param string $errorcode key for the corresponding error string
 243       * @param object $a extra words and phrases that might be required in the error string
 244       * @param string $debuginfo optional debugging information
 245       */
 246      public function __construct($errorcode, $a = null, $debuginfo = null) {
 247          parent::__construct($errorcode, $a, $debuginfo);
 248      }
 249  }
 250  
 251  /**
 252   * base_element exception to control all the errors related with attributes handling
 253   */
 254  class base_element_attribute_exception extends base_atom_exception {
 255  
 256      /**
 257       * Constructor - instantiates one base_element_attribute_exception
 258       *
 259       * @param string $errorcode key for the corresponding error string
 260       * @param object $a extra words and phrases that might be required in the error string
 261       * @param string $debuginfo optional debugging information
 262       */
 263      public function __construct($errorcode, $a = null, $debuginfo = null) {
 264          parent::__construct($errorcode, $a, $debuginfo);
 265      }
 266  }