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 nestable element (non final) piece of information 29 */ 30 abstract class base_nested_element extends base_final_element { 31 32 /** @var array final elements of the element (maps to XML final elements of the tag) */ 33 private $final_elements; 34 35 /** @var array children base_elements of this element (describes structure of the XML file) */ 36 private $children; 37 38 /** @var base_optigroup optional group of this element (branches to be processed conditionally) */ 39 private $optigroup; 40 41 /** @var array elements already used by the base_element, to avoid circular references */ 42 private $used; 43 44 /** 45 * Constructor - instantiates one base_nested_element, specifying its basic info. 46 * 47 * @param string $name name of the element 48 * @param array $attributes attributes this element will handle (optional, defaults to null) 49 * @param array $final_elements this element will handle (optional, defaults to null) 50 */ 51 public function __construct($name, $attributes = null, $final_elements = null) { 52 parent::__construct($name, $attributes); 53 $this->final_elements = array(); 54 if (!empty($final_elements)) { 55 $this->add_final_elements($final_elements); 56 } 57 $this->children = array(); 58 $this->optigroup = null; 59 $this->used[] = $name; 60 } 61 62 /** 63 * Destroy all circular references. It helps PHP 5.2 a lot! 64 */ 65 public function destroy() { 66 // Before reseting anything, call destroy recursively 67 foreach ($this->children as $child) { 68 $child->destroy(); 69 } 70 foreach ($this->final_elements as $element) { 71 $element->destroy(); 72 } 73 if ($this->optigroup) { 74 $this->optigroup->destroy(); 75 } 76 // Everything has been destroyed recursively, now we can reset safely 77 $this->children = array(); 78 $this->final_elements = array(); 79 $this->optigroup = null; 80 // Delegate to parent to destroy other bits 81 parent::destroy(); 82 } 83 84 protected function get_used() { 85 return $this->used; 86 } 87 88 protected function set_used($used) { 89 $this->used = $used; 90 } 91 92 protected function add_used($element) { 93 $this->used = array_merge($this->used, $element->get_used()); 94 } 95 96 protected function check_and_set_used($element) { 97 // First of all, check the element being added doesn't conflict with own final elements 98 if (array_key_exists($element->get_name(), $this->final_elements)) { 99 throw new base_element_struct_exception('baseelementchildnameconflict', $element->get_name()); 100 } 101 $grandparent = $this->get_grandoptigroupelement_or_grandparent(); 102 if ($existing = array_intersect($grandparent->get_used(), $element->get_used())) { // Check the element isn't being used already 103 throw new base_element_struct_exception('baseelementexisting', implode($existing)); 104 } 105 $grandparent->add_used($element); 106 // If the parent is one optigroup, add the element useds to it too 107 if ($grandparent->get_parent() instanceof base_optigroup) { 108 $grandparent->get_parent()->add_used($element); 109 } 110 111 } 112 113 /// Public API starts here 114 115 public function get_final_elements() { 116 return $this->final_elements; 117 } 118 119 public function get_final_element($name) { 120 if (array_key_exists($name, $this->final_elements)) { 121 return $this->final_elements[$name]; 122 } else { 123 return null; 124 } 125 } 126 127 public function get_children() { 128 return $this->children; 129 } 130 131 public function get_child($name) { 132 if (array_key_exists($name, $this->children)) { 133 return $this->children[$name]; 134 } else { 135 return null; 136 } 137 } 138 139 public function get_optigroup() { 140 return $this->optigroup; 141 } 142 143 public function add_final_elements($final_elements) { 144 if ($final_elements instanceof base_final_element || is_string($final_elements)) { // Accept 1 final_element, object or string 145 $final_elements = array($final_elements); 146 } 147 if (is_array($final_elements)) { 148 foreach ($final_elements as $final_element) { 149 if (is_string($final_element)) { // Accept string final_elements 150 $final_element = $this->get_new_final_element($final_element); 151 } 152 if (!($final_element instanceof base_final_element)) { 153 throw new base_element_struct_exception('baseelementnofinalelement', get_class($final_element)); 154 } 155 if (array_key_exists($final_element->get_name(), $this->final_elements)) { 156 throw new base_element_struct_exception('baseelementexists', $final_element->get_name()); 157 } 158 $this->final_elements[$final_element->get_name()] = $final_element; 159 $final_element->set_parent($this); 160 } 161 } else { 162 throw new base_element_struct_exception('baseelementincorrect'); 163 } 164 } 165 166 public function add_child($element) { 167 if (!is_object($element) || !($element instanceof base_nested_element)) { // parameter must be a base_nested_element 168 if (!is_object($element) || !($found = get_class($element))) { 169 $found = 'non object'; 170 } 171 throw new base_element_struct_exception('nestedelementincorrect', $found); 172 } 173 $this->check_and_set_used($element); 174 $this->children[$element->get_name()] = $element; 175 $element->set_parent($this); 176 } 177 178 public function add_optigroup($optigroup) { 179 if (!($optigroup instanceof base_optigroup)) { // parameter must be a base_optigroup 180 if (!$found = get_class($optigroup)) { 181 $found = 'non object'; 182 } 183 throw new base_element_struct_exception('optigroupincorrect', $found); 184 } 185 if ($this->optigroup !== null) { 186 throw new base_element_struct_exception('optigroupalreadyset', $found); 187 } 188 $this->check_and_set_used($optigroup); 189 $this->optigroup = $optigroup; 190 $optigroup->set_parent($this); 191 } 192 193 public function get_value() { 194 throw new base_element_struct_exception('nestedelementnotvalue'); 195 } 196 197 public function set_value($value) { 198 throw new base_element_struct_exception('nestedelementnotvalue'); 199 } 200 201 public function clean_value() { 202 throw new base_element_struct_exception('nestedelementnotvalue'); 203 } 204 205 public function clean_values() { 206 parent::clean_values(); 207 if (!empty($this->final_elements)) { 208 foreach ($this->final_elements as $final_element) { 209 $final_element->clean_values(); 210 } 211 } 212 if (!empty($this->children)) { 213 foreach ($this->children as $child) { 214 $child->clean_values(); 215 } 216 } 217 if (!empty($this->optigroup)) { 218 $this->optigroup->clean_values(); 219 } 220 } 221 222 public function to_string($showvalue = false) { 223 $output = parent::to_string($showvalue); 224 if (!empty($this->final_elements)) { 225 foreach ($this->final_elements as $final_element) { 226 $output .= PHP_EOL . $final_element->to_string($showvalue); 227 } 228 } 229 if (!empty($this->children)) { 230 foreach ($this->children as $child) { 231 $output .= PHP_EOL . $child->to_string($showvalue); 232 } 233 } 234 if (!empty($this->optigroup)) { 235 $output .= PHP_EOL . $this->optigroup->to_string($showvalue); 236 } 237 return $output; 238 } 239 240 // Implementable API 241 242 /** 243 * Returns one instace of the @final_element class to work with 244 * when final_elements are added simply by name 245 */ 246 abstract protected function get_new_final_element($name); 247 } 248 249 /** 250 * base_element exception to control all the errors while building the nested tree 251 * 252 * This exception will be thrown each time the base_element class detects some 253 * inconsistency related with the building of the nested tree representing one base part 254 * (invalid objects, circular references, double parents...) 255 */ 256 class base_element_struct_exception extends base_atom_exception { 257 258 /** 259 * Constructor - instantiates one base_element_struct_exception 260 * 261 * @param string $errorcode key for the corresponding error string 262 * @param object $a extra words and phrases that might be required in the error string 263 * @param string $debuginfo optional debugging information 264 */ 265 public function __construct($errorcode, $a = null, $debuginfo = null) { 266 parent::__construct($errorcode, $a, $debuginfo); 267 } 268 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body