Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
    /** @var array To be used in case we pass one in-memory structure */
33
    protected $var_array;
34
    /** @var string */
35
    protected $table;     // Table (without prefix) to fetch records from
36
    /** @var string */
37
    protected $tablesortby; // The field to sort by when using the table methods
38
    /** @var string */
39
    protected $sql;       // Raw SQL to fetch records from
40
    /** @var mixed */
41
    protected $params;    // Unprocessed params as specified in the set_source() call
42
    /** @var array */
43
    protected $procparams;// Processed (path resolved) params array
44
    /** @var array */
45
    protected $aliases;   // Define DB->final element aliases
46
    /** @var array */
47
    protected $fileannotations;   // array of file areas to be searched by file annotations
48
    /** @var int */
49
    protected $counter;   // Number of instances of this element that have been processed
50
    /** @var array */
51
    protected $results;  // Logs the results we encounter during the process.
52
    /** @var stdClass[] */
53
    protected $logs;     // Some log messages that could be retrieved later.
54
 
55
    /**
56
     * Constructor - instantiates one backup_nested_element, specifying its basic info.
57
     *
58
     * @param string $name name of the element
59
     * @param array  $attributes attributes this element will handle (optional, defaults to null)
60
     * @param array  $final_elements this element will handle (optional, defaults to null)
61
     */
62
    public function __construct($name, $attributes = null, $final_elements = null) {
63
        parent::__construct($name, $attributes, $final_elements);
64
        $this->var_array = null;
65
        $this->table     = null;
66
        $this->tablesortby = null;
67
        $this->sql       = null;
68
        $this->params    = null;
69
        $this->procparams= null;
70
        $this->aliases   = array();
71
        $this->fileannotations = array();
72
        $this->counter   = 0;
73
        $this->results  = array();
74
        $this->logs     = array();
75
    }
76
 
77
    /**
78
     * Process the nested element
79
     *
80
     * @param object $processor the processor
81
     * @return void
82
     */
83
    public function process($processor) {
84
        if (!$processor instanceof base_processor) { // No correct processor, throw exception
85
            throw new base_element_struct_exception('incorrect_processor');
86
        }
87
 
88
        $iterator = $this->get_iterator($processor); // Get the iterator over backup-able data
89
 
90
        foreach ($iterator as $key => $values) { // Process each "ocurrrence" of the nested element (recordset or array)
91
 
92
            // Fill the values of the attributes and final elements with the $values from the iterator
93
            $this->fill_values($values);
94
 
95
            // Perform pre-process tasks for the nested_element
96
            $processor->pre_process_nested_element($this);
97
 
98
            // Delegate the process of each attribute
99
            foreach ($this->get_attributes() as $attribute) {
100
                $attribute->process($processor);
101
            }
102
 
103
            // Main process tasks for the nested element, once its attributes have been processed
104
            $processor->process_nested_element($this);
105
 
106
            // Delegate the process of each final_element
107
            foreach ($this->get_final_elements() as $final_element) {
108
                $final_element->process($processor);
109
            }
110
 
111
            // Delegate the process to the optigroup
112
            if ($this->get_optigroup()) {
113
                $this->get_optigroup()->process($processor);
114
            }
115
 
116
            // Delegate the process to each child nested_element
117
            foreach ($this->get_children() as $child) {
118
                $child->process($processor);
119
            }
120
 
121
            // Perform post-process tasks for the nested element
122
            $processor->post_process_nested_element($this);
123
 
124
            // Everything processed, clean values before next iteration
125
            $this->clean_values();
126
 
127
            // Increment counter for this element
128
            $this->counter++;
129
 
130
            // For root element, check we only have 1 element
131
            if ($this->get_parent() === null && $this->counter > 1) {
132
                throw new base_element_struct_exception('root_only_one_ocurrence', $this->get_name());
133
            }
134
        }
135
        // Close the iterator (DB recordset / array iterator)
136
        $iterator->close();
137
    }
