1<?php 2 3/** 4 * DB_Table is a database API and data type SQL abstraction class. 5 * 6 * DB_Table provides database API abstraction, data type abstraction, 7 * automated SELECT, INSERT, and UPDATE queries, automated table 8 * creation, automated validation of inserted/updated column values, 9 * and automated creation of QuickForm elements based on the column 10 * definitions. 11 * 12 * PHP versions 4 and 5 13 * 14 * LICENSE: 15 * 16 * Copyright (c) 1997-2007, Paul M. Jones <pmjones@php.net> 17 * David C. Morse <morse@php.net> 18 * Mark Wiesemann <wiesemann@php.net> 19 * All rights reserved. 20 * 21 * Redistribution and use in source and binary forms, with or without 22 * modification, are permitted provided that the following conditions 23 * are met: 24 * 25 * * Redistributions of source code must retain the above copyright 26 * notice, this list of conditions and the following disclaimer. 27 * * Redistributions in binary form must reproduce the above copyright 28 * notice, this list of conditions and the following disclaimer in the 29 * documentation and/or other materials provided with the distribution. 30 * * The names of the authors may not be used to endorse or promote products 31 * derived from this software without specific prior written permission. 32 * 33 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 34 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 35 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 36 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 37 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 38 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 39 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 40 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 41 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 42 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 43 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 44 * 45 * @category Database 46 * @package DB_Table 47 * @author Paul M. Jones <pmjones@php.net> 48 * @author David C. Morse <morse@php.net> 49 * @author Mark Wiesemann <wiesemann@php.net> 50 * @license http://opensource.org/licenses/bsd-license.php New BSD License 51 * @version CVS: $Id: Table.php,v 1.90 2008/12/25 19:56:35 wiesemann Exp $ 52 * @link http://pear.php.net/package/DB_Table 53 */ 54 55/** 56 * Error code at instantiation time when the first parameter to the 57 * constructor is not a PEAR DB object. 58 */ 59define('DB_TABLE_ERR_NOT_DB_OBJECT', -1); 60 61/** 62 * Error code at instantiation time when the PEAR DB/MDB2 $phptype is not 63 * supported by DB_Table. 64 */ 65define('DB_TABLE_ERR_PHPTYPE', -2); 66 67/** 68 * Error code when you call select() or selectResult() and the first 69 * parameter is a string that does not match any of the $this->sql keys. 70 */ 71define('DB_TABLE_ERR_SQL_UNDEF', -3); 72 73/** 74 * Error code when you call select*() or buildSQL() and the first 75 * parameter is neither an array nor a string 76 */ 77define('DB_TABLE_ERR_SQL_NOT_STRING', -4); 78 79/** 80 * Error code when you try to insert data to a column that is not in the 81 * $this->col array. 82 */ 83define('DB_TABLE_ERR_INS_COL_NOMAP', -5); 84 85/** 86 * Error code when you try to insert data, and that data does not have a 87 * column marked as 'require' in the $this->col array. 88 */ 89define('DB_TABLE_ERR_INS_COL_REQUIRED', -6); 90 91/** 92 * Error code when auto-validation fails on data to be inserted. 93 */ 94define('DB_TABLE_ERR_INS_DATA_INVALID', -7); 95 96/** 97 * Error code when you try to update data to a column that is not in the 98 * $this->col array. 99 */ 100define('DB_TABLE_ERR_UPD_COL_NOMAP', -8); 101 102/** 103 * Error code when you try to update data, and that data does not have a 104 * column marked as 'require' in the $this->col array. 105 */ 106define('DB_TABLE_ERR_UPD_COL_REQUIRED', -9); 107 108/** 109 * Error code when auto-validation fails on update data. 110 */ 111define('DB_TABLE_ERR_UPD_DATA_INVALID', -10); 112 113/** 114 * Error code when you use a create() flag that is not recognized (must 115 * be 'safe', 'drop', 'verify' or boolean false. 116 */ 117define('DB_TABLE_ERR_CREATE_FLAG', -11); 118 119/** 120 * Error code at create() time when you define an index in $this->idx 121 * that has no columns. 122 */ 123define('DB_TABLE_ERR_IDX_NO_COLS', -12); 124 125/** 126 * Error code at create() time when you define an index in $this->idx 127 * that refers to a column that does not exist in the $this->col array. 128 */ 129define('DB_TABLE_ERR_IDX_COL_UNDEF', -13); 130 131/** 132 * Error code at create() time when you define a $this->idx index type 133 * that is not recognized (must be 'normal' or 'unique'). 134 */ 135define('DB_TABLE_ERR_IDX_TYPE', -14); 136 137/** 138 * Error code at create() time when you have an error in a 'char' or 139 * 'varchar' definition in $this->col (usually because 'size' is wrong). 140 */ 141define('DB_TABLE_ERR_DECLARE_STRING', -15); 142 143/** 144 * Error code at create() time when you have an error in a 'decimal' 145 * definition (usually becuase the 'size' or 'scope' are wrong). 146 */ 147define('DB_TABLE_ERR_DECLARE_DECIMAL', -16); 148 149/** 150 * Error code at create() time when you define a column in $this->col 151 * with an unrecognized 'type'. 152 */ 153define('DB_TABLE_ERR_DECLARE_TYPE', -17); 154 155/** 156 * Error code at validation time when a column in $this->col has an 157 * unrecognized 'type'. 158 */ 159define('DB_TABLE_ERR_VALIDATE_TYPE', -18); 160 161/** 162 * Error code at create() time when you define a column in $this->col 163 * with an invalid column name (usually because it's a reserved keyword). 164 */ 165define('DB_TABLE_ERR_DECLARE_COLNAME', -19); 166 167/** 168 * Error code at create() time when you define an index in $this->idx 169 * with an invalid index name (usually because it's a reserved keyword). 170 */ 171define('DB_TABLE_ERR_DECLARE_IDXNAME', -20); 172 173/** 174 * Error code at create() time when you define an index in $this->idx 175 * that refers to a CLOB column. 176 */ 177define('DB_TABLE_ERR_IDX_COL_CLOB', -21); 178 179/** 180 * Error code at create() time when you define a column name that is 181 * more than 30 chars long (an Oracle restriction). 182 */ 183define('DB_TABLE_ERR_DECLARE_STRLEN', -22); 184 185/** 186 * Error code at create() time when the index name ends up being more 187 * than 30 chars long (an Oracle restriction). 188 */ 189define('DB_TABLE_ERR_IDX_STRLEN', -23); 190 191/** 192 * Error code at create() time when the table name is more than 30 chars 193 * long (an Oracle restriction). 194 */ 195define('DB_TABLE_ERR_TABLE_STRLEN', -24); 196 197/** 198 * Error code at nextID() time when the sequence name is more than 30 199 * chars long (an Oracle restriction). 200 */ 201define('DB_TABLE_ERR_SEQ_STRLEN', -25); 202 203/** 204 * Error code at verify() time when the table does not exist in the 205 * database. 206 */ 207define('DB_TABLE_ERR_VER_TABLE_MISSING', -26); 208 209/** 210 * Error code at verify() time when the column does not exist in the 211 * database table. 212 */ 213define('DB_TABLE_ERR_VER_COLUMN_MISSING', -27); 214 215/** 216 * Error code at verify() time when the column type does not match the 217 * type specified in the column declaration. 218 */ 219define('DB_TABLE_ERR_VER_COLUMN_TYPE', -28); 220 221/** 222 * Error code at instantiation time when the column definition array 223 * does not contain at least one column. 224 */ 225define('DB_TABLE_ERR_NO_COLS', -29); 226 227/** 228 * Error code at verify() time when an index cannot be found in the 229 * database table. 230 */ 231define('DB_TABLE_ERR_VER_IDX_MISSING', -30); 232 233/** 234 * Error code at verify() time when an index does not contain all 235 * columns that it should contain. 236 */ 237define('DB_TABLE_ERR_VER_IDX_COL_MISSING', -31); 238 239/** 240 * Error code at instantiation time when a creation mode 241 * is not available for a phptype. 242 */ 243define('DB_TABLE_ERR_CREATE_PHPTYPE', -32); 244 245/** 246 * Error code at create() time when you define more than one primary key 247 * in $this->idx. 248 */ 249define('DB_TABLE_ERR_DECLARE_PRIMARY', -33); 250 251/** 252 * Error code at create() time when a primary key is defined in $this->idx 253 * and SQLite is used (SQLite does not support primary keys). 254 */ 255define('DB_TABLE_ERR_DECLARE_PRIM_SQLITE', -34); 256 257/** 258 * Error code at alter() time when altering a table field is not possible 259 * (e.g. because MDB2 has no support for the change or because the DBMS 260 * does not support the change). 261 */ 262define('DB_TABLE_ERR_ALTER_TABLE_IMPOS', -35); 263 264/** 265 * Error code at alter() time when altering a(n) index/constraint is not possible 266 * (e.g. because MDB2 has no support for the change or because the DBMS 267 * does not support the change). 268 */ 269define('DB_TABLE_ERR_ALTER_INDEX_IMPOS', -36); 270 271/** 272 * Error code at insert() time due to invalid the auto-increment column 273 * definition. This column must be an integer type and required. 274 */ 275define('DB_TABLE_ERR_AUTO_INC_COL', -37); 276 277/** 278 * Error code at instantiation time when both the $table parameter 279 * and the $table class property are missing. 280 */ 281define('DB_TABLE_ERR_TABLE_NAME_MISSING', -38); 282 283/** 284 * The DB_Table_Base parent class 285 */ 286require_once 'DB/Table/Base.php'; 287 288/** 289 * The PEAR class for errors 290 */ 291require_once 'PEAR.php'; 292 293/** 294 * The Date class for recasting date and time values 295 */ 296require_once 'DB/Table/Date.php'; 297 298 299/** 300 * DB_Table supports these RDBMS engines and their various native data 301 * types; we need these here instead of in Manager.php because the 302 * initial array key tells us what databases are supported. 303 */ 304$GLOBALS['_DB_TABLE']['type'] = array( 305 'fbsql' => array( 306 'boolean' => 'DECIMAL(1,0)', 307 'char' => 'CHAR', 308 'varchar' => 'VARCHAR', 309 'smallint' => 'SMALLINT', 310 'integer' => 'INTEGER', 311 'bigint' => 'LONGINT', 312 'decimal' => 'DECIMAL', 313 'single' => 'REAL', 314 'double' => 'DOUBLE PRECISION', 315 'clob' => 'CLOB', 316 'date' => 'CHAR(10)', 317 'time' => 'CHAR(8)', 318 'timestamp' => 'CHAR(19)' 319 ), 320 'ibase' => array( 321 'boolean' => 'DECIMAL(1,0)', 322 'char' => 'CHAR', 323 'varchar' => 'VARCHAR', 324 'smallint' => 'SMALLINT', 325 'integer' => 'INTEGER', 326 'bigint' => 'BIGINT', 327 'decimal' => 'DECIMAL', 328 'single' => 'FLOAT', 329 'double' => 'DOUBLE PRECISION', 330 'clob' => 'BLOB SUB_TYPE 1', 331 'date' => 'DATE', 332 'time' => 'TIME', 333 'timestamp' => 'TIMESTAMP' 334 ), 335 'mssql' => array( 336 'boolean' => 'DECIMAL(1,0)', 337 'char' => 'CHAR', 338 'varchar' => 'VARCHAR', 339 'smallint' => 'SMALLINT', 340 'integer' => 'INTEGER', 341 'bigint' => 'BIGINT', 342 'decimal' => 'DECIMAL', 343 'single' => 'REAL', 344 'double' => 'FLOAT', 345 'clob' => 'TEXT', 346 'date' => 'CHAR(10)', 347 'time' => 'CHAR(8)', 348 'timestamp' => 'CHAR(19)' 349 ), 350 'mysql' => array( 351 'boolean' => 'DECIMAL(1,0)', 352 'char' => 'CHAR', 353 'varchar' => 'VARCHAR', 354 'smallint' => 'SMALLINT', 355 'integer' => 'INTEGER', 356 'bigint' => 'BIGINT', 357 'decimal' => 'DECIMAL', 358 'single' => 'FLOAT', 359 'double' => 'DOUBLE', 360 'clob' => 'LONGTEXT', 361 'date' => 'CHAR(10)', 362 'time' => 'CHAR(8)', 363 'timestamp' => 'CHAR(19)' 364 ), 365 'mysqli' => array( 366 'boolean' => 'DECIMAL(1,0)', 367 'char' => 'CHAR', 368 'varchar' => 'VARCHAR', 369 'smallint' => 'SMALLINT', 370 'integer' => 'INTEGER', 371 'bigint' => 'BIGINT', 372 'decimal' => 'DECIMAL', 373 'single' => 'FLOAT', 374 'double' => 'DOUBLE', 375 'clob' => 'LONGTEXT', 376 'date' => 'CHAR(10)', 377 'time' => 'CHAR(8)', 378 'timestamp' => 'CHAR(19)' 379 ), 380 'oci8' => array( 381 'boolean' => 'NUMBER(1)', 382 'char' => 'CHAR', 383 'varchar' => 'VARCHAR2', 384 'smallint' => 'NUMBER(6)', 385 'integer' => 'NUMBER(11)', 386 'bigint' => 'NUMBER(19)', 387 'decimal' => 'NUMBER', 388 'single' => 'REAL', 389 'double' => 'DOUBLE PRECISION', 390 'clob' => 'CLOB', 391 'date' => 'CHAR(10)', 392 'time' => 'CHAR(8)', 393 'timestamp' => 'CHAR(19)' 394 ), 395 'pgsql' => array( 396 'boolean' => 'DECIMAL(1,0)', 397 'char' => 'CHAR', 398 'varchar' => 'VARCHAR', 399 'smallint' => 'SMALLINT', 400 'integer' => 'INTEGER', 401 'bigint' => 'BIGINT', 402 'decimal' => 'DECIMAL', 403 'single' => 'REAL', 404 'double' => 'DOUBLE PRECISION', 405 'clob' => 'TEXT', 406 'date' => 'CHAR(10)', 407 'time' => 'CHAR(8)', 408 'timestamp' => 'CHAR(19)' 409 ), 410 'sqlite' => array( 411 'boolean' => 'BOOLEAN', 412 'char' => 'CHAR', 413 'varchar' => 'VARCHAR', 414 'smallint' => 'SMALLINT', 415 'integer' => 'INTEGER', 416 'bigint' => 'BIGINT', 417 'decimal' => 'NUMERIC', 418 'single' => 'FLOAT', 419 'double' => 'DOUBLE', 420 'clob' => 'CLOB', 421 'date' => 'DATE', 422 'time' => 'TIME', 423 'timestamp' => 'TIMESTAMP' 424 ) 425); 426 427 428/** 429 * US-English default error messages. If you want to internationalize, you can 430 * set the translated messages via $GLOBALS['_DB_TABLE']['error']. You can also 431 * use DB_Table::setErrorMessage(). Examples: 432 * 433 * <code> 434 * (1) $GLOBALS['_DB_TABLE]['error'] = array(DB_TABLE_ERR_PHPTYPE => '...', 435 * DB_TABLE_ERR_SQL_UNDEF => '...'); 436 * (2) DB_Table::setErrorMessage(DB_TABLE_ERR_PHPTYPE, '...'); 437 * DB_Table::setErrorMessage(DB_TABLE_ERR_SQL_UNDEF, '...'); 438 * (3) DB_Table::setErrorMessage(array(DB_TABLE_ERR_PHPTYPE => '...'); 439 * DB_TABLE_ERR_SQL_UNDEF => '...'); 440 * (4) $obj =& new DB_Table(); 441 * $obj->setErrorMessage(DB_TABLE_ERR_PHPTYPE, '...'); 442 * $obj->setErrorMessage(DB_TABLE_ERR_SQL_UNDEF, '...'); 443 * (5) $obj =& new DB_Table(); 444 * $obj->setErrorMessage(array(DB_TABLE_ERR_PHPTYPE => '...'); 445 * DB_TABLE_ERR_SQL_UNDEF => '...'); 446 * </code> 447 * 448 * For errors that can occur with-in the constructor call (i.e. e.g. creating 449 * or altering the database table), only the code from examples (1) to (3) 450 * will alter the default error messages early enough. For errors that can 451 * occur later, examples (4) and (5) are also valid. 452 */ 453$GLOBALS['_DB_TABLE']['default_error'] = array( 454 DB_TABLE_ERR_NOT_DB_OBJECT => 'First parameter must be a DB/MDB2 object', 455 DB_TABLE_ERR_PHPTYPE => 'DB/MDB2 phptype (or dbsyntax) not supported', 456 DB_TABLE_ERR_SQL_UNDEF => 'Select query string not in a key of $sql. Key', 457 DB_TABLE_ERR_SQL_NOT_STRING => 'Select query is neither an array nor a string', 458 DB_TABLE_ERR_INS_COL_NOMAP => 'Insert column not in map', 459 DB_TABLE_ERR_INS_COL_REQUIRED => 'Insert data must be set and non-null for column', 460 DB_TABLE_ERR_INS_DATA_INVALID => 'Insert data not valid for column', 461 DB_TABLE_ERR_UPD_COL_NOMAP => 'Update column not in map', 462 DB_TABLE_ERR_UPD_COL_REQUIRED => 'Update column must be set and non-null', 463 DB_TABLE_ERR_UPD_DATA_INVALID => 'Update data not valid for column', 464 DB_TABLE_ERR_CREATE_FLAG => 'Create flag not valid', 465 DB_TABLE_ERR_IDX_NO_COLS => 'No columns for index', 466 DB_TABLE_ERR_IDX_COL_UNDEF => 'Column not in map for index', 467 DB_TABLE_ERR_IDX_TYPE => 'Type not valid for index', 468 DB_TABLE_ERR_DECLARE_STRING => 'String column declaration not valid', 469 DB_TABLE_ERR_DECLARE_DECIMAL => 'Decimal column declaration not valid', 470 DB_TABLE_ERR_DECLARE_TYPE => 'Column type not valid', 471 DB_TABLE_ERR_VALIDATE_TYPE => 'Cannot validate for unknown type on column', 472 DB_TABLE_ERR_DECLARE_COLNAME => 'Column name not valid', 473 DB_TABLE_ERR_DECLARE_IDXNAME => 'Index name not valid', 474 DB_TABLE_ERR_DECLARE_TYPE => 'Column type not valid', 475 DB_TABLE_ERR_IDX_COL_CLOB => 'CLOB column not allowed for index', 476 DB_TABLE_ERR_DECLARE_STRLEN => 'Column name too long, 30 char max', 477 DB_TABLE_ERR_IDX_STRLEN => 'Index name too long, 30 char max', 478 DB_TABLE_ERR_TABLE_STRLEN => 'Table name too long, 30 char max', 479 DB_TABLE_ERR_SEQ_STRLEN => 'Sequence name too long, 30 char max', 480 DB_TABLE_ERR_VER_TABLE_MISSING => 'Verification failed: table does not exist', 481 DB_TABLE_ERR_VER_COLUMN_MISSING => 'Verification failed: column does not exist', 482 DB_TABLE_ERR_VER_COLUMN_TYPE => 'Verification failed: wrong column type', 483 DB_TABLE_ERR_NO_COLS => 'Column definition array may not be empty', 484 DB_TABLE_ERR_VER_IDX_MISSING => 'Verification failed: index does not exist', 485 DB_TABLE_ERR_VER_IDX_COL_MISSING => 'Verification failed: index does not contain all specified cols', 486 DB_TABLE_ERR_CREATE_PHPTYPE => 'Creation mode is not supported for this phptype', 487 DB_TABLE_ERR_DECLARE_PRIMARY => 'Only one primary key is allowed', 488 DB_TABLE_ERR_DECLARE_PRIM_SQLITE => 'SQLite does not support primary keys', 489 DB_TABLE_ERR_ALTER_TABLE_IMPOS => 'Alter table failed: changing the field type not possible', 490 DB_TABLE_ERR_ALTER_INDEX_IMPOS => 'Alter table failed: changing the index/constraint not possible', 491 DB_TABLE_ERR_AUTO_INC_COL => 'Illegal auto-increment column definition', 492 DB_TABLE_ERR_TABLE_NAME_MISSING => 'Table name missing in constructor and class' 493); 494 495// merge default and user-defined error messages 496if (!isset($GLOBALS['_DB_TABLE']['error'])) { 497 $GLOBALS['_DB_TABLE']['error'] = array(); 498} 499foreach ($GLOBALS['_DB_TABLE']['default_error'] as $code => $message) { 500 if (!array_key_exists($code, $GLOBALS['_DB_TABLE']['error'])) { 501 $GLOBALS['_DB_TABLE']['error'][$code] = $message; 502 } 503} 504 505// set default value for length check switch 506if (!isset($GLOBALS['_DB_TABLE']['disable_length_check'])) { 507 $GLOBALS['_DB_TABLE']['disable_length_check'] = false; 508} 509 510/** 511 * DB_Table is a database API and data type SQL abstraction class. 512 * 513 * DB_Table provides database API abstraction, data type abstraction, 514 * automated SELECT, INSERT, and UPDATE queries, automated table 515 * creation, automated validation of inserted/updated column values, 516 * and automated creation of QuickForm elemnts based on the column 517 * definitions. 518 * 519 * @category Database 520 * @package DB_Table 521 * @author Paul M. Jones <pmjones@php.net> 522 * @author David C. Morse <morse@php.net> 523 * @author Mark Wiesemann <wiesemann@php.net> 524 * @version Release: 1.5.6 525 * @link http://pear.php.net/package/DB_Table 526 */ 527 528class DB_Table extends DB_Table_Base 529{ 530 531 /** 532 * The table or view in the database to which this object binds. 533 * 534 * @access public 535 * @var string 536 */ 537 var $table = null; 538 539 /** 540 * DB_Table_Database instance that this table belongs to. 541 * 542 * @access private 543 * @var object 544 */ 545 var $_database = null; 546 547 548 /** 549 * Associative array of column definitions. 550 * 551 * @access public 552 * @var array 553 */ 554 var $col = array(); 555 556 557 /** 558 * Associative array of index definitions. 559 * 560 * @access public 561 * @var array 562 */ 563 var $idx = array(); 564 565 /** 566 * Name of an auto-increment column, if any. Null otherwise. 567 * 568 * A table can contain at most one auto-increment column. 569 * Auto-incrementing is implemented in the insert() method, 570 * using a sequence accessed by the nextID() method. 571 * 572 * @access public 573 * @var string 574 */ 575 var $auto_inc_col = null; 576 577 578 /** 579 * Boolean flag to turn on (true) or off (false) auto-incrementing. 580 * 581 * Auto-increment column $auto_inc_col upon insertion only if $_auto_inc is 582 * true and the value of that column is null in the data to be inserted. 583 * 584 * @var bool 585 * @access private 586 */ 587 var $_auto_inc = true; 588 589 590 /** 591 * Whether or not to automatically validate data at insert-time. 592 * 593 * @var bool 594 * @access private 595 */ 596 var $_valid_insert = true; 597 598 /** 599 * Whether or not to automatically validate data at update-time. 600 * 601 * @var bool 602 * @access private 603 */ 604 var $_valid_update = true; 605 606 607 /** 608 * Whether or not to automatically recast data at insert- and update-time. 609 * 610 * @var bool 611 * @access private 612 */ 613 var $_auto_recast = true; 614 615 616 /** 617 * Constructor. 618 * 619 * The constructor returns a DB_Table object that wraps an 620 * instance $db DB or MDB2, and that binds to a specific database 621 * table named $table. It can optionally create the database table 622 * or verify that its schema matches that declared in the $col and 623 * $idx parameters, depending on the value of the $create parameter. 624 * 625 * If there is an error on instantiation, $this->error will be 626 * populated with the PEAR_Error. 627 * 628 * @param object &$db A PEAR DB/MDB2 object. 629 * 630 * @param string $table The table name to connect to in the database. 631 * 632 * @param mixed $create The automatic table creation mode to pursue: 633 * - boolean false to not attempt creation 634 * - 'safe' to create the table only if it does not exist 635 * - 'drop' to drop any existing table with the same name and re-create it 636 * - 'verify' to check whether the table exists, whether all the columns 637 * exist, whether the columns have the right type, and whether the indexes 638 * exist and have the right type 639 * - 'alter' does the same as 'safe' if the table does not exist; if it 640 * exists, a verification for columns existence, the column types, the 641 * indexes existence, and the indexes types will be performed and the 642 * table schema will be modified if needed 643 * 644 * @return object DB_Table 645 * @access public 646 */ 647 function DB_Table(&$db, $table = null, $create = false) 648 { 649 // Identify the class for error handling by parent class 650 $this->_primary_subclass = 'DB_TABLE'; 651 652 // is the first argument a DB/MDB2 object? 653 $this->backend = null; 654 if (is_subclass_of($db, 'db_common')) { 655 $this->backend = 'db'; 656 } elseif (is_subclass_of($db, 'mdb2_driver_common')) { 657 $this->backend = 'mdb2'; 658 } 659 660 if (is_null($this->backend)) { 661 $this->error =& DB_Table::throwError(DB_TABLE_ERR_NOT_DB_OBJECT); 662 return; 663 } 664 665 // set the class properties 666 $this->db =& $db; 667 if (is_null($table)) { 668 // $table parameter not given => check $table class property 669 if (is_null($this->table)) { 670 $this->error =& DB_Table::throwError(DB_TABLE_ERR_TABLE_NAME_MISSING); 671 return; 672 } 673 } else { 674 $this->table = $table; 675 } 676 677 // is the RDBMS supported? 678 $phptype = $db->phptype; 679 $dbsyntax = $db->dbsyntax; 680 if (! DB_Table::supported($phptype, $dbsyntax)) { 681 $this->error =& DB_Table::throwError( 682 DB_TABLE_ERR_PHPTYPE, 683 "({$db->phptype})" 684 ); 685 return; 686 } 687 688 // load MDB2_Extended module 689 if ($this->backend == 'mdb2') { 690 $this->db->loadModule('Extended', null, false); 691 } 692 693 // should we attempt table creation? 694 if ($create) { 695 696 if ($this->backend == 'mdb2') { 697 $this->db->loadModule('Manager'); 698 } 699 700 // check whether the chosen mode is supported 701 $mode_supported = DB_Table::modeSupported($create, $phptype); 702 if (PEAR::isError($mode_supported)) { 703 $this->error =& $mode_supported; 704 return; 705 } 706 if (!$mode_supported) { 707 $this->error =& $this->throwError( 708 DB_TABLE_ERR_CREATE_PHPTYPE, 709 "('$create', '$phptype')" 710 ); 711 return; 712 } 713 714 include_once 'DB/Table/Manager.php'; 715 716 switch ($create) { 717 718 case 'alter': 719 $result = $this->alter(); 720 break; 721 722 case 'drop': 723 case 'safe': 724 $result = $this->create($create); 725 break; 726 727 case 'verify': 728 $result = $this->verify(); 729 break; 730 } 731 732 if (PEAR::isError($result)) { 733 // problem creating/altering/verifing the table 734 $this->error =& $result; 735 return; 736 } 737 } 738 } 739 740 741 /** 742 * Is a particular RDBMS supported by DB_Table? 743 * 744 * @static 745 * @param string $phptype The RDBMS type for PHP. 746 * @param string $dbsyntax The chosen database syntax. 747 * @return bool True if supported, false if not. 748 * @access public 749 */ 750 751 function supported($phptype, $dbsyntax = '') 752 { 753 // only Firebird is supported, not its ancestor Interbase 754 if ($phptype == 'ibase' && $dbsyntax != 'firebird') { 755 return false; 756 } 757 $supported = array_keys($GLOBALS['_DB_TABLE']['type']); 758 return in_array(strtolower($phptype), $supported); 759 } 760 761 762 /** 763 * Is a creation mode supported for a RDBMS by DB_Table? 764 * 765 * @param string $mode The chosen creation mode. 766 * @param string $phptype The RDBMS type for PHP. 767 * @return bool True if supported, false if not (PEAR_Error on failure) 768 * 769 * @throws PEAR_Error if 770 * Unknown creation mode is specified (DB_TABLE_ERR_CREATE_FLAG) 771 * 772 * @access public 773 */ 774 function modeSupported($mode, $phptype) 775 { 776 // check phptype for validity 777 $supported = array_keys($GLOBALS['_DB_TABLE']['type']); 778 if (!in_array(strtolower($phptype), $supported)) { 779 return false; 780 } 781 782 switch ($mode) { 783 case 'drop': 784 case 'safe': 785 // supported for all RDBMS 786 return true; 787 788 case 'alter': 789 case 'verify': 790 // not supported for fbsql and mssql (yet) 791 switch ($phptype) { 792 case 'fbsql': 793 case 'mssql': 794 return false; 795 default: 796 return true; 797 } 798 799 default: 800 // unknown creation mode 801 return $this->throwError( 802 DB_TABLE_ERR_CREATE_FLAG, 803 "('$mode')" 804 ); 805 } 806 } 807 808 809 /** 810 * Overwrite one or more error messages, e.g. to internationalize them. 811 * 812 * @param mixed $code If string, the error message with code $code will 813 * be overwritten by $message. If array, the error messages with code 814 * of each array key will be overwritten by the key's value. 815 * 816 * @param string $message Only used if $key is not an array. 817 * @return void 818 * @access public 819 */ 820 function setErrorMessage($code, $message = null) { 821 if (is_array($code)) { 822 foreach ($code as $single_code => $single_message) { 823 $GLOBALS['_DB_TABLE']['error'][$single_code] = $single_message; 824 } 825 } else { 826 $GLOBALS['_DB_TABLE']['error'][$code] = $message; 827 } 828 } 829 830 831 /** 832 * 833 * Returns all or part of the $this->col property array. 834 * 835 * @param mixed $col If null, returns the $this->col property array 836 * as it is. If string, returns that column name from the $this->col 837 * array. If an array, returns those columns named as the array 838 * values from the $this->col array as an array. 839 * 840 * @return mixed All or part of the $this->col property array, or 841 * boolean false if no matching column names are found. 842 * @access public 843 */ 844 function getColumns($col = null) 845 { 846 // by default, return all column definitions 847 if (is_null($col)) { 848 return $this->col; 849 } 850 851 // if the param is a string, only return the column definition 852 // named by the that string 853 if (is_string($col)) { 854 if (isset($this->col[$col])) { 855 return $this->col[$col]; 856 } else { 857 return false; 858 } 859 } 860 861 // if the param is a sequential array of column names, 862 // return only those columns named in that array 863 if (is_array($col)) { 864 $set = array(); 865 foreach ($col as $name) { 866 $set[$name] = $this->getColumns($name); 867 } 868 869 if (count($set) == 0) { 870 return false; 871 } else { 872 return $set; 873 } 874 } 875 876 // param was not null, string, or array 877 return false; 878 } 879 880 881 /** 882 * Returns all or part of the $this->idx property array. 883 * 884 * @param mixed $idx Index name (key in $this->idx), or array of 885 * index name strings. 886 * 887 * @return mixed All or part of the $this->idx property array, 888 * or boolean false if $idx is not null but invalid 889 * 890 * @access public 891 */ 892 function getIndexes($idx = null) 893 { 894 // by default, return all index definitions 895 if (is_null($idx)) { 896 return $this->idx; 897 } 898 899 // if the param is a string, only return the index definition 900 // named by the that string 901 if (is_string($idx)) { 902 if (isset($this->idx[$idx])) { 903 return $this->idx[$idx]; 904 } else { 905 return false; 906 } 907 } 908 909 // if the param is a sequential array of index names, 910 // return only those indexes named in that array 911 if (is_array($idx)) { 912 $set = array(); 913 foreach ($idx as $name) { 914 $set[$name] = $this->getIndexes($name); 915 } 916 917 if (count($set) == 0) { 918 return false; 919 } else { 920 return $set; 921 } 922 } 923 924 // param was not null, string, or array 925 return false; 926 } 927 928 929 /** 930 * Connect or disconnect a DB_Table_Database instance to this table 931 * instance. 932 * 933 * Used to re-connect this DB_Table object to a parent DB_Table_Database 934 * object during unserialization. Can also disconnect if the $database 935 * parameter is null. Use the DB_Table_Database::addTable method instead 936 * to add a table to a new DB_Table_Database. 937 * 938 * @param object &$database DB_Table_Database instance that this table 939 * belongs to (or null to disconnect from instance). 940 * 941 * @return void 942 * @access public 943 */ 944 function setDatabaseInstance(&$database) 945 { 946 if (is_a($database, 'DB_Table_Database')) { 947 $this->_database =& $database; 948 } elseif (is_null($database)) { 949 $this->_database = null; 950 } 951 } 952 953 954 /** 955 * Inserts a single table row. 956 * 957 * Inserts data from associative array $data, in which keys are column 958 * names and values are column values. All required columns (except an 959 * auto-increment column) must be included in the data array. Columns 960 * values that are not set or null are inserted as SQL NULL values. 961 * 962 * If an auto-increment column is declared (by setting $this->auto_inc_col), 963 * and the value of that column in $data is not set or null, then a new 964 * sequence value will be generated and inserted. 965 * 966 * If auto-recasting is enabled (if $this->_auto_recast), the method will 967 * try, if necessary to recast $data to proper column types, with recast(). 968 * 969 * If auto-validation is enabled (if $this->_valid_insert), the method 970 * will validates column types with validInsert() before insertion. 971 * 972 * @access public 973 * 974 * @param array $data An associative array of key-value pairs where 975 * the key is the column name and the value is the column value. 976 * This is the data that will be inserted into the table. 977 * 978 * @return mixed Void on success (PEAR_Error on failure) 979 * 980 * @throws PEAR_Error if: 981 * - Error in auto_inc_col declaration (DB_TABLE_ERR_AUTO_INC_COL) 982 * - Error returned by DB/MDB2::autoExecute() (Error bubbled up) 983 * 984 * @see validInsert() 985 * @see DB::autoExecute() 986 * @see MDB2::autoExecute() 987 */ 988 function insert($data) 989 { 990 // Auto-increment if enabled and input value is null or not set 991 if ($this->_auto_inc 992 && !is_null($this->auto_inc_col) 993 && !isset($data[$this->auto_inc_col]) 994 ) { 995 $column = $this->auto_inc_col; 996 // check that the auto-increment column exists 997 if (!array_key_exists($column, $this->col)) { 998 return $this->throwError( 999 DB_TABLE_ERR_AUTO_INC_COL, 1000 ": $column does not exist"); 1001 } 1002 // check that the column is integer 1003 if (!in_array($this->col[$column]['type'], 1004 array('integer','smallint','bigint'))) { 1005 return $this->throwError( 1006 DB_TABLE_ERR_AUTO_INC_COL, 1007 ": $column is not an integer"); 1008 } 1009 // check that the column is required 1010 // Note: The insert method will replace a null input value 1011 // of $data[$column] with a sequence value. This makes 1012 // the column effectively 'not null'. This column must be 1013 // 'required' for consistency, to make this explicit. 1014 if (!$this->isRequired($column)) { 1015 return $this->throwError( 1016 DB_TABLE_ERR_AUTO_INC_COL, 1017 ": $column is not required"); 1018 } 1019 // set the value 1020 $id = $this->nextID(); 1021 if (PEAR::isError($id)) { 1022 return $id; 1023 } 1024 $data[$column] = $id; 1025 } 1026 1027 // forcibly recast the data elements to their proper types? 1028 if ($this->_auto_recast) { 1029 $this->recast($data); 1030 } 1031 1032 // validate the data if auto-validation is turned on 1033 if ($this->_valid_insert) { 1034 $result = $this->validInsert($data); 1035 if (PEAR::isError($result)) { 1036 return $result; 1037 } 1038 } 1039 1040 // Does a parent DB_Table_Database object exist? 1041 if ($this->_database) { 1042 1043 $_database = $this->_database; 1044 1045 // Validate foreign key values (if enabled) 1046 if ($_database->_check_fkey) { 1047 $result = $_database->validForeignKeys($this->table, $data); 1048 if (PEAR::isError($result)) { 1049 return $result; 1050 } 1051 } 1052 1053 } 1054 1055 // Do insertion 1056 if ($this->backend == 'mdb2') { 1057 $result = $this->db->extended->autoExecute($this->table, $data, 1058 MDB2_AUTOQUERY_INSERT); 1059 } else { 1060 $result = $this->db->autoExecute($this->table, $data, 1061 DB_AUTOQUERY_INSERT); 1062 } 1063 return $result; 1064 } 1065 1066 1067 /** 1068 * Turns on or off auto-incrementing of $auto_inc_col column (if any) 1069 * 1070 * For auto-incrementing to work, an $auto_inc_col column must be declared, 1071 * auto-incrementing must be enabled (by this method), and the value of 1072 * the $auto_inc_col column must be not set or null in the $data passed to 1073 * the insert method. 1074 * 1075 * @param bool $flag True to turn on auto-increment, false to turn off. 1076 * @return void 1077 * @access public 1078 */ 1079 function setAutoInc($flag = true) 1080 { 1081 if ($flag) { 1082 $this->_auto_inc = true; 1083 } else { 1084 $this->_auto_inc = false; 1085 } 1086 } 1087 1088 1089 /** 1090 * Turns on (or off) automatic validation of inserted data. 1091 * 1092 * Enables (if $flag is true) or disables (if $flag is false) automatic 1093 * validation of data types prior to actual insertion into the database 1094 * by the DB_Table::insert() method. 1095 * 1096 * @param bool $flag True to turn on auto-validation, false to turn off. 1097 * @return void 1098 * @access public 1099 */ 1100 function autoValidInsert($flag = true) 1101 { 1102 if ($flag) { 1103 $this->_valid_insert = true; 1104 } else { 1105 $this->_valid_insert = false; 1106 } 1107 } 1108 1109 1110 /** 1111 * Validates an array for insertion into the table. 1112 * 1113 * @param array $data An associative array of key-value pairs where 1114 * the key is the column name and the value is the column value. This 1115 * is the data that will be inserted into the table. Data is checked 1116 * against the column data type for validity. 1117 * 1118 * @return boolean true on success (PEAR_Error on failure) 1119 * 1120 * @throws PEAR_Error if: 1121 * - Invalid column name key in $data (DB_TABLE_ERR_INS_COL_NOMAP) 1122 * - Missing required column value (DB_TABLE_ERR_INS_COL_NOMAP) 1123 * - Column value doesn't match type (DB_TABLE_ERR_INS_DATA_INVALID) 1124 * 1125 * @access public 1126 * 1127 * @see insert() 1128 */ 1129 function validInsert(&$data) 1130 { 1131 // loop through the data, and disallow insertion of unmapped 1132 // columns 1133 foreach ($data as $col => $val) { 1134 if (! isset($this->col[$col])) { 1135 return $this->throwError( 1136 DB_TABLE_ERR_INS_COL_NOMAP, 1137 "('$col')" 1138 ); 1139 } 1140 } 1141 1142 // loop through each column mapping, and check the data to be 1143 // inserted into it against the column data type. we loop through 1144 // column mappings instead of the insert data to make sure that 1145 // all necessary columns are being inserted. 1146 foreach ($this->col as $col => $val) { 1147 1148 // is the value allowed to be null? 1149 if (isset($val['require']) && 1150 $val['require'] == true && 1151 (! isset($data[$col]) || is_null($data[$col]))) { 1152 return $this->throwError( 1153 DB_TABLE_ERR_INS_COL_REQUIRED, 1154 "'$col'" 1155 ); 1156 } 1157 1158 // does the value to be inserted match the column data type? 1159 if (isset($data[$col]) && 1160 ! $this->isValid($data[$col], $col)) { 1161 return $this->throwError( 1162 DB_TABLE_ERR_INS_DATA_INVALID, 1163 "'$col' ('$data[$col]')" 1164 ); 1165 } 1166 } 1167 1168 return true; 1169 } 1170 1171 1172 /** 1173 * Update table row or rows that match a custom WHERE clause 1174 * 1175 * Constructs and submits an SQL UPDATE command to update columns whose 1176 * names are keys in the $data array parameter, in all rows that match 1177 * the logical condition given by the $where string parameter. 1178 * 1179 * If auto-recasting is enabled (if $this->_auto_recast), update() will 1180 * try, if necessary, to recast $data to proper column types, with recast(). 1181 * 1182 * If auto-validation is enabled (if $this->_valid_insert), update() 1183 * validates column types with validUpdate() before insertion. 1184 * 1185 * @param array $data An associative array of key-value pairs where the 1186 * key is the column name and the value is the column value. These are 1187 * the columns that will be updated with new values. 1188 * 1189 * @param string $where An SQL WHERE clause limiting which records are 1190 * are to be updated. 1191 * 1192 * @return mixed Void on success, a PEAR_Error object on failure. 1193 * 1194 * @throws PEAR_Error if: 1195 * - Data fails type validation (bubbles error returned by validUpdate) 1196 * - Error thrown by DB/MDB2::autoexecute() 1197 * 1198 * @access public 1199 * 1200 * @see validUpdate() 1201 * @see DB::autoExecute() 1202 * @see MDB2::autoExecute() 1203 */ 1204 function update($data, $where) 1205 { 1206 // forcibly recast the data elements to their proper types? 1207 if ($this->_auto_recast) { 1208 $this->recast($data); 1209 } 1210 1211 // validate the data if auto-validation is turned on 1212 if ($this->_valid_update) { 1213 $result = $this->validUpdate($data); 1214 if (PEAR::isError($result)) { 1215 return $result; 1216 } 1217 } 1218 1219 // Does a parent DB_Table_Database object exist? 1220 if ($this->_database) { 1221 1222 $_database =& $this->_database; 1223 1224 // Validate foreign key values (if enabled) 1225 if ($_database->_check_fkey) { 1226 $result = $_database->validForeignKeys($this->table, $data); 1227 if (PEAR::isError($result)) { 1228 return $result; 1229 } 1230 } 1231 1232 // Implement any relevant ON UPDATE actions 1233 $result = $_database->onUpdateAction($this, $data, $where); 1234 if (PEAR::isError($result)) { 1235 return $result; 1236 } 1237 1238 } 1239 1240 // Submit update command 1241 if ($this->backend == 'mdb2') { 1242 $result = $this->db->extended->autoExecute($this->table, $data, 1243 MDB2_AUTOQUERY_UPDATE, $where); 1244 } else { 1245 $result = $this->db->autoExecute($this->table, $data, 1246 DB_AUTOQUERY_UPDATE, $where); 1247 } 1248 return $result; 1249 1250 } 1251 1252 1253 /** 1254 * Turns on (or off) automatic validation of updated data. 1255 * 1256 * Enables (if $flag is true) or disables (if $flag is false) automatic 1257 * validation of data types prior to updating rows in the database by 1258 * the {@link update()} method. 1259 * 1260 * @param bool $flag True to turn on auto-validation, false to turn off. 1261 * @return void 1262 * @access public 1263 */ 1264 function autoValidUpdate($flag = true) 1265 { 1266 if ($flag) { 1267 $this->_valid_update = true; 1268 } else { 1269 $this->_valid_update = false; 1270 } 1271 } 1272 1273 1274 /** 1275 * Validates an array for updating the table. 1276 * 1277 * @param array $data An associative array of key-value pairs where 1278 * the key is the column name and the value is the column value. This 1279 * is the data that will be inserted into the table. Data is checked 1280 * against the column data type for validity. 1281 * 1282 * @return mixed Boolean true on success (PEAR_Error object on failure) 1283 * 1284 * @throws PEAR_Error if 1285 * - Invalid column name key in $data (DB_TABLE_ERR_UPD_COL_NOMAP) 1286 * - Missing required column value (DB_TABLE_ERR_UPD_COL_NOMAP) 1287 * - Column value doesn't match type (DB_TABLE_ERR_UPD_DATA_INVALID) 1288 * 1289 * @access public 1290 * 1291 * @see update() 1292 */ 1293 function validUpdate(&$data) 1294 { 1295 // loop through each data element, and check the 1296 // data to be updated against the column data type. 1297 foreach ($data as $col => $val) { 1298 1299 // does the column exist? 1300 if (! isset($this->col[$col])) { 1301 return $this->throwError( 1302 DB_TABLE_ERR_UPD_COL_NOMAP, 1303 "('$col')" 1304 ); 1305 } 1306 1307 // the column definition 1308 $defn = $this->col[$col]; 1309 1310 // is it allowed to be null? 1311 if (isset($defn['require']) && 1312 $defn['require'] == true && 1313 isset($data[$col]) && 1314 is_null($data[$col])) { 1315 return $this->throwError( 1316 DB_TABLE_ERR_UPD_COL_REQUIRED, 1317 $col 1318 ); 1319 } 1320 1321 // does the value to be inserted match the column data type? 1322 if (! $this->isValid($data[$col], $col)) { 1323 return $this->throwError( 1324 DB_TABLE_ERR_UPD_DATA_INVALID, 1325 "$col ('$data[$col]')" 1326 ); 1327 } 1328 } 1329 1330 return true; 1331 } 1332 1333 1334 /** 1335 * Deletes table rows matching a custom WHERE clause. 1336 * 1337 * Constructs and submits and SQL DELETE command with the specified WHERE 1338 * clause. Command is submitted by DB::query() or MDB2::exec(). 1339 * 1340 * If a reference to a DB_Table_Database instance exists, carry out any 1341 * ON DELETE actions declared in that instance before actual insertion, 1342 * if emulation of ON DELETE actions is enabled in that instance. 1343 * 1344 * @param string $where Logical condition in the WHERE clause of the 1345 * delete command. 1346 * 1347 * @return mixed void on success (PEAR_Error on failure) 1348 * 1349 * @throws PEAR_Error if 1350 * DB::query() or MDB2::exec() returns error (bubbles up) 1351 * 1352 * @access public 1353 * 1354 * @see DB::query() 1355 * @see MDB2::exec() 1356 */ 1357 function delete($where) 1358 { 1359 // Does a parent DB_Table_Database object exist? 1360 if ($this->_database) { 1361 1362 $_database =& $this->_database; 1363 1364 // Implement any relevant ON DELETE actions 1365 $result = $_database->onDeleteAction($this, $where); 1366 if (PEAR::isError($result)) { 1367 return $result; 1368 } 1369 1370 } 1371 1372 if ($this->backend == 'mdb2') { 1373 $result = $this->db->exec("DELETE FROM $this->table WHERE $where"); 1374 } else { 1375 $result = $this->db->query("DELETE FROM $this->table WHERE $where"); 1376 } 1377 return $result; 1378 } 1379 1380 1381 /** 1382 * 1383 * Generates and returns a sequence value. 1384 * 1385 * Generates a sequence value by calling the DB or MDB2::nextID() method. The 1386 * sequence name defaults to the table name, or may be specified explicitly. 1387 * 1388 * @param string $seq_name The sequence name; defaults to table_id. 1389 * 1390 * @return integer The next value in the sequence (PEAR_Error on failure) 1391 * 1392 * @throws PEAR_Error if 1393 * Sequence name too long (>26 char + _seq) (DB_TABLE_ERR_SEQ_STRLEN) 1394 * 1395 * @access public 1396 * 1397 * @see DB::nextID() 1398 * @see MDB2::nextID() 1399 */ 1400 function nextID($seq_name = null) 1401 { 1402 if (is_null($seq_name)) { 1403 $seq_name = "{$this->table}"; 1404 } else { 1405 $seq_name = "{$this->table}_{$seq_name}"; 1406 } 1407 1408 // the maximum length is 30, but PEAR DB/MDB2 will add "_seq" to the 1409 // name, so the max length here is less 4 chars. we have to 1410 // check here because the sequence will be created automatically 1411 // by PEAR DB/MDB2, which will not check for length on its own. 1412 if ( $GLOBALS['_DB_TABLE']['disable_length_check'] === false 1413 && strlen($seq_name) > 26 1414 ) { 1415 return DB_Table::throwError( 1416 DB_TABLE_ERR_SEQ_STRLEN, 1417 " ('$seq_name')" 1418 ); 1419 1420 } 1421 return $this->db->nextId($seq_name); 1422 } 1423 1424 1425 /** 1426 * Escapes and enquotes a value for use in an SQL query. 1427 * 1428 * Simple wrapper for DB_Common::quoteSmart() or MDB2::quote(), which 1429 * returns the value of one of these functions. Helps makes user input 1430 * safe against SQL injection attack. 1431 * 1432 * @param mixed $val The value to be quoted 1433 * 1434 * @return string The value with quotes escaped, inside single quotes if 1435 * non-numeric. 1436 * 1437 * @throws PEAR_Error if 1438 * DB_Common::quoteSmart() or MDB2::quote() returns Error (bubbled up) 1439 * 1440 * @access public 1441 * 1442 * @see DB_Common::quoteSmart() 1443 * @see MDB2::quote() 1444 */ 1445 function quote($val) 1446 { 1447 if ($this->backend == 'mdb2') { 1448 $val = $this->db->quote($val); 1449 } else { 1450 $val = $this->db->quoteSmart($val); 1451 } 1452 return $val; 1453 } 1454 1455 1456 /** 1457 * Returns a blank row array based on the column map. 1458 * 1459 * The array keys are the column names, and all values are set to null. 1460 * 1461 * @return array An associative array where keys are column names and 1462 * all values are null. 1463 * @access public 1464 */ 1465 function getBlankRow() 1466 { 1467 $row = array(); 1468 1469 foreach ($this->col as $key => $val) { 1470 $row[$key] = null; 1471 } 1472 1473 $this->recast($row); 1474 1475 return $row; 1476 } 1477 1478 1479 /** 1480 * Turns on (or off) automatic recasting of insert and update data. 1481 * 1482 * Turns on (if $flag is true) or off (if $flag is false) automatic forcible 1483 * recasting of data to the declared data type, if required, prior to inserting 1484 * or updating. The recasting is done by calling the DB_Table::recast() 1485 * method from within the DB_Table::insert() and DB_Table::update(). 1486 * 1487 * @param bool $flag True to automatically recast insert and update data, 1488 * false to not do so. 1489 * @return void 1490 * @access public 1491 */ 1492 function autoRecast($flag = true) 1493 { 1494 if ($flag) { 1495 $this->_auto_recast = true; 1496 } else { 1497 $this->_auto_recast = false; 1498 } 1499 } 1500 1501 1502 /** 1503 * Forces array elements to the proper types for their columns. 1504 * 1505 * This will not valiate the data, and will forcibly change the data 1506 * to match the recast-type. 1507 * 1508 * The date, time, and timestamp recasting has special logic for 1509 * arrays coming from an HTML_QuickForm object so that the arrays 1510 * are converted into properly-formatted strings. 1511 * 1512 * @todo If a column key holds an array of values (say from a multiple 1513 * select) then this method will not work properly; it will recast the 1514 * value to the string 'Array'. Is this bad? 1515 * 1516 * @param array &$data The data array to re-cast. 1517 * 1518 * @return void 1519 * 1520 * @access public 1521 */ 1522 function recast(&$data) 1523 { 1524 $keys = array_keys($data); 1525 1526 $null_if_blank = array('date', 'time', 'timestamp', 'smallint', 1527 'integer', 'bigint', 'decimal', 'single', 'double'); 1528 1529 foreach ($keys as $key) { 1530 1531 if (! isset($this->col[$key])) { 1532 continue; 1533 } 1534 1535 unset($val); 1536 $val =& $data[$key]; 1537 1538 // convert blanks to null for non-character field types 1539 $convert = in_array($this->col[$key]['type'], $null_if_blank); 1540 if (is_array($val)) { // if one of the given array values is 1541 // empty, null will be the new value if 1542 // the field is not required 1543 $tmp_val = implode('', $val); 1544 foreach ($val as $array_val) { 1545 if (trim((string) $array_val) == '') { 1546 $tmp_val = ''; 1547 break; 1548 } 1549 } 1550 } else { 1551 $tmp_val = $val; 1552 } 1553 if ($convert && trim((string) $tmp_val) == '' && ( 1554 !isset($this->col[$key]['require']) || 1555 $this->col[$key]['require'] === false 1556 ) 1557 ) { 1558 $val = null; 1559 } 1560 1561 // skip explicit NULL values 1562 if (is_null($val)) { 1563 continue; 1564 } 1565 1566 // otherwise, recast to the column type 1567 switch ($this->col[$key]['type']) { 1568 1569 case 'boolean': 1570 $val = ($val) ? 1 : 0; 1571 break; 1572 1573 case 'char': 1574 case 'varchar': 1575 case 'clob': 1576 settype($val, 'string'); 1577 break; 1578 1579 case 'date': 1580 1581 // smart handling of non-standard (i.e. Y-m-d) date formats, 1582 // this allows to use two-digit years (y) and short (M) or 1583 // long (F) names of months without having to recast the 1584 // date value yourself 1585 if (is_array($val)) { 1586 if (isset($val['y'])) { 1587 $val['Y'] = $val['y']; 1588 } 1589 if (isset($val['F'])) { 1590 $val['m'] = $val['F']; 1591 } 1592 if (isset($val['M'])) { 1593 $val['m'] = $val['M']; 1594 } 1595 } 1596 1597 if (is_array($val) && 1598 isset($val['Y']) && 1599 isset($val['m']) && 1600 isset($val['d'])) { 1601 1602 // the date is in HTML_QuickForm format, 1603 // convert into a string 1604 $y = (strlen($val['Y']) < 4) 1605 ? str_pad($val['Y'], 4, '0', STR_PAD_LEFT) 1606 : $val['Y']; 1607 1608 $m = (strlen($val['m']) < 2) 1609 ? '0'.$val['m'] : $val['m']; 1610 1611 $d = (strlen($val['d']) < 2) 1612 ? '0'.$val['d'] : $val['d']; 1613 1614 $val = "$y-$m-$d"; 1615 1616 } else { 1617 1618 // convert using the Date class 1619 $tmp =& new DB_Table_Date($val); 1620 $val = $tmp->format('%Y-%m-%d'); 1621 1622 } 1623 1624 break; 1625 1626 case 'time': 1627 1628 if (is_array($val) && 1629 isset($val['H']) && 1630 isset($val['i']) && 1631 isset($val['s'])) { 1632 1633 // the time is in HTML_QuickForm format, 1634 // convert into a string 1635 $h = (strlen($val['H']) < 2) 1636 ? '0' . $val['H'] : $val['H']; 1637 1638 $i = (strlen($val['i']) < 2) 1639 ? '0' . $val['i'] : $val['i']; 1640 1641 $s = (strlen($val['s']) < 2) 1642 ? '0' . $val['s'] : $val['s']; 1643 1644 1645 $val = "$h:$i:$s"; 1646 1647 } else { 1648 // date does not matter in this case, so 1649 // pre 1970 and post 2040 are not an issue. 1650 $tmp = strtotime(date('Y-m-d') . " $val"); 1651 $val = date('H:i:s', $tmp); 1652 } 1653 1654 break; 1655 1656 case 'timestamp': 1657 1658 // smart handling of non-standard (i.e. Y-m-d) date formats, 1659 // this allows to use two-digit years (y) and short (M) or 1660 // long (F) names of months without having to recast the 1661 // date value yourself 1662 if (is_array($val)) { 1663 if (isset($val['y'])) { 1664 $val['Y'] = $val['y']; 1665 } 1666 if (isset($val['F'])) { 1667 $val['m'] = $val['F']; 1668 } 1669 if (isset($val['M'])) { 1670 $val['m'] = $val['M']; 1671 } 1672 } 1673 1674 if (is_array($val) && 1675 isset($val['Y']) && 1676 isset($val['m']) && 1677 isset($val['d']) && 1678 isset($val['H']) && 1679 isset($val['i']) && 1680 isset($val['s'])) { 1681 1682 // timestamp is in HTML_QuickForm format, 1683 // convert each element to a string. pad 1684 // with zeroes as needed. 1685 1686 $y = (strlen($val['Y']) < 4) 1687 ? str_pad($val['Y'], 4, '0', STR_PAD_LEFT) 1688 : $val['Y']; 1689 1690 $m = (strlen($val['m']) < 2) 1691 ? '0'.$val['m'] : $val['m']; 1692 1693 $d = (strlen($val['d']) < 2) 1694 ? '0'.$val['d'] : $val['d']; 1695 1696 $h = (strlen($val['H']) < 2) 1697 ? '0' . $val['H'] : $val['H']; 1698 1699 $i = (strlen($val['i']) < 2) 1700 ? '0' . $val['i'] : $val['i']; 1701 1702 $s = (strlen($val['s']) < 2) 1703 ? '0' . $val['s'] : $val['s']; 1704 1705 $val = "$y-$m-$d $h:$i:$s"; 1706 1707 } else { 1708 // convert using the Date class 1709 $tmp =& new DB_Table_Date($val); 1710 $val = $tmp->format('%Y-%m-%d %H:%M:%S'); 1711 } 1712 1713 break; 1714 1715 case 'smallint': 1716 case 'integer': 1717 case 'bigint': 1718 settype($val, 'integer'); 1719 break; 1720 1721 case 'decimal': 1722 case 'single': 1723 case 'double': 1724 settype($val, 'float'); 1725 break; 1726 1727 } 1728 } 1729 } 1730 1731 1732 /** 1733 * Creates the table based on $this->col and $this->idx. 1734 * 1735 * @param string $flag The automatic table creation mode to pursue: 1736 * - 'safe' to create the table only if it does not exist 1737 * - 'drop' to drop any existing table with the same name and re-create it 1738 * 1739 * @return mixed Boolean true if the table was successfully created, 1740 * false if there was no need to create the table, or 1741 * a PEAR_Error if the attempted creation failed. 1742 * 1743 * @throws PEAR_Error if 1744 * - DB_Table_Manager::tableExists() returns Error (bubbles up) 1745 * - DB_Table_Manager::create() returns Error (bubbles up) 1746 * 1747 * @access public 1748 * 1749 * @see DB_Table_Manager::tableExists() 1750 * @see DB_Table_Manager::create() 1751 */ 1752 function create($flag) 1753 { 1754 include_once 'DB/Table/Manager.php'; 1755 1756 // are we OK to create the table? 1757 $ok = false; 1758 1759 // check the create-flag 1760 switch ($flag) { 1761 1762 case 'drop': 1763 // drop only if table exists 1764 $table_exists = DB_Table_Manager::tableExists($this->db, 1765 $this->table); 1766 if (PEAR::isError($table_exists)) { 1767 return $table_exists; 1768 } 1769 if ($table_exists) { 1770 // forcibly drop an existing table 1771 if ($this->backend == 'mdb2') { 1772 $this->db->manager->dropTable($this->table); 1773 } else { 1774 $this->db->query("DROP TABLE {$this->table}"); 1775 } 1776 } 1777 $ok = true; 1778 break; 1779 1780 case 'safe': 1781 // create only if table does not exist 1782 $table_exists = DB_Table_Manager::tableExists($this->db, 1783 $this->table); 1784 if (PEAR::isError($table_exists)) { 1785 return $table_exists; 1786 } 1787 // ok to create only if table does not exist 1788 $ok = !$table_exists; 1789 break; 1790 1791 } 1792 1793 // are we going to create the table? 1794 if (! $ok) { 1795 return false; 1796 } 1797 1798 return DB_Table_Manager::create( 1799 $this->db, $this->table, $this->col, $this->idx 1800 ); 1801 } 1802 1803 1804 /** 1805 * Alters the table to match schema declared in $this->col and $this->idx. 1806 * 1807 * If the table does not exist, create it instead. 1808 * 1809 * @return boolean true if altering is successful (PEAR_Error on failure) 1810 * 1811 * @throws PEAR_Error if 1812 * - DB_Table_Manager::tableExists() returns Error (bubbles up) 1813 * - DB_Table_Manager::create() returns Error (bubbles up) 1814 * - DB_Table_Manager::alter() returns Error (bubbles up) 1815 * 1816 * @access public 1817 * 1818 * @see DB_Table_Manager::tableExists() 1819 * @see DB_Table_Manager::create() 1820 * @see DB_Table_Manager::alter() 1821 */ 1822 function alter() 1823 { 1824 $create = false; 1825 1826 // alter the table columns and indexes if the table exists 1827 $table_exists = DB_Table_Manager::tableExists($this->db, 1828 $this->table); 1829 if (PEAR::isError($table_exists)) { 1830 return $table_exists; 1831 } 1832 if (!$table_exists) { 1833 // table does not exist => just create the table, there is 1834 // nothing that could be altered 1835 $create = true; 1836 } 1837 1838 if ($create) { 1839 return DB_Table_Manager::create( 1840 $this->db, $this->table, $this->col, $this->idx 1841 ); 1842 } 1843 1844 return DB_Table_Manager::alter( 1845 $this->db, $this->table, $this->col, $this->idx 1846 ); 1847 } 1848 1849 1850 /** 1851 * Verifies the table based on $this->col and $this->idx. 1852 * 1853 * @return boolean true if verification succees (PEAR_Error on failure). 1854 * 1855 * @throws PEAR_Error if 1856 * DB_Table_Manager::verify() returns Error (bubbles up) 1857 * 1858 * @access public 1859 * 1860 * @see DB_Table_Manager::verify() 1861 */ 1862 function verify() 1863 { 1864 return DB_Table_Manager::verify( 1865 $this->db, $this->table, $this->col, $this->idx 1866 ); 1867 } 1868 1869 1870 /** 1871 * Checks if a value validates against the DB_Table data type for a 1872 * given column. This only checks that it matches the data type; it 1873 * does not do extended validation. 1874 * 1875 * @param array $val A value to check against the column's DB_Table 1876 * data type. 1877 * 1878 * @param array $col A column name from $this->col. 1879 * 1880 * @return boolean True if $val validates against data type, false if not 1881 * 1882 * @throws PEAR_Error if 1883 * Invalid column type in $this->col (DB_TABLE_ERR_VALIDATE_TYPE) 1884 * 1885 * @access public 1886 * 1887 * @see DB_Table_Valid 1888 */ 1889 function isValid($val, $col) 1890 { 1891 // is the value null? 1892 if (is_null($val)) { 1893 // is the column required? 1894 if ($this->isRequired($col)) { 1895 // yes, so not valid 1896 return false; 1897 } else { 1898 // not required, so it's valid 1899 return true; 1900 } 1901 } 1902 1903 // make sure we have the validation class 1904 include_once 'DB/Table/Valid.php'; 1905 1906 // validate values per the column type. we use sqlite 1907 // as the single authentic list of allowed column types, 1908 // regardless of the actual rdbms being used. 1909 $map = array_keys($GLOBALS['_DB_TABLE']['type']['sqlite']); 1910 1911 // is the column type on the map? 1912 if (! in_array($this->col[$col]['type'], $map)) { 1913 return $this->throwError( 1914 DB_TABLE_ERR_VALIDATE_TYPE, 1915 "'$col' ('{$this->col[$col]['type']}')" 1916 ); 1917 } 1918 1919 // validate for the type 1920 switch ($this->col[$col]['type']) { 1921 1922 case 'char': 1923 case 'varchar': 1924 $result = DB_Table_Valid::isChar( 1925 $val, 1926 $this->col[$col]['size'] 1927 ); 1928 break; 1929 1930 case 'decimal': 1931 $result = DB_Table_Valid::isDecimal( 1932 $val, 1933 $this->col[$col]['size'], 1934 $this->col[$col]['scope'] 1935 ); 1936 break; 1937 1938 default: 1939 $result = call_user_func( 1940 array( 1941 'DB_Table_Valid', 1942 'is' . ucwords($this->col[$col]['type']) 1943 ), 1944 $val 1945 ); 1946 break; 1947 1948 } 1949 1950 // have we passed the check so far, and should we 1951 // also check for allowed values? 1952 if ($result && isset($this->col[$col]['qf_vals'])) { 1953 $keys = array_keys($this->col[$col]['qf_vals']); 1954 1955 $result = in_array( 1956 $val, 1957 array_keys($this->col[$col]['qf_vals']) 1958 ); 1959 } 1960 1961 return $result; 1962 } 1963 1964 1965 /** 1966 * Is a specific column required to be set and non-null? 1967 * 1968 * @param mixed $column The column to check against. 1969 * @return boolean True if required, false if not. 1970 * @access public 1971 */ 1972 function isRequired($column) 1973 { 1974 if (isset($this->col[$column]['require']) && 1975 $this->col[$column]['require'] == true) { 1976 return true; 1977 } else { 1978 return false; 1979 } 1980 } 1981 1982 1983 /** 1984 * 1985 * Creates and returns a QuickForm object based on table columns. 1986 * 1987 * @param array $columns A sequential array of column names to use in 1988 * the form; if null, uses all columns. 1989 * 1990 * @param string $array_name By default, the form will use the names 1991 * of the columns as the names of the form elements. If you pass 1992 * $array_name, the column names will become keys in an array named 1993 * for this parameter. 1994 * 1995 * @param array $args An associative array of optional arguments to 1996 * pass to the QuickForm object. The keys are... 1997 * 1998 * 'formName' : String, name of the form; defaults to the name of this 1999 * table. 2000 * 2001 * 'method' : String, form method; defaults to 'post'. 2002 * 2003 * 'action' : String, form action; defaults to 2004 * $_SERVER['REQUEST_URI']. 2005 * 2006 * 'target' : String, form target target; defaults to '_self' 2007 * 2008 * 'attributes' : Associative array, extra attributes for <form> 2009 * tag; the key is the attribute name and the value is attribute 2010 * value. 2011 * 2012 * 'trackSubmit' : Boolean, whether to track if the form was 2013 * submitted by adding a special hidden field 2014 * 2015 * @param string $clientValidate By default, validation will match 2016 * the 'qf_client' value from the column definition. However, if 2017 * you set $clientValidate to true or false, this will override the 2018 * value from the column definition. 2019 * 2020 * @param array $formFilters An array with filter function names or 2021 * callbacks that will be applied to all form elements. 2022 * 2023 * @return object HTML_QuickForm 2024 * 2025 * @access public 2026 * 2027 * @see HTML_QuickForm 2028 * @see DB_Table_QuickForm 2029 */ 2030 function &getForm($columns = null, $array_name = null, $args = array(), 2031 $clientValidate = null, $formFilters = null) 2032 { 2033 include_once 'DB/Table/QuickForm.php'; 2034 $coldefs = $this->_getFormColDefs($columns); 2035 $form =& DB_Table_QuickForm::getForm($coldefs, $array_name, $args, 2036 $clientValidate, $formFilters); 2037 return $form; 2038 } 2039 2040 2041 /** 2042 * Adds elements and rules to a pre-existing HTML_QuickForm object. 2043 * 2044 * By default, the form will use the names of the columns as the names 2045 * of the form elements. If you pass $array_name, the column names 2046 * will become keys in an array named for this parameter. 2047 * 2048 * @param object &$form An HTML_QuickForm object. 2049 * 2050 * @param array $columns A sequential array of column names to use in 2051 * the form; if null, uses all columns. 2052 * 2053 * @param string $array_name Name of array of column names 2054 * 2055 * @param clientValidate 2056 * 2057 * @return void 2058 * 2059 * @access public 2060 * 2061 * @see HTML_QuickForm 2062 * 2063 * @see DB_Table_QuickForm 2064 */ 2065 function addFormElements(&$form, $columns = null, $array_name = null, 2066 $clientValidate = null) 2067 { 2068 include_once 'DB/Table/QuickForm.php'; 2069 $coldefs = $this->_getFormColDefs($columns); 2070 DB_Table_QuickForm::addElements($form, $coldefs, $array_name); 2071 DB_Table_QuickForm::addRules($form, $coldefs, $array_name, 2072 $clientValidate); 2073 } 2074 2075 2076 /** 2077 * Adds static form elements like 'header', 'static', 'submit' or 'reset' 2078 * to a pre-existing HTML_QuickForm object. The form elements needs to be 2079 * defined in a property called $frm. 2080 * 2081 * @param object &$form An HTML_QuickForm object. 2082 * @return void 2083 * @access public 2084 * 2085 * @see HTML_QuickForm 2086 * @see DB_Table_QuickForm 2087 */ 2088 function addStaticFormElements(&$form) 2089 { 2090 include_once 'DB/Table/QuickForm.php'; 2091 DB_Table_QuickForm::addStaticElements($form, $this->frm); 2092 } 2093 2094 2095 /** 2096 * 2097 * Creates and returns an array of QuickForm elements based on an 2098 * array of DB_Table column names. 2099 * 2100 * @param array $columns A sequential array of column names to use in 2101 * the form; if null, uses all columns. 2102 * 2103 * @param string $array_name By default, the form will use the names 2104 * of the columns as the names of the form elements. If you pass 2105 * $array_name, the column names will become keys in an array named 2106 * for this parameter. 2107 * 2108 * @return array An array of HTML_QuickForm_Element objects. 2109 * 2110 * @access public 2111 * 2112 * @see HTML_QuickForm 2113 * @see DB_Table_QuickForm 2114 */ 2115 function &getFormGroup($columns = null, $array_name = null) 2116 { 2117 include_once 'DB/Table/QuickForm.php'; 2118 $coldefs = $this->_getFormColDefs($columns); 2119 $group =& DB_Table_QuickForm::getGroup($coldefs, $array_name); 2120 return $group; 2121 } 2122 2123 2124 /** 2125 * Creates and returns a single QuickForm element based on a DB_Table 2126 * column name. 2127 * 2128 * @param string $column A DB_Table column name. 2129 * @param string $elemname The name to use for the generated QuickForm 2130 * element. 2131 * 2132 * @return object HTML_QuickForm_Element 2133 * 2134 * @access public 2135 * 2136 * @see HTML_QuickForm 2137 * @see DB_Table_QuickForm 2138 */ 2139 2140 function &getFormElement($column, $elemname) 2141 { 2142 include_once 'DB/Table/QuickForm.php'; 2143 $coldef = $this->_getFormColDefs($column); 2144 DB_Table_QuickForm::fixColDef($coldef[$column], $elemname); 2145 $element =& DB_Table_QuickForm::getElement($coldef[$column], 2146 $elemname); 2147 return $element; 2148 } 2149 2150 /** 2151 * Creates and returns an array of QuickForm elements based on a DB_Table 2152 * column name. 2153 * 2154 * @author Ian Eure <ieure@php.net> 2155 * 2156 * @param array $cols Array of DB_Table column names 2157 * @param string $array_name The name to use for the generated QuickForm 2158 * elements. 2159 * @return object HTML_QuickForm_Element 2160 * 2161 * @access public 2162 * 2163 * @see HTML_QuickForm 2164 * @see DB_Table_QuickForm 2165 */ 2166 function &getFormElements($cols, $array_name = null) 2167 { 2168 include_once 'DB/Table/QuickForm.php'; 2169 $elements =& DB_Table_QuickForm::getElements($cols, $array_name); 2170 return $elements; 2171 } 2172 2173 2174 /** 2175 * Creates a column definition array suitable for DB_Table_QuickForm. 2176 * 2177 * @param string|array $column_set A string column name, a sequential 2178 * array of columns names, or an associative array where the key is a 2179 * column name and the value is the default value for the generated 2180 * form element. If null, uses all columns for this class. 2181 * 2182 * @return array An array of column defintions suitable for passing 2183 * to DB_Table_QuickForm. 2184 * 2185 * @access public 2186 * 2187 */ 2188 function _getFormColDefs($column_set = null) 2189 { 2190 if (is_null($column_set)) { 2191 // no columns or columns+values; just return the $this->col 2192 // array. 2193 return $this->getColumns($column_set); 2194 } 2195 2196 // check to see if the keys are sequential integers. if so, 2197 // the $column_set is just a list of columns. 2198 settype($column_set, 'array'); 2199 $keys = array_keys($column_set); 2200 $all_integer = true; 2201 foreach ($keys as $val) { 2202 if (! is_integer($val)) { 2203 $all_integer = false; 2204 break; 2205 } 2206 } 2207 2208 if ($all_integer) { 2209 2210 // the column_set is just a list of columns; get back the $this->col 2211 // array elements matching this list. 2212 $coldefs = $this->getColumns($column_set); 2213 2214 } else { 2215 2216 // the columns_set is an associative array where the key is a 2217 // column name and the value is the form element value. 2218 $coldefs = $this->getColumns($keys); 2219 foreach ($coldefs as $key => $val) { 2220 $coldefs[$key]['qf_setvalue'] = $column_set[$key]; 2221 } 2222 2223 } 2224 2225 return $coldefs; 2226 } 2227 2228 /** 2229 * Returns XML string representation of the table 2230 * 2231 * @param string $indent string of whitespace 2232 * @return string XML string 2233 * @access public 2234 */ 2235 function toXML($indent = '') { 2236 require_once 'DB/Table/XML.php'; 2237 $s = array(); 2238 $s[] = DB_Table_XML::openTag('table', $indent); 2239 $s[] = DB_Table_XML::lineElement('name', $this->table, $indent); 2240 $s[] = DB_Table_XML::openTag('declaration', $indent); 2241 // Column declarations 2242 foreach ($this->col as $name => $col) { 2243 $type = (isset($col['type'])) ? $col['type'] : null; 2244 $size = (isset($col['size'])) ? $col['size'] : null; 2245 $scope = (isset($col['scope'])) ? $col['scope'] : null; 2246 $require = (isset($col['require'])) ? $col['require'] : null; 2247 $default = (isset($col['set default'])) ? $col['set default'] : null; 2248 $line = ' ' . $name . ' ' . $type; 2249 $s[] = DB_Table_XML::openTag('field', $indent); 2250 $s[] = DB_Table_XML::lineElement('name', $name, $indent); 2251 $s[] = DB_Table_XML::lineElement('type', $type, $indent); 2252 if ($size) { 2253 $s[] = DB_Table_XML::lineElement('length', $size, $indent); 2254 } 2255 if ($require) { 2256 $require = (int) $require; 2257 $s[] = DB_Table_XML::lineElement('notnull', $require, $indent); 2258 } 2259 if (!($default === null)) { 2260 $s[] = DB_Table_XML::lineElement('set default', $default, $indent); 2261 } 2262 if ($this->auto_inc_col == $name) { 2263 $s[] = DB_Table_XML::lineElement('autoincrement', '1', $indent); 2264 } 2265 $s[] = DB_Table_XML::closeTag('field', $indent); 2266 } 2267 // Index declarations 2268 foreach ($this->idx as $name => $idx) { 2269 $s[] = DB_Table_XML::openTag('index', $indent); 2270 $cols = $idx['cols']; 2271 $type = $idx['type']; 2272 if (is_string($name)) { 2273 $s[] = DB_Table_XML::lineElement('name', $name, $indent); 2274 } 2275 if ($type == 'primary') { 2276 $s[] = DB_Table_XML::lineElement('primary', '1', $indent); 2277 } elseif ($type == 'unique') { 2278 $s[] = DB_Table_XML::lineElement('unique', '1', $indent); 2279 } 2280 if (is_string($cols)) { 2281 $cols = array($cols); 2282 } 2283 foreach ($cols as $col) { 2284 $s[] = DB_Table_XML::lineElement('field', $col, $indent); 2285 } 2286 $s[] = DB_Table_XML::closeTag('index', $indent); 2287 } 2288 // Foreign key references (if $this->_database is not null) 2289 if ($this->_database) { 2290 if (isset($this->_database->_ref[$this->table])) { 2291 $refs = $this->_database->_ref[$this->table]; 2292 foreach ($refs as $rtable => $def) { 2293 $fkey = $def['fkey']; // foreign key of referencing table 2294 $rkey = $def['rkey']; // referenced/primary key 2295 if (is_string($fkey)) { 2296 $fkey = array($fkey); 2297 } 2298 if (is_string($rkey)) { 2299 $rkey = array($rkey); 2300 } 2301 $on_delete = $def['on_delete']; // on-delete action 2302 $on_update = $def['on_update']; // on-update action 2303 $s[] = DB_Table_XML::openTag('foreign', $indent); 2304 foreach ($fkey as $fcol) { 2305 $s[] = DB_Table_XML::lineElement('field', $fcol, $indent); 2306 } 2307 $s[] = DB_Table_XML::openTag('references', $indent); 2308 $s[] = DB_Table_XML::lineElement('table', $rtable, $indent); 2309 if ($rkey) { 2310 foreach ($rkey as $rcol) { 2311 $s[] = DB_Table_XML::lineElement('field', $rcol, 2312 $indent); 2313 } 2314 } 2315 $s[] = DB_Table_XML::closeTag('references', $indent); 2316 if ($on_delete) { 2317 $s[] = DB_Table_XML::lineElement('ondelete', $on_delete, 2318 $indent); 2319 } 2320 if ($on_update) { 2321 $s[] = DB_Table_XML::lineElement('onupdate', $on_update, 2322 $indent); 2323 } 2324 $s[] = DB_Table_XML::closeTag('foreign', $indent); 2325 } 2326 } 2327 } 2328 $s[] = DB_Table_XML::closeTag('declaration', $indent); 2329 $s[] = DB_Table_XML::closeTag('table', $indent); 2330 return implode("\n", $s); 2331 } 2332 2333} 2334?> 2335