commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-old / civicrm / packages / DB / Table / Database.php
1 <?php
2
3 // vim: set et ts=4 sw=4 fdm=marker:
4
5 /**
6 * DB_Table_Database relational database abstraction class
7 *
8 * PHP versions 4 and 5
9 *
10 * LICENSE:
11 *
12 * Copyright (c) 1997-2007, Paul M. Jones <pmjones@php.net>
13 * David C. Morse <morse@php.net>
14 * Mark Wiesemann <wiesemann@php.net>
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 *
21 * * Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * * Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
26 * * The names of the authors may not be used to endorse or promote products
27 * derived from this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
30 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
31 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
33 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
34 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
37 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * @category Database
42 * @package DB_Table
43 * @author David C. Morse <morse@php.net>
44 * @license http://opensource.org/licenses/bsd-license.php New BSD License
45 * @version CVS: $Id: Database.php,v 1.15 2007/12/13 16:52:14 wiesemann Exp $
46 * @link http://pear.php.net/package/DB_Table
47 */
48
49 // {{{ Error code constants
50
51 /**
52 * Parameter is not a DB/MDB2 object
53 */
54 define('DB_TABLE_DATABASE_ERR_DB_OBJECT', -201);
55
56 /**
57 * Error in addTable, parameter $table_obj is not a DB_Table object
58 */
59 define('DB_TABLE_DATABASE_ERR_DBTABLE_OBJECT', -202);
60
61 /**
62 * Error for table name that does not exist in the database
63 */
64 define('DB_TABLE_DATABASE_ERR_NO_TBL', -203);
65
66 /**
67 * Error for table name parameter that is not a string
68 */
69 define('DB_TABLE_DATABASE_ERR_TBL_NOT_STRING', -204);
70
71 /**
72 * Error in getCol for a non-existent column name
73 */
74 define('DB_TABLE_DATABASE_ERR_NO_COL', -205);
75
76 /**
77 * Error in getForeignCol for a non-existent foreign key column
78 */
79 define('DB_TABLE_DATABASE_ERR_NO_FOREIGN_COL', -206);
80
81 /**
82 * Error for column name that is not a string
83 */
84 define('DB_TABLE_DATABASE_ERR_COL_NOT_STRING', -207);
85
86 /**
87 * Error in addTable for multiple primary keys
88 */
89 define('DB_TABLE_DATABASE_ERR_MULT_PKEY', -208);
90
91 /**
92 * Error in addRef for a non-existent foreign key table
93 */
94 define('DB_TABLE_DATABASE_ERR_NO_FTABLE', -209);
95
96 /**
97 * Error in addRef for non-existence referenced table
98 */
99 define('DB_TABLE_DATABASE_ERR_NO_RTABLE', -210);
100
101 /**
102 * Error in addRef for null referenced key in a table with no primary key
103 */
104 define('DB_TABLE_DATABASE_ERR_NO_PKEY', -211);
105
106 /**
107 * Error in addRef for an invalid foreign key, neither string nor array
108 */
109 define('DB_TABLE_DATABASE_ERR_FKEY', -212);
110
111 /**
112 * Error in addRef for referenced key that is not a string, string foreign key
113 */
114 define('DB_TABLE_DATABASE_ERR_RKEY_NOT_STRING', -213);
115
116 /**
117 * Error in addRef for referenced key that is not an array, array foreign key
118 */
119 define('DB_TABLE_DATABASE_ERR_RKEY_NOT_ARRAY', -214);
120
121 /**
122 * Error in addRef for wrong number of columns in referenced key
123 */
124 define('DB_TABLE_DATABASE_ERR_RKEY_COL_NUMBER', -215);
125
126 /**
127 * Error in addRef for non-existence foreign key (referencing) column
128 */
129 define('DB_TABLE_DATABASE_ERR_NO_FCOL', -216);
130
131 /**
132 * Error in addRef for non-existence referenced column
133 */
134 define('DB_TABLE_DATABASE_ERR_NO_RCOL', -217);
135
136 /**
137 * Error in addRef for referencing and referenced columns of different types
138 */
139 define('DB_TABLE_DATABASE_ERR_REF_TYPE', -218);
140
141 /**
142 * Error in addRef for multiple references from one table to another
143 */
144 define('DB_TABLE_DATABASE_ERR_MULT_REF', -219);
145
146 /**
147 * Error due to invalid ON DELETE action name
148 */
149 define('DB_TABLE_DATABASE_ERR_ON_DELETE_ACTION', -220);
150
151 /**
152 * Error due to invalid ON UPDATE action name
153 */
154 define('DB_TABLE_DATABASE_ERR_ON_UPDATE_ACTION', -221);
155
156 /**
157 * Error in addLink due to missing required reference
158 */
159 define('DB_TABLE_DATABASE_ERR_NO_REF_LINK', -222);
160
161 /**
162 * Error in validCol for a column name that does not exist in the datase
163 */
164 define('DB_TABLE_DATABASE_ERR_NO_COL_DB', -223);
165
166 /**
167 * Error in validCol for column name that does not exist in the specified table
168 */
169 define('DB_TABLE_DATABASE_ERR_NO_COL_TBL', -224);
170
171 /**
172 * Error in a buildSQL or select* method for an undefined key of $this->sql
173 */
174 define('DB_TABLE_DATABASE_ERR_SQL_UNDEF', -225);
175
176 /**
177 * Error in a buildSQL or select* method for a key of $this->sql that is
178 * not a string
179 */
180 define('DB_TABLE_DATABASE_ERR_SQL_NOT_STRING', -226);
181
182 /**
183 * Error in buildFilter due to invalid match type
184 */
185 define('DB_TABLE_DATABASE_ERR_MATCH_TYPE', -227);
186
187 /**
188 * Error in buildFilter due to invalid key for full match
189 */
190 define('DB_TABLE_DATABASE_ERR_DATA_KEY', -228);
191
192 /**
193 * Error in buildFilter due to invalid key for full match
194 */
195 define('DB_TABLE_DATABASE_ERR_FILT_KEY', -229);
196
197 /**
198 * Error in buildFilter due to invalid key for full match
199 */
200 define('DB_TABLE_DATABASE_ERR_FULL_KEY', -230);
201
202 /**
203 * Error in insert for a failed foreign key constraint
204 */
205 define('DB_TABLE_DATABASE_ERR_FKEY_CONSTRAINT', -231);
206
207 /**
208 * Error in delete due to a referentially triggered 'restrict' action
209 */
210 define('DB_TABLE_DATABASE_ERR_RESTRICT_DELETE', -232);
211
212 /**
213 * Error in update due to a referentially triggered 'restrict' action
214 */
215 define('DB_TABLE_DATABASE_ERR_RESTRICT_UPDATE', -233);
216
217 /**
218 * Error in fromXML for table with multiple auto_increment columns
219 */
220 define('DB_TABLE_DATABASE_ERR_XML_MULT_AUTO_INC', -234);
221
222 /**
223 * Error in autoJoin, column and tables parameter both null
224 */
225 define('DB_TABLE_DATABASE_ERR_NO_COL_NO_TBL', -235);
226
227 /**
228 * Error in autoJoin for ambiguous column name
229 */
230 define('DB_TABLE_DATABASE_ERR_COL_NOT_UNIQUE', -236);
231
232 /**
233 * Error in autoJoin for non-unique set of join conditions
234 */
235 define('DB_TABLE_DATABASE_ERR_AMBIG_JOIN', -237);
236
237 /**
238 * Error in autoJoin for failed construction of join
239 */
240 define('DB_TABLE_DATABASE_ERR_FAIL_JOIN', -238);
241
242 /**
243 * Error in fromXML for PHP 4 (this function requires PHP 5)
244 */
245 define('DB_TABLE_DATABASE_ERR_PHP_VERSION', -239);
246
247 /**
248 * Error parsing XML string in fromXML
249 */
250 define('DB_TABLE_DATABASE_ERR_XML_PARSE', -240);
251
252 // }}}
253 // {{{ Includes
254
255 /**
256 * DB_Table_Base base class
257 */
258 require_once 'DB/Table/Base.php';
259
260 /**
261 * DB_Table table abstraction class
262 */
263 require_once 'DB/Table.php';
264
265 /**
266 * The PEAR class for errors
267 */
268 require_once 'PEAR.php';
269
270 // }}}
271 // {{{ Error messages
272
273 /**
274 * US-English default error messages. If you want to internationalize, you can
275 * set the translated messages via $GLOBALS['_DB_TABLE_DATABASE']['error'].
276 * You can also use DB_Table_Database::setErrorMessage(). Examples:
277 *
278 * <code>
279 * (1) $GLOBALS['_DB_TABLE_DATABASE']['error'] = array(
280 * DB_TABLE_DATABASE_ERR_.. => '...',
281 * DB_TABLE_DATABASE_ERR_.. => '...');
282 * (2) DB_Table_Database::setErrorMessage(DB_TABLE_DATABASE_ERR_.., '...');
283 * DB_Table_Database::setErrorMessage(DB_TABLE_DATABASE_ERR_.., '...');
284 * (3) DB_Table_Database::setErrorMessage(array(
285 * DB_TABLE_DATABASE_ERR_.. => '...');
286 * DB_TABLE_DATABASE_ERR_.. => '...');
287 * (4) $obj =& new DB_Table();
288 * $obj->setErrorMessage(DB_TABLE_DATABASE_ERR_.., '...');
289 * $obj->setErrorMessage(DB_TABLE_DATABASE_ERR_.., '...');
290 * (5) $obj =& new DB_Table();
291 * $obj->setErrorMessage(array(DB_TABLE_DATABASE_ERR_.. => '...');
292 * DB_TABLE_DATABASE_ERR_.. => '...');
293 * </code>
294 *
295 * For errors that can occur with-in the constructor call (i.e. e.g. creating
296 * or altering the database table), only the code from examples (1) to (3)
297 * will alter the default error messages early enough. For errors that can
298 * occur later, examples (4) and (5) are also valid.
299 */
300 $GLOBALS['_DB_TABLE_DATABASE']['default_error'] = array(
301 DB_TABLE_DATABASE_ERR_DB_OBJECT =>
302 'Invalid DB/MDB2 object parameter. Function',
303 DB_TABLE_DATABASE_ERR_NO_TBL =>
304 'Table does not exist in database. Method, Table =',
305 DB_TABLE_DATABASE_ERR_TBL_NOT_STRING =>
306 'Table name parameter is not a string in method',
307 DB_TABLE_DATABASE_ERR_NO_COL =>
308 'In getCol, non-existent column name parameter',
309 DB_TABLE_DATABASE_ERR_NO_FOREIGN_COL =>
310 'In getForeignCol, non-existent column name parameter',
311 DB_TABLE_DATABASE_ERR_COL_NOT_STRING =>
312 'Column name parameter is not a string in method',
313 DB_TABLE_DATABASE_ERR_DBTABLE_OBJECT =>
314 'Parameter of addTable is not a DB_Table object',
315 DB_TABLE_DATABASE_ERR_MULT_PKEY =>
316 'Multiple primary keys in one table detected in addTable. Table',
317 DB_TABLE_DATABASE_ERR_NO_FTABLE =>
318 'Foreign key reference from non-existent table in addRef. Reference',
319 DB_TABLE_DATABASE_ERR_NO_RTABLE =>
320 'Reference to a non-existent referenced table in addRef. Reference',
321 DB_TABLE_DATABASE_ERR_NO_PKEY =>
322 'Missing primary key of referenced table in addRef. Reference',
323 DB_TABLE_DATABASE_ERR_FKEY =>
324 'Foreign / referencing key is not a string or array in addRef',
325 DB_TABLE_DATABASE_ERR_RKEY_NOT_STRING =>
326 'Foreign key is a string, referenced key is not a string in addRef',
327 DB_TABLE_DATABASE_ERR_RKEY_NOT_ARRAY =>
328 'Foreign key is an array, referenced key is not an array in addRef',
329 DB_TABLE_DATABASE_ERR_RKEY_COL_NUMBER =>
330 'Wrong number of columns in referencing key in addRef',
331 DB_TABLE_DATABASE_ERR_NO_FCOL =>
332 'Nonexistent foreign / referencing key column in addRef. Reference',
333 DB_TABLE_DATABASE_ERR_NO_RCOL =>
334 'Nonexistent referenced key column in addRef. Reference',
335 DB_TABLE_DATABASE_ERR_REF_TYPE =>
336 'Different referencing and referenced column types in addRef. Reference',
337 DB_TABLE_DATABASE_ERR_MULT_REF =>
338 'Multiple references between two tables in addRef. Reference',
339 DB_TABLE_DATABASE_ERR_ON_DELETE_ACTION =>
340 'Invalid ON DELETE action. Reference',
341 DB_TABLE_DATABASE_ERR_ON_UPDATE_ACTION =>
342 'Invalid ON UPDATE action. Reference',
343 DB_TABLE_DATABASE_ERR_NO_REF_LINK =>
344 'Error in addLink due to missing required reference(s)',
345 DB_TABLE_DATABASE_ERR_NO_COL_DB =>
346 'In validCol, column name does not exist in database. Column',
347 DB_TABLE_DATABASE_ERR_NO_COL_TBL =>
348 'In validCol, column does not exist in specified table. Column',
349 DB_TABLE_DATABASE_ERR_SQL_UNDEF =>
350 'Query string is not a key of $sql property array. Key is',
351 DB_TABLE_DATABASE_ERR_SQL_NOT_STRING =>
352 'Query is neither an array nor a string',
353 DB_TABLE_DATABASE_ERR_MATCH_TYPE =>
354 'Invalid match parameter of buildFilter',
355 DB_TABLE_DATABASE_ERR_DATA_KEY =>
356 'Invalid data_key in buildFilter, neither string nor array',
357 DB_TABLE_DATABASE_ERR_FILT_KEY =>
358 'Incompatible data_key and filter_key in buildFilter',
359 DB_TABLE_DATABASE_ERR_FULL_KEY =>
360 'Invalid key value in buildFilter: Mixed null and not null',
361 DB_TABLE_DATABASE_ERR_FKEY_CONSTRAINT =>
362 'Foreign key constraint failure: Key does not reference any rows',
363 DB_TABLE_DATABASE_ERR_RESTRICT_DELETE =>
364 'Referentially trigger restrict of delete from table',
365 DB_TABLE_DATABASE_ERR_RESTRICT_UPDATE =>
366 'Referentially trigger restrict of update of table',
367 DB_TABLE_DATABASE_ERR_NO_COL_NO_TBL =>
368 'No columns or tables provided as parameters to autoJoin',
369 DB_TABLE_DATABASE_ERR_COL_NOT_UNIQUE =>
370 'Ambiguous column name in autoJoin. Column',
371 DB_TABLE_DATABASE_ERR_AMBIG_JOIN =>
372 'Ambiguous join in autoJoin, during join of table',
373 DB_TABLE_DATABASE_ERR_FAIL_JOIN =>
374 'Failed join in autoJoin, failed to join table',
375 DB_TABLE_DATABASE_ERR_PHP_VERSION =>
376 'PHP 5 is required for fromXML method. Interpreter version is',
377 DB_TABLE_DATABASE_ERR_XML_PARSE =>
378 'Error parsing XML in fromXML method'
379 );
380
381 // merge default and user-defined error messages
382 if (!isset($GLOBALS['_DB_TABLE_DATABASE']['error'])) {
383 $GLOBALS['_DB_TABLE_DATABASE']['error'] = array();
384 }
385 foreach ($GLOBALS['_DB_TABLE_DATABASE']['default_error'] as $code => $message) {
386 if (!array_key_exists($code, $GLOBALS['_DB_TABLE_DATABASE']['error'])) {
387 $GLOBALS['_DB_TABLE_DATABASE']['error'][$code] = $message;
388 }
389 }
390
391 // }}}
392 // {{{ DB_Table_Database
393
394 /**
395 * Relational database abstraction class
396 *
397 * DB_Table_Database is an abstraction class for a relational database.
398 * It is a layer built on top of DB_Table, in which each table in a
399 * database is represented as an instance of DB_Table. It provides:
400 *
401 * - an object-oriented representation of the database schema
402 * - automated construction of SQL commands for simple joins
403 * - an API for insert, update, and select commands very similar
404 * to that of DB_Table, with optional emulation of standard SQL
405 * foreign key integrity checks and referential triggered actions
406 * such as cascading deletes.
407 * - Serialization and unserialization of the database schema via
408 * either php serialization or XML, using the MDB2 XML schema.
409 *
410 * @category Database
411 * @package DB_Table
412 * @author David C. Morse <morse@php.net>
413 * @version Release: 1.5.6
414 * @link http://pear.php.net/package/DB_Table
415 */
416 class DB_Table_Database extends DB_Table_Base
417 {
418
419 // {{{ properties
420
421 /**
422 * Name of the database
423 *
424 * @var string
425 * @access public
426 */
427 var $name = null;
428
429 /**
430 * Associative array of DB_Table object references. Keys are table names.
431 *
432 * Associative array in which keys are table names, values are references to
433 * DB_Table objects. Each referenced DB_Table object represents one table in
434 * the database.
435 *
436 * @var array
437 * @access private
438 */
439 var $_table = array();
440
441 /**
442 * Array in which keys are table names, values are DB_Table subclass names.
443 *
444 * See the getTableSubclass() method docblock for further details.
445 *
446 * @var array
447 * @access private
448 */
449 var $_table_subclass = array();
450
451 /**
452 * Path to directory containing DB_Table subclass declaration files
453 *
454 * See the setTableSubclassPath() method docblock for further details.
455 *
456 * @var string
457 * @access private
458 */
459 var $_table_subclass_path = '';
460
461 /**
462 * Array in which keys are table names, values are primary keys.
463 *
464 * Each primary key value may be a column name string, a sequential array of
465 * column name strings, or null.
466 *
467 * See the getPrimaryKey() method docblock for details.
468 *
469 * @var array
470 * @access private
471 */
472 var $_primary_key = array();
473
474 /**
475 * Associative array that maps column names keys to table names.
476 *
477 * Each key is the name string of a column in the database. Each value
478 * is a numerical array containing the names of all tables that contain
479 * a column with that name.
480 *
481 * See the getCol() method docblock for details.
482 *
483 * @var array
484 * @access private
485 */
486 var $_col = array();
487
488 /**
489 * Associative array that maps names of foreign key columns to table names
490 *
491 * Each key is the name string of a foreign key column. Each value is a
492 * sequential array containing the names of all tables that contain a
493 * foreign key column with that name.
494 *
495 * See the getForeignCol() method docblock for further details.
496 *
497 * @var array
498 * @access private
499 */
500 var $_foreign_col = array();
501
502 /**
503 * Two-dimensional associative array of foreign key references.
504 *
505 * Keys are pairs of table names (referencing table first, referenced
506 * table second). Each value is an array containing information about
507 * the referencing and referenced keys, and about any referentially
508 * triggered actions (e.g., cascading delete).
509 *
510 * See the getRef() docblock for further details.
511 *
512 * @var array
513 * @access private
514 */
515 var $_ref = array();
516
517 /**
518 * Array in which each key is the names of a referenced tables, each value
519 * an sequential array containing names of referencing tables.
520 *
521 * See the docblock for the getRefTo() method for further discussion.
522 *
523 * @var array
524 * @access private
525 */
526 var $_ref_to = array();
527
528 /**
529 * Two-dimensional associative array of linking tables.
530 *
531 * Two-dimensional associative array in which pairs of keys are names
532 * of pairs of tables that are linked by one or more linking/association
533 * table. Each value is an array containing the names of all table that
534 * link the tables specified by the pair of keys. A linking table is a
535 * table that creates a many-to-many relationship between two linked
536 * tables, via foreign key references from the linking table to the two
537 * linked tables. The $_link property is used by the autoJoin() method
538 * to join tables that are related only through such a linking table.
539 *
540 * See the getLink() method docblock for further details.
541 *
542 * @var array
543 * @access private
544 */
545 var $_link = array();
546
547 /**
548 * Take on_update actions if $_act_on_update is true
549 *
550 * By default, on_update actions are enabled ($_act_on_update = true)
551 *
552 * @var boolean
553 * @access private
554 */
555 var $_act_on_update = true;
556
557 /**
558 * Take on_delete actions if $_act_on_delete is true
559 *
560 * By default, on_delete actions are enabled ($_act_on_delete = true)
561 *
562 * @var boolean
563 * @access private
564 */
565 var $_act_on_delete = true;
566
567 /**
568 * Validate foreign keys before insert or update if $_check_fkey is true
569 *
570 * By default, validation is disabled ($_check_fkey = false)
571 *
572 * @var boolean
573 * @access private
574 */
575 var $_check_fkey = false;
576
577 /**
578 * If the column keys in associative array return sets are fixed case
579 * (all upper or lower case) this property should be set true.
580 *
581 * The column keys in rows of associative array return sets may either
582 * preserve capitalization of the column names or they may be fixed case,
583 * depending on the options set in the backend (DB/MDB2) and on phptype.
584 * If these column names are returned with a fixed case (either upper
585 * or lower), $_fix_case must be set true in order for php emulation of
586 * ON DELETE and ON UPDATE actions to work correctly. Otherwise, the
587 * $_fix_case property should be false (the default).
588 *
589 * The choice between mixed or fixed case column keys may be made by using
590 * using the setFixCase() method, which resets both the behavior of the
591 * backend and the $_fix_case property. It may also be changed by using the
592 * setOption() method of the DB or MDB2 backend object to directly set the
593 * DB_PORTABILITY_LOWERCASE or MDB2_PORTABILITY_FIX_CASE bits of the
594 * DB/MDB2 'portability' option.
595 *
596 * By default, DB returns mixed case and MDB2 returns lower case.
597 *
598 * @see DB_Table_Database::setFixCase()
599 * @see DB::setOption()
600 * @see MDB2::setOption()
601 *
602 * @var boolean
603 * @access private
604 */
605 var $_fix_case = false;
606
607 // }}}
608 // {{{ Methods
609
610 // {{{ function DB_Table_Database(&$db, $name)
611
612 /**
613 * Constructor
614 *
615 * If an error is encountered during instantiation, the error
616 * message is stored in the $this->error property of the resulting
617 * object. See $error property docblock for a discussion of error
618 * handling.
619 *
620 * @param object &$db DB/MDB2 database connection object
621 * @param string $name the database name
622 * @return object DB_Table_Database
623 * @access public
624 */
625 function DB_Table_Database(&$db, $name)
626 {
627 // Is $db an DB/MDB2 object or null?
628 if (is_a($db, 'db_common')) {
629 $this->backend = 'db';
630 $this->fetchmode = DB_FETCHMODE_ORDERED;
631 } elseif (is_a($db, 'mdb2_driver_common')) {
632 $this->backend = 'mdb2';
633 $this->fetchmode = MDB2_FETCHMODE_ORDERED;
634 } else {
635 $code = DB_TABLE_DATABASE_ERR_DB_OBJECT ;
636 $text = $GLOBALS['_DB_TABLE_DATABASE']['error'][$code]
637 . ' DB_Table_Database';
638 $this->error = PEAR::throwError($text, $code);
639 return;
640 }
641 $this->db =& $db;
642 $this->name = $name;
643
644 $this->_primary_subclass = 'DB_TABLE_DATABASE';
645 $this->setFixCase(false);
646 }
647
648 // }}}
649 // {{{ function setDBconnection(&$db)
650
651 /**
652 * Set DB/MDB2 connection instance for database and all tables
653 *
654 * Assign a reference to the DB/MDB2 object $db to $this->db, set
655 * $this->backend to 'db' or 'mdb2', and set the same pair of
656 * values for the $db and $backend properties of every DB_Table
657 * object in the database.
658 *
659 * @param object &$db DB/MDB2 connection object
660 * @return boolean True on success (PEAR_Error on failure)
661 *
662 * @throws PEAR_Error if
663 * $db is not a DB or MDB2 object(DB_TABLE_DATABASE_ERR_DB_OBJECT)
664 *
665 * @access public
666 */
667 function setDBconnection(&$db)
668 {
669 // Is the first argument a DB/MDB2 object ?
670 if (is_subclass_of($db, 'DB_Common')) {
671 $backend = 'db';
672 } elseif (is_subclass_of($db, 'MDB2_Driver_Common')) {
673 $backend = 'mdb2';
674 } else {
675 return $this->throwError(
676 DB_TABLE_DATABASE_ERR_DB_OBJECT,
677 "setDBconnection");
678 }
679
680 // Set db and backend for database and all of its tables
681 $this->db =& $db;
682 $this->backend = $backend;
683 foreach ($this->_table as $name => $table) {
684 $table->db =& $db;
685 $table->backend = $backend;
686 }
687 return true;
688 }
689
690 // }}}
691 // {{{ function setActOnDelete($flag = true)
692
693 /**
694 * Turns on (or off) automatic php emulation of SQL ON DELETE actions
695 *
696 * @param bool $flag True to enable action, false to disable
697 * @return void
698 * @access public
699 */
700 function setActOnDelete($flag = true)
701 {
702 if ($flag) {
703 $this->_act_on_delete = true;
704 } else {
705 $this->_act_on_delete = false;
706 }
707 }
708
709 // }}}
710 // {{{ function setActOnUpdate($flag = true)
711
712 /**
713 * Turns on (or off) automatic php emulation of ON UPDATE actions
714 *
715 * @param bool $flag True to enable action, false to disable
716 * @return void
717 * @access public
718 */
719 function setActOnUpdate($flag = true)
720 {
721 if ($flag) {
722 $this->_act_on_update = true;
723 } else {
724 $this->_act_on_update = false;
725 }
726 }
727
728 // }}}
729 // {{{ function setCheckFKey($flag = true)
730
731 /**
732 * Turns on (or off) validation of foreign key values on insert and update
733 *
734 * @param bool $flag True to enable foreign key validation, false to disable
735 * @return void
736 * @access public
737 */
738 function setCheckFKey($flag = true)
739 {
740 if ($flag) {
741 $this->_check_fkey = true;
742 } else {
743 $this->_check_fkey = false;
744 }
745 }
746
747 // }}}
748 // {{{ function setFixCase($flag = false)
749
750 /**
751 * Sets backend option such that column keys in associative array return
752 * sets are converted to fixed case, if true, or mixed case, if false.
753 *
754 * Sets the DB/MDB2 'portability' option, and sets $this->_fix_case = $flag.
755 * Because it sets an option in the underlying DB/MDB2 connection object,
756 * this effects the behavior of all objects that share the connection.
757 *
758 * @param bool $flag True for fixed lower case, false for mixed
759 * @return void
760 * @access public
761 */
762 function setFixCase($flag = false)
763 {
764 $flag = (bool) $flag;
765 $option = $this->db->getOption('portability');
766 if ($this->backend == 'db') {
767 $option = $option | DB_PORTABILITY_LOWERCASE;
768 if (!$flag) {
769 $option = $option ^ DB_PORTABILITY_LOWERCASE;
770 }
771 } else {
772 $option = $option | MDB2_PORTABILITY_FIX_CASE;
773 if (!$flag) {
774 $option = $option ^ MDB2_PORTABILITY_FIX_CASE;
775 }
776 }
777 $this->db->setOption('portability', $option);
778 $this->_fix_case = $flag;
779 }
780
781 // }}}
782 // {{{ function &getDBInstance()
783
784 /**
785 * Return reference to $this->db DB/MDB2 object wrapped by $this
786 *
787 * @return object Reference to DB/MDB2 object
788 * @access public
789 */
790 function &getDBInstance()
791 {
792 return $this->db;
793 }
794
795 // }}}
796 // {{{ function getTable($name = null)
797
798 /**
799 * Returns all or part of $_table property array
800 *
801 * If $name is absent or null, return entire $_table property array.
802 * If $name is a table name, return $this->_table[$name] DB_Table object
803 * reference
804 *
805 * The $_table property is an associative array in which keys are table
806 * name strings and values are references to DB_Table objects. Each of
807 * the referenced objects represents one table in the database.
808 *
809 * @param string $name Name of table
810 * @return mixed $_table property, or one element of $_table
811 * (PEAR_Error on failure)
812 *
813 * @throws PEAR_Error if:
814 * - $name is not a string ( DB_TABLE_DATABASE_ERR_TBL_NOT_STRING )
815 * - $name is not valid table name ( DB_TABLE_DATABASE_ERR_NO_TBL )
816 *
817 * @access public
818 */
819 function getTable($name = null)
820 {
821 if (is_null($name)) {
822 return $this->_table;
823 } elseif (is_string($name)) {
824 if (isset($this->_table[$name])) {
825 return $this->_table[$name];
826 } else {
827 return $this->throwError(
828 DB_TABLE_DATABASE_ERR_NO_TBL,
829 "getTable, $name");
830 }
831 } else {
832 return $this->throwError(
833 DB_TABLE_DATABASE_ERR_TBL_NOT_STRING,
834 "getTable");
835 }
836 }
837
838 // }}}
839 // {{{ function getPrimaryKey($name = null)
840
841 /**
842 * Returns all or part of the $_primary_key property array
843 *
844 * If $name is null, return the $this->_primary_key property array
845 * If $name is a table name, return $this->_primary_key[$name]
846 *
847 * The $_primary_key property is an associative array in which each key
848 * a table name, and each value is the primary key of that table. Each
849 * primary key value may be a column name string, a sequential array of
850 * column name strings (for a multi-column key), or null (if no primary
851 * key has been declared).
852 *
853 * @param string $name Name of table
854 * @return mixed $this->primary_key array or $this->_primary_key[$name]
855 * (PEAR_Error on failure)
856 *
857 * @throws PEAR_Error if:
858 * - $name is not a string ( DB_TABLE_DATABASE_ERR_TBL_NOT_STRING )
859 * - $name is not valid table name ( DB_TABLE_DATABASE_ERR_NO_TBL )
860 *
861 * @access public
862 */
863 function getPrimaryKey($name = null)
864 {
865 if (is_null($name)) {
866 return $this->_primary_key;
867 } elseif (is_string($name)) {
868 if (isset($this->_primary_key[$name])) {
869 return $this->_primary_key[$name];
870 } else {
871 return $this->throwError(
872 DB_TABLE_DATABASE_ERR_NO_TBL,
873 "getPrimaryKey, $name");
874 }
875 } else {
876 return $this->throwError(
877 DB_TABLE_DATABASE_ERR_TBL_NOT_STRING,
878 "getPrimaryKey");
879 }
880 }
881
882 // }}}
883 // {{{ function getTableSubclass($name = null)
884
885 /**
886 * Returns all or part of the $_table_subclass property array
887 *
888 * If $name is null, return the $this->_table_subclass property array
889 * If $name is a table name, return $this->_table_subclass[$name]
890 *
891 * The $_table_subclass property is an associative array in which each key
892 * is a table name string, and each value is the name of the corresponding
893 * subclass of DB_Table. The value is null if the table is an instance of
894 * DB_Table itself.
895 *
896 * Subclass names are set within the addTable method by applying the
897 * built in get_class() function to a DB_Table object. The class names
898 * returned by get_class() are stored unmodified. In PHP 4, get_class
899 * converts all class names to lower case. In PHP 5, it preserves the
900 * capitalization of the name used in the class definition.
901 *
902 * For autoloading of class definitions to work properly in the
903 * __wakeup() method, the base name of each subclass definition
904 * file (excluding the .php extension) should thus be a identical
905 * to the class name in PHP 5, and a lower case version of the
906 * class name in PHP 4 or
907 *
908 * @param string $name Name of table
909 * @return mixed $_table_subclass array or $this->_table_subclass[$name]
910 * (PEAR_Error on failure)
911 *
912 * @throws PEAR_Error if:
913 * - $name is not a string ( DB_TABLE_DATABASE_TBL_NOT_STRING )
914 * - $name is not valid table name ( DB_TABLE_DATABASE_NO_TBL )
915 *
916 * @access public
917 *
918 @ @see DB_Table_Database::__wakeup()
919 */
920 function getTableSubclass($name = null)
921 {
922 if (is_null($name)) {
923 return $this->_table_subclass;
924 } elseif (is_string($name)) {
925 if (isset($this->_table_subclass[$name])) {
926 return $this->_table_subclass[$name];
927 } else {
928 return $this->throwError(
929 DB_TABLE_DATABASE_ERR_NO_TBL,
930 "getTableSubclass, $name");
931 }
932 } else {
933 return $this->throwError(
934 DB_TABLE_DATABASE_ERR_TBL_NOT_STRING,
935 "getTableSubclass");
936 }
937 }
938
939 // }}}
940 // {{{ function getCol($column_name = null)
941
942 /**
943 * Returns all or part of the $_col property array
944 *
945 * If $column_name is null, return $_col property array
946 * If $column_name is valid, return $_col[$column_name] subarray
947 *
948 * The $_col property is an associative array in which each key is the
949 * name of a column in the database, and each value is a numerical array
950 * containing the names of all tables that contain a column with that
951 * name.
952 *
953 * @param string $column_name a column name string
954 * @return mixed $this->_col property array or $this->_col[$column_name]
955 * (PEAR_Error on failure)
956 *
957 * @throws PEAR_Error if:
958 * - $column_name is not a string (DB_TABLE_DATABASE_ERR_COL_NOT_STRING)
959 * - $column_name is not valid column name (DB_TABLE_DATABASE_NO_COL)
960 *
961 * @access public
962 */
963 function getCol($column_name = null)
964 {
965 if (is_null($column_name)) {
966 return $this->_col;
967 } elseif (is_string($column_name)) {
968 if (isset($this->_col[$column_name])) {
969 return $this->_col[$column_name];
970 } else {
971 return $this->throwError(
972 DB_TABLE_DATABASE_ERR_NO_COL,
973 "'$column_name'");
974 }
975 } else {
976 return $this->throwError(
977 DB_TABLE_DATABASE_ERR_COL_NOT_STRING,
978 'getCol');
979 }
980 }
981
982 // }}}
983 // {{{ function getForeignCol($column_name = null)
984
985 /**
986 * Returns all or part of the $_foreign_col property array
987 *
988 * If $column_name is null, return $this->_foreign_col property array
989 * If $column_name is valid, return $this->_foreign_col[$column_name]
990 *
991 * The $_foreign_col property is an associative array in which each
992 * key is the name string of a foreign key column, and each value is a
993 * sequential array containing the names of all tables that contain a
994 * foreign key column with that name.
995 *
996 * If a column $column in a referencing table $ftable is part of the
997 * foreign key for references to two or more different referenced tables
998 * tables, the name $ftable will also appear multiple times in the array
999 * $this->_foreign_col[$column].
1000 *
1001 * Returns a PEAR_Error with the following DB_TABLE_DATABASE_* error
1002 * codes if:
1003 * - $column_name is not a string ( _COL_NOT_STRING )
1004 * - $column_name is not valid foreign column name ( _NO_FOREIGN_COL )
1005 *
1006 * @param string column name string for foreign key column
1007 * @return array $_foreign_col property array
1008 * @access public
1009 */
1010 function getForeignCol($column_name = null)
1011 {
1012 if (is_null($column_name)) {
1013 return $this->_foreign_col;
1014 } elseif (is_string($column_name)) {
1015 if (isset($this->_foreign_col[$column_name])) {
1016 return $this->_foreign_col[$column_name];
1017 } else {
1018 return $this->throwError(
1019 DB_TABLE_DATABASE_ERR_NO_FOREIGN_COL,
1020 $column_name);
1021 }
1022 } else {
1023 return $this->throwError(
1024 DB_TABLE_DATABASE_ERR_COL_NOT_STRING,
1025 'getForeignCol');
1026 }
1027 }
1028
1029 // }}}
1030 // {{{ function getRef($table1 = null, $table2 = null)
1031
1032 /**
1033 * Returns all or part of the $_ref two-dimensional property array
1034 *
1035 * Returns $this->_ref 2D property array if $table1 and $table2 are null.
1036 * Returns $this->_ref[$table1] subarray if only $table2 is null.
1037 * Returns $this->_ref[$table1][$table2] if both parameters are present.
1038 *
1039 * Returns null if $table1 is a table that references no others, or
1040 * if $table1 and $table2 are both valid table names, but there is no
1041 * reference from $table1 to $table2.
1042 *
1043 * The $_ref property is a two-dimensional associative array in which
1044 * the keys are pairs of table names, each value is an array containing
1045 * information about referenced and referencing keys, and referentially
1046 * triggered actions (if any). An element of the $_ref array is of the
1047 * form $ref[$ftable][$rtable] = $reference, where $ftable is the name
1048 * of a referencing (or foreign key) table and $rtable is the name of
1049 * a corresponding referenced table. The value $reference is an array
1050 * $reference = array($fkey, $rkey, $on_delete, $on_update) in which
1051 * $fkey and $rkey are the foreign (or referencing) and referenced
1052 * keys, respectively: Foreign key $fkey of table $ftable references
1053 * key $rkey of table $rtable. The values of $fkey and $rkey must either
1054 * both be valid column name strings for columns of the same type, or
1055 * they may both be sequential arrays of column name names, with equal
1056 * numbers of columns of corresponding types, for multi-column keys. The
1057 * $on_delete and $on_update values may be either null or string values
1058 * that indicate actions to be taken upon deletion or updating of a
1059 * referenced row (e.g., cascading deletes). A null value of $on_delete
1060 * or $on_update indicates that no referentially triggered action will
1061 * be taken. See addRef() for further details about allowed values of
1062 * these action strings.
1063 *
1064 * @param string $table1 name of referencing table
1065 * @param string $table2 name of referenced table
1066 * @return mixed $ref property array, sub-array, or value
1067 *
1068 * @throws a PEAR_Error if:
1069 * - $table1 or $table2 is not a string (.._DATABASE_ERR_TBL_NOT_STRING)
1070 * - $table1 or $table2 is not a table name (.._DATABASE_ERR_NO_TBL)
1071 *
1072 * @access public
1073 */
1074 function getRef($table1 = null, $table2 = null)
1075 {
1076 if (is_null($table1)) {
1077 return $this->_ref;
1078 } elseif (is_string($table1)) {
1079 if (isset($this->_ref[$table1])) {
1080 if (is_null($table2)) {
1081 return $this->_ref[$table1];
1082 } elseif (is_string($table2)) {
1083 if (isset($this->_ref[$table1][$table2])) {
1084 return $this->_ref[$table1][$table2];
1085 } else {
1086 if (isset($this->_table[$table2])) {
1087 // Valid table names but no references to
1088 return null;
1089 } else {
1090 // Invalid table name
1091 return $this->throwError(
1092 DB_TABLE_DATABASE_ERR_NO_TBL,
1093 "getRef, $table2");
1094 }
1095 }
1096 } else {
1097 return $this->throwError(
1098 DB_TABLE_DATABASE_ERR_TBL_NOT_STRING,
1099 "getRef");
1100 }
1101 } else {
1102 if (isset($this->_table[$table1])) {
1103 // Valid table name, but no references from
1104 return null;
1105 } else {
1106 // Invalid table name
1107 return $this->throwError(
1108 DB_TABLE_DATABASE_ERR_NO_TBL,
1109 "getRef, $table1");
1110 }
1111 }
1112 } else {
1113 return $this->throwError(
1114 DB_TABLE_DATABASE_ERR_TBL_NOT_STRING,
1115 "getRef");
1116 }
1117
1118 }
1119
1120 // }}}
1121 // {{{ function getRefTo($table_name = null)
1122
1123 /**
1124 * Returns all or part of the $_ref_to property array
1125 *
1126 * Returns $this->_ref_to property array if $table_name is null.
1127 * Returns $this->_ref_to[$table_name] if $table_name is not null.
1128 *
1129 * The $_ref_to property is an associative array in which each key
1130 * is the name of a referenced table, and each value is a sequential
1131 * array containing the names of all tables that contain foreign keys
1132 * that reference that table. Each element is thus of the form
1133 * $_ref_to[$rtable] = array($ftable1, $ftable2,...), where
1134 * $ftable1, $ftable2, ... are the names of tables that reference
1135 * the table named $rtable.
1136 *
1137 * @param string $table_name name of table
1138 * @return mixed $_ref_to property array or subarray
1139 * (PEAR_Error on failure)
1140 *
1141 * @throws PEAR_Error if:
1142 * - $table_name is not a string ( .._DATABASE_ERR_TBL_NOT_STRING )
1143 * - $table_name is not a table name ( .._DATABASE_ERR_NO_TBL )
1144 *
1145 * @access public
1146 */
1147 function getRefTo($table_name = null)
1148 {
1149 if (is_null($table_name)) {
1150 return $this->_ref_to;
1151 } elseif (is_string($table_name)) {
1152 if (isset($this->_ref_to[$table_name])) {
1153 return $this->_ref_to[$table_name];
1154 } else {
1155 if (isset($this->_table[$table_name])) {
1156 // Valid table name, but no references to
1157 return null;
1158 } else {
1159 // Invalid table name
1160 return $this->throwError(
1161 DB_TABLE_DATABASE_ERR_NO_TBL,
1162 "getRefTo, $table_name");
1163 }
1164 }
1165 } else {
1166 return $this->throwError(
1167 DB_TABLE_DATABASE_ERR_TBL_NOT_STRING,
1168 "getRefTo");
1169 }
1170 }
1171
1172 // }}}
1173 // {{{ function getLink($table1 = null, $table2 = null)
1174
1175 /**
1176 * Returns all or part of the $link two-dimensional property array
1177 *
1178 * Returns $this->_link 2D property array if $table1 and $table2 are null.
1179 * Returns $this->_link[$table1] subarray if only $table2 is null.
1180 * Returns $this->_link[$table1][$table2] if both parameters are present.
1181 *
1182 * Returns null if $table1 is a valid table with links to no others, or
1183 * if $table1 and $table2 are both valid table names but there is no
1184 * link between them.
1185 *
1186 * The $_link property is a two-dimensional associative array with
1187 * elements of the form $this->_link[$table1][$table2] = array($link1, ...),
1188 * in which the value is an array containing the names of all tables
1189 * that `link' tables named $table1 and $table2, and thereby create a
1190 * many-to-many relationship between these two tables.
1191 *
1192 * The $_link property is used in the autoJoin method to join tables
1193 * that are related by a many-to-many relationship via a linking table,
1194 * rather than via a direct foreign key reference. A table that is
1195 * declared to be linking table for tables $table1 and $table2 must
1196 * contain foreign keys that reference both of these tables.
1197 *
1198 * Each binary link in a database is listed twice in $_link, in
1199 * $_link[$table1][$table2] and in $_link[$table2][$table1]. If a
1200 * linking table contains foreign key references to N tables, with
1201 * N > 2, each of the resulting binary links is listed separately.
1202 * For example, a table with references to 3 tables A, B, and C can
1203 * create three binary links (AB, AC, and BC) and six entries in the
1204 * link property array (i.e., in $_link[A][B], $_link[B][A], ... ).
1205 *
1206 * Linking tables may be added to the $_link property by using the
1207 * addLink method or deleted using the delLink method. Alternatively,
1208 * all possible linking tables can be identified and added to the
1209 * $_link array at once by the addAllLinks() method.
1210 *
1211 * @param string $table1 name of linked table
1212 * @param string $table2 name of linked table
1213 * @return mixed $_link property array, sub-array, or value
1214 *
1215 * @throws PEAR_Error:
1216 * - $table1 or $table2 is not a string (..DATABASE_ERR_TBL_NOT_STRING)
1217 * - $table1 or $table2 is not a table name (..DATABASE_ERR_NO_TBL)
1218 *
1219 * @access public
1220 */
1221 function getLink($table1 = null, $table2 = null)
1222 {
1223 if (is_null($table1)) {
1224 return $this->_link;
1225 } elseif (is_string($table1)) {
1226 if (isset($this->_link[$table1])) {
1227 if (is_null($table2)) {
1228 return $this->_link[$table1];
1229 } elseif (is_string($table2)) {
1230 if (isset($this->_link[$table1][$table2])) {
1231 return $this->_link[$table1][$table2];
1232 } else {
1233 if (isset($this->_table[$table2])) {
1234 // Valid table names, but no links
1235 return null;
1236 } else {
1237 // Invalid 2nd table name string
1238 return $this->throwError(
1239 DB_TABLE_DATABASE_ERR_NO_TBL,
1240 "getLink, $table2");
1241 }
1242 }
1243 } else {
1244 return $this->throwError(
1245 DB_TABLE_DATABASE_ERR_TBL_NOT_STRING,
1246 "getLink");
1247 }
1248 } else {
1249 if (isset($this->_table[$table1])) {
1250 // Valid first table name, but no links
1251 return null;
1252 } else {
1253 // Invalid 1st table name string
1254 return $this->throwError(
1255 DB_TABLE_DATABASE_ERR_NO_TBL,
1256 "getLink, $table1");
1257 }
1258 }
1259 } else {
1260 return $this->throwError(
1261 DB_TABLE_DATABASE_ERR_TBL_NOT_STRING,
1262 "getLink");
1263 }
1264 }
1265
1266 // }}}
1267 // {{{ function setTableSubclassPath($path)
1268
1269 /**
1270 * Sets path to a directory containing DB_Table subclass definitions.
1271 *
1272 * This method sets the $_table_subclass_path string property. The value of
1273 * this property is the path to the directory containing DB_Table subclass
1274 * definitions, without a trailing directory separator.
1275 *
1276 * This path may be used by the __wakeup(), if necessary, in an attempt to
1277 * autoload class definitions when unserializing a DB_Table_Database object
1278 * and its child DB_Table objects. If a DB_Table subclass $subclass_name
1279 * has not been defined when it is needed in DB_Table_Database::__wakeup(),
1280 * to unserialize an instance of this class, the __wakeup() method attempts
1281 * to include a class definition file from this directory, as follows:
1282 * <code>
1283 * $dir = $this->_table_subclass_path;
1284 * require_once $dir . '/' . $subclass . '.php';
1285 * </code>
1286 * See the getTableSubclass() docblock for a discusion of capitalization
1287 * conventions in PHP 4 and 5 for subclass file names.
1288 *
1289 * @param string $path path to directory containing class definitions
1290 * @return void
1291 * @access public
1292 *
1293 * @see DB_Table_Database::getTableSubclass()
1294 */
1295 function setTableSubclassPath($path)
1296 {
1297 $this->_table_subclass_path = $path;
1298 }
1299
1300 // }}}
1301 // {{{ function addTable(&$table_obj)
1302
1303 /**
1304 * Adds a table to the database.
1305 *
1306 * Creates references between $this DB_Table_Database object and
1307 * the child DB_Table object, by adding a reference to $table_obj
1308 * to the $this->_table array, and setting $table_obj->database =
1309 * $this.
1310 *
1311 * Adds the primary key to $this->_primary_key array. The relevant
1312 * element of $this->_primary_key is set to null if no primary key
1313 * index is declared. Returns an error if more than one primary key
1314 * is declared.
1315 *
1316 * Returns true on success, and PEAR error on failure. Returns the
1317 * following DB_TABLE_DATABASE_ERR_* error codes if:
1318 * - $table_obj is not a DB_Table ( _DBTABLE_OBJECT )
1319 * - more than one primary key is defined ( _ERR_MULT_PKEY )
1320 *
1321 * @param object &$table_obj the DB_Table object (reference)
1322 * @return boolean true on success (PEAR_Error on failure)
1323 * @access public
1324 */
1325 function addTable(&$table_obj)
1326 {
1327 // Check that $table_obj is a DB_Table object
1328 // Identify subclass name, if any
1329 if (is_subclass_of($table_obj, 'DB_Table')) {
1330 $subclass = get_class($table_obj);
1331 } elseif (is_a($table_obj, 'DB_Table')) {
1332 $subclass = null;
1333 } else {
1334 return $this->throwError(
1335 DB_TABLE_DATABASE_ERR_DBTABLE_OBJECT);
1336 }
1337
1338 // Identify table name and table object (sub)class name
1339 $table = $table_obj->table;
1340
1341 // Set $this->_primary_key[$table]
1342 $this->_primary_key[$table] = null;
1343 foreach ($table_obj->idx as $idx_name => $idx_def) {
1344 if ($idx_def['type'] == 'primary') {
1345 if (is_null($this->_primary_key[$table])) {
1346 $this->_primary_key[$table] = $idx_def['cols'];
1347 } else {
1348 // More than one primary key defined in the table
1349 unset($this->_primary_key[$table]);
1350 return $this->throwError(
1351 DB_TABLE_DATABASE_ERR_MULT_PKEY, $table);
1352 }
1353 }
1354 }
1355
1356 // Add references between $this parent and child table object
1357 $this->_table[$table] =& $table_obj;
1358 $table_obj->setDatabaseInstance($this);
1359
1360 // Add subclass name (if any) to $this->_table_subclass
1361 $this->_table_subclass[$table] = $subclass;
1362
1363 // Set shared properties
1364 $table_obj->db =& $this->db;
1365 $table_obj->backend = $this->backend;
1366 $table_obj->fetchmode = $this->fetchmode;
1367
1368 // Add all columns to $_col property
1369 foreach ($table_obj->col as $key => $def) {
1370 if (!isset($this->_col[$key])) {
1371 $this->_col[$key] = array();
1372 }
1373 $this->_col[$key][] = $table;
1374 }
1375
1376 return true;
1377 }
1378
1379 // }}}
1380 // {{{ function deleteTable($table)
1381
1382 /**
1383 * Deletes a table from $this database object.
1384 *
1385 * Removes all dependencies on $table from the database model. The table
1386 * is removed from $_table and $_primary_key properties. Its columns are
1387 * removed from the $_col and $_foreign_col properties. References to
1388 * and from the table are removed from the $_ref, $_ref_to, and $_link
1389 * properties. Referencing columns are removed from $_foreign_col.
1390 *
1391 * @param string $table name of table to be deleted
1392 * @return void
1393 * @access public
1394 */
1395 function deleteTable($table)
1396 {
1397 if (isset($this->_table[$table])) {
1398 $table_obj =& $this->_table[$table];
1399 } else {
1400 return;
1401 }
1402
1403 // Remove reference to database from table object
1404 $null_instance = null;
1405 $table_obj->setDatabaseInstance($null_instance);
1406
1407 // Remove columns from $_col and $_foreign_col property arrays
1408 foreach ($table_obj->col as $column => $def) {
1409 $key = array_search($table, $this->_col[$column]);
1410 if (is_integer($key)) {
1411 unset($this->_col[$column][$key]);
1412 if (count($this->_col[$column]) == 0) {
1413 unset($this->_col[$column]);
1414 } else {
1415 $new = array_values($this->_col[$column]);
1416 $this->_col[$column] = $new;
1417 }
1418 }
1419 if (isset($this->_foreign_col[$column])) {
1420 $key = array_search($table, $this->_foreign_col[$column]);
1421 if (is_integer($key)) {
1422 unset($this->_foreign_col[$column][$key]);
1423 if (count($this->_foreign_col[$column]) == 0) {
1424 unset($this->_foreign_col[$column]);
1425 } else {
1426 $new = array_values($this->_foreign_col[$column]);
1427 $this->_foreign_col[$column] = $new;
1428 }
1429 }
1430 }
1431 }
1432
1433 // Remove all references involving the deleted table.
1434 // Corresponding links are removed from $this->_link by deleteRef
1435 // Referencing columns are removed from $this->_foreign_col by deleteRef
1436 foreach ($this->_ref as $ftable => $referenced) {
1437 foreach ($referenced as $rtable => $ref) {
1438 if ($ftable == $table || $rtable == $table) {
1439 $this->deleteRef($ftable, $rtable);
1440 }
1441 }
1442 }
1443
1444 // Remove table from $this->_table and $this->_primary_key
1445 unset($this->_table[$table]);
1446 unset($this->_primary_key[$table]);
1447 }
1448
1449 // }}}
1450 // {{{ function addRef($ftable, $fkey, $rtable, [$rkey], [$on_delete], [$on_update])
1451
1452 /**
1453 * Adds a foreign key reference to the database.
1454 *
1455 * Adds a reference from foreign key $fkey of table $ftable to
1456 * referenced key $rkey of table named $rtable to the $this->_ref
1457 * property. The values of $fkey and $rkey (if not null) may either
1458 * both be column name strings (for single column keys) or they
1459 * may both be numerically indexed arrays of corresponding column
1460 * names (for multi-column keys). If $rkey is null (the default),
1461 * the referenced key taken to be the primary key of $rtable, if
1462 * any.
1463 *
1464 * The $on_delete and $on_update parameters may be either be null,
1465 * or may have string values 'restrict', 'cascade', 'set null', or
1466 * 'set default' that indicate referentially triggered actions to be
1467 * taken deletion or updating of referenced row in $rtable. Each of
1468 * these actions corresponds to a standard SQL action (e.g., cascading
1469 * delete) that may be taken upon referencing rows of table $ftable
1470 * when a referenced row of $rtable is deleted or updated. A PHP
1471 * null value for either parameter (the default) signifies that no
1472 * such action will be taken upon deletion or updating.
1473 *
1474 * There may no more than one reference from a table to another, though
1475 * reference may contain multiple columns.
1476 *
1477 * Returns true on success, and PEAR error on failure. Returns the
1478 * following DB_TABLE_DATABASE_ERR_* error codes if:
1479 * - $ftable does not exist ( _NO_FTABLE )
1480 * - $rtable does not exist ( _NO_RTABLE )
1481 * - $rkey is null and $rtable has no primary key ( _NO_PKEY )
1482 * - $fkey is neither a string nor an array ( _FKEY )
1483 * - $rkey is not a string, $fkey is a string ( _RKEY_NOT_STRING )
1484 * - $rkey is not an array, $fkey is an array ( _RKEY_NOT_ARRAY )
1485 * - A column of $fkey does not exist ( _NO_FCOL )
1486 * - A column of $rkey does not exist ( _NO_RCOL )
1487 * - A column of $fkey and $rkey have different types ( _REF_TYPE )
1488 * - A reference from $ftable to $rtable already exists ( _MULT_REF )
1489 *
1490 * @param string $ftable name of foreign/referencing table
1491 * @param mixed $fkey foreign key in referencing table
1492 * @param string $rtable name of referenced table
1493 * @param mixed $rkey referenced key in referenced table
1494 * @param string $on_delete action upon delete of a referenced row.
1495 * @param string $on_update action upon update of a referenced row.
1496 * @return boolean true on success (PEAR_Error on failure)
1497 * @access public
1498 */
1499 function addRef($ftable, $fkey, $rtable, $rkey = null,
1500 $on_delete = null, $on_update = null)
1501 {
1502 // Check existence of $ftable is a key in $this->_table.
1503 if (isset($this->_table[$ftable])) {
1504 $ftable_obj =& $this->_table[$ftable];
1505 } else {
1506 return $this->throwError(
1507 DB_TABLE_DATABASE_ERR_NO_FTABLE,
1508 "$ftable => $rtable");
1509 }
1510
1511 // Check existence of referenced table
1512 if (isset($this->_table[$rtable])) {
1513 $rtable_obj =& $this->_table[$rtable];
1514 } else {
1515 return $this->throwError(
1516 DB_TABLE_DATABASE_ERR_NO_RTABLE,
1517 "$ftable => $rtable");
1518 }
1519
1520 // If referenced key is null, set it to the primary key
1521 if (!$rkey) {
1522 if (isset($this->_primary_key[$rtable])) {
1523 $rkey = $this->_primary_key[$rtable];
1524 } else {
1525 // Error: null referenced key and no primary key
1526 return $this->throwError(
1527 DB_TABLE_DATABASE_ERR_NO_PKEY,
1528 "$ftable => $rtable");
1529 }
1530 }
1531
1532 // Check $fkey and $rkey types and compatibility
1533 if (is_string($fkey)) {
1534 if (!is_string($rkey)) {
1535 return $this->throwError(
1536 DB_TABLE_DATABASE_ERR_RKEY_NOT_STRING,
1537 "$ftable => $rtable");
1538 }
1539 if (!isset($ftable_obj->col[$fkey])) {
1540 return $this->throwError(
1541 DB_TABLE_DATABASE_ERR_NO_FCOL,
1542 "$ftable.$fkey => $rtable.$rkey");
1543 }
1544 if (!isset($rtable_obj->col[$rkey])) {
1545 return $this->throwError(
1546 DB_TABLE_DATABASE_ERR_NO_RCOL,
1547 "$ftable.$fkey => $rtable.$rkey");
1548 }
1549 $ftype = $ftable_obj->col[$fkey]['type'];
1550 $rtype = $rtable_obj->col[$rkey]['type'];
1551 if (!($rtype == $ftype)) {
1552 return $this->throwError(
1553 DB_TABLE_DATABASE_ERR_REF_TYPE,
1554 "$ftable.$fkey => $rtable.$rkey");
1555 }
1556 } elseif (is_array($fkey)) {
1557 if (!is_array($rkey)) {
1558 return $this->throwError(
1559 DB_TABLE_DATABASE_ERR_RKEY_NOT_ARRAY,
1560 "$ftable => $rtable");
1561 }
1562 if (!(count($fkey) == count($rkey))) {
1563 return $this->throwError(
1564 DB_TABLE_DATABASE_ERR_RKEY_COL_NUMBER,
1565 "$ftable => $rtable");
1566 }
1567 for ($i=0 ; $i < count($rkey) ; $i++) {
1568 $fcol = $fkey[$i];
1569 $rcol = $rkey[$i];
1570 if (!isset($ftable_obj->col[$fcol])) {
1571 return $this->throwError(
1572 DB_TABLE_DATABASE_ERR_NO_FCOL,
1573 "$ftable.$fcol => $rtable.$rcol");
1574 }
1575 if (!isset($rtable_obj->col[$rcol])) {
1576 return $this->throwError(
1577 DB_TABLE_DATABASE_ERR_NO_RCOL,
1578 "$ftable.$fcol => $rtable.$rcol");
1579 }
1580 $ftype = $ftable_obj->col[$fcol]['type'];
1581 $rtype = $rtable_obj->col[$rcol]['type'];
1582 if (!($rtype == $ftype)) {
1583 return $this->throwError(
1584 DB_TABLE_DATABASE_ERR_REF_TYPE,
1585 "$ftable.$fcol => $rtable.$rcol");
1586 }
1587 }
1588 } else {
1589 return $this->throwError(
1590 DB_TABLE_DATABASE_ERR_FKEY,
1591 "$ftable => $rtable");
1592 }
1593
1594 // Check validity of on_delete and on_update actions
1595 $valid_actions =
1596 array(null, 'cascade', 'set null', 'set default', 'restrict');
1597 if (!in_array($on_delete, $valid_actions)) {
1598 return $this->throwError(
1599 DB_TABLE_DATABASE_ERR_ON_DELETE_ACTION,
1600 "$ftable => $rtable");
1601 }
1602 if (!in_array($on_update, $valid_actions)) {
1603 return $this->throwError(
1604 DB_TABLE_DATABASE_ERR_ON_UPDATE_ACTION,
1605 "$ftable => $rtable");
1606 }
1607
1608 // Add reference to $this->_ref;
1609 $ref = array(
1610 'fkey' => $fkey,
1611 'rkey' => $rkey,
1612 'on_delete' => $on_delete,
1613 'on_update' => $on_update);
1614 if (!isset($this->_ref[$ftable])) {
1615 $this->_ref[$ftable] = array();
1616 } else {
1617 if (isset($this->_ref[$ftable][$rtable])) {
1618 // Multiple references from $ftable to $rtable
1619 return $this->throwError(
1620 DB_TABLE_DATABASE_ERR_MULT_REF,
1621 "$ftable => $rtable");
1622 }
1623 }
1624 $this->_ref[$ftable][$rtable] = $ref;
1625
1626 // Add referencing table $ftable to $ref_to property
1627 if (!isset($this->_ref_to[$rtable])) {
1628 $this->_ref_to[$rtable] = array();
1629 }
1630 $this->_ref_to[$rtable][] = $ftable;
1631
1632 // Add foreign key columns to $this->_foreign_col
1633 if (is_string($fkey)) {
1634 if (!isset($this->_foreign_col[$fkey])) {
1635 $this->_foreign_col[$fkey] = array();
1636 }
1637 $this->_foreign_col[$fkey][] = $ftable;
1638 } elseif (is_array($fkey)) {
1639 foreach ($fkey as $fcol) {
1640 if (!isset($this->_foreign_col[$fcol])) {
1641 $this->_foreign_col[$fcol] = array();
1642 }
1643 $this->_foreign_col[$fcol][] = $ftable;
1644 }
1645 }
1646
1647 // Normal completion
1648 return true;
1649 }
1650
1651 // }}}
1652 // {{{ function deleteRef($ftable, $rtable)
1653
1654 /**
1655 * Deletes one reference from database model
1656 *
1657 * Removes reference from referencing (foreign key) table named
1658 * $ftable to referenced table named $rtable. Unsets relevant elements
1659 * of the $ref, $_ref_to, and $_link property arrays, and removes the
1660 * foreign key columns of $ftable from the $_foreign_col property.
1661 *
1662 * Does nothing, silently, if no such reference exists, i.e., if
1663 * $this->_ref[$ftable][$rtable] is not set.
1664 *
1665 * @param $ftable name of referencing (foreign key) table
1666 * @param $rtable name of referenced table
1667 * @return void
1668 * @access public
1669 */
1670 function deleteRef($ftable, $rtable)
1671 {
1672 // Delete from $_ref property
1673 if (isset($this->_ref[$ftable])) {
1674 if (isset($this->_ref[$ftable][$rtable])) {
1675 $fkey = $this->_ref[$ftable][$rtable]['fkey'];
1676 unset($this->_ref[$ftable][$rtable]);
1677 } else {
1678 // No such reference, abort silently
1679 return;
1680 }
1681 }
1682
1683 // Remove foreign key columns from $foreign_col property
1684 if (isset($fkey)) {
1685 if (is_string($fkey)) {
1686 $fkey = array($fkey);
1687 }
1688 foreach ($fkey as $column) {
1689 if (isset($this->_foreign_col[$column])) {
1690 $key = array_search($ftable,
1691 $this->_foreign_col[$column]);
1692 if (is_integer($key)) {
1693 unset($this->_foreign_col[$column][$key]);
1694 if (count($this->_foreign_col[$column]) == 0) {
1695 unset($this->_foreign_col[$column]);
1696 } else {
1697 $new = array_values($this->_foreign_col[$column]);
1698 $this->_foreign_col[$column] = $new;
1699 }
1700 }
1701 }
1702 }
1703 }
1704
1705 // Delete from $_ref_to property
1706 if (isset($this->_ref_to[$rtable])) {
1707 $key = array_search($ftable, $this->_ref_to[$rtable]);
1708 // Unset element
1709 unset($this->_ref_to[$rtable][$key]);
1710 if (count($this->_ref_to[$rtable]) == 0) {
1711 unset($this->_ref_to[$rtable]);
1712 } else {
1713 // Redefine numerical keys of remaining elements
1714 $ref_to = array_values($this->_ref_to[$rtable]);
1715 $this->_ref_to[$rtable] = $ref_to;
1716 }
1717 }
1718
1719 // Delete all relevant links from $_link property
1720 if (isset($this->_link[$rtable])) {
1721 foreach ($this->_link[$rtable] as $table2 => $links) {
1722 if (in_array($ftable, $links)) {
1723 $this->deleteLink($rtable, $table2, $ftable);
1724 }
1725 }
1726 }
1727 }
1728
1729 // }}}
1730 // {{{ function setOnDelete($ftable, $rtable, $action)
1731
1732 /**
1733 * Modifies the on delete action for one foreign key reference.
1734 *
1735 * Modifies the value of the on_delete action associated with a reference
1736 * from $ftable to $rtable. The parameter action may be one of the action
1737 * strings 'cascade', 'restrict', 'set null', or 'set default', or it may
1738 * be php null. A null value of $action indicates that no action should be
1739 * taken upon deletion of a referenced row.
1740 *
1741 * Returns true on success, and PEAR error on failure. Returns the error
1742 * code DB_TABLE_DATABASE_ERR_REF_TRIG_ACTION if $action is a neither a
1743 * valid action string nor null. Returns true, and does nothing, if
1744 * $this->_ref[$ftable][$rtable] is not set.
1745 *
1746 * @param string $ftable name of referencing (foreign key) table
1747 * @param string $rtable name of referenced table
1748 * @param string $action on delete action (action string or null)
1749 * @return boolean true on normal completion (PEAR_Error on failure)
1750 * @access public
1751 */
1752 function setOnDelete($ftable, $rtable, $action)
1753 {
1754 $valid_actions =
1755 array(null, 'cascade', 'set null', 'set default', 'restrict');
1756
1757 if (isset($this->_ref[$ftable])) {
1758 if (isset($this->_ref[$ftable][$rtable])) {
1759 if (!in_array($action, $valid_actions)) {
1760 return $this->throwError(
1761 DB_TABLE_DATABASE_ERR_REF_ON_DELETE_ACTION,
1762 "$ftable => $rtable");
1763 }
1764 $this->_ref[$ftable][$rtable]['on_delete'] = $action;
1765 }
1766 }
1767 return true;
1768 }
1769
1770 // }}}
1771 // {{{ function setOnUpdate($ftable, $rtable, $action)
1772
1773 /**
1774 * Modifies on update action for one foreign key reference.
1775 *
1776 * Similar to setOnDelete. See setOnDelete for further details.
1777 *
1778 * @param string $ftable name of referencing (foreign key) table
1779 * @param string $rtable name of referenced table
1780 * @param array $action on update action (action string or null)
1781 * @return boolean true on normal completion (PEAR_Error on failure)
1782 * @access public
1783 */
1784 function setOnUpdate($ftable, $rtable, $action)
1785 {
1786 $valid_actions =
1787 array(null, 'cascade', 'set null', 'set default', 'restrict');
1788
1789 if (isset($this->_ref[$ftable])) {
1790 if (isset($this->_ref[$ftable][$rtable])) {
1791 if (!in_array($action, $valid_actions)) {
1792 return $this->throwError(
1793 DB_TABLE_DATABASE_ERR_REF_ON_UPDATE_ACTION,
1794 "$ftable => $rtable");
1795 }
1796 $this->_ref[$ftable][$rtable]['on_update'] = $action;
1797 }
1798 }
1799 return true;
1800 }
1801
1802 // }}}
1803 // {{{ function addLink($table1, $table2, $link)
1804
1805 /**
1806 * Identifies a linking/association table that links two others
1807 *
1808 * Adds table name $link to $this->_link[$table1][$table2] and
1809 * to $this->_link[$table2][$table1].
1810 *
1811 * Returns true on success, and PEAR error on failure. Returns the
1812 * following DB_TABLE_DATABASE_ERR_* error codes if:
1813 * - $ftable does not exist ( _NO_FTABLE )
1814 * - $rtable does not exist ( _NO_RTABLE )
1815 *
1816 * @param string $table1 name of 1st linked table
1817 * @param string $table2 name of 2nd linked table
1818 * @param string $link name of linking/association table.
1819 * @return boolean true on success (PEAR_Error on failure)
1820 * @access public
1821 */
1822 function addLink($table1, $table2, $link)
1823 {
1824
1825 // Check for existence of all three tables
1826 if (is_string($table1)) {
1827 if (!isset($this->_table[$table1])) {
1828 return $this->throwError(
1829 DB_TABLE_DATABASE_ERR_NO_TBL,
1830 "addLink, $table1");
1831 }
1832 } else {
1833 return $this->throwError(
1834 DB_TABLE_DATABASE_ERR_NO_TBL,
1835 "addLink, $table1");
1836 }
1837 if (!isset($this->_table[$table2])) {
1838 return $this->throwError(
1839 DB_TABLE_DATABASE_ERR_NO_TBL,
1840 "addLink, $table2");
1841 }
1842 if (!isset($this->_table[$link])) {
1843 return $this->throwError(
1844 DB_TABLE_DATABASE_ERR_NO_TBL,
1845 "addLink, $link");
1846 }
1847 if (!isset($this->_ref[$link])) {
1848 return $this->throwError(
1849 DB_TABLE_DATABASE_ERR_NO_REF_LINK,
1850 "$link => $table1, $table2");
1851 } else {
1852 if (!isset($this->_ref[$link][$table1])) {
1853 return $this->throwError(
1854 DB_TABLE_DATABASE_ERR_NO_REF_LINK,
1855 "$link => $table1");
1856 }
1857 if (!isset($this->_ref[$link][$table2])) {
1858 return $this->throwError(
1859 DB_TABLE_DATABASE_ERR_NO_REF_LINK,
1860 "$link => $table2");
1861 }
1862 }
1863
1864 // Add $this_link[$table1][$table2]
1865 if (!key_exists($table1, $this->_link)) {
1866 $this->_link[$table1] = array();
1867 }
1868 if (!key_exists($table2, $this->_link[$table1])) {
1869 $this->_link[$table1][$table2] = array();
1870 }
1871 $this->_link[$table1][$table2][] = $link;
1872
1873 // Add $this_link[$table2][$table1]
1874 if (!key_exists($table2, $this->_link)) {
1875 $this->_link[$table2] = array();
1876 }
1877 if (!key_exists($table1, $this->_link[$table2])) {
1878 $this->_link[$table2][$table1] = array();
1879 }
1880 $this->_link[$table2][$table1][] = $link;
1881 }
1882
1883 // }}}
1884 // {{{ function addAllLink()
1885
1886 /**
1887 * Adds all possible linking tables to the $_link property array
1888 *
1889 * Identifies all potential linking tables in the datbase, and adds
1890 * them all to the $_link property. Table $link is taken to be a
1891 * link between tables $table1 and $table2 if it contains foreign
1892 * key references to both $table1 and $table2.
1893 *
1894 * @return void
1895 * @access public
1896 */
1897 function addAllLinks()
1898 {
1899 foreach ($this->_table as $link => $link_obj) {
1900 if (isset($this->_ref[$link])) {
1901 $ref = $this->_ref[$link];
1902 $n = count($ref);
1903 $names = array_keys($ref);
1904 if ($n > 1) {
1905 $is_link = true;
1906 } else {
1907 $is_link = false;
1908 }
1909 if ($is_link) {
1910 if ($n == 2) {
1911 $table1 = $names[0];
1912 $table2 = $names[1];
1913 $this->addLink($table1, $table2, $link);
1914 } elseif ($n > 2) {
1915 for ($i=1 ; $i < $n; $i++) {
1916 for ($j=0 ; $j < $i; $j++) {
1917 $table1 = $names[$j];
1918 $table2 = $names[$i];
1919 $this->addLink($table1, $table2, $link);
1920 }
1921 }
1922 }
1923 }
1924 }
1925 }
1926 }
1927
1928 // }}}
1929 // {{{ function deleteLink($table1, $table2, $link = null)
1930
1931 /**
1932 * Removes a link between two tables from the $_link property
1933 *
1934 * If $link is not null, remove table $link from the list of links
1935 * between $table1 and $table2, if present. If $link is null, delete
1936 * all links between $table1 and $table2.
1937 *
1938 * @param string $table1 name of 1st linked table
1939 * @param string $table2 name of 2nd linked table
1940 * @param string $link name of linking table
1941 * @return void
1942 * @access public
1943 */
1944 function deleteLink($table1, $table2, $link = null)
1945 {
1946 if (isset($this->_link[$table1])) {
1947 if (isset($this->_link[$table1][$table2])) {
1948 if ($link) {
1949 // Find numerical key of $link in _link[$table1][$table2]
1950 $key = array_search($link, $this->_link[$table1][$table2]);
1951 if (is_integer($key)) {
1952 unset($this->_link[$table1][$table2][$key]);
1953 if (count($this->_link[$table1][$table2]) == 0) {
1954 unset($this->_link[$table1][$table2]);
1955 unset($this->_link[$table2][$table1]);
1956 if (count($this->_link[$table1]) == 0) {
1957 unset($this->_link[$table1]);
1958 }
1959 if (count($this->_link[$table2]) == 0) {
1960 unset($this->_link[$table2]);
1961 }
1962 } else {
1963 // Reset remaining indices sequentially from zero
1964 $new = array_values($this->_link[$table1][$table2]);
1965 $this->_link[$table1][$table2] = $new;
1966 $this->_link[$table2][$table1] = $new;
1967 }
1968 }
1969 } else {
1970 unset($this->_link[$table1][$table2]);
1971 unset($this->_link[$table2][$table1]);
1972 if (count($this->_link[$table1]) == 0) {
1973 unset($this->_link[$table1]);
1974 }
1975 if (count($this->_link[$table2]) == 0) {
1976 unset($this->_link[$table2]);
1977 }
1978 }
1979 }
1980 }
1981 }
1982
1983 // }}}
1984 // {{{ function validCol($col, $from = null)
1985
1986 /**
1987 * Validates and (if necessary) disambiguates a column name.
1988 *
1989 * The parameter $col is a string may be either a column name or
1990 * a column name qualified by a table name, using the SQL syntax
1991 * "$table.$column". If $col contains a table name, and is valid,
1992 * an array($table, $column) is returned. If $col is not qualified
1993 * by a column name, an array array($table, $column) is returned,
1994 * in which $table is either the name of one table, or an array
1995 * containing the names of two or more tables containing a column
1996 * named $col.
1997 *
1998 * The $from parameter, if present, is a numerical array of
1999 * names of tables with which $col should be associated, if no
2000 * explicit table name is provided, and if possible. If one
2001 * or more of the tables in $from contains a column $col, the
2002 * returned table or set of tables is restricted to those in
2003 * array $from.
2004 *
2005 * If the table name remains ambiguous after testing for tables in
2006 * the $from set, and $col is not a foreign key in one or more of
2007 * the remaining tables, the returned table or set of tables is
2008 * restricted to those in which $col is not a foreign key.
2009 *
2010 * Returns a PEAR_Error with the following DB_TABLE_DATABASE_ERR_* error
2011 * codes if:
2012 * - column $col does not exist in the database ( _NO_COL_DB )
2013 * - column $col does not exist in the specified table ( _NO_COL_TBL )
2014 *
2015 * @param string $col column name, optionally qualified by a table name
2016 * @param array $from array of tables from which $col should be chosen,
2017 * if possible.
2018 * @return array array($table, $column), or PEAR_Error on failure
2019 * $column is a string, $table is a string or array
2020 * @access public
2021 */
2022 function validCol($col, $from = null)
2023 {
2024 $col = explode('.',trim($col));
2025 if (count($col) == 1) {
2026 // Parameter $col is a column name with no table name
2027 $column = $col[0];
2028 // Does $column exist in database ?
2029 if (!isset($this->_col[$column])) {
2030 return $this->throwError(
2031 DB_TABLE_DATABASE_ERR_NO_COL_DB,
2032 "$column");
2033 }
2034 $table = $this->_col[$column];
2035 // If $table is not unique, try restricting to arrays in $from
2036 if (count($table) > 1 && $from) {
2037 $ptable = array_intersect($table, $from);
2038 if (count($ptable) > 0) {
2039 $table = array_values($ptable);
2040 }
2041 }
2042 // If count($table)>1, try excluding foreign key columns
2043 if (count($table) > 1 && isset($this->_foreign_col[$column])) {
2044 $ptable = array_diff($table, $this->_foreign_col[$column]);
2045 if (count($ptable) > 0) {
2046 $table = array_values($ptable);
2047 }
2048 }
2049 // If only one table remains, set $table = table name string
2050 if (count($table) == 1) {
2051 $table = $table[0];
2052 }
2053 } elseif (count($col) == 2) {
2054 // parameter $col is qualified by a table name
2055 $table = $col[0];
2056 $column = $col[1];
2057 if (isset($this->_table[$table])) {
2058 $table_obj =& $this->_table[$table];
2059 $col_array = $table_obj->col;
2060 if (!isset($col_array[$column])) {
2061 return $this->throwError(
2062 DB_TABLE_DATABASE_ERR_NO_COL_TBL,
2063 "$table.$column");
2064 }
2065 } else {
2066 return $this->throwError(
2067 DB_TABLE_DATABASE_ERR_NO_TBL, "validCol, $table");
2068 }
2069 }
2070 return array($table, $column);
2071 }
2072
2073 // }}}
2074 // {{{ function createTables($flag = 'safe')
2075
2076 /**
2077 * Creates all the tables in a database in a RDBMS
2078 *
2079 * Note: this method creates all the tables in a database, but does
2080 * NOT create the parent database or set it to the current or default
2081 * database -- the database must exist before the method is called.
2082 *
2083 * If creation of any table fails, the method immediately returns the
2084 * PEAR error returned by DB_Table::create($flag).
2085 *
2086 * @param mixed $flag The automatic database creation mode, which is
2087 * applied to each table in the database. It can have
2088 * values:
2089 * - 'safe' to create a table only if it does not exist
2090 * - 'drop' to drop and recreate any existing table
2091 with the same name
2092 *
2093 * @return boolean true on sucess (PEAR_Error on failure of any table)
2094 * @access public
2095 *
2096 * @see DB_Table::create()
2097 */
2098 function createTables($flag = 'safe')
2099 {
2100 foreach ($this->_table as $name => $table) {
2101 $result = $table->create($flag);
2102 if (PEAR::isError($result)) {
2103 return $result;
2104 }
2105 }
2106 return true;
2107 }
2108
2109 // }}}
2110 // {{{ function validForeignKeys($table_name, $data)
2111
2112 /**
2113 * Check validity of any foreign key values in associative array $data
2114 * containing values to be inserted or updated in table $table_name.
2115 *
2116 * Returns true if each foreign key in $data matches a row in the
2117 * referenced table, or if there are no foreign key columns in $data.
2118 * Returns a PEAR_Error if any foreign key column in associative array
2119 * $data (which may contain a full or partial row of $table_name), does
2120 * not match the the value of the referenced column in any row of the
2121 * referenced table.
2122 *
2123 * @param $table_name name of the referencing table containing $data
2124 * @param @data associative array containing all or part of a row
2125 * of data of $table_name, with column name keys.
2126 * @return bool true if all foreign keys are valid, returns PEAR_Error
2127 * if foreign keys are invalid or if an error is thrown
2128 * by a required query
2129 *
2130 * @throws PEAR error if:
2131 * - Error thrown by _buildFKeyFilter method (bubbles up)
2132 * - Error thrown by select method for required query (bubbles up)
2133 *
2134 * @access public
2135 */
2136 function validForeignKeys($table_name, $data)
2137 {
2138 if (isset($this->_ref[$table_name])) {
2139 foreach ($this->_ref[$table_name] as $rtable_name => $ref) {
2140 $fkey = $ref['fkey'];
2141 $rkey = $ref['rkey'];
2142 $rtable_obj =& $this->_table[$rtable_name];
2143
2144 // Construct select where clause for referenced rows,
2145 // $filter = '' if $data contains no foreign key columns,
2146 $filter = $this->_buildFKeyFilter($data, $fkey, $rkey);
2147 if (PEAR::isError($filter)) {
2148 return $filter;
2149 }
2150
2151 // If inserted data contain FK columns referenced by rtable,
2152 // select referenced row of rtable, return error if none is
2153 // found
2154 if ($filter) {
2155 $sql = array('select'=> '*',
2156 'from' => $rtable_name,
2157 'where' => $filter);
2158 $referenced_rows = $this->select($sql);
2159 // Check for failed query
2160 if (PEAR::isError($referenced_rows)) {
2161 return $referenced_rows;
2162 }
2163 // Check for failed foreign key constraint
2164 if (count($referenced_rows) == 0) {
2165 return $this->throwError(
2166 DB_TABLE_DATABASE_ERR_FKEY_CONSTRAINT);
2167 }
2168 }
2169 }
2170 }
2171 return true;
2172 }
2173
2174 // }}}
2175 // {{{ function insert($table_name, $data)
2176
2177 /**
2178 * Inserts a single table row
2179 *
2180 * Wrapper for insert method of the corresponding DB_Table object.
2181 *
2182 * Data will be validated before insertion using validForeignKey(),
2183 * if foreign key validation in enabled.
2184 *
2185 * @param string $table_name Name of table into which to insert data
2186 * @param array $data Associative array, in which each key is a column
2187 * name and each value is that column's value.
2188 * This is the data that will be inserted into
2189 * the table. Data is checked against the column
2190 * names and data types for validity.
2191 * @return boolean true on success (PEAR_Error on failure)
2192 * @access public
2193 */
2194 function insert($table_name, $data)
2195 {
2196 // Dereference table object
2197 if (isset($this->_table[$table_name])) {
2198 $table_obj =& $this->_table[$table_name];
2199 } else {
2200 return $this->throwError(
2201 DB_TABLE_DATABASE_ERR_NO_TBL,
2202 "insert, $table_name");
2203 }
2204
2205 // Insert into $table_obj
2206 $result = $table_obj->insert($data);
2207
2208 // Return value: true or PEAR_Error
2209 if (PEAR::isError($result)) {
2210 return $result;
2211 } else {
2212 return true;
2213 }
2214
2215 }
2216
2217 // }}}
2218 // {{{ function autoValidInsert($flag = true)
2219
2220 /**
2221 * Turns on or off automatic validation of inserted data for all tables
2222 *
2223 * @param bool $flag true to turn on auto-validation, false to turn off.
2224 * @return void
2225 * @access public
2226 */
2227 function autoValidInsert($flag = true)
2228 {
2229 foreach ($this->_table as $table_obj) {
2230 $table_obj->autoValidInsert($flag);
2231 }
2232 }
2233
2234 // }}}
2235 // {{{ function update($table_name, $data, $where)
2236
2237 /**
2238 * Updates all row(s) of table that match a custom where clause.
2239 *
2240 * Wrapper for insert method of the corresponding DB_Table object.
2241 *
2242 * Data will be validated before insertion using validForeignKey(),
2243 * if foreign key validation in enabled.
2244 *
2245 * Implements any required ON UPDATE actions on tables that
2246 * reference updated columns, if on update actions are enabled.
2247 *
2248 * @param string $table_name name of table to update
2249 * @param array $data associative array in which keys are names of
2250 * columns to be updated values are new values.
2251 * @param string $where SQL WHERE clause that limits the set of
2252 * records to update.
2253 * @return boolean true on success (PEAR_Error on failure)
2254 * @access public
2255 */
2256 function update($table_name, $data, $where)
2257 {
2258 // Dereference table object
2259 if (isset($this->_table[$table_name])) {
2260 $table_obj =& $this->_table[$table_name];
2261 } else {
2262 return $this->throwError(
2263 DB_TABLE_DATABASE_ERR_NO_TBL,
2264 "update, $table_name");
2265 }
2266
2267 // Apply update
2268 $result = $table_obj->update($data, $where);
2269
2270 // Return value: true or PEAR_Error
2271 if (PEAR::isError($result)) {
2272 return $result;
2273 } else {
2274 return true;
2275 }
2276
2277 }
2278
2279 // }}}
2280 // {{{ function autoValidUpdate($flag = true)
2281
2282 /**
2283 * Turns on (or off) automatic validation of updated data for all tables.
2284 *
2285 * @param bool $flag true to turn on auto-validation, false to turn off
2286 * @return void
2287 * @access public
2288 */
2289 function autoValidUpdate($flag = true)
2290 {
2291 foreach ($this->_table as $table_obj) {
2292 $table_obj->autoValidUpdate($flag);
2293 }
2294 }
2295
2296 // }}}
2297 // {{{ function onUpdateAction(&$table_obj, $data, $where)
2298
2299 /**
2300 * Implements any ON UPDATE actions triggered by updating of rows of
2301 * $table_obj that match logical condition $where.
2302 *
2303 * This method is called by the DB_Table::update() method if the table
2304 * has a parent DB_Table_Database object, and if ON UPDATE actions are
2305 * enabled in the database object. It is called indirectly by the
2306 * DB_Table_Database::delete() method, which is simply a wrapper for
2307 * the DB_Table method.
2308 *
2309 * @param object &$table_obj Reference to a DB_Table object
2310 * @param array $data Data to updated, column name keys, data values
2311 * @param string $where SQL logical condition for updated rows
2312 * @return boolean true on success (PEAR_Error on failure)
2313 * @access public
2314 */
2315 function onUpdateAction(&$table_obj, $data, $where)
2316 {
2317 $table_name = $table_obj->table;
2318 if ($this->_act_on_update and isset($this->_ref_to[$table_name])) {
2319 $update_rows = null;
2320 foreach ($this->_ref_to[$table_name] as $ftable_name) {
2321 $ref = $this->_ref[$ftable_name][$table_name];
2322 $action = isset($ref['on_update']) ? $ref['on_update'] : null;
2323 if (is_null($action)) {
2324 continue;
2325 }
2326 $rtable_obj =& $this->_table[$table_name];
2327 $ftable_obj =& $this->_table[$ftable_name];
2328 $fkey = $ref['fkey'];
2329 $rkey = $ref['rkey'];
2330
2331 // Check if any column(s) of referenced $rkey are updated
2332 $rkey_updated = false;
2333 foreach ($data as $key => $value) {
2334 if (is_string($rkey)){
2335 if ($key == $rkey) {
2336 $rkey_updated = true;
2337 break;
2338 }
2339 } else {
2340 if (in_array($key, $rkey)) {
2341 $rkey_updated = true;
2342 break;
2343 }
2344 }
2345 }
2346
2347 // If $rkey is not updated, continue to next referencing table
2348 if (!$rkey_updated) {
2349 continue;
2350 }
2351
2352 // Select rows to be updated, if not done previously
2353 if ($update_rows === null) {
2354 if ($this->backend == 'mdb2') {
2355 $fetchmode_assoc = MDB2_FETCHMODE_ASSOC;
2356 } else {
2357 $fetchmode_assoc = DB_FETCHMODE_ASSOC;
2358 }
2359 $sql = array('select' => '*',
2360 'from' => $table_name,
2361 'where' => $where,
2362 'fetchmode' => $fetchmode_assoc);
2363 $update_rows = $this->select($sql);
2364 if (PEAR::isError($update_rows)) {
2365 return $update_rows;
2366 }
2367 }
2368
2369 // Construct $fdata array if cascade, set null, or set default
2370 $fdata = null;
2371 if ($action == 'cascade') {
2372 if (is_string($rkey)) {
2373 if (array_key_exists($rkey, $data)) {
2374 $fdata = array($fkey => $data[$rkey]);
2375 }
2376 } else {
2377 $fdata = array();
2378 for ($i=0; $i < count($rkey); $i++) {
2379 $rcol = $rkey[$i];
2380 $fcol = $fkey[$i];
2381 if (array_key_exists($rcol, $data)) {
2382 $fdata[$fcol] = $data[$rcol];
2383 }
2384 }
2385 if (count($fdata) == 0) {
2386 $fdata = null;
2387 }
2388 }
2389 } elseif ($action == 'set null' or $action == 'set default') {
2390 if (is_string($fkey)) {
2391 if ($action == 'set default') {
2392 $value = isset($ftable_obj->col[$fkey]['default'])
2393 ? $ftable_obj->col[$fkey]['default'] : null;
2394 } else {
2395 $value = null;
2396 }
2397 $fdata = array($fkey => $value);
2398 } else {
2399 $fdata = array();
2400 foreach ($fkey as $fcol) {
2401 if ($action == 'set default') {
2402 $value = isset($ftable_obj->col[$fcol]['default'])
2403 ? $ftable_obj->col[$fcol]['default'] : null;
2404 } else {
2405 $value = null;
2406 }
2407 $fdata[$fcol] = $value;
2408 }
2409 if (count($fdata) == 0) {
2410 $fdata = null;
2411 }
2412 }
2413 } elseif ($action == 'restrict') {
2414 $fdata = true;
2415 } elseif ($action == null) {
2416 $fdata = null;
2417 } else {
2418 return $this->throwError(
2419 DB_TABLE_DATABASE_ERR_ON_UPDATE_ACTION,
2420 "$ftable_name => $table_name");
2421 }
2422
2423 if (!is_null($fdata)) {
2424
2425 // Loop over rows to be updated from $table
2426 foreach ($update_rows as $update_row) {
2427
2428 // If necessary, restore case of column names
2429 if ($this->_fix_case) {
2430 $cols = array_keys($table_obj->col);
2431 $update_row = $this->_replaceKeys($update_row, $cols);
2432 }
2433
2434 // Construct filter for rows that reference $update_row
2435 $filter = $this->_buildFKeyFilter($update_row,
2436 $rkey, $fkey);
2437
2438 // Apply action to foreign/referencing rows
2439 if ($action == 'restrict') {
2440 $sql = array('select'=>'*',
2441 'from' => $ftable_name,
2442 'where' => $filter);
2443 $frows = $this->select($sql);
2444 if (PEAR::isError($frows)) {
2445 return $frows;
2446 }
2447 if (count($frows) > 0) {
2448 return $this->throwError(
2449 DB_TABLE_DATABASE_ERR_RESTRICT_UPDATE,
2450 $table_name);
2451 }
2452 } else {
2453 // If 'cascade', 'set null', or 'set default',
2454 // then update the referencing foreign key.
2455 // Note: Turn off foreign key validity check
2456 // during update, then restore original value
2457 $check_fkey = $this->_check_fkey;
2458 $this->_check_fkey = false;
2459 $result = $this->update($ftable_name, $fdata,
2460 $filter);
2461 $this->_check_fkey = $check_fkey;
2462 if (PEAR::isError($result)) {
2463 return $result;
2464 }
2465 }
2466 } // foreach ($update_row)
2467 } // if (!is_null($fdata))
2468
2469 } // foreach loop over referencing tables
2470 } // end if
2471
2472 // Normal completion
2473 return true;
2474
2475 }
2476
2477 // }}}
2478 // {{{ function autoRecast($flag = true)
2479
2480 /**
2481 * Turns on (or off) automatic recasting of insert and update data
2482 * for all tables
2483 *
2484 * @param bool $flag True to automatically recast insert and update
2485 * data, in all tables, false to not do so.
2486 * @return void
2487 * @access public
2488 */
2489 function autoRecast($flag = true)
2490 {
2491 foreach ($this->_table as $table_obj) {
2492 $table_obj->autoRecast($flag);
2493 }
2494 }
2495
2496 // }}}
2497 // {{{ function autoInc($flag = true)
2498
2499 /**
2500 * Turns on (or off) php implementation of auto-incrementing on insertion
2501 * for all tables
2502 *
2503 * @param bool $flag True to turn on auto-incrementing, false to turn off
2504 * @return void
2505 * @access public
2506 */
2507 function autoInc($flag = true)
2508 {
2509 foreach ($this->_table as $table_obj) {
2510 $table_obj->auto_inc = $flag;
2511 }
2512 }
2513
2514 // }}}
2515 // {{{ function delete($table_name, $where)
2516
2517 /**
2518 * Deletes all row(s) of table that match a custom where clause.
2519 *
2520 * Wrapper for insert method of the corresponding DB_Table object.
2521 *
2522 * Implements any required ON DELETE action on tables that reference
2523 * deleted rows, if on delete actions are enabled.
2524 *
2525 * @param string $table_name name of table from which to delete
2526 * @param string $where SQL WHERE clause that limits the set
2527 * of records to delete
2528 * @return boolean true on success (PEAR_Error on failure)
2529 * @access public
2530 */
2531 function delete($table_name, $where)
2532 {
2533 // Dereference table object
2534 if (isset($this->_table[$table_name])) {
2535 $table_obj =& $this->_table[$table_name];
2536 } else {
2537 return $this->throwError(
2538 DB_TABLE_DATABASE_ERR_NO_TBL,
2539 "delete, $table_name");
2540 }
2541
2542 // Delete from $table_obj
2543 $result = $table_obj->delete($where);
2544
2545 // Return value: true or PEAR_Error
2546 if (PEAR::isError($result)) {
2547 return $result;
2548 } else {
2549 return true;
2550 }
2551
2552 }
2553
2554 // }}}
2555 // {{{ function onDeleteAction(&$table_obj, $where)
2556
2557 /**
2558 * Implements ON DELETE actions triggered by deletion of rows of
2559 * $table_obj that match logical condition $where.
2560 *
2561 * This method is called by the DB_Table::delete() method if the table
2562 * has a parent DB_Table_Database object, and if ON DELETE actions are
2563 * enabled in the database object. It is called indirectly by the
2564 * DB_Table_Database::delete() method, which is simply a wrapper for
2565 * the DB_Table method.
2566 *
2567 * @param object &$table_obj Reference to a DB_Table object
2568 * @param string $where SQL logical condition for deleted rows
2569 * @return boolean true on success (PEAR_Error on failure)
2570 * @access public
2571 */
2572 function onDeleteAction(&$table_obj, $where)
2573 {
2574 $table_name = $table_obj->table;
2575 if ($this->_act_on_delete and isset($this->_ref_to[$table_name])) {
2576 $delete_rows = null;
2577 foreach ($this->_ref_to[$table_name] as $ftable_name) {
2578 $ref = $this->_ref[$ftable_name][$table_name];
2579 $action = $ref['on_delete'];
2580 if (is_null($action)) {
2581 continue;
2582 }
2583 $ftable_obj =& $this->_table[$ftable_name];
2584 $rtable_obj =& $this->_table[$table_name];
2585 $fkey = $ref['fkey'];
2586 $rkey = $ref['rkey'];
2587
2588 // Select rows to be deleted, if not done previously
2589 if ($delete_rows === null) {
2590 if ($this->backend == 'mdb2') {
2591 $fetchmode_assoc = MDB2_FETCHMODE_ASSOC;
2592 } else {
2593 $fetchmode_assoc = DB_FETCHMODE_ASSOC;
2594 }
2595 $sql = array('select' => '*',
2596 'from' => $table_name,
2597 'where' => $where,
2598 'fetchmode' => $fetchmode_assoc);
2599 $delete_rows = $this->select($sql);
2600 if (PEAR::isError($delete_rows)) {
2601 return $delete_rows;
2602 }
2603 }
2604
2605 // If set null or set default, construct update $fdata
2606 // $fdata contains data for updating referencing rows
2607 if ($action == 'set null' or $action == 'set default') {
2608 if (is_string($fkey)) {
2609 if ($action == 'set default') {
2610 $value = isset($ftable_obj->col[$fkey]['default'])
2611 ? $ftable_obj->col[$fkey]['default'] : null;
2612 } else {
2613 $value = null;
2614 }
2615 $fdata = array($fkey => $value);
2616 } else {
2617 $fdata = array();
2618 foreach ($fkey as $fcol) {
2619 if ($action == 'set default') {
2620 $value = isset($ftable_obj->col[$fcol]['default'])
2621 ? $ftable_obj->col[$fcol]['default'] : null;
2622 } else {
2623 $value = null;
2624 }
2625 $fdata[$fcol] = $value;
2626 }
2627 }
2628 }
2629
2630 // Loop over rows to be deleted from $table_name
2631 foreach ($delete_rows as $delete_row) {
2632
2633 // If necessary, restore case of $delete_row column names
2634 if ($this->_fix_case) {
2635 $cols = array_keys($table_obj->col);
2636 $delete_row = $this->_replaceKeys($delete_row, $cols);
2637 }
2638
2639 // Construct filter for referencing rows in $ftable_name
2640 $filter = $this->_buildFKeyFilter($delete_row,
2641 $rkey, $fkey);
2642
2643 // Apply action for one deleted row
2644 if ($action == 'restrict') {
2645 // Select for referencing rows throw error if found
2646 $sql = array('select'=>'*',
2647 'from' => $ftable_name,
2648 'where' => $filter);
2649 $frows = $this->select($sql);
2650 if (PEAR::isError($frows)) {
2651 return $frows;
2652 }
2653 if (count($frows) > 0) {
2654 return $this->throwError(
2655 DB_TABLE_DATABASE_ERR_RESTRICT_DELETE,
2656 $table_name);
2657 }
2658 } elseif ($action == 'cascade') {
2659 // Delete referencing rows
2660 // Note: Recursion on delete
2661 $result = $this->delete($ftable_name, $filter);
2662 if (PEAR::isError($result)) {
2663 return $result;
2664 }
2665 } elseif ($action == 'set null' OR $action == 'set default') {
2666 // Update referencing rows, using $fdata
2667 // Note: Turn off foreign key validity check during
2668 // update of referencing key to null or default, then
2669 // restore $this->_check_fkey to original value
2670 $check_fkey = $this->_check_fkey;
2671 $this->_check_fkey = false;
2672 $result = $this->update($ftable_name, $fdata, $filter);
2673 $this->_check_fkey = $check_fkey;
2674 #$result = $ftable_obj->update($fdata, $filter);
2675 if (PEAR::isError($result)) {
2676 return $result;
2677 }
2678 } else {
2679 // Invalid $action name, throw Error
2680 return $this->throwError(
2681 DB_TABLE_DATABASE_ERR_ON_DELETE_ACTION,
2682 "$ftable_name => $table_name");
2683 }
2684 } // end foreach ($delete_rows)
2685
2686 } // end foreach ($this->_ref_to[...] as $ftable_name)
2687 } // end if
2688
2689 // Normal completion
2690 return true;
2691
2692 }
2693
2694 // }}}
2695 // {{{ function _replaceKeys($data, $keys)
2696
2697 /**
2698 * Returns array in which keys of associative array $data are replaced
2699 * by values of sequential array $keys.
2700 *
2701 * This function is used by the onDeleteAction() and onUpdateAction()
2702 * methods to restore the case of column names in associative arrays
2703 * that are returned from an automatically generated query "SELECT *
2704 * FROM $table WHERE ...", when these column name keys are returned
2705 * with a fixed case. In this usage, $keys is a sequential array of
2706 * the names of all columns in $table.
2707 *
2708 * @param array $data associative array
2709 * @param array $key numerical array of replacement key names
2710 * @return array associative array in which keys of $data have been
2711 * replaced by the values of array $keys.
2712 * @access private
2713 */
2714 function _replaceKeys($data, $keys)
2715 {
2716 $new_data = array();
2717 $i = 0;
2718 foreach ($data as $old_key => $value) {
2719 $new_key = $keys[$i];
2720 $new_data[$new_key] = $value;
2721 $i = $i + 1;
2722 }
2723 return $new_data;
2724 }
2725
2726 // }}}
2727 // {{{ function autoJoin($cols = null, $tables = null, $filter = null)
2728
2729 /**
2730 * Builds a select command involving joined tables from
2731 * a list of column names and/or a list of table names.
2732 *
2733 * Returns an query array of the form used in $this->buildSQL,
2734 * constructed on the basis of a sequential array $cols of
2735 * column names and/or a sequential array $tables of table
2736 * names. The 'FROM' clause in the resulting SQL contains
2737 * all the table listed in the $tables parameter and all
2738 * those containing the columns listed in the $cols array,
2739 * as well as any linking tables required to establish
2740 * many to many relationships between these tables. The
2741 * 'WHERE' clause is constructed so as to create an inner
2742 * join of these tables.
2743 *
2744 * The $cols parameter is a sequential array in which the
2745 * values are column names. Column names may be qualified
2746 * by a table name, using the SQL table.column syntax, but
2747 * need not be qualified if they are unambiguous. The
2748 * values in $cols can only be column names, and may not
2749 * be functions or more complicated SQL expressions. If
2750 * cols is null, the resulting SQL command will start with
2751 * 'SELECT * FROM ...' .
2752 *
2753 * The $tables parameter is a sequential array in which the
2754 * values are table names. If $tables is null, the FROM
2755 * clause is constructed from the tables containing the
2756 * columns in the $cols.
2757 *
2758 * The $params array is an associative array can have
2759 * 'filter', and 'order' keys, which are both optional.
2760 * A value $params['filter'] is an condition string to
2761 * add (i.e., AND) to the automatically constructed set
2762 * of join conditions. A value $params['order'] is an
2763 * SQL 'ORDER BY' clause, with no 'ORDER BY' prefix.
2764 *
2765 * The function returns an associative array with keys
2766 * ('select', 'from', 'where', ['order']), for which the
2767 * associated values are strings containing the SELECT,
2768 * FROM, WHERE and (optionally) ORDER BY clauses of the
2769 * select statement. The entire SELECT command string
2770 * can be obtained by passing the resulting array to
2771 * the buildSQL method.
2772 *
2773 * @param array $cols sequential array of column names
2774 * @param array $tables sequential array of table names
2775 * @param array $filter SQL logical expression to be added
2776 * (ANDed) to the where clause
2777 * @return array sql query array for select statement
2778 * @access public
2779 */
2780 function autoJoin($cols = null, $tables = null, $filter = null)
2781 {
2782 // initialize array containing clauses of select statement
2783 $query = array();
2784
2785 if (is_null($tables)) {
2786 if (is_null($cols)) {
2787 return $this->throwError(
2788 DB_TABLE_DATABASE_ERR_NO_COL_NO_TBL);
2789 }
2790 $tables = array();
2791 }
2792
2793 if (!$cols) {
2794 // If no columns specified, SELECT * FROM ...
2795 $query['select'] = '*';
2796 } else {
2797
2798 // Qualify unqualified columns with table names
2799 $all_tables = $tables;
2800 foreach ($cols as $key => $col) {
2801 $col_array = $this->validCol($col, $tables);
2802 if (PEAR::isError($col_array)) {
2803 return $col_array;
2804 }
2805 $table = $col_array[0];
2806 $column = $col_array[1];
2807 if (is_array($table)) {
2808 return $this->throwError(
2809 DB_TABLE_DATABASE_ERR_COL_NOT_UNIQUE, $col);
2810 }
2811 $cols[$key] = "$table.$column";
2812 if (!in_array($table, $all_tables)) {
2813 $all_tables[] = $table;
2814 }
2815 }
2816 $tables = $all_tables;
2817
2818 // Construct select clause
2819 $query['select'] = implode(', ', $cols);
2820
2821 }
2822
2823 // Construct array $joins of join conditions
2824 $n_tables = count($tables);
2825 if ($n_tables == 1) {
2826 $query['from'] = $tables[0];
2827 } else {
2828 $join_tables = array($tables[0]); // list of joined tables
2829 $link_tables = array(); // list of required linking tables
2830 $joins = array(); // list of join conditions
2831 // Initialize linked list of unjoined tables
2832 $next = array();
2833 for ( $i=1 ; $i < $n_tables-1 ; $i++) {
2834 $next[$tables[$i]] = $tables[$i+1];
2835 $prev[$tables[$i+1]] = $tables[$i];
2836 }
2837 $next[$tables[$n_tables-1]] = $tables[1];
2838 $prev[$tables[1]] = $tables[$n_tables-1];
2839 $n_remain = $n_tables - 1;
2840 $head = $tables[1];
2841 $table1 = $tables[1];
2842 $joined = false;
2843 $direct = true;
2844 while ($n_remain > 0) {
2845
2846 if ($direct) {
2847
2848 // Search for references from table1 to joined tables
2849 if (isset($this->_ref[$table1])) {
2850 $list = $this->_ref[$table1];
2851 foreach ($list as $table2 => $def) {
2852 if (in_array($table2, $join_tables)) {
2853 if ($joined) {
2854 return $this->throwError(
2855 DB_TABLE_DATABASE_ERR_AMBIG_JOIN,
2856 $table1);
2857 }
2858 $fkey = $def['fkey'];
2859 $rkey = $def['rkey'];
2860 if (is_string($fkey)) {
2861 $joins[] = "$table1.$fkey = $table2.$rkey";
2862 } else {
2863 for ($i=0; $i < count($fkey); $i++ ) {
2864 $fcol = $fkey[$i];
2865 $rcol = $rkey[$i];
2866 $joins[] =
2867 "$table1.$fcol = $table2.$rcol";
2868 }
2869 }
2870 $joined = true;
2871 }
2872 }
2873 }
2874
2875 // Search for references to table1 from joined tables
2876 if (isset($this->_ref_to[$table1])) {
2877 $list = $this->_ref_to[$table1];
2878 foreach ($list as $table2) {
2879 if (in_array($table2, $join_tables)) {
2880 if ($joined) {
2881 return $this->throwError(
2882 DB_TABLE_DATABASE_ERR_AMBIG_JOIN,
2883 $table1);
2884 }
2885 $def = $this->_ref[$table2][$table1];
2886 $fkey = $def['fkey'];
2887 $rkey = $def['rkey'];
2888 if (is_string($fkey)) {
2889 $joins[] = "$table2.$fkey = $table1.$rkey";
2890 } else {
2891 for ($i=0; $i < count($fkey); $i++ ) {
2892 $fcol = $fkey[$i];
2893 $rcol = $rkey[$i];
2894 $joins[] =
2895 "$table2.$fcol = $table1.$rcol";
2896 }
2897 }
2898 $joined = true;
2899 }
2900 }
2901 }
2902
2903 } else {
2904
2905 // Search for indirect linking table to table1
2906 if (isset($this->_link[$table1])) {
2907 foreach ($this->_link[$table1] as $table2 => $links) {
2908 if (in_array($table2, $join_tables)) {
2909 $n_link = count($links);
2910 if ($n_link > 1) {
2911 return $this->throwError(
2912 DB_TABLE_DATABASE_ERR_AMBIG_JOIN,
2913 $table1);
2914 }
2915 if ($joined and $n_link > 0) {
2916 return $this->throwError(
2917 DB_TABLE_DATABASE_ERR_AMBIG_JOIN,
2918 $table1);
2919 }
2920 $link = $links[0];
2921 $def1 = $this->_ref[$link][$table1];
2922 $fkey1 = $def1['fkey'];
2923 $rkey1 = $def1['rkey'];
2924 if (is_string($fkey1)) {
2925 $joins[] = "$link.$fkey1 = $table1.$rkey1";
2926 } else {
2927 for ($i=0; $i < count($fkey1); $i++ ) {
2928 $fcol1 = $fkey1[$i];
2929 $rcol1 = $rkey1[$i];
2930 $joins[] =
2931 "$link.$fcol1 = $table1.$rcol1";
2932 }
2933 }
2934 $def2 = $this->_ref[$link][$table2];
2935 $fkey2 = $def2['fkey'];
2936 $rkey2 = $def2['rkey'];
2937 if (is_string($fkey2)) {
2938 $joins[] = "$link.$fkey2 = $table2.$rkey2";
2939 } else {
2940 for ($i=0; $i < count($fkey2); $i++ ) {
2941 $fcol2 = $fkey2[$i];
2942 $rcol2 = $rkey2[$i];
2943 $joins[] =
2944 "$link.$fcol2 = $table2.$rcol2";
2945 }
2946 }
2947 $link_tables[] = $link;
2948 $joined = true;
2949 }
2950 }
2951 }
2952
2953 }
2954
2955 if ($joined) {
2956 $join_tables[] = $table1;
2957 $n_remain = $n_remain - 1;
2958 if ($n_remain > 0) {
2959 $head = $next[$table1];
2960 $tail = $prev[$table1];
2961 $prev[$head] = $tail;
2962 $next[$tail] = $head;
2963 $table1 = $head;
2964 $joined = false;
2965 $direct = true;
2966 }
2967 } else {
2968 $table1 = $next[$table1];
2969 if ($table1 == $head) {
2970 if ($direct) {
2971 $direct = false;
2972 } else {
2973 return $this->throwError(
2974 DB_TABLE_DATABASE_ERR_FAIL_JOIN,$table1);
2975 }
2976 }
2977 }
2978
2979 }
2980
2981 // Add any required linking tables to $tables array
2982 if ($link_tables) {
2983 foreach ($link_tables as $link) {
2984 if (!in_array($link, $tables)) {
2985 $tables[] = $link;
2986 }
2987 }
2988 }
2989
2990 // Construct from clause from $tables array
2991 $query['from'] = implode(', ', $tables);
2992
2993 // Construct where clause from $joins array
2994 $query['where'] = implode("\n AND ", $joins);
2995
2996 }
2997
2998 // Add $filter condition, if present
2999 if ($filter) {
3000 if (isset($query['where'])) {
3001 $query['where'] = '( ' . $query['where'] . " )\n" .
3002 ' AND ( ' . $filter . ')';
3003 } else {
3004 $query['where'] = $filter;
3005 }
3006 }
3007
3008 return $query;
3009 }
3010
3011 // }}}
3012 // {{{ function _buildFKeyFilter($data, $data_key = null, $filt_key = null, $match = 'simple')
3013
3014 /**
3015 * Returns WHERE clause equating values of $data array to database column
3016 * values
3017 *
3018 * Usage: The function is designed to return an SQL logical
3019 * expression that equates the values of a set of foreign key columns in
3020 * associative array $data, which is a row to be inserted or updated in
3021 * one table, to the values of the corresponding columns of a referenced
3022 * table. In this usage, $data_key is the foreign key (a column name or
3023 * numerical array of column names), and $filt_key is the corresponding
3024 * referenced key.
3025 *
3026 * Parameters: Parameter $data is an associative array containing data to
3027 * be inserted into or used to update one row of a database table, in which
3028 * array keys are column names. When present, $data_key contains either
3029 * the name of a single array key of interest, or a numerical array of such
3030 * keys. These are usually the names of the columns of a foreign key in
3031 * that table. When, $data_key is null or absent, it is taken to be equal
3032 * to an array containing all of the keys of $data. When present, $filt_key
3033 * contains either a string or a numerical array of strings that are
3034 * aliases for the keys in $data_key. These are usually the names of the
3035 * corresponding columns in the referenced table. When $filt_key is null
3036 * or absent, it is equated with $data_key internally. The function
3037 * returns an SQL logical expression that equates the values in $data
3038 * whose keys are specified by $data_key, to the values of database
3039 * columns whose names are specified in $filt_key.
3040 *
3041 * General case: _buildFKeyFilter returns a SQL logical expression that
3042 * equates the values of $data whose keys are given in $data_key with the
3043 * values of database columns with names given in $filt_key. For example,
3044 * if
3045 * <code>
3046 * $data = array( 'k1' => $v1, 'k2' => $v2, ... , 'k10' => $v10 );
3047 * $data_key = array('k2', 'k5', 'k7');
3048 * $filt_key = array('c2', 'c5', 'c7');
3049 * </code>
3050 * then buildFilter($data, $data_key, $filt_key) returns a string
3051 * <code>
3052 * "c2 = $v2 AND c5 = $v5 AND c7 = $v7"
3053 * </code>
3054 * in which the values $v2, $v5, $v7 are replaced by properly quoted
3055 * SQL literal values. If, in the above example, $data_key = 'k5'
3056 * and $filt_key = 'c5', then the function will return
3057 * <code>
3058 * "c5 = $v5"
3059 * </code>
3060 * where (again) $v5 is replaced by an SQL literal.
3061 *
3062 * Simple case: If parameters $data_key and $filt_key are null, the
3063 * behavior is the same as that of the DB_Table_Base::buildFilter() method.
3064 * For example, if
3065 * <code>
3066 * $data = array( 'c1' => $v1, 'c2' => $v2, 'c3' => $v3)
3067 * </code>
3068 * then _buildFKeyFilter($data) returns a string
3069 * <code>
3070 * "c1 => $val1 AND c2 => $val2 AND c3 = $v3"
3071 * </code>
3072 * in which the values $v1, $v2, $v3 are replaced by SQL literal values,
3073 * quoted and escaped as appropriate for each data type and the backend.
3074 *
3075 * Quoting is done by the DB_Table_Database::quote() method, based on
3076 * the php type of the values in $array. The treatment of null values
3077 * in $data depends upon the value of the $match parameter.
3078 *
3079 * Null values: The treatment to null values in $data depends upon
3080 * the value of the $match parameter . If $match == 'simple', an empty
3081 * string is returned if any $value of $data with a key in $data_key
3082 * is null. If $match == 'partial', the returned SQL expression
3083 * equates only the relevant non-null values of $data to the values of
3084 * corresponding database columns. If $match == 'full', the function
3085 * returns an empty string if all of the relevant values of data are
3086 * null, and returns a PEAR_Error if some of the selected values are
3087 * null and others are not null.
3088 *
3089 * @param array $data associative array, keys are column names
3090 * @param mixed $data_key string or numerical array of strings, in which
3091 * values are a set of keys of interest in $data
3092 * @param mixed $data_key string or numerical array of strings, in which
3093 * values are names of a corresponding set of
3094 * database column names.
3095 * @return string SQL expression equating values in $data, for which keys
3096 * also appear in $data_key, to values of corresponding
3097 * database columns named in $filt_key.
3098 * @access private
3099 */
3100 function _buildFKeyFilter($data, $data_key = null, $filt_key = null,
3101 $match = 'simple')
3102 {
3103 // Check $match type value
3104 if (!in_array($match, array('simple', 'partial', 'full'))) {
3105 return $this->throwError(
3106 DB_TABLE_DATABASE_ERR_MATCH_TYPE);
3107 }
3108
3109 // Simple case: Build filter from $data array alone
3110 if (is_null($data_key) && is_null($filt_key)) {
3111 return $this->buildFilter($data, $match);
3112 }
3113
3114 // Defaults for $data_key and $filt_key:
3115 if (is_null($data_key)) {
3116 $data_key = array_keys($data);
3117 }
3118 if (is_null($filt_key)) {
3119 $filt_key = $data_key;
3120 }
3121
3122 // General case: $data_key and/or $filt_key not null
3123 if (is_string($data_key)) {
3124 if (!is_string($filt_key)) {
3125 return $this->throwError(
3126 DB_TABLE_DATABASE_ERR_FILT_KEY);
3127 }
3128 if (array_key_exists($data_key, $data)) {
3129 $value = $data[$data_key];
3130 if (!is_null($value)) {
3131 $value = (string) $this->quote($data[$data_key]);
3132 return "$filt_key = $value";
3133 } else {
3134 return '';
3135 }
3136 } else {
3137 return '';
3138 }
3139 } elseif (is_array($data_key)) {
3140 if (!is_array($filt_key)) {
3141 return $this->throwError(
3142 DB_TABLE_DATABASE_ERR_FILT_KEY);
3143 }
3144 $filter = array();
3145 for ($i=0; $i < count($data_key); $i++) {
3146 $data_col = $data_key[$i];
3147 $filt_col = $filt_key[$i];
3148 if (array_key_exists($data_col, $data)) {
3149 $value = $data[$data_col];
3150 if (!is_null($value)) {
3151 if ($match == 'full' && isset($found_null)) {
3152 return $this->throwError(
3153 DB_TABLE_DATABASE_ERR_FULL_KEY);
3154 }
3155 $value = $this->quote($value);
3156 $filter[] = "$filt_col = $value";
3157 } else {
3158 $found_null = true;
3159 }
3160 }
3161 }
3162 if ($match == 'simple' && isset($found_null)) {
3163 return '';
3164 }
3165 if (count($filter) == 0) {
3166 return '';
3167 }
3168 return implode(' AND ', $filter);
3169 } else {
3170 // Invalid data key
3171 return $this->throwError(
3172 DB_TABLE_DATABASE_ERR_DATA_KEY);
3173 }
3174 }
3175
3176 // }}}
3177 // {{{ function quote($value)
3178
3179 /**
3180 * Returns SQL literal string representation of a php value
3181 *
3182 * Calls MDB2::quote() or DB_Common::quoteSmart() to enquote and
3183 * escape string values. If $value is:
3184 * - a string, return the string enquoted and escaped
3185 * - a number, return cast of number to string, without quotes
3186 * - a boolean, return '1' for true and '0' for false
3187 * - null, return the string 'NULL'
3188 *
3189 * @param mixed $value
3190 * @return string Representation of value as an SQL literal
3191 *
3192 * @access public
3193 *
3194 * @see DB_Common::quoteSmart()
3195 * @see MDB2::quote()
3196 */
3197 function quote($value)
3198 {
3199 if (is_bool($value)) {
3200 return $value ? '1' : '0';
3201 }
3202 if ($this->backend == 'mdb2') {
3203 $value = $this->db->quote($value);
3204 } else {
3205 $value = $this->db->quoteSmart($value);
3206 }
3207 return (string) $value;
3208 }
3209
3210 // }}}
3211 // {{{ function __sleep()
3212
3213 /**
3214 * Serializes all table references and sets $db = null, $backend = null
3215 *
3216 * @return array names of all properties
3217 * @access public
3218 */
3219 function __sleep()
3220 {
3221 $this->db = null;
3222 $this->backend = null;
3223 // needed in setDatabaseInstance, where null is passed by reference
3224 $null_variable = null;
3225 foreach ($this->_table as $name => $table_obj) {
3226 $table_obj->db = null;
3227 $table_obj->setDatabaseInstance($null_variable);
3228 $this->_table[$name] = serialize($table_obj);
3229 }
3230 return array_keys(get_object_vars($this));
3231 }
3232
3233 // }}}
3234 // {{{ function __wakeup()
3235
3236 /**
3237 * Unserializes DB_Table_Database object and all child DB_Table objects
3238 *
3239 * Immediately after unserialization, a DB_Table_Database object
3240 * has null $db and $backend properties, as do all of its child
3241 * DB_Table objects. The DB_Table_Database::setDB method should
3242 * be called immediately after unserialization to re-establish
3243 * the database connection, like so:
3244 * <code>
3245 * $db_object = unserialize($serialized_db);
3246 * $db_object->setDB($conn);
3247 * </code>
3248 * where $conn is a DB/MDB2 object. This establishes a DB/MDB2
3249 * connection for both the parent database and all child tables.
3250 *
3251 * This method unserializes all of the child DB_Table objects of
3252 * a DB_Table_Database object. It must thus have access to the
3253 * definitions of the associated DB_Table subclasses. These are
3254 * listed in the $_table_subclass property. If a required subclass
3255 * named $subclass is not defined, the __wakeup() method attempts
3256 * to autoload a file "$subclass.php" in the directory specified
3257 * by $this->table_subclass_path.
3258 *
3259 * @return void
3260 * @access public
3261 */
3262 function __wakeup()
3263 {
3264 foreach ($this->_table as $name => $table_string) {
3265
3266 // Check for subclass definition, and autoload if necessary.
3267 $subclass = $this->_table_subclass[$name];
3268 if (!is_null($subclass)) {
3269 if (!class_exists($subclass)) {
3270 $dir = $this->_table_subclass_path;
3271 require_once $dir . '/' . $subclass . '.php';
3272 }
3273 }
3274 // Unserialize table
3275 $table_obj = unserialize($table_string);
3276 // Reset references between database and table objects
3277 $table_obj->setDatabaseInstance($this);
3278 $this->_table[$name] = $table_obj;
3279 }
3280 }
3281
3282 // }}}
3283 // {{{ function toXML()
3284
3285 /**
3286 * Returns XML string representation of database declaration
3287 *
3288 * @param string $indent string of whitespace, prefix to each line
3289 * @return string XML string representation
3290 * @access public
3291 */
3292 function toXML($indent = '') {
3293 require_once 'DB/Table/XML.php';
3294 $s = array();
3295 $s[] = DB_Table_XML::openTag('database', $indent);
3296 foreach ($this->_table as $name => $table_obj) {
3297 $s[] = $table_obj->toXML($indent);
3298 }
3299 $s[] = DB_Table_XML::closeTag('database', $indent);
3300 return implode("\n", $s);
3301 }
3302
3303 // }}}
3304 // {{{ function fromXML($xml_string, $conn)
3305
3306 /**
3307 * Returns a DB_Table_Database object constructed from an XML string
3308 *
3309 * Uses the MDB2 XML schema for a database element, including a new
3310 * syntax for foreign key indices.
3311 *
3312 * NOTE: This function requires PHP 5. It throws an error if used
3313 * with PHP 4.
3314 *
3315 * @param string XML string representation
3316 * @return object DB_Table_Database object on success (PEAR_Error on failure)
3317 *
3318 * @throws PEAR_Error if:
3319 * - PHP version is not >= 5.0.0 (...DATABASE_ERR_PHP_VERSION )
3320 * - Parsing by simpleXML fails (...DATABASE_ERR_XML_PARSE )
3321 *
3322 * @access public
3323 */
3324 function fromXML($xml_string, $conn)
3325 {
3326 // Check PHP version. Throw error if not >= PHP 5.0.0
3327 $version = phpversion();
3328 if (version_compare($version, '5.0.0', "<")) {
3329 return $this->throwError(
3330 DB_TABLE_DATABASE_ERR_PHP_VERSION,
3331 $version);
3332 }
3333
3334 $xml = simplexml_load_string($xml_string);
3335 if ($xml == false) {
3336 return $this->throwError(
3337 DB_TABLE_DATABASE_ERR_XML_PARSE);
3338 }
3339
3340 // Instantiate database object
3341 $database_name = (string) $xml->name;
3342 $database_obj = new DB_Table_Database($conn, $database_name);
3343
3344 // Create array of foreign key references
3345 $ref = array();
3346
3347 // Loop over tables
3348 foreach ($xml->table as $table) {
3349 $table_name = (string) $table->name;
3350
3351 // Instantiate table object
3352 $table_obj = new DB_Table($conn, $table_name);
3353
3354 // Add columns to table object
3355 $declaration = $table->declaration;
3356 foreach ($declaration->field as $field) {
3357 $col_name = (string) $field->name;
3358 $type = (string) $field->type;
3359 $def = array('type' => $type);
3360 if (isset($field->length)) {
3361 $def['size'] = (integer) $field->length;
3362 }
3363 if (isset($field->notnull)) {
3364 if ($field->notnull) {
3365 $def['require'] = true;
3366 } else {
3367 $def['require'] = false;
3368 }
3369 }
3370 if (isset($field->default)) {
3371 $def['default'] = $field->default;
3372 }
3373 if (isset($field->autoincrement)) {
3374 if (is_null($table_obj->auto_inc_col)) {
3375 $table_obj->auto_inc_col = $col_name;
3376 } else {
3377 return $this->throwError(
3378 DB_TABLE_DATABASE_ERR_XML_MULT_AUTO_INC);
3379 }
3380 }
3381 $table_obj->col[$col_name] = $def;
3382 }
3383
3384 // Add indices
3385 foreach ($declaration->index as $index) {
3386 if (isset($index->name)) {
3387 $name = (string) $index->name;
3388 } else {
3389 $name = null;
3390 }
3391 $def = array();
3392 if (isset($index->primary)) {
3393 $def['type'] = 'primary';
3394 } elseif (isset($index->unique)) {
3395 $def['type'] = 'unique';
3396 } else {
3397 $def['type'] = 'normal';
3398 }
3399 foreach ($index->field as $field) {
3400 $def['cols'][] = (string) $field;
3401 }
3402 if ($name) {
3403 $table_obj->idx[$name] = $def;
3404 } else {
3405 $table_obj->idx[] = $def;
3406 }
3407 }
3408
3409 // Add table object to database object
3410 $database_obj->addTable($table_obj);
3411
3412 // Foreign key references
3413 foreach ($declaration->foreign as $foreign) {
3414 if (isset($foreign->name)) {
3415 $name = (string) $foreign->name;
3416 } else {
3417 $name = null;
3418 }
3419 $fkey = array();
3420 foreach ($foreign->field as $field) {
3421 $fkey[] = (string) $field;
3422 }
3423 if (count($fkey) == 1) {
3424 $fkey = $fkey[0];
3425 }
3426 $rtable = (string) $foreign->references->table;
3427 if (isset($foreign->references->field)) {
3428 $rkey = array();
3429 foreach ($foreign->references->field as $field) {
3430 $rkey[] = (string) $field;
3431 }
3432 if (count($rkey)==1) {
3433 $rkey = $rkey[0];
3434 }
3435 } else {
3436 $rkey = null;
3437 }
3438 if (isset($foreign->ondelete)) {
3439 $on_delete = (string) $foreign->ondelete;
3440 } else {
3441 $on_delete = null;
3442 }
3443 if (isset($foreign->onupdate)) {
3444 $on_update = (string) $foreign->onupdate;
3445 } else {
3446 $on_update = null;
3447 }
3448
3449 // Add reference definition to $ref array
3450 $def = array();
3451 $def['fkey'] = $fkey;
3452 $def['rkey'] = $rkey;
3453 $def['on_delete'] = $on_delete;
3454 $def['on_update'] = $on_update;
3455 if (!isset($ref[$table_name])) {
3456 $ref[$table_name] = array();
3457 }
3458 $ref[$table_name][$rtable] = $def;
3459
3460 }
3461
3462 // Release variable $table_obj to refer to another table
3463 unset($table_obj);
3464 }
3465
3466 // Add all references to database object
3467 foreach ($ref as $ftable => $list) {
3468 foreach ($list as $rtable => $def) {
3469 $fkey = $def['fkey'];
3470 $rkey = $def['rkey'];
3471 $on_delete = $def['on_delete'];
3472 $on_update = $def['on_update'];
3473 $database_obj->addRef($ftable, $fkey, $rtable, $rkey,
3474 $on_delete, $on_update);
3475 }
3476 }
3477
3478 return $database_obj;
3479 }
3480
3481 // }}}
3482
3483 // }}}
3484 }
3485
3486 // }}}
3487
3488 /* Local variables:
3489 * tab-width: 4
3490 * c-basic-offset: 4
3491 * c-hanging-comment-ender-p: nil
3492 * End:
3493 */
3494
3495 ?>