3 ///////////////////////////////////////////////////////////////////////////
5 // NOTICE OF COPYRIGHT //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
10 // Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
11 // (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
13 // This program is free software; you can redistribute it and/or modify //
14 // it under the terms of the GNU General Public License as published by //
15 // the Free Software Foundation; either version 2 of the License, or //
16 // (at your option) any later version. //
18 // This program is distributed in the hope that it will be useful, //
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
21 // GNU General Public License for more details: //
23 // http://www.gnu.org/copyleft/gpl.html //
25 ///////////////////////////////////////////////////////////////////////////
27 /// This class represent one XMLDB structure
29 class xmldb_structure
extends xmldb_object
{
36 * Creates one new xmldb_structure
38 function __construct($name) {
39 parent
::__construct($name);
41 $this->version
= NULL;
42 $this->tables
= array();
46 * Returns the path of the structure
53 * Returns the version of the structure
55 function getVersion() {
56 return $this->version
;
60 * Returns one xmldb_table
62 function &getTable($tablename) {
63 $i = $this->findTableInArray($tablename);
65 return $this->tables
[$i];
72 * Returns the position of one table in the array.
74 function &findTableInArray($tablename) {
75 foreach ($this->tables
as $i => $table) {
76 if ($tablename == $table->getName()) {
85 * This function will reorder the array of tables
87 function orderTables() {
88 $result = $this->orderElements($this->tables
);
90 $this->setTables($result);
98 * Returns the tables of the structure
100 function &getTables() {
101 return $this->tables
;
105 * Set the structure version
107 function setVersion($version) {
108 $this->version
= $version;
112 * Add one table to the structure, allowing to specify the desired order
113 * If it's not specified, then the table is added at the end.
115 function addTable(&$table, $after=NULL) {
117 /// Calculate the previous and next tables
122 $alltables =& $this->getTables();
125 $prevtable =& $alltables[key($alltables)];
128 $prevtable =& $this->getTable($after);
130 if ($prevtable && $prevtable->getNext()) {
131 $nexttable =& $this->getTable($prevtable->getNext());
134 /// Set current table previous and next attributes
136 $table->setPrevious($prevtable->getName());
137 $prevtable->setNext($table->getName());
140 $table->setNext($nexttable->getName());
141 $nexttable->setPrevious($table->getName());
143 /// Some more attributes
144 $table->setLoaded(true);
145 $table->setChanged(true);
146 /// Add the new table
147 $this->tables
[] =& $table;
148 /// Reorder the whole structure
149 $this->orderTables($this->tables
);
150 /// Recalculate the hash
151 $this->calculateHash(true);
152 /// We have one new table, so the structure has changed
153 $this->setVersion(userdate(time(), '%Y%m%d', 99, false));
154 $this->setChanged(true);
158 * Delete one table from the Structure
160 function deleteTable($tablename) {
162 $table =& $this->getTable($tablename);
164 $i = $this->findTableInArray($tablename);
165 /// Look for prev and next table
166 $prevtable =& $this->getTable($table->getPrevious());
167 $nexttable =& $this->getTable($table->getNext());
168 /// Change their previous and next attributes
170 $prevtable->setNext($table->getNext());
173 $nexttable->setPrevious($table->getPrevious());
176 unset($this->tables
[$i]);
177 /// Reorder the tables
178 $this->orderTables($this->tables
);
179 /// Recalculate the hash
180 $this->calculateHash(true);
181 /// We have one deleted table, so the structure has changed
182 $this->setVersion(userdate(time(), '%Y%m%d', 99, false));
183 $this->setChanged(true);
190 function setTables(&$tables) {
191 $this->tables
= $tables;
195 * Load data from XML to the structure
197 function arr2xmldb_structure($xmlarr) {
203 /// Debug the structure
204 /// traverse_xmlize($xmlarr); //Debug
205 /// print_object ($GLOBALS['traverse_array']); //Debug
206 /// $GLOBALS['traverse_array']=""; //Debug
208 /// Process structure attributes (path, comment and version)
209 if (isset($xmlarr['XMLDB']['@']['PATH'])) {
210 $this->path
= trim($xmlarr['XMLDB']['@']['PATH']);
212 $this->errormsg
= 'Missing PATH attribute';
213 $this->debug($this->errormsg
);
216 if (isset($xmlarr['XMLDB']['@']['VERSION'])) {
217 $this->version
= trim($xmlarr['XMLDB']['@']['VERSION']);
219 $this->errormsg
= 'Missing VERSION attribute';
220 $this->debug($this->errormsg
);
223 if (isset($xmlarr['XMLDB']['@']['COMMENT'])) {
224 $this->comment
= trim($xmlarr['XMLDB']['@']['COMMENT']);
225 } else if (!empty($CFG->xmldbdisablecommentchecking
)) {
228 $this->errormsg
= 'Missing COMMENT attribute';
229 $this->debug($this->errormsg
);
233 /// Iterate over tables
234 if (isset($xmlarr['XMLDB']['#']['TABLES']['0']['#']['TABLE'])) {
235 foreach ($xmlarr['XMLDB']['#']['TABLES']['0']['#']['TABLE'] as $xmltable) {
236 if (!$result) { //Skip on error
239 $name = trim($xmltable['@']['NAME']);
240 $table = new xmldb_table($name);
241 $table->arr2xmldb_table($xmltable);
242 $this->tables
[] = $table;
243 if (!$table->isLoaded()) {
244 $this->errormsg
= 'Problem loading table ' . $name;
245 $this->debug($this->errormsg
);
250 $this->errormsg
= 'Missing TABLES section';
251 $this->debug($this->errormsg
);
255 /// Perform some general checks over tables
256 if ($result && $this->tables
) {
257 /// Check tables names are ok (lowercase, a-z _-)
258 if (!$this->checkNameValues($this->tables
)) {
259 $this->errormsg
= 'Some TABLES name values are incorrect';
260 $this->debug($this->errormsg
);
263 /// Check previous & next are ok (duplicates and existing tables)
264 $this->fixPrevNext($this->tables
);
265 if ($result && !$this->checkPreviousNextValues($this->tables
)) {
266 $this->errormsg
= 'Some TABLES previous/next values are incorrect';
267 $this->debug($this->errormsg
);
271 if ($result && !$this->orderTables($this->tables
)) {
272 $this->errormsg
= 'Error ordering the tables';
273 $this->debug($this->errormsg
);
278 /// Set some attributes
280 $this->loaded
= true;
282 $this->calculateHash();
287 * This function calculate and set the hash of one xmldb_structure
289 function calculateHash($recursive = false) {
290 if (!$this->loaded
) {
293 $key = $this->name
. $this->path
. $this->comment
;
295 foreach ($this->tables
as $tbl) {
296 $table =& $this->getTable($tbl->getName());
298 $table->calculateHash($recursive);
300 $key .= $table->getHash();
303 $this->hash
= md5($key);
308 * This function will output the XML text for one structure
310 function xmlOutput() {
311 $o = '<?xml version="1.0" encoding="UTF-8" ?>' . "\n";
312 $o.= '<XMLDB PATH="' . $this->path
. '"';
313 $o.= ' VERSION="' . $this->version
. '"';
314 if ($this->comment
) {
315 $o.= ' COMMENT="' . htmlspecialchars($this->comment
) . '"'."\n";
317 $rel = array_fill(0, count(explode('/', $this->path
)), '..');
318 $rel = implode('/', $rel);
319 $o.= ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'."\n";
320 $o.= ' xsi:noNamespaceSchemaLocation="'.$rel.'/lib/xmldb/xmldb.xsd"'."\n";
324 $o.= ' <TABLES>' . "\n";
325 foreach ($this->tables
as $table) {
326 $o.= $table->xmlOutput();
328 $o.= ' </TABLES>' . "\n";
336 * This function returns the number of uses of one table inside
337 * a whole XMLDStructure. Useful to detect if the table must be
338 * locked. Return false if no uses are found.
340 function getTableUses($tablename) {
344 /// Check if some foreign key in the whole structure is using it
345 /// (by comparing the reftable with the tablename)
346 $alltables = $this->getTables();
348 foreach ($alltables as $table) {
349 $keys = $table->getKeys();
351 foreach ($keys as $key) {
352 if ($key->getType() == XMLDB_KEY_FOREIGN
) {
353 if ($tablename == $key->getRefTable()) {
354 $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
371 * This function returns the number of uses of one field inside
372 * a whole xmldb_structure. Useful to detect if the field must be
373 * locked. Return false if no uses are found.
375 function getFieldUses($tablename, $fieldname) {
379 /// Check if any key in the table is using it
380 $table = $this->getTable($tablename);
381 if ($keys = $table->getKeys()) {
382 foreach ($keys as $key) {
383 if (in_array($fieldname, $key->getFields()) ||
384 in_array($fieldname, $key->getRefFields())) {
385 $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
389 /// Check if any index in the table is using it
390 $table = $this->getTable($tablename);
391 if ($indexes = $table->getIndexes()) {
392 foreach ($indexes as $index) {
393 if (in_array($fieldname, $index->getFields())) {
394 $uses[] = 'table ' . $table->getName() . ' index ' . $index->getName();
398 /// Check if some foreign key in the whole structure is using it
399 /// By comparing the reftable and refields with the field)
400 $alltables = $this->getTables();
402 foreach ($alltables as $table) {
403 $keys = $table->getKeys();
405 foreach ($keys as $key) {
406 if ($key->getType() == XMLDB_KEY_FOREIGN
) {
407 if ($tablename == $key->getRefTable()) {
408 $reffieds = $key->getRefFields();
409 if (in_array($fieldname, $key->getRefFields())) {
410 $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
428 * This function returns the number of uses of one key inside
429 * a whole xmldb_structure. Useful to detect if the key must be
430 * locked. Return false if no uses are found.
432 function getKeyUses($tablename, $keyname) {
436 /// Check if some foreign key in the whole structure is using it
437 /// (by comparing the reftable and reffields with the fields in the key)
438 $mytable = $this->getTable($tablename);
439 $mykey = $mytable->getKey($keyname);
440 $alltables = $this->getTables();
441 if ($alltables && $mykey) {
442 foreach ($alltables as $table) {
443 $allkeys = $table->getKeys();
445 foreach ($allkeys as $key) {
446 if ($key->getType() != XMLDB_KEY_FOREIGN
) {
449 if ($key->getRefTable() == $tablename &&
450 implode(',', $key->getRefFields()) == implode(',', $mykey->getFields())) {
451 $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
467 * This function returns the number of uses of one index inside
468 * a whole xmldb_structure. Useful to detect if the index must be
469 * locked. Return false if no uses are found.
471 function getIndexUses($tablename, $indexname) {
475 /// Nothing to check, beause indexes haven't uses! Leave it here
476 /// for future checks...
487 * This function will return all the errors found in one structure
488 * looking recursively inside each table. Returns
489 * an array of errors or false
491 function getAllErrors() {
494 /// First the structure itself
495 if ($this->getError()) {
496 $errors[] = $this->getError();
498 /// Delegate to tables
499 if ($tables = $this->getTables()) {
500 foreach ($tables as $table) {
501 if ($tableerrors = $table->getAllErrors()) {
505 /// Add them to the errors array
507 $errors = array_merge($errors, $tableerrors);
511 if (count($errors)) {