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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body