138
 
139
    /**
140
     * Saves a log message to an array
141
     *
142
     * @see backup_helper::log()
143
     * @param string $message to add to the logs
144
     * @param int $level level of importance {@link backup::LOG_DEBUG} and other constants
145
     * @param mixed $a to be included in $message
146
     * @param int $depth of the message
147
     * @param display $bool supporting translation via get_string() if true
148
     * @return void
149
     */
150
    protected function add_log($message, $level, $a = null, $depth = null, $display = false) {
151
        // Adding the result to the oldest parent.
152
        if ($this->get_parent()) {
153
            $parent = $this->get_grandparent();
154
            $parent->add_log($message, $level, $a, $depth, $display);
155
        } else {
156
            $log = new stdClass();
157
            $log->message = $message;
158
            $log->level = $level;
159
            $log->a = $a;
160
            $log->depth = $depth;
161
            $log->display = $display;
162
            $this->logs[] = $log;
163
        }
164
    }
165
 
166
    /**
167
     * Saves the results to an array
168
     *
169
     * @param array $result associative array
170
     * @return void
171
     */
172
    protected function add_result($result) {
173
        if (is_array($result)) {
174
            // Adding the result to the oldest parent.
175
            if ($this->get_parent()) {
176
                $parent = $this->get_grandparent();
177
                $parent->add_result($result);
178
            } else {
179
                $this->results = array_merge($this->results, $result);
180
            }
181
        }
182
    }
183
 
184
    /**
185
     * Returns the logs
186
     *
187
     * @return array of log objects
188
     */
189
    public function get_logs() {
190
        return $this->logs;
191
    }
192
 
193
    /**
194
     * Returns the results
195
     *
196
     * @return associative array of results
197
     */
198
    public function get_results() {
199
        return $this->results;
200
    }
201
 
202
    public function set_source_array($arr) {
203
        // TODO: Only elements having final elements can set source
204
        $this->var_array = $arr;
205
    }
206
 
207
    public function set_source_table($table, $params, $sortby = null) {
208
        if (!is_array($params)) { // Check we are passing array
209
            throw new base_element_struct_exception('setsourcerequiresarrayofparams');
210
        }
211
        // TODO: Only elements having final elements can set source
212
        $this->table = $table;
213
        $this->procparams = $this->convert_table_params($params);
214
        if ($sortby) {
215
            $this->tablesortby = $sortby;
216
        }
217
    }
218
 
219
    public function set_source_sql($sql, $params) {
220
        if (!is_array($params)) { // Check we are passing array
221
            throw new base_element_struct_exception('setsourcerequiresarrayofparams');
222
        }
223
        // TODO: Only elements having final elements can set source
224
        $this->sql = $sql;
225
        $this->procparams = $this->convert_sql_params($params);
226
    }
227
 
228
    public function set_source_alias($dbname, $finalelementname) {
229
        // Get final element
230
        $finalelement = $this->get_final_element($finalelementname);
231
        if (!$finalelement) { // Final element incorrect, throw exception
232
            throw new base_element_struct_exception('incorrectaliasfinalnamenotfound', $finalelementname);
233
        } else {
234
            $this->aliases[$dbname] = $finalelement;
235
        }
236
    }
237
 
238
    public function annotate_files($component, $filearea, $elementname, $filesctxid = null) {
239
        if (!array_key_exists($component, $this->fileannotations)) {
240
            $this->fileannotations[$component] = array();
241
        }
242
 
243
        if ($elementname !== null) { // Check elementname is valid
244
            $elementname = $this->find_element($elementname); //TODO: no warning here? (skodak)
245
        }
246
 
247
        if (array_key_exists($filearea, $this->fileannotations[$component])) {
248
            throw new base_element_struct_exception('annotate_files_duplicate_annotation', "$component/$filearea/$elementname");
249
        }
250
 
251
        $info = new stdclass();
252
        $info->element   = $elementname;
253
        $info->contextid = $filesctxid;
254
        $this->fileannotations[$component][$filearea] = $info;
255
    }
