1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * This class represent one XMLDB Key
19 *
20 * @package    core_xmldb
21 * @copyright  1999 onwards Martin Dougiamas     http://dougiamas.com
22 *             2001-3001 Eloy Lafuente (stronk7) http://contiento.com
23 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28
29class xmldb_key extends xmldb_object {
30
31    /** @var int type of key */
32    protected $type;
33
34    /** @var array of fields */
35    protected $fields;
36
37    /** @var string referenced table */
38    protected $reftable;
39
40    /** @var array referenced fields */
41    protected $reffields;
42
43    /**
44     * Creates one new xmldb_key
45     * @param string $name
46     * @param string $type XMLDB_KEY_[PRIMARY|UNIQUE|FOREIGN|FOREIGN_UNIQUE]
47     * @param array $fields an array of fieldnames to build the key over
48     * @param string $reftable name of the table the FK points to or null
49     * @param array $reffields an array of fieldnames in the FK table or null
50     */
51    public function __construct($name, $type=null, $fields=array(), $reftable=null, $reffields=null) {
52        $this->type = null;
53        $this->fields = array();
54        $this->reftable = null;
55        $this->reffields = array();
56        parent::__construct($name);
57        $this->set_attributes($type, $fields, $reftable, $reffields);
58    }
59
60    /**
61     * Set all the attributes of one xmldb_key
62     *
63     * @param string $type XMLDB_KEY_[PRIMARY|UNIQUE|FOREIGN|FOREIGN_UNIQUE]
64     * @param array $fields an array of fieldnames to build the key over
65     * @param string $reftable name of the table the FK points to or null
66     * @param array $reffields an array of fieldnames in the FK table or null
67     */
68    public function set_attributes($type, $fields, $reftable=null, $reffields=null) {
69        $this->type = $type;
70        $this->fields = $fields;
71        $this->reftable = $reftable;
72        $this->reffields = empty($reffields) ? array() : $reffields;
73    }
74
75    /**
76     * Get the key type
77     * @return int
78     */
79    public function getType() {
80        return $this->type;
81    }
82
83    /**
84     * Set the key type
85     * @param int $type
86     */
87    public function setType($type) {
88        $this->type = $type;
89    }
90
91    /**
92     * Set the key fields
93     * @param array $fields
94     */
95    public function setFields($fields) {
96        $this->fields = $fields;
97    }
98
99    /**
100     * Set the key reftable
101     * @param string $reftable
102     */
103    public function setRefTable($reftable) {
104        $this->reftable = $reftable;
105    }
106
107    /**
108     * Set the key reffields
109     * @param array $reffields
110     */
111    public function setRefFields($reffields) {
112        $this->reffields = $reffields;
113    }
114
115    /**
116     * Get the key fields
117     * @return array
118     */
119    public function getFields() {
120        return $this->fields;
121    }
122
123    /**
124     * Get the key reftable
125     * @return string
126     */
127    public function getRefTable() {
128        return $this->reftable;
129    }
130
131    /**
132     * Get the key reffields
133     * @return array reference to ref fields
134     */
135    public function getRefFields() {
136        return $this->reffields;
137    }
138
139    /**
140     * Load data from XML to the key
141     * @param array $xmlarr
142     * @return bool success
143     */
144    public function arr2xmldb_key($xmlarr) {
145
146        $result = true;
147
148        // Debug the table
149        // traverse_xmlize($xmlarr);                   //Debug
150        // print_object ($GLOBALS['traverse_array']);  //Debug
151        // $GLOBALS['traverse_array']="";              //Debug
152
153        // Process key attributes (name, type, fields, reftable,
154        // reffields, comment, previous, next)
155        if (isset($xmlarr['@']['NAME'])) {
156            $this->name = trim($xmlarr['@']['NAME']);
157        } else {
158            $this->errormsg = 'Missing NAME attribute';
159            $this->debug($this->errormsg);
160            $result = false;
161        }
162
163        if (isset($xmlarr['@']['TYPE'])) {
164            // Check for valid type
165            $type = $this->getXMLDBKeyType(trim($xmlarr['@']['TYPE']));
166            if ($type) {
167                $this->type = $type;
168            } else {
169                $this->errormsg = 'Invalid TYPE attribute';
170                $this->debug($this->errormsg);
171                $result = false;
172            }
173        } else {
174            $this->errormsg = 'Missing TYPE attribute';
175            $this->debug($this->errormsg);
176            $result = false;
177        }
178
179        if (isset($xmlarr['@']['FIELDS'])) {
180            $fields = strtolower(trim($xmlarr['@']['FIELDS']));
181            if ($fields) {
182                $fieldsarr = explode(',',$fields);
183                if ($fieldsarr) {
184                    foreach ($fieldsarr as $key => $element) {
185                        $fieldsarr [$key] = trim($element);
186                    }
187                } else {
188                    $this->errormsg = 'Incorrect FIELDS attribute (comma separated of fields)';
189                    $this->debug($this->errormsg);
190                    $result = false;
191                }
192            } else {
193                $this->errormsg = 'Empty FIELDS attribute';
194                $this->debug($this->errormsg);
195                $result = false;
196            }
197        } else {
198            $this->errormsg = 'Missing FIELDS attribute';
199            $this->debug($this->errormsg);
200            $result = false;
201        }
202        // Finally, set the array of fields
203        $this->fields = $fieldsarr;
204
205        if (isset($xmlarr['@']['REFTABLE'])) {
206            // Check we are in a FK
207            if ($this->type == XMLDB_KEY_FOREIGN ||
208                $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
209                $reftable = strtolower(trim($xmlarr['@']['REFTABLE']));
210                if (!$reftable) {
211                    $this->errormsg = 'Empty REFTABLE attribute';
212                    $this->debug($this->errormsg);
213                    $result = false;
214                }
215            } else {
216                $this->errormsg = 'Wrong REFTABLE attribute (only FK can have it)';
217                $this->debug($this->errormsg);
218                $result = false;
219            }
220        } else if ($this->type == XMLDB_KEY_FOREIGN ||
221                   $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
222            $this->errormsg = 'Missing REFTABLE attribute';
223            $this->debug($this->errormsg);
224            $result = false;
225        }
226        // Finally, set the reftable
227        if ($this->type == XMLDB_KEY_FOREIGN ||
228            $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
229            $this->reftable = $reftable;
230        }
231
232        if (isset($xmlarr['@']['REFFIELDS'])) {
233            // Check we are in a FK
234            if ($this->type == XMLDB_KEY_FOREIGN ||
235                $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
236                $reffields = strtolower(trim($xmlarr['@']['REFFIELDS']));
237                if ($reffields) {
238                    $reffieldsarr = explode(',',$reffields);
239                    if ($reffieldsarr) {
240                        foreach ($reffieldsarr as $key => $element) {
241                            $reffieldsarr [$key] = trim($element);
242                        }
243                    } else {
244                        $this->errormsg = 'Incorrect REFFIELDS attribute (comma separated of fields)';
245                        $this->debug($this->errormsg);
246                        $result = false;
247                    }
248                } else {
249                    $this->errormsg = 'Empty REFFIELDS attribute';
250                    $this->debug($this->errormsg);
251                    $result = false;
252                }
253            } else {
254                $this->errormsg = 'Wrong REFFIELDS attribute (only FK can have it)';
255                $this->debug($this->errormsg);
256                $result = false;
257            }
258        } else if ($this->type == XMLDB_KEY_FOREIGN ||
259                   $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
260            $this->errormsg = 'Missing REFFIELDS attribute';
261            $this->debug($this->errormsg);
262            $result = false;
263        }
264        // Finally, set the array of reffields
265        if ($this->type == XMLDB_KEY_FOREIGN ||
266            $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
267            $this->reffields = $reffieldsarr;
268        }
269
270        if (isset($xmlarr['@']['COMMENT'])) {
271            $this->comment = trim($xmlarr['@']['COMMENT']);
272        }
273
274        // Set some attributes
275        if ($result) {
276            $this->loaded = true;
277        }
278        $this->calculateHash();
279        return $result;
280    }
281
282    /**
283     * This function returns the correct XMLDB_KEY_XXX value for the
284     * string passed as argument
285     * @param string $type
286     * @return int
287     */
288    public function getXMLDBKeyType($type) {
289
290        $result = XMLDB_KEY_INCORRECT;
291
292        switch (strtolower($type)) {
293            case 'primary':
294                $result = XMLDB_KEY_PRIMARY;
295                break;
296            case 'unique':
297                $result = XMLDB_KEY_UNIQUE;
298                break;
299            case 'foreign':
300                $result = XMLDB_KEY_FOREIGN;
301                break;
302            case 'foreign-unique':
303                $result = XMLDB_KEY_FOREIGN_UNIQUE;
304                break;
305            // case 'check':  //Not supported
306            //     $result = XMLDB_KEY_CHECK;
307            //     break;
308        }
309        // Return the normalized XMLDB_KEY
310        return $result;
311    }
312
313    /**
314     * This function returns the correct name value for the
315     * XMLDB_KEY_XXX passed as argument
316     * @param int $type
317     * @return string
318     */
319    public function getXMLDBKeyName($type) {
320
321        $result = '';
322
323        switch ($type) {
324            case XMLDB_KEY_PRIMARY:
325                $result = 'primary';
326                break;
327            case XMLDB_KEY_UNIQUE:
328                $result = 'unique';
329                break;
330            case XMLDB_KEY_FOREIGN:
331                $result = 'foreign';
332                break;
333            case XMLDB_KEY_FOREIGN_UNIQUE:
334                $result = 'foreign-unique';
335                break;
336            // case XMLDB_KEY_CHECK:  //Not supported
337            //     $result = 'check';
338            //     break;
339        }
340        // Return the normalized name
341        return $result;
342    }
343
344    /**
345     * This function calculate and set the hash of one xmldb_key
346     * @param bool $recursive
347     */
348     public function calculateHash($recursive = false) {
349        if (!$this->loaded) {
350            $this->hash = null;
351        } else {
352            $key = $this->type . implode(', ', $this->fields);
353            if ($this->type == XMLDB_KEY_FOREIGN ||
354                $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
355                $key .= $this->reftable . implode(', ', $this->reffields);
356            }
357                    ;
358            $this->hash = md5($key);
359        }
360    }
361
362    /**
363     *This function will output the XML text for one key
364     * @return string
365     */
366    public function xmlOutput() {
367        $o = '';
368        $o.= '        <KEY NAME="' . $this->name . '"';
369        $o.= ' TYPE="' . $this->getXMLDBKeyName($this->type) . '"';
370        $o.= ' FIELDS="' . implode(', ', $this->fields) . '"';
371        if ($this->type == XMLDB_KEY_FOREIGN ||
372            $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
373            $o.= ' REFTABLE="' . $this->reftable . '"';
374            $o.= ' REFFIELDS="' . implode(', ', $this->reffields) . '"';
375        }
376        if ($this->comment) {
377            $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
378        }
379        $o.= '/>' . "\n";
380
381        return $o;
382    }
383
384    /**
385     * This function will set all the attributes of the xmldb_key object
386     * based on information passed in one ADOkey
387     * @oaram array $adokey
388     */
389    public function setFromADOKey($adokey) {
390
391        // Calculate the XMLDB_KEY
392        switch (strtolower($adokey['name'])) {
393            case 'primary':
394                $this->type = XMLDB_KEY_PRIMARY;
395                break;
396            default:
397                $this->type = XMLDB_KEY_UNIQUE;
398        }
399        // Set the fields, converting all them to lowercase
400        $fields = array_flip(array_change_key_case(array_flip($adokey['columns'])));
401        $this->fields = $fields;
402        // Some more fields
403        $this->loaded = true;
404        $this->changed = true;
405    }
406
407    /**
408     * Returns the PHP code needed to define one xmldb_key
409     * @return string
410     */
411    public function getPHP() {
412
413        $result = '';
414
415        // The type
416        switch ($this->getType()) {
417            case XMLDB_KEY_PRIMARY:
418                $result .= 'XMLDB_KEY_PRIMARY' . ', ';
419                break;
420            case XMLDB_KEY_UNIQUE:
421                $result .= 'XMLDB_KEY_UNIQUE' . ', ';
422                break;
423            case XMLDB_KEY_FOREIGN:
424                $result .= 'XMLDB_KEY_FOREIGN' . ', ';
425                break;
426            case XMLDB_KEY_FOREIGN_UNIQUE:
427                $result .= 'XMLDB_KEY_FOREIGN_UNIQUE' . ', ';
428                break;
429        }
430        // The fields
431        $keyfields = $this->getFields();
432        if (!empty($keyfields)) {
433            $result .= "['".  implode("', '", $keyfields) . "']";
434        } else {
435            $result .= 'null';
436        }
437        // The FKs attributes
438        if ($this->getType() == XMLDB_KEY_FOREIGN ||
439            $this->getType() == XMLDB_KEY_FOREIGN_UNIQUE) {
440            // The reftable
441            $reftable = $this->getRefTable();
442            if (!empty($reftable)) {
443                $result .= ", '" . $reftable . "', ";
444            } else {
445                $result .= 'null, ';
446            }
447            // The reffields
448            $reffields = $this->getRefFields();
449            if (!empty($reffields)) {
450                $result .= "['".  implode("', '", $reffields) . "']";
451            } else {
452                $result .= 'null';
453            }
454        }
455        // Return result
456        return $result;
457    }
458
459    /**
460     * Shows info in a readable format
461     * @return string
462     */
463    public function readableInfo() {
464        $o = '';
465        // type
466        $o .= $this->getXMLDBKeyName($this->type);
467        // fields
468        $o .= ' (' . implode(', ', $this->fields) . ')';
469        // foreign key
470        if ($this->type == XMLDB_KEY_FOREIGN ||
471            $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
472            $o .= ' references ' . $this->reftable . ' (' . implode(', ', $this->reffields) . ')';
473        }
474
475        return $o;
476    }
477}
478