forked from moodle/moodle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathxmldb_object.php
494 lines (450 loc) · 16.3 KB
/
xmldb_object.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
<?php
///////////////////////////////////////////////////////////////////////////
// //
// NOTICE OF COPYRIGHT //
// //
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
// http://moodle.com //
// //
// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
// (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation; either version 2 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License for more details: //
// //
// http://www.gnu.org/copyleft/gpl.html //
// //
///////////////////////////////////////////////////////////////////////////
/// This class represent the XMLDB base class where all the common pieces
/// are defined
class xmldb_object {
var $name;
var $comment;
var $previous;
var $next;
var $hash;
var $loaded;
var $changed;
var $errormsg;
/**
* Creates one new xmldb_object
*/
function __construct($name) {
$this->name = $name;
$this->comment = NULL;
$this->previous = NULL;
$this->next = NULL;
$this->hash = NULL;
$this->loaded = false;
$this->changed = false;
$this->errormsg = NULL;
}
/**
* This function returns true/false, if the xmldb_object has been loaded
*/
function isLoaded() {
return $this->loaded;
}
/**
* This function returns true/false, if the xmldb_object has changed
*/
function hasChanged() {
return $this->changed;
}
/**
* This function returns the comment of one xmldb_object
*/
function getComment() {
return $this->comment;
}
/**
* This function returns the hash of one xmldb_object
*/
function getHash() {
return $this->hash;
}
/**
* This function will return the name of the previous xmldb_object
*/
function getPrevious() {
return $this->previous;
}
/**
* This function will return the name of the next xmldb_object
*/
function getNext() {
return $this->next;
}
/**
* This function will return the name of the xmldb_object
*/
function getName() {
return $this->name;
}
/**
* This function will return the error detected in the object
*/
function getError() {
return $this->errormsg;
}
/**
* This function will set the comment of the xmldb_object
*/
function setComment($comment) {
$this->comment = $comment;
}
/**
* This function will set the previous of the xmldb_object
*/
function setPrevious($previous) {
$this->previous = $previous;
}
/**
* This function will set the next of the xmldb_object
*/
function setNext($next) {
$this->next = $next;
}
/**
* This function will set the hash of the xmldb_object
*/
function setHash($hash) {
$this->hash = $hash;
}
/**
* This function will set the loaded field of the xmldb_object
*/
function setLoaded($loaded = true) {
$this->loaded = $loaded;
}
/**
* This function will set the changed field of the xmldb_object
*/
function setChanged($changed = true) {
$this->changed = $changed;
}
/**
* This function will set the name field of the xmldb_object
*/
function setName($name) {
$this->name = $name;
}
/**
* This function will check if one key name is ok or no (true/false)
* only lowercase a-z, 0-9 and _ are allowed
*/
function checkName () {
$result = true;
if ($this->name != preg_replace('/[^a-z0-9_ -]/i', '', $this->name)) {
$result = false;
}
return $result;
}
/**
* This function will check that all the elements in one array
* have a correct name [a-z0-9_]
*/
function checkNameValues(&$arr) {
$result = true;
/// TODO: Perhaps, add support for reserved words
/// Check the name only contains valid chars
if ($arr) {
foreach($arr as $element) {
if (!$element->checkName()) {
$result = false;
}
}
}
/// Check there aren't duplicate names
if ($arr) {
$existing_fields = array();
foreach($arr as $element) {
if (in_array($element->getName(), $existing_fields)) {
debugging('Object ' . $element->getName() . ' is duplicated!', DEBUG_DEVELOPER);
$result = false;
}
$existing_fields[] = $element->getName();
}
}
return $result;
}
/**
* Reconstruct previous/next attributes.
*/
function fixPrevNext(&$arr) {
global $CFG;
if (empty($CFG->xmldbreconstructprevnext)) {
return false;
}
$tweaked = false;
$prev = null;
foreach ($arr as $key=>$el) {
$prev_value = $arr[$key]->previous;
$next_value = $arr[$key]->next;
$arr[$key]->next = null;
$arr[$key]->previous = null;
if ($prev !== null) {
$arr[$prev]->next = $arr[$key]->name;
$arr[$key]->previous = $arr[$prev]->name;
}
$prev = $key;
if ($prev_value != $arr[$key]->previous or $next_value != $arr[$key]->next) {
$tweaked = true;
}
}
return $tweaked;
}
/**
* This function will check that all the elements in one array
* have a consistent info in their previous/next fields
*/
function checkPreviousNextValues(&$arr) {
global $CFG;
if (!empty($CFG->xmldbdisablenextprevchecking)) {
return true;
}
$result = true;
/// Check that only one element has the previous not set
if ($arr) {
$counter = 0;
foreach($arr as $element) {
if (!$element->getPrevious()) {
$counter++;
}
}
if ($counter != 1) {
debugging('The number of objects with previous not set is different from 1', DEBUG_DEVELOPER);
$result = false;
}
}
/// Check that only one element has the next not set
if ($result && $arr) {
$counter = 0;
foreach($arr as $element) {
if (!$element->getNext()) {
$counter++;
}
}
if ($counter != 1) {
debugging('The number of objects with next not set is different from 1', DEBUG_DEVELOPER);
$result = false;
}
}
/// Check that all the previous elements are existing elements
if ($result && $arr) {
foreach($arr as $element) {
if ($element->getPrevious()) {
$i = $this->findObjectInArray($element->getPrevious(), $arr);
if ($i === NULL) {
debugging('Object ' . $element->getName() . ' says PREVIOUS="' . $element->getPrevious() . '" but that other object does not exist.', DEBUG_DEVELOPER);
$result = false;
}
}
}
}
/// Check that all the next elements are existing elements
if ($result && $arr) {
foreach($arr as $element) {
if ($element->getNext()) {
$i = $this->findObjectInArray($element->getNext(), $arr);
if ($i === NULL) {
debugging('Object ' . $element->getName() . ' says NEXT="' . $element->getNext() . '" but that other object does not exist.', DEBUG_DEVELOPER);
$result = false;
}
}
}
}
/// Check that there aren't duplicates in the previous values
if ($result && $arr) {
$existarr = array();
foreach($arr as $element) {
if (in_array($element->getPrevious(), $existarr)) {
$result = false;
debugging('Object ' . $element->getName() . ' says PREVIOUS="' . $element->getPrevious() . '" but another object has already said that!', DEBUG_DEVELOPER);
} else {
$existarr[] = $element->getPrevious();
}
}
}
/// Check that there aren't duplicates in the next values
if ($result && $arr) {
$existarr = array();
foreach($arr as $element) {
if (in_array($element->getNext(), $existarr)) {
$result = false;
debugging('Object ' . $element->getName() . ' says NEXT="' . $element->getNext() . '" but another object has already said that!', DEBUG_DEVELOPER);
} else {
$existarr[] = $element->getNext();
}
}
}
/// Check that there aren't next values pointing to themselves
if ($result && $arr) {
foreach($arr as $element) {
if ($element->getNext() == $element->getName()) {
$result = false;
debugging('Object ' . $element->getName() . ' says NEXT="' . $element->getNext() . '" and that is wrongly recursive!', DEBUG_DEVELOPER);
}
}
}
/// Check that there aren't prev values pointing to themselves
if ($result && $arr) {
foreach($arr as $element) {
if ($element->getPrevious() == $element->getName()) {
$result = false;
debugging('Object ' . $element->getName() . ' says PREVIOUS="' . $element->getPrevious() . '" and that is wrongly recursive!', DEBUG_DEVELOPER);
}
}
}
return $result;
}
/**
* This function will order all the elements in one array, following
* the previous/next rules
*/
function orderElements($arr) {
global $CFG;
$result = true;
if (!empty($CFG->xmldbdisablenextprevchecking)) {
return $arr;
}
/// Create a new array
$newarr = array();
if (!empty($arr)) {
$currentelement = NULL;
/// Get the element without previous
foreach($arr as $key => $element) {
if (!$element->getPrevious()) {
$currentelement = $arr[$key];
$newarr[0] = $arr[$key];
}
}
if (!$currentelement) {
$result = false;
}
/// Follow the next rules
$counter = 1;
while ($result && $currentelement->getNext()) {
$i = $this->findObjectInArray($currentelement->getNext(), $arr);
$currentelement = $arr[$i];
$newarr[$counter] = $arr[$i];
$counter++;
}
/// Compare number of elements between original and new array
if ($result && count($arr) != count($newarr)) {
$result = false;
}
/// Check that previous/next is ok (redundant but...)
if ($this->checkPreviousNextValues($newarr)) {
$result = $newarr;
} else {
$result = false;
}
} else {
$result = array();
}
return $result;
}
/**
* Returns the position of one object in the array.
*/
function &findObjectInArray($objectname, $arr) {
foreach ($arr as $i => $object) {
if ($objectname == $object->getName()) {
return $i;
}
}
$null = NULL;
return $null;
}
/**
* This function will display a readable info about the xmldb_object
* (should be implemented inside each XMLDBxxx object)
*/
function readableInfo() {
return get_class($this);
}
/**
* This function will perform the central debug of all the XMLDB classes
* being called automatically every time one error is found. Apart from
* the main actions performed in it (XMLDB agnostic) it looks for one
* function called xmldb_debug() and invokes it, passing both the
* message code and the whole object.
* So, to perform custom debugging just add such function to your libs.
*
* Call to the external hook function can be disabled by request by
* defining XMLDB_SKIP_DEBUG_HOOK
*/
function debug($message) {
/// Check for xmldb_debug($message, $xmldb_object)
$funcname = 'xmldb_debug';
/// If exists and XMLDB_SKIP_DEBUG_HOOK is undefined
if (function_exists($funcname) && !defined('XMLDB_SKIP_DEBUG_HOOK')) {
$funcname($message, $this);
}
}
/**
* Returns one array of elements from one comma separated string,
* supporting quoted strings containing commas and concat function calls
*/
function comma2array($string) {
$foundquotes = array();
$foundconcats = array();
/// Extract all the concat elements from the string
preg_match_all("/(CONCAT\(.*?\))/is", $string, $matches);
foreach (array_unique($matches[0]) as $key=>$value) {
$foundconcats['<#'.$key.'#>'] = $value;
}
if (!empty($foundconcats)) {
$string = str_replace($foundconcats,array_keys($foundconcats),$string);
}
/// Extract all the quoted elements from the string (skipping
/// backslashed quotes that are part of the content.
preg_match_all("/(''|'.*?[^\\\\]')/is", $string, $matches);
foreach (array_unique($matches[0]) as $key=>$value) {
$foundquotes['<%'.$key.'%>'] = $value;
}
if (!empty($foundquotes)) {
$string = str_replace($foundquotes,array_keys($foundquotes),$string);
}
/// Explode safely the string
$arr = explode (',', $string);
/// Put the concat and quoted elements back again, trimming every element
if ($arr) {
foreach ($arr as $key => $element) {
/// Clear some spaces
$element = trim($element);
/// Replace the quoted elements if exists
if (!empty($foundquotes)) {
$element = str_replace(array_keys($foundquotes), $foundquotes, $element);
}
/// Replace the concat elements if exists
if (!empty($foundconcats)) {
$element = str_replace(array_keys($foundconcats), $foundconcats, $element);
}
/// Delete any backslash used for quotes. XMLDB stuff will add them before insert
$arr[$key] = str_replace("\\'", "'", $element);
}
}
return $arr;
}
/**
* Validates the definition of objects and returns error message.
*
* The error message should not be localised because it is intended for developers,
* end users and admins should never see these problems!
*
* @param xmldb_table $xmldb_table optional when object is table
* @return string null if ok, error message if problem found
*/
function validateDefinition(xmldb_table $xmldb_table=null) {
return null;
}
}