256
 
257
    public function annotate_ids($itemname, $elementname) {
258
        $element = $this->find_element($elementname);
259
        $element->set_annotation_item($itemname);
260
    }
261
 
262
    /**
263
     * Returns one array containing the element in the
264
     * @backup_structure and the areas to be searched
265
     */
266
    public function get_file_annotations() {
267
        return $this->fileannotations;
268
    }
269
 
270
    public function get_source_array() {
271
        return $this->var_array;
272
    }
273
 
274
    public function get_source_table() {
275
        return $this->table;
276
    }
277
 
278
    public function get_source_table_sortby() {
279
        return $this->tablesortby;
280
    }
281
 
282
    public function get_source_sql() {
283
        return $this->sql;
284
    }
285
 
286
    public function get_counter() {
287
        return $this->counter;
288
    }
289
 
290
    /**
291
     * Simple filler that, matching by name, will fill both attributes and final elements
292
     * depending of this nested element, debugging info about non-matching elements and/or
293
     * elements present in both places. Accept both arrays and objects.
294
     */
295
    public function fill_values($values) {
296
        $values = (array)$values;
297
 
298
        foreach ($values as $key => $value) {
299
            $found = 0;
300
            if ($attribute = $this->get_attribute($key)) { // Set value for attributes
301
                $attribute->set_value($value);
302
                $found++;
303
            }
304
            if ($final = $this->get_final_element($key)) { // Set value for final elements
305
                $final->set_value($value);
306
                $found++;
307
            }
308
            if (isset($this->aliases[$key])) { // Last chance, set value by processing final element aliases
309
                $this->aliases[$key]->set_value($value);
310
                $found++;
311
            }
312
            // Found more than once, notice
313
                // TODO: Route this through backup loggers
314
            if ($found > 1) {
315
                debugging('Key found more than once ' . $key, DEBUG_DEVELOPER);
316
            }
317
        }
318
 
319
    }
320
 
321
// Protected API starts here
322
 
323
    protected function convert_table_params($params) {
324
        return $this->convert_sql_params($params);
325
    }
326
 
327
    protected function convert_sql_params($params) {
328
        $procparams = array(); // Reset processed params
329
        foreach ($params as $key => $param) {
330
            $procparams[$key] = $this->find_element($param);
331
        }
332
        return $procparams;
333
    }
334
 
335
    protected function find_element($param) {
336
        if ($param == backup::VAR_PARENTID) { // Look for first parent having id attribute/final_element
337
            $param = $this->find_first_parent_by_name('id');
338
 
339
        // If the param is array, with key 'sqlparam', return the value without modifications
340
        } else if (is_array($param) && array_key_exists('sqlparam', $param)) {
341
            return $param['sqlparam'];
342
 
343
        } else if (((int)$param) >= 0) {  // Search by path if param isn't a backup::XXX candidate
344
            $param = $this->find_element_by_path($param);
345
        }
346
        return $param; // Return the param unmodified
347
    }
348
 
349
    /**
350
     * Returns one instace of the @base_attribute class to work with
351
     * when attributes are added simply by name
352
     */
353
    protected function get_new_attribute($name) {
354
        return new backup_attribute($name);
355
    }
356
 
357
    /**
358
     * Returns one instace of the @final_element class to work with
359
     * when final_elements are added simply by name
360
     */
361
    protected function get_new_final_element($name) {
362
        return new backup_final_element($name);
363
    }
364
 
365
    /**
366
     * Returns one PHP iterator over each "ocurrence" of this nested
367
     * element (array or DB recordset). Delegated to backup_structure_dbops class
368
     */
369
    protected function get_iterator($processor) {
370
        return backup_structure_dbops::get_iterator($this, $this->procparams, $processor);
371
    }
372
}