Commit | Line | Data |
---|---|---|
69dd0c8c EL |
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 | * Instantiable class representing one nestable element (non final) piece of information on backup | |
29 | */ | |
30 | class backup_nested_element extends base_nested_element implements processable { | |
31 | ||
32 | protected $var_array; // To be used in case we pass one in-memory structure | |
33 | protected $table; // Table (without prefix) to fetch records from | |
34 | protected $sql; // Raw SQL to fetch records from | |
35 | protected $params; // Unprocessed params as specified in the set_source() call | |
36 | protected $procparams;// Processed (path resolved) params array | |
37 | protected $aliases; // Define DB->final element aliases | |
38 | protected $fileannotelement; // Element to be used as itemid for file annotations | |
39 | protected $fileannotareas; // array of file areas to be searched by file annotations | |
40 | protected $counter; // Number of instances of this element that have been processed | |
41 | ||
42 | /** | |
43 | * Constructor - instantiates one backup_nested_element, specifying its basic info. | |
44 | * | |
45 | * @param string $name name of the element | |
46 | * @param array $attributes attributes this element will handle (optional, defaults to null) | |
47 | * @param array $final_elements this element will handle (optional, defaults to null) | |
48 | */ | |
49 | public function __construct($name, $attributes = null, $final_elements = null) { | |
50 | parent::__construct($name, $attributes, $final_elements); | |
51 | $this->var_array = null; | |
52 | $this->table = null; | |
53 | $this->sql = null; | |
54 | $this->params = null; | |
55 | $this->procparams= null; | |
56 | $this->aliases = array(); | |
57 | $this->fileannotelement = null; | |
58 | $this->fileannotareas = array(); | |
59 | $this->counter = 0; | |
60 | } | |
61 | ||
62 | public function process($processor) { | |
63 | if (!$processor instanceof base_processor) { // No correct processor, throw exception | |
64 | throw new base_element_struct_exception('incorrect_processor'); | |
65 | } | |
66 | ||
67 | $iterator = $this->get_iterator($processor); // Get the iterator over backup-able data | |
68 | ||
69 | foreach ($iterator as $key => $values) { // Process each "ocurrrence" of the nested element (recordset or array) | |
70 | ||
71 | // Fill the values of the attributes and final elements with the $values from the iterator | |
72 | $this->fill_values($values); | |
73 | ||
74 | // Perform pre-process tasks for the nested_element | |
75 | $processor->pre_process_nested_element($this); | |
76 | ||
77 | // Delegate the process of each attribute | |
78 | foreach ($this->get_attributes() as $attribute) { | |
79 | $attribute->process($processor); | |
80 | } | |
81 | ||
82 | // Main process tasks for the nested element, once its attributes have been processed | |
83 | $processor->process_nested_element($this); | |
84 | ||
85 | // Delegate the process of each final_element | |
86 | foreach ($this->get_final_elements() as $final_element) { | |
87 | $final_element->process($processor); | |
88 | } | |
89 | ||
90 | // Delegate the process to the optigroup | |
91 | if ($this->get_optigroup()) { | |
92 | $this->get_optigroup()->process($processor); | |
93 | } | |
94 | ||
95 | // Delegate the process to each child nested_element | |
96 | foreach ($this->get_children() as $child) { | |
97 | $child->process($processor); | |
98 | } | |
99 | ||
100 | // Perform post-process tasks for the nested element | |
101 | $processor->post_process_nested_element($this); | |
102 | ||
103 | // Everything processed, clean values before next iteration | |
104 | $this->clean_values(); | |
105 | ||
106 | // Increment counter for this element | |
107 | $this->counter++; | |
108 | ||
109 | // For root element, check we only have 1 element | |
110 | if ($this->get_parent() === null && $this->counter > 1) { | |
111 | throw new base_element_struct_exception('root_only_one_ocurrence', $this->get_name()); | |
112 | } | |
113 | } | |
114 | // Close the iterator (DB recordset / array iterator) | |
115 | $iterator->close(); | |
116 | } | |
117 | ||
118 | public function set_source_array($arr) { | |
119 | // TODO: Only elements having final elements can set source | |
120 | $this->var_array = $arr; | |
121 | } | |
122 | ||
123 | public function set_source_table($table, $params) { | |
124 | if (!is_array($params)) { // Check we are passing array | |
125 | throw new base_element_struct_exception('setsourcerequiresarrayofparams'); | |
126 | } | |
127 | // TODO: Only elements having final elements can set source | |
128 | $this->table = $table; | |
129 | $this->procparams = $this->convert_table_params($params); | |
130 | } | |
131 | ||
132 | public function set_source_sql($sql, $params) { | |
133 | if (!is_array($params)) { // Check we are passing array | |
134 | throw new base_element_struct_exception('setsourcerequiresarrayofparams'); | |
135 | } | |
136 | // TODO: Only elements having final elements can set source | |
137 | $this->sql = $sql; | |
138 | $this->procparams = $this->convert_sql_params($params); | |
139 | } | |
140 | ||
141 | public function set_source_alias($dbname, $finalelementname) { | |
142 | // Get final element | |
143 | $finalelement = $this->get_final_element($finalelementname); | |
144 | if (!$finalelement) { // Final element incorrect, throw exception | |
145 | throw new base_element_struct_exception('incorrectaliasfinalnamenotfound', $finalelementname); | |
146 | } else { | |
147 | $this->aliases[$dbname] = $finalelement; | |
148 | } | |
149 | } | |
150 | ||
151 | public function annotate_files($areas, $elementname) { | |
152 | if (!is_array($areas)) { // Check we are passing array | |
153 | throw new base_element_struct_exception('annotate_files_requires_array_of_areas', $areas); | |
154 | } | |
155 | $annotations = $this->get_file_annotations(); | |
156 | if (!empty($annotations[0])) { // Check we haven't defined file annotations already | |
157 | throw new base_element_struct_exception('annotate_files_already_defined', $this->get_name()); | |
158 | } | |
159 | if ($elementname !== null) { // Check elementname is valid | |
160 | $element = $this->find_element($elementname); | |
161 | // Annotate the element | |
162 | $this->fileannotelement= $element; | |
163 | } | |
164 | // Annotate the areas | |
165 | $this->fileannotareas = $areas; | |
166 | } | |
167 | ||
168 | public function annotate_ids($itemname, $elementname) { | |
169 | $element = $this->find_element($elementname); | |
170 | $element->set_annotation_item($itemname); | |
171 | } | |
172 | ||
173 | /** | |
174 | * Returns one array containing the element in the | |
175 | * @backup_structure and the areas to be searched | |
176 | */ | |
177 | public function get_file_annotations() { | |
178 | if (empty($this->fileannotareas)) { | |
179 | return array(null, null); | |
180 | } | |
181 | return array($this->fileannotareas, $this->fileannotelement); | |
182 | } | |
183 | ||
184 | public function get_source_array() { | |
185 | return $this->var_array; | |
186 | } | |
187 | ||
188 | public function get_source_table() { | |
189 | return $this->table; | |
190 | } | |
191 | ||
192 | public function get_source_sql() { | |
193 | return $this->sql; | |
194 | } | |
195 | ||
196 | public function get_counter() { | |
197 | return $this->counter; | |
198 | } | |
199 | ||
200 | /** | |
201 | * Simple filler that, matching by name, will fill both attributes and final elements | |
202 | * depending of this nested element, debugging info about non-matching elements and/or | |
203 | * elements present in both places. Accept both arrays and objects. | |
204 | */ | |
205 | public function fill_values($values) { | |
206 | $values = (array)$values; | |
207 | ||
208 | foreach ($values as $key => $value) { | |
209 | $found = 0; | |
210 | if ($attribute = $this->get_attribute($key)) { // Set value for attributes | |
211 | $attribute->set_value($value); | |
212 | $found++; | |
213 | } | |
214 | if ($final = $this->get_final_element($key)) { // Set value for final elements | |
215 | $final->set_value($value); | |
216 | $found++; | |
217 | } | |
218 | if (isset($this->aliases[$key])) { // Last chance, set value by processing final element aliases | |
219 | $this->aliases[$key]->set_value($value); | |
220 | $found++; | |
221 | } | |
222 | // Found more than once, notice | |
223 | // TODO: Route this through backup loggers | |
224 | if ($found > 1) { | |
225 | debugging('Key found more than once ' . $key, DEBUG_DEVELOPER); | |
226 | } | |
227 | } | |
228 | ||
229 | } | |
230 | ||
231 | // Protected API starts here | |
232 | ||
233 | protected function convert_table_params($params) { | |
234 | return $this->convert_sql_params($params); | |
235 | } | |
236 | ||
237 | protected function convert_sql_params($params) { | |
238 | $procparams = array(); // Reset processed params | |
239 | foreach ($params as $key => $param) { | |
240 | $procparams[$key] = $this->find_element($param); | |
241 | } | |
242 | return $procparams; | |
243 | } | |
244 | ||
245 | protected function find_element($param) { | |
246 | if ($param == backup::VAR_PARENTID) { // Look for first parent having id attribute/final_element | |
247 | $param = $this->find_first_parent_by_name('id'); | |
248 | ||
249 | // If the param is array, with key 'sqlparam', return the value without modifications | |
250 | } else if (is_array($param) && isset($param['sqlparam'])) { | |
251 | return $param['sqlparam']; | |
252 | ||
253 | } else if (((int)$param) >= 0) { // Search by path if param isn't a backup::XXX candidate | |
254 | $param = $this->find_element_by_path($param); | |
255 | } | |
256 | return $param; // Return the param unmodified | |
257 | } | |
258 | ||
259 | /** | |
260 | * Returns one instace of the @base_attribute class to work with | |
261 | * when attributes are added simply by name | |
262 | */ | |
263 | protected function get_new_attribute($name) { | |
264 | return new backup_attribute($name); | |
265 | } | |
266 | ||
267 | /** | |
268 | * Returns one instace of the @final_element class to work with | |
269 | * when final_elements are added simply by name | |
270 | */ | |
271 | protected function get_new_final_element($name) { | |
272 | return new backup_final_element($name); | |
273 | } | |
274 | ||
275 | /** | |
276 | * Returns one PHP iterator over each "ocurrence" of this nested | |
277 | * element (array or DB recordset). Delegated to backup_structure_dbops class | |
278 | */ | |
279 | protected function get_iterator($processor) { | |
280 | return backup_structure_dbops::get_iterator($this, $this->procparams, $processor); | |
281 | } | |
282 | } |