home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / DB / Table.php < prev   
Encoding:
PHP Script  |  2008-07-02  |  76.7 KB  |  2,328 lines

  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.89 2008/03/28 17:58:17 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.  */
  59. define('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.  */
  65. define('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.  */
  71. define('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.  */
  77. define('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.  */
  83. define('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.  */
  89. define('DB_TABLE_ERR_INS_COL_REQUIRED', -6);
  90.  
  91. /**
  92.  * Error code when auto-validation fails on data to be inserted.
  93.  */
  94. define('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.  */
  100. define('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.  */
  106. define('DB_TABLE_ERR_UPD_COL_REQUIRED', -9);
  107.  
  108. /**
  109.  * Error code when auto-validation fails on update data.
  110.  */
  111. define('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.  */
  117. define('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.  */
  123. define('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.  */
  129. define('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.  */
  135. define('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.  */
  141. define('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.  */
  147. define('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.  */
  153. define('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.  */
  159. define('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.  */
  165. define('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.  */
  171. define('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.  */
  177. define('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.  */
  183. define('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.  */
  189. define('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.  */
  195. define('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.  */
  201. define('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.  */
  207. define('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.  */
  213. define('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.  */
  219. define('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.  */
  225. define('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.  */
  231. define('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.  */
  237. define('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.  */
  243. define('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.  */
  249. define('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.  */
  255. define('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.  */
  262. define('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.  */
  269. define('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.  */
  275. define('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.  */
  281. define('DB_TABLE_ERR_TABLE_NAME_MISSING', -38);
  282.  
  283. /**
  284.  * The DB_Table_Base parent class
  285.  */
  286. require_once 'DB/Table/Base.php';
  287.  
  288. /**
  289.  * The PEAR class for errors
  290.  */
  291. require_once 'PEAR.php';
  292.  
  293. /**
  294.  * The Date class for recasting date and time values
  295.  */
  296. require_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
  496. if (!isset($GLOBALS['_DB_TABLE']['error'])) {
  497.     $GLOBALS['_DB_TABLE']['error'] = array();
  498. }
  499. foreach ($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. /**
  506.  * DB_Table is a database API and data type SQL abstraction class.
  507.  * 
  508.  * DB_Table provides database API abstraction, data type abstraction,
  509.  * automated SELECT, INSERT, and UPDATE queries, automated table
  510.  * creation, automated validation of inserted/updated column values,
  511.  * and automated creation of QuickForm elemnts based on the column
  512.  * definitions.
  513.  * 
  514.  * @category Database
  515.  * @package  DB_Table
  516.  * @author   Paul M. Jones <pmjones@php.net>
  517.  * @author   David C. Morse <morse@php.net>
  518.  * @author   Mark Wiesemann <wiesemann@php.net>
  519.  * @version  Release: 1.5.5
  520.  * @link     http://pear.php.net/package/DB_Table
  521.  */
  522.  
  523. class DB_Table extends DB_Table_Base 
  524. {
  525.     
  526.     /**
  527.      * The table or view in the database to which this object binds.
  528.      * 
  529.      * @access public
  530.      * @var string
  531.      */
  532.     var $table = null;
  533.     
  534.     /**
  535.      * DB_Table_Database instance that this table belongs to.
  536.      * 
  537.      * @access private
  538.      * @var object
  539.      */
  540.     var $_database = null;
  541.  
  542.  
  543.     /**
  544.      * Associative array of column definitions.
  545.      * 
  546.      * @access public
  547.      * @var array
  548.      */
  549.     var $col = array();
  550.     
  551.     
  552.     /**
  553.      * Associative array of index definitions.
  554.      * 
  555.      * @access public
  556.      * @var array
  557.      */
  558.     var $idx = array();
  559.     
  560.     /**
  561.      * Name of an auto-increment column, if any. Null otherwise.
  562.      *
  563.      * A table can contain at most one auto-increment column. 
  564.      * Auto-incrementing is implemented in the insert() method,
  565.      * using a sequence accessed by the nextID() method.
  566.      *
  567.      * @access public
  568.      * @var string
  569.      */
  570.     var $auto_inc_col = null;
  571.  
  572.  
  573.     /**
  574.      * Boolean flag to turn on (true) or off (false) auto-incrementing.
  575.      * 
  576.      * Auto-increment column $auto_inc_col upon insertion only if $_auto_inc is
  577.      * true and the value of that column is null in the data to be inserted.
  578.      *
  579.      * @var bool
  580.      * @access private
  581.      */
  582.     var $_auto_inc = true;
  583.  
  584.  
  585.     /**
  586.      * Whether or not to automatically validate data at insert-time.
  587.      * 
  588.      * @var bool
  589.      * @access private
  590.      */
  591.     var $_valid_insert = true;
  592.     
  593.     /**
  594.      * Whether or not to automatically validate data at update-time.
  595.      * 
  596.      * @var bool
  597.      * @access private
  598.      */
  599.     var $_valid_update = true;
  600.     
  601.  
  602.     /**
  603.      * Whether or not to automatically recast data at insert- and update-time.
  604.      * 
  605.      * @var    bool
  606.      * @access private
  607.      */
  608.     var $_auto_recast = true;
  609.     
  610.     
  611.     /**
  612.      * Constructor.
  613.      *
  614.      * The constructor returns a DB_Table object that wraps an
  615.      * instance $db DB or MDB2, and that binds to a specific database
  616.      * table named $table. It can optionally create the database table
  617.      * or verify that its schema matches that declared in the $col and
  618.      * $idx parameters, depending on the value of the $create parameter.
  619.      *
  620.      * If there is an error on instantiation, $this->error will be 
  621.      * populated with the PEAR_Error.
  622.      * 
  623.      * @param object &$db A PEAR DB/MDB2 object.
  624.      * 
  625.      * @param string $table The table name to connect to in the database.
  626.      * 
  627.      * @param mixed $create The automatic table creation mode to pursue:
  628.      * - boolean false to not attempt creation
  629.      * - 'safe' to create the table only if it does not exist
  630.      * - 'drop' to drop any existing table with the same name and re-create it
  631.      * - 'verify' to check whether the table exists, whether all the columns
  632.      *   exist, whether the columns have the right type, and whether the indexes
  633.      *   exist and have the right type
  634.      * - 'alter' does the same as 'safe' if the table does not exist; if it
  635.      *   exists, a verification for columns existence, the column types, the
  636.      *   indexes existence, and the indexes types will be performed and the
  637.      *   table schema will be modified if needed
  638.      * 
  639.      * @return object DB_Table
  640.      * @access public
  641.      */
  642.     function DB_Table(&$db, $table = null, $create = false)
  643.     {
  644.         // Identify the class for error handling by parent class
  645.         $this->_primary_subclass = 'DB_TABLE';
  646.  
  647.         // is the first argument a DB/MDB2 object?
  648.         $this->backend = null;
  649.         if (is_subclass_of($db, 'db_common')) {
  650.             $this->backend = 'db';
  651.         } elseif (is_subclass_of($db, 'mdb2_driver_common')) {
  652.             $this->backend = 'mdb2';
  653.         }
  654.  
  655.         if (is_null($this->backend)) {
  656.             $this->error =& DB_Table::throwError(DB_TABLE_ERR_NOT_DB_OBJECT);
  657.             return;
  658.         }
  659.         
  660.         // set the class properties
  661.         $this->db =& $db;
  662.         if (is_null($table)) {
  663.             // $table parameter not given => check $table class property
  664.             if (is_null($this->table)) {
  665.                 $this->error =& DB_Table::throwError(DB_TABLE_ERR_TABLE_NAME_MISSING);
  666.                 return;
  667.             }
  668.         } else {
  669.             $this->table = $table;
  670.         }
  671.         
  672.         // is the RDBMS supported?
  673.         $phptype = $db->phptype;
  674.         $dbsyntax = $db->dbsyntax;
  675.         if (! DB_Table::supported($phptype, $dbsyntax)) {
  676.             $this->error =& DB_Table::throwError(
  677.                 DB_TABLE_ERR_PHPTYPE,
  678.                 "({$db->phptype})"
  679.             );
  680.             return;
  681.         }
  682.  
  683.         // load MDB2_Extended module
  684.         if ($this->backend == 'mdb2') {
  685.             $this->db->loadModule('Extended', null, false);
  686.         }
  687.  
  688.         // should we attempt table creation?
  689.         if ($create) {
  690.  
  691.             if ($this->backend == 'mdb2') {
  692.                 $this->db->loadModule('Manager');
  693.             }
  694.  
  695.             // check whether the chosen mode is supported
  696.             $mode_supported = DB_Table::modeSupported($create, $phptype);
  697.             if (PEAR::isError($mode_supported)) {
  698.                 $this->error =& $mode_supported;
  699.                 return;
  700.             }
  701.             if (!$mode_supported) {
  702.                 $this->error =& $this->throwError(
  703.                     DB_TABLE_ERR_CREATE_PHPTYPE,
  704.                     "('$create', '$phptype')"
  705.                 );
  706.                 return;
  707.             }
  708.  
  709.             include_once 'DB/Table/Manager.php';
  710.  
  711.             switch ($create) {
  712.  
  713.                 case 'alter':
  714.                     $result = $this->alter();
  715.                     break;
  716.  
  717.                 case 'drop':
  718.                 case 'safe':
  719.                     $result = $this->create($create);
  720.                     break;
  721.  
  722.                 case 'verify':
  723.                     $result = $this->verify();
  724.                     break;
  725.             }
  726.             
  727.             if (PEAR::isError($result)) {
  728.                 // problem creating/altering/verifing the table
  729.                 $this->error =& $result;
  730.                 return;
  731.             }
  732.         }
  733.     }
  734.     
  735.     
  736.     /**
  737.      * Is a particular RDBMS supported by DB_Table?
  738.      * 
  739.      * @static
  740.      * @param string $phptype The RDBMS type for PHP.
  741.      * @param string $dbsyntax The chosen database syntax.
  742.      * @return bool  True if supported, false if not.
  743.      * @access public
  744.      */
  745.     
  746.     function supported($phptype, $dbsyntax = '')
  747.     {
  748.         // only Firebird is supported, not its ancestor Interbase
  749.         if ($phptype == 'ibase' && $dbsyntax != 'firebird') {
  750.             return false;
  751.         }
  752.         $supported = array_keys($GLOBALS['_DB_TABLE']['type']);
  753.         return in_array(strtolower($phptype), $supported);
  754.     }
  755.  
  756.  
  757.     /**
  758.      * Is a creation mode supported for a RDBMS by DB_Table?
  759.      * 
  760.      * @param string $mode The chosen creation mode.
  761.      * @param string $phptype The RDBMS type for PHP.
  762.      * @return bool  True if supported, false if not (PEAR_Error on failure)
  763.      *
  764.      * @throws PEAR_Error if
  765.      *     Unknown creation mode is specified (DB_TABLE_ERR_CREATE_FLAG)
  766.      * 
  767.      * @access public
  768.      */
  769.     function modeSupported($mode, $phptype)
  770.     {
  771.         // check phptype for validity
  772.         $supported = array_keys($GLOBALS['_DB_TABLE']['type']);
  773.         if (!in_array(strtolower($phptype), $supported)) {
  774.             return false;
  775.         }
  776.  
  777.         switch ($mode) {
  778.             case 'drop':
  779.             case 'safe':
  780.                 // supported for all RDBMS
  781.                 return true;
  782.  
  783.             case 'alter':
  784.             case 'verify':
  785.                 // not supported for fbsql and mssql (yet)
  786.                 switch ($phptype) {
  787.                     case 'fbsql':
  788.                     case 'mssql':
  789.                         return false;
  790.                     default:
  791.                         return true;
  792.                 }
  793.  
  794.             default:
  795.                 // unknown creation mode
  796.                 return $this->throwError(
  797.                     DB_TABLE_ERR_CREATE_FLAG,
  798.                     "('$mode')"
  799.                 );
  800.         }
  801.     }
  802.  
  803.  
  804.     /**
  805.      * Overwrite one or more error messages, e.g. to internationalize them.
  806.      * 
  807.      * @param mixed $code If string, the error message with code $code will
  808.      * be overwritten by $message. If array, the error messages with code
  809.      * of each array key will be overwritten by the key's value.
  810.      * 
  811.      * @param string $message Only used if $key is not an array.
  812.      * @return void
  813.      * @access public
  814.      */
  815.     function setErrorMessage($code, $message = null) {
  816.         if (is_array($code)) {
  817.             foreach ($code as $single_code => $single_message) {
  818.                 $GLOBALS['_DB_TABLE']['error'][$single_code] = $single_message;
  819.             }
  820.         } else {
  821.             $GLOBALS['_DB_TABLE']['error'][$code] = $message;
  822.         }
  823.     }
  824.  
  825.  
  826.     /**
  827.      * 
  828.      * Returns all or part of the $this->col property array.
  829.      * 
  830.      * @param mixed $col If null, returns the $this->col property array
  831.      * as it is.  If string, returns that column name from the $this->col
  832.      * array. If an array, returns those columns named as the array
  833.      * values from the $this->col array as an array.
  834.      *
  835.      * @return mixed All or part of the $this->col property array, or
  836.      *               boolean false if no matching column names are found.
  837.      * @access public
  838.      */
  839.     function getColumns($col = null)
  840.     {
  841.         // by default, return all column definitions
  842.         if (is_null($col)) {
  843.             return $this->col;
  844.         }
  845.         
  846.         // if the param is a string, only return the column definition
  847.         // named by the that string
  848.         if (is_string($col)) {
  849.             if (isset($this->col[$col])) {
  850.                 return $this->col[$col];
  851.             } else {
  852.                 return false;
  853.             }
  854.         }
  855.         
  856.         // if the param is a sequential array of column names,
  857.         // return only those columns named in that array
  858.         if (is_array($col)) {
  859.             $set = array();
  860.             foreach ($col as $name) {
  861.                 $set[$name] = $this->getColumns($name);
  862.             }
  863.             
  864.             if (count($set) == 0) {
  865.                 return false;
  866.             } else {
  867.                 return $set;
  868.             }
  869.         }
  870.         
  871.         // param was not null, string, or array
  872.         return false;
  873.     }
  874.     
  875.     
  876.     /**
  877.      * Returns all or part of the $this->idx property array.
  878.      * 
  879.      * @param mixed $idx Index name (key in $this->idx), or array of
  880.      *                   index name strings.
  881.      * 
  882.      * @return mixed All or part of the $this->idx property array, 
  883.      *               or boolean false if $idx is not null but invalid
  884.      * 
  885.      * @access public
  886.      */
  887.     function getIndexes($idx = null)
  888.     {
  889.         // by default, return all index definitions
  890.         if (is_null($idx)) {
  891.             return $this->idx;
  892.         }
  893.         
  894.         // if the param is a string, only return the index definition
  895.         // named by the that string
  896.         if (is_string($idx)) {
  897.             if (isset($this->idx[$idx])) {
  898.                 return $this->idx[$idx];
  899.             } else {
  900.                 return false;
  901.             }
  902.         }
  903.         
  904.         // if the param is a sequential array of index names,
  905.         // return only those indexes named in that array
  906.         if (is_array($idx)) {
  907.             $set = array();
  908.             foreach ($idx as $name) {
  909.                 $set[$name] = $this->getIndexes($name);
  910.             }
  911.             
  912.             if (count($set) == 0) {
  913.                 return false;
  914.             } else {
  915.                 return $set;
  916.             }
  917.         }
  918.         
  919.         // param was not null, string, or array
  920.         return false;
  921.     }
  922.     
  923.     
  924.     /**
  925.      * Connect or disconnect a DB_Table_Database instance to this table
  926.      * instance.
  927.      * 
  928.      * Used to re-connect this DB_Table object to a parent DB_Table_Database
  929.      * object during unserialization. Can also disconnect if the $database 
  930.      * parameter is null. Use the DB_Table_Database::addTable method instead 
  931.      * to add a table to a new DB_Table_Database.
  932.      * 
  933.      * @param object &$database DB_Table_Database instance that this table
  934.      *               belongs to (or null to disconnect from instance).
  935.      * 
  936.      * @return void
  937.      * @access public
  938.      */
  939.     function setDatabaseInstance(&$database)
  940.     {
  941.         if (is_a($database, 'DB_Table_Database')) {
  942.             $this->_database =& $database;
  943.         } elseif (is_null($database)) {
  944.             $this->_database = null;
  945.         }
  946.     }
  947.  
  948.     
  949.     /**
  950.      * Inserts a single table row.
  951.      *
  952.      * Inserts data from associative array $data, in which keys are column
  953.      * names and values are column values. All required columns (except an
  954.      * auto-increment column) must be included in the data array. Columns
  955.      * values that are not set or null are inserted as SQL NULL values. 
  956.      *
  957.      * If an auto-increment column is declared (by setting $this->auto_inc_col),
  958.      * and the value of that column in $data is not set or null, then a new
  959.      * sequence value will be generated and inserted.
  960.      *
  961.      * If auto-recasting is enabled (if $this->_auto_recast), the method will
  962.      * try, if necessary to recast $data to proper column types, with recast().
  963.      *
  964.      * If auto-validation is enabled (if $this->_valid_insert), the method
  965.      * will validates column types with validInsert() before insertion.
  966.      *
  967.      * @access public
  968.      * 
  969.      * @param array $data An associative array of key-value pairs where
  970.      * the key is the column name and the value is the column value. 
  971.      * This is the data that will be inserted into the table.  
  972.      * 
  973.      * @return mixed Void on success (PEAR_Error on failure)
  974.      *
  975.      * @throws PEAR_Error if:
  976.      *     - Error in auto_inc_col declaration (DB_TABLE_ERR_AUTO_INC_COL)
  977.      *     - Error returned by DB/MDB2::autoExecute() (Error bubbled up)
  978.      *
  979.      * @see validInsert()
  980.      * @see DB::autoExecute()
  981.      * @see MDB2::autoExecute()
  982.      */
  983.     function insert($data)
  984.     {
  985.         // Auto-increment if enabled and input value is null or not set
  986.         if ($this->_auto_inc 
  987.             && !is_null($this->auto_inc_col) 
  988.             && !isset($data[$this->auto_inc_col]) 
  989.            ) {
  990.             $column = $this->auto_inc_col;
  991.             // check that the auto-increment column exists
  992.             if (!array_key_exists($column, $this->col)) {
  993.                 return $this->throwError(
  994.                         DB_TABLE_ERR_AUTO_INC_COL,
  995.                         ": $column does not exist");
  996.             }
  997.             // check that the column is integer 
  998.             if (!in_array($this->col[$column]['type'],
  999.                            array('integer','smallint','bigint'))) {
  1000.                 return $this->throwError(
  1001.                         DB_TABLE_ERR_AUTO_INC_COL,
  1002.                         ": $column is not an integer");
  1003.             }
  1004.             // check that the column is required
  1005.             // Note: The insert method will replace a null input value 
  1006.             // of $data[$column] with a sequence value. This makes 
  1007.             // the column effectively 'not null'. This column must be
  1008.             // 'required' for consistency, to make this explicit.
  1009.             if (!$this->isRequired($column)) {
  1010.                 return $this->throwError(
  1011.                         DB_TABLE_ERR_AUTO_INC_COL,
  1012.                         ": $column is not required");
  1013.             }
  1014.             // set the value
  1015.             $id = $this->nextID();
  1016.             if (PEAR::isError($id)) {
  1017.                 return $id;
  1018.             }
  1019.             $data[$column] = $id;
  1020.         }
  1021.  
  1022.         // forcibly recast the data elements to their proper types?
  1023.         if ($this->_auto_recast) {
  1024.             $this->recast($data);
  1025.         }
  1026.  
  1027.         // validate the data if auto-validation is turned on
  1028.         if ($this->_valid_insert) {
  1029.             $result = $this->validInsert($data);
  1030.             if (PEAR::isError($result)) {
  1031.                 return $result;
  1032.             }
  1033.         }
  1034.  
  1035.         // Does a parent DB_Table_Database object exist?
  1036.         if ($this->_database) {
  1037.  
  1038.             $_database = $this->_database;
  1039.   
  1040.             // Validate foreign key values (if enabled)
  1041.             if ($_database->_check_fkey) {
  1042.                $result = $_database->validForeignKeys($this->table, $data);
  1043.                if (PEAR::isError($result)) {
  1044.                    return $result;
  1045.                }
  1046.             }
  1047.     
  1048.         }
  1049.        
  1050.         // Do insertion
  1051.         if ($this->backend == 'mdb2') {
  1052.             $result = $this->db->extended->autoExecute($this->table, $data,
  1053.                 MDB2_AUTOQUERY_INSERT);
  1054.         } else {
  1055.             $result = $this->db->autoExecute($this->table, $data,
  1056.                 DB_AUTOQUERY_INSERT);
  1057.         }
  1058.         return $result;
  1059.     }
  1060.     
  1061.     
  1062.     /**
  1063.      * Turns on or off auto-incrementing of $auto_inc_col column (if any)
  1064.      * 
  1065.      * For auto-incrementing to work, an $auto_inc_col column must be declared,
  1066.      * auto-incrementing must be enabled (by this method), and the value of
  1067.      * the $auto_inc_col column must be not set or null in the $data passed to
  1068.      * the insert method. 
  1069.      * 
  1070.      * @param  bool $flag True to turn on auto-increment, false to turn off.
  1071.      * @return void
  1072.      * @access public
  1073.      */
  1074.     function setAutoInc($flag = true)
  1075.     {
  1076.         if ($flag) {
  1077.             $this->_auto_inc = true;
  1078.         } else {
  1079.             $this->_auto_inc = false;
  1080.         }
  1081.     }
  1082.     
  1083.     
  1084.     /**
  1085.      * Turns on (or off) automatic validation of inserted data.
  1086.      * 
  1087.      * Enables (if $flag is true) or disables (if $flag is false) automatic 
  1088.      * validation of data types prior to actual insertion into the database 
  1089.      * by the DB_Table::insert() method.
  1090.      *
  1091.      * @param  bool $flag True to turn on auto-validation, false to turn off.
  1092.      * @return void
  1093.      * @access public
  1094.      */
  1095.     function autoValidInsert($flag = true)
  1096.     {
  1097.         if ($flag) {
  1098.             $this->_valid_insert = true;
  1099.         } else {
  1100.             $this->_valid_insert = false;
  1101.         }
  1102.     }
  1103.     
  1104.     
  1105.     /**
  1106.      * Validates an array for insertion into the table.
  1107.      * 
  1108.      * @param array $data An associative array of key-value pairs where
  1109.      * the key is the column name and the value is the column value.  This
  1110.      * is the data that will be inserted into the table.  Data is checked
  1111.      * against the column data type for validity.
  1112.      * 
  1113.      * @return boolean true on success (PEAR_Error on failure)
  1114.      *
  1115.      * @throws PEAR_Error if:
  1116.      *     - Invalid column name key in $data (DB_TABLE_ERR_INS_COL_NOMAP)
  1117.      *     - Missing required column value    (DB_TABLE_ERR_INS_COL_NOMAP)
  1118.      *     - Column value doesn't match type  (DB_TABLE_ERR_INS_DATA_INVALID)
  1119.      *
  1120.      * @access public
  1121.      * 
  1122.      * @see insert()
  1123.      */
  1124.     function validInsert(&$data)
  1125.     {
  1126.         // loop through the data, and disallow insertion of unmapped
  1127.         // columns
  1128.         foreach ($data as $col => $val) {
  1129.             if (! isset($this->col[$col])) {
  1130.                 return $this->throwError(
  1131.                     DB_TABLE_ERR_INS_COL_NOMAP,
  1132.                     "('$col')"
  1133.                 );
  1134.             }
  1135.         }
  1136.         
  1137.         // loop through each column mapping, and check the data to be
  1138.         // inserted into it against the column data type. we loop through
  1139.         // column mappings instead of the insert data to make sure that
  1140.         // all necessary columns are being inserted.
  1141.         foreach ($this->col as $col => $val) {
  1142.             
  1143.             // is the value allowed to be null?
  1144.             if (isset($val['require']) &&
  1145.                 $val['require'] == true &&
  1146.                 (! isset($data[$col]) || is_null($data[$col]))) {
  1147.                 return $this->throwError(
  1148.                     DB_TABLE_ERR_INS_COL_REQUIRED,
  1149.                     "'$col'"
  1150.                 );
  1151.             }
  1152.             
  1153.             // does the value to be inserted match the column data type?
  1154.             if (isset($data[$col]) &&
  1155.                 ! $this->isValid($data[$col], $col)) {
  1156.                 return $this->throwError(
  1157.                     DB_TABLE_ERR_INS_DATA_INVALID,
  1158.                     "'$col' ('$data[$col]')"
  1159.                 );
  1160.             }
  1161.         }
  1162.         
  1163.         return true;
  1164.     }
  1165.     
  1166.     
  1167.     /**
  1168.      * Update table row or rows that match a custom WHERE clause
  1169.      *
  1170.      * Constructs and submits an SQL UPDATE command to update columns whose
  1171.      * names are keys in the $data array parameter, in all rows that match
  1172.      * the logical condition given by the $where string parameter.
  1173.      * 
  1174.      * If auto-recasting is enabled (if $this->_auto_recast), update() will
  1175.      * try, if necessary, to recast $data to proper column types, with recast().
  1176.      *
  1177.      * If auto-validation is enabled (if $this->_valid_insert), update() 
  1178.      * validates column types with validUpdate() before insertion.
  1179.      *
  1180.      * @param array $data An associative array of key-value pairs where the
  1181.      * key is the column name and the value is the column value. These are
  1182.      * the columns that will be updated with new values.
  1183.      * 
  1184.      * @param string $where An SQL WHERE clause limiting which records are
  1185.      * are to be updated.
  1186.      * 
  1187.      * @return mixed Void on success, a PEAR_Error object on failure.
  1188.      *
  1189.      * @throws PEAR_Error if:
  1190.      *     - Data fails type validation (bubbles error returned by validUpdate)
  1191.      *     - Error thrown by DB/MDB2::autoexecute()
  1192.      *
  1193.      * @access public
  1194.      * 
  1195.      * @see validUpdate()
  1196.      * @see DB::autoExecute()
  1197.      * @see MDB2::autoExecute()
  1198.      */
  1199.     function update($data, $where)
  1200.     {
  1201.         // forcibly recast the data elements to their proper types?
  1202.         if ($this->_auto_recast) {
  1203.             $this->recast($data);
  1204.         }
  1205.         
  1206.         // validate the data if auto-validation is turned on
  1207.         if ($this->_valid_update) {
  1208.             $result = $this->validUpdate($data);
  1209.             if (PEAR::isError($result)) {
  1210.                 return $result;
  1211.             }
  1212.         }
  1213.  
  1214.         // Does a parent DB_Table_Database object exist?
  1215.         if ($this->_database) {
  1216.   
  1217.             $_database =& $this->_database;
  1218.  
  1219.             // Validate foreign key values (if enabled)
  1220.             if ($_database->_check_fkey) {
  1221.                $result = $_database->validForeignKeys($this->table, $data);
  1222.                if (PEAR::isError($result)) {
  1223.                    return $result;
  1224.                } 
  1225.             }
  1226.     
  1227.             // Implement any relevant ON UPDATE actions
  1228.             $result = $_database->onUpdateAction($this, $data, $where);
  1229.             if (PEAR::isError($result)) {
  1230.                 return $result;
  1231.             }
  1232.  
  1233.         }
  1234.        
  1235.         // Submit update command 
  1236.         if ($this->backend == 'mdb2') {
  1237.             $result = $this->db->extended->autoExecute($this->table, $data,
  1238.                 MDB2_AUTOQUERY_UPDATE, $where);
  1239.         } else {
  1240.             $result = $this->db->autoExecute($this->table, $data,
  1241.                 DB_AUTOQUERY_UPDATE, $where);
  1242.         }
  1243.         return $result;
  1244.  
  1245.     }
  1246.     
  1247.     
  1248.     /**
  1249.      * Turns on (or off) automatic validation of updated data.
  1250.      * 
  1251.      * Enables (if $flag is true) or disables (if $flag is false) automatic 
  1252.      * validation of data types prior to updating rows in the database by
  1253.      * the {@link update()} method.
  1254.      *
  1255.      * @param  bool $flag True to turn on auto-validation, false to turn off.
  1256.      * @return void
  1257.      * @access public
  1258.      */
  1259.     function autoValidUpdate($flag = true)
  1260.     {
  1261.         if ($flag) {
  1262.             $this->_valid_update = true;
  1263.         } else {
  1264.             $this->_valid_update = false;
  1265.         }
  1266.     }
  1267.     
  1268.     
  1269.     /**
  1270.      * Validates an array for updating the table.
  1271.      * 
  1272.      * @param array $data An associative array of key-value pairs where
  1273.      * the key is the column name and the value is the column value.  This
  1274.      * is the data that will be inserted into the table.  Data is checked
  1275.      * against the column data type for validity.
  1276.      * 
  1277.      * @return mixed Boolean true on success (PEAR_Error object on failure)
  1278.      *
  1279.      * @throws PEAR_Error if
  1280.      *     - Invalid column name key in $data (DB_TABLE_ERR_UPD_COL_NOMAP)
  1281.      *     - Missing required column value    (DB_TABLE_ERR_UPD_COL_NOMAP)
  1282.      *     - Column value doesn't match type  (DB_TABLE_ERR_UPD_DATA_INVALID)
  1283.      *
  1284.      * @access public
  1285.      * 
  1286.      * @see update()
  1287.      */
  1288.     function validUpdate(&$data)
  1289.     {
  1290.         // loop through each data element, and check the
  1291.         // data to be updated against the column data type.
  1292.         foreach ($data as $col => $val) {
  1293.             
  1294.             // does the column exist?
  1295.             if (! isset($this->col[$col])) {
  1296.                 return $this->throwError(
  1297.                     DB_TABLE_ERR_UPD_COL_NOMAP,
  1298.                     "('$col')"
  1299.                 );
  1300.             }
  1301.             
  1302.             // the column definition
  1303.             $defn = $this->col[$col];
  1304.             
  1305.             // is it allowed to be null?
  1306.             if (isset($defn['require']) &&
  1307.                 $defn['require'] == true &&
  1308.                 isset($data[$col]) &&
  1309.                 is_null($data[$col])) {
  1310.                 return $this->throwError(
  1311.                     DB_TABLE_ERR_UPD_COL_REQUIRED,
  1312.                     $col
  1313.                 );
  1314.             }
  1315.             
  1316.             // does the value to be inserted match the column data type?
  1317.             if (! $this->isValid($data[$col], $col)) {
  1318.                 return $this->throwError(
  1319.                     DB_TABLE_ERR_UPD_DATA_INVALID,
  1320.                     "$col ('$data[$col]')"
  1321.                 );
  1322.             }
  1323.         }
  1324.         
  1325.         return true;
  1326.     }
  1327.     
  1328.     
  1329.     /**
  1330.      * Deletes table rows matching a custom WHERE clause.
  1331.      * 
  1332.      * Constructs and submits and SQL DELETE command with the specified WHERE 
  1333.      * clause. Command is submitted by DB::query() or MDB2::exec().
  1334.      *
  1335.      * If a reference to a DB_Table_Database instance exists, carry out any
  1336.      * ON DELETE actions declared in that instance before actual insertion, 
  1337.      * if emulation of ON DELETE actions is enabled in that instance.
  1338.      *
  1339.      * @param string $where Logical condition in the WHERE clause of the 
  1340.      *                      delete command.
  1341.      *
  1342.      * @return mixed void on success (PEAR_Error on failure)
  1343.      *
  1344.      * @throws PEAR_Error if
  1345.      *     DB::query() or MDB2::exec() returns error (bubbles up)
  1346.      *
  1347.      * @access public
  1348.      * 
  1349.      * @see DB::query()
  1350.      * @see MDB2::exec()
  1351.      */
  1352.     function delete($where)
  1353.     {
  1354.         // Does a parent DB_Table_Database object exist?
  1355.         if ($this->_database) {
  1356.   
  1357.             $_database =& $this->_database;
  1358.  
  1359.             // Implement any relevant ON DELETE actions
  1360.             $result = $_database->onDeleteAction($this, $where);
  1361.             if (PEAR::isError($result)) {
  1362.                 return $result;
  1363.             }
  1364.  
  1365.         }
  1366.        
  1367.         if ($this->backend == 'mdb2') {
  1368.             $result = $this->db->exec("DELETE FROM $this->table WHERE $where");
  1369.         } else {
  1370.             $result = $this->db->query("DELETE FROM $this->table WHERE $where");
  1371.         }
  1372.         return $result;
  1373.     }
  1374.     
  1375.     
  1376.     /**
  1377.      *
  1378.      * Generates and returns a sequence value.
  1379.      *
  1380.      * Generates a sequence value by calling the DB or MDB2::nextID() method. The
  1381.      * sequence name defaults to the table name, or may be specified explicitly.
  1382.      * 
  1383.      * @param  string  $seq_name The sequence name; defaults to table_id.
  1384.      * 
  1385.      * @return integer The next value in the sequence (PEAR_Error on failure)
  1386.      *
  1387.      * @throws PEAR_Error if
  1388.      *     Sequence name too long (>26 char + _seq) (DB_TABLE_ERR_SEQ_STRLEN)
  1389.      *
  1390.      * @access public
  1391.      * 
  1392.      * @see DB::nextID()
  1393.      * @see MDB2::nextID()
  1394.      */
  1395.     function nextID($seq_name = null)
  1396.     {
  1397.         if (is_null($seq_name)) {
  1398.             $seq_name = "{$this->table}";
  1399.         } else {
  1400.             $seq_name = "{$this->table}_{$seq_name}";
  1401.         }
  1402.         
  1403.         // the maximum length is 30, but PEAR DB/MDB2 will add "_seq" to the
  1404.         // name, so the max length here is less 4 chars. we have to
  1405.         // check here because the sequence will be created automatically
  1406.         // by PEAR DB/MDB2, which will not check for length on its own.
  1407.         if (strlen($seq_name) > 26) {
  1408.             return DB_Table::throwError(
  1409.                 DB_TABLE_ERR_SEQ_STRLEN,
  1410.                 " ('$seq_name')"
  1411.             );
  1412.             
  1413.         }
  1414.         return $this->db->nextId($seq_name);
  1415.     }
  1416.     
  1417.     
  1418.     /**
  1419.      * Escapes and enquotes a value for use in an SQL query.
  1420.      * 
  1421.      * Simple wrapper for DB_Common::quoteSmart() or MDB2::quote(), which 
  1422.      * returns the value of one of these functions. Helps makes user input 
  1423.      * safe against SQL injection attack.
  1424.      * 
  1425.      * @param mixed $val The value to be quoted
  1426.      *
  1427.      * @return string The value with quotes escaped, inside single quotes if 
  1428.      *                non-numeric.
  1429.      *
  1430.      * @throws PEAR_Error if
  1431.      *     DB_Common::quoteSmart() or MDB2::quote() returns Error (bubbled up)
  1432.      * 
  1433.      * @access public
  1434.      * 
  1435.      * @see DB_Common::quoteSmart()
  1436.      * @see MDB2::quote()
  1437.      */
  1438.     function quote($val)
  1439.     {
  1440.         if ($this->backend == 'mdb2') {
  1441.             $val = $this->db->quote($val);
  1442.         } else {
  1443.             $val = $this->db->quoteSmart($val);
  1444.         }
  1445.         return $val;
  1446.     }
  1447.     
  1448.     
  1449.     /**
  1450.      * Returns a blank row array based on the column map.
  1451.      * 
  1452.      * The array keys are the column names, and all values are set to null.
  1453.      * 
  1454.      * @return array An associative array where keys are column names and
  1455.      *               all values are null.
  1456.      * @access public
  1457.      */
  1458.     function getBlankRow()
  1459.     {
  1460.         $row = array();
  1461.         
  1462.         foreach ($this->col as $key => $val) {
  1463.             $row[$key] = null;
  1464.         }
  1465.         
  1466.         $this->recast($row);
  1467.         
  1468.         return $row;
  1469.     }
  1470.     
  1471.     
  1472.     /**
  1473.      * Turns on (or off) automatic recasting of insert and update data.
  1474.      * 
  1475.      * Turns on (if $flag is true) or off (if $flag is false) automatic forcible 
  1476.      * recasting of data to the declared data type, if required, prior to inserting 
  1477.      * or updating.  The recasting is done by calling the DB_Table::recast() 
  1478.      * method from within the DB_Table::insert() and DB_Table::update().
  1479.      * 
  1480.      * @param bool $flag True to automatically recast insert and update data,
  1481.      *                   false to not do so.
  1482.      * @return void
  1483.      * @access public
  1484.      */
  1485.     function autoRecast($flag = true)
  1486.     {
  1487.         if ($flag) {
  1488.             $this->_auto_recast = true;
  1489.         } else {
  1490.             $this->_auto_recast = false;
  1491.         }
  1492.     }
  1493.     
  1494.     
  1495.     /**
  1496.      * Forces array elements to the proper types for their columns.
  1497.      * 
  1498.      * This will not valiate the data, and will forcibly change the data
  1499.      * to match the recast-type.
  1500.      * 
  1501.      * The date, time, and timestamp recasting has special logic for
  1502.      * arrays coming from an HTML_QuickForm object so that the arrays
  1503.      * are converted into properly-formatted strings.
  1504.      * 
  1505.      * @todo If a column key holds an array of values (say from a multiple
  1506.      * select) then this method will not work properly; it will recast the
  1507.      * value to the string 'Array'.  Is this bad?
  1508.      * 
  1509.      * @param array   &$data The data array to re-cast.
  1510.      * 
  1511.      * @return void
  1512.      * 
  1513.      * @access public
  1514.      */
  1515.     function recast(&$data)
  1516.     {
  1517.         $keys = array_keys($data);
  1518.         
  1519.         $null_if_blank = array('date', 'time', 'timestamp', 'smallint',
  1520.             'integer', 'bigint', 'decimal', 'single', 'double');
  1521.         
  1522.         foreach ($keys as $key) {
  1523.         
  1524.             if (! isset($this->col[$key])) {
  1525.                 continue;
  1526.             }
  1527.             
  1528.             unset($val);
  1529.             $val =& $data[$key];
  1530.             
  1531.             // convert blanks to null for non-character field types
  1532.             $convert = in_array($this->col[$key]['type'], $null_if_blank);
  1533.             if (is_array($val)) {  // if one of the given array values is
  1534.                                    // empty, null will be the new value if
  1535.                                    // the field is not required
  1536.                 $tmp_val = implode('', $val);
  1537.                 foreach ($val as $array_val) {
  1538.                     if (trim((string) $array_val) == '') {
  1539.                         $tmp_val = '';
  1540.                         break;
  1541.                     }
  1542.                 }
  1543.             } else {
  1544.                 $tmp_val = $val;
  1545.             }
  1546.             if ($convert && trim((string) $tmp_val) == '' && (
  1547.                 !isset($this->col[$key]['require']) ||
  1548.                 $this->col[$key]['require'] === false
  1549.               )
  1550.             ) {
  1551.                 $val = null;
  1552.             }
  1553.             
  1554.             // skip explicit NULL values
  1555.             if (is_null($val)) {
  1556.                 continue;
  1557.             }
  1558.             
  1559.             // otherwise, recast to the column type
  1560.             switch ($this->col[$key]['type']) {
  1561.             
  1562.             case 'boolean':
  1563.                 $val = ($val) ? 1 : 0;
  1564.                 break;
  1565.                 
  1566.             case 'char':
  1567.             case 'varchar':
  1568.             case 'clob':
  1569.                 settype($val, 'string');
  1570.                 break;
  1571.                 
  1572.             case 'date':
  1573.  
  1574.                 // smart handling of non-standard (i.e. Y-m-d) date formats,
  1575.                 // this allows to use two-digit years (y) and short (M) or
  1576.                 // long (F) names of months without having to recast the
  1577.                 // date value yourself
  1578.                 if (is_array($val)) {
  1579.                     if (isset($val['y'])) {
  1580.                         $val['Y'] = $val['y'];
  1581.                     }
  1582.                     if (isset($val['F'])) {
  1583.                         $val['m'] = $val['F'];
  1584.                     }
  1585.                     if (isset($val['M'])) {
  1586.                         $val['m'] = $val['M'];
  1587.                     }
  1588.                 }
  1589.  
  1590.                 if (is_array($val) &&
  1591.                     isset($val['Y']) &&
  1592.                     isset($val['m']) &&
  1593.                     isset($val['d'])) {
  1594.                     
  1595.                     // the date is in HTML_QuickForm format,
  1596.                     // convert into a string
  1597.                     $y = (strlen($val['Y']) < 4)
  1598.                         ? str_pad($val['Y'], 4, '0', STR_PAD_LEFT)
  1599.                         : $val['Y'];
  1600.                     
  1601.                     $m = (strlen($val['m']) < 2)
  1602.                         ? '0'.$val['m'] : $val['m'];
  1603.                         
  1604.                     $d = (strlen($val['d']) < 2)
  1605.                         ? '0'.$val['d'] : $val['d'];
  1606.                         
  1607.                     $val = "$y-$m-$d";
  1608.                     
  1609.                 } else {
  1610.                 
  1611.                     // convert using the Date class
  1612.                     $tmp =& new DB_Table_Date($val);
  1613.                     $val = $tmp->format('%Y-%m-%d');
  1614.                     
  1615.                 }
  1616.                 
  1617.                 break;
  1618.             
  1619.             case 'time':
  1620.             
  1621.                 if (is_array($val) &&
  1622.                     isset($val['H']) &&
  1623.                     isset($val['i']) &&
  1624.                     isset($val['s'])) {
  1625.                     
  1626.                     // the time is in HTML_QuickForm format,
  1627.                     // convert into a string
  1628.                     $h = (strlen($val['H']) < 2)
  1629.                         ? '0' . $val['H'] : $val['H'];
  1630.                     
  1631.                     $i = (strlen($val['i']) < 2)
  1632.                         ? '0' . $val['i'] : $val['i'];
  1633.                         
  1634.                     $s = (strlen($val['s']) < 2)
  1635.                         ? '0' . $val['s'] : $val['s'];
  1636.                         
  1637.                         
  1638.                     $val = "$h:$i:$s";
  1639.                     
  1640.                 } else {
  1641.                     // date does not matter in this case, so
  1642.                     // pre 1970 and post 2040 are not an issue.
  1643.                     $tmp = strtotime(date('Y-m-d') . " $val");
  1644.                     $val = date('H:i:s', $tmp);
  1645.                 }
  1646.                 
  1647.                 break;
  1648.                 
  1649.             case 'timestamp':
  1650.  
  1651.                 // smart handling of non-standard (i.e. Y-m-d) date formats,
  1652.                 // this allows to use two-digit years (y) and short (M) or
  1653.                 // long (F) names of months without having to recast the
  1654.                 // date value yourself
  1655.                 if (is_array($val)) {
  1656.                     if (isset($val['y'])) {
  1657.                         $val['Y'] = $val['y'];
  1658.                     }
  1659.                     if (isset($val['F'])) {
  1660.                         $val['m'] = $val['F'];
  1661.                     }
  1662.                     if (isset($val['M'])) {
  1663.                         $val['m'] = $val['M'];
  1664.                     }
  1665.                 }
  1666.  
  1667.                 if (is_array($val) &&
  1668.                     isset($val['Y']) &&
  1669.                     isset($val['m']) &&
  1670.                     isset($val['d']) &&
  1671.                     isset($val['H']) &&
  1672.                     isset($val['i']) &&
  1673.                     isset($val['s'])) {
  1674.                     
  1675.                     // timestamp is in HTML_QuickForm format,
  1676.                     // convert each element to a string. pad
  1677.                     // with zeroes as needed.
  1678.                 
  1679.                     $y = (strlen($val['Y']) < 4)
  1680.                         ? str_pad($val['Y'], 4, '0', STR_PAD_LEFT)
  1681.                         : $val['Y'];
  1682.                     
  1683.                     $m = (strlen($val['m']) < 2)
  1684.                         ? '0'.$val['m'] : $val['m'];
  1685.                         
  1686.                     $d = (strlen($val['d']) < 2)
  1687.                         ? '0'.$val['d'] : $val['d'];
  1688.                         
  1689.                     $h = (strlen($val['H']) < 2)
  1690.                         ? '0' . $val['H'] : $val['H'];
  1691.                     
  1692.                     $i = (strlen($val['i']) < 2)
  1693.                         ? '0' . $val['i'] : $val['i'];
  1694.                         
  1695.                     $s = (strlen($val['s']) < 2)
  1696.                         ? '0' . $val['s'] : $val['s'];
  1697.                         
  1698.                     $val = "$y-$m-$d $h:$i:$s";
  1699.                     
  1700.                 } else {
  1701.                     // convert using the Date class
  1702.                     $tmp =& new DB_Table_Date($val);
  1703.                     $val = $tmp->format('%Y-%m-%d %H:%M:%S');
  1704.                 }
  1705.                 
  1706.                 break;
  1707.             
  1708.             case 'smallint':
  1709.             case 'integer':
  1710.             case 'bigint':
  1711.                 settype($val, 'integer');
  1712.                 break;
  1713.             
  1714.             case 'decimal':
  1715.             case 'single':
  1716.             case 'double':
  1717.                 settype($val, 'float');
  1718.                 break;
  1719.  
  1720.             }
  1721.         }
  1722.     }
  1723.     
  1724.     
  1725.     /**
  1726.      * Creates the table based on $this->col and $this->idx.
  1727.      * 
  1728.      * @param string $flag The automatic table creation mode to pursue:
  1729.      * - 'safe' to create the table only if it does not exist
  1730.      * - 'drop' to drop any existing table with the same name and re-create it
  1731.      * 
  1732.      * @return mixed Boolean true if the table was successfully created,
  1733.      *               false if there was no need to create the table, or
  1734.      *               a PEAR_Error if the attempted creation failed.
  1735.      *
  1736.      * @throws PEAR_Error if
  1737.      *     - DB_Table_Manager::tableExists() returns Error (bubbles up)
  1738.      *     - DB_Table_Manager::create() returns Error (bubbles up)
  1739.      * 
  1740.      * @access public
  1741.      * 
  1742.      * @see DB_Table_Manager::tableExists()
  1743.      * @see DB_Table_Manager::create()
  1744.      */
  1745.     function create($flag)
  1746.     {
  1747.         include_once 'DB/Table/Manager.php';
  1748.  
  1749.         // are we OK to create the table?
  1750.         $ok = false;
  1751.         
  1752.         // check the create-flag
  1753.         switch ($flag) {
  1754.  
  1755.             case 'drop':
  1756.                 // drop only if table exists
  1757.                 $table_exists = DB_Table_Manager::tableExists($this->db,
  1758.                                                               $this->table);
  1759.                 if (PEAR::isError($table_exists)) {
  1760.                     return $table_exists;
  1761.                 }
  1762.                 if ($table_exists) {
  1763.                     // forcibly drop an existing table
  1764.                     if ($this->backend == 'mdb2') {
  1765.                         $this->db->manager->dropTable($this->table);
  1766.                     } else {
  1767.                         $this->db->query("DROP TABLE {$this->table}");
  1768.                     }
  1769.                 }
  1770.                 $ok = true;
  1771.                 break;
  1772.  
  1773.             case 'safe':
  1774.                 // create only if table does not exist
  1775.                 $table_exists = DB_Table_Manager::tableExists($this->db,
  1776.                                                               $this->table);
  1777.                 if (PEAR::isError($table_exists)) {
  1778.                     return $table_exists;
  1779.                 }
  1780.                 // ok to create only if table does not exist
  1781.                 $ok = !$table_exists;
  1782.                 break;
  1783.  
  1784.         }
  1785.  
  1786.         // are we going to create the table?
  1787.         if (! $ok) {
  1788.             return false;
  1789.         }
  1790.  
  1791.         return DB_Table_Manager::create(
  1792.             $this->db, $this->table, $this->col, $this->idx
  1793.         );
  1794.     }
  1795.     
  1796.     
  1797.     /**
  1798.      * Alters the table to match schema declared in $this->col and $this->idx.
  1799.      *
  1800.      * If the table does not exist, create it instead.
  1801.      * 
  1802.      * @return boolean true if altering is successful (PEAR_Error on failure)
  1803.      *
  1804.      * @throws PEAR_Error if
  1805.      *     - DB_Table_Manager::tableExists() returns Error (bubbles up)
  1806.      *     - DB_Table_Manager::create() returns Error (bubbles up)
  1807.      *     - DB_Table_Manager::alter() returns Error (bubbles up)
  1808.      *
  1809.      * @access public
  1810.      *
  1811.      * @see DB_Table_Manager::tableExists()
  1812.      * @see DB_Table_Manager::create()
  1813.      * @see DB_Table_Manager::alter()
  1814.      */
  1815.     function alter()
  1816.     {
  1817.         $create = false;
  1818.         
  1819.         // alter the table columns and indexes if the table exists
  1820.         $table_exists = DB_Table_Manager::tableExists($this->db,
  1821.                                                       $this->table);
  1822.         if (PEAR::isError($table_exists)) {
  1823.             return $table_exists;
  1824.         }
  1825.         if (!$table_exists) {
  1826.             // table does not exist => just create the table, there is
  1827.             // nothing that could be altered
  1828.             $create = true;
  1829.         }
  1830.  
  1831.         if ($create) {
  1832.             return DB_Table_Manager::create(
  1833.                 $this->db, $this->table, $this->col, $this->idx
  1834.             );
  1835.         }
  1836.  
  1837.         return DB_Table_Manager::alter(
  1838.             $this->db, $this->table, $this->col, $this->idx
  1839.         );
  1840.     }
  1841.     
  1842.     
  1843.     /**
  1844.      * Verifies the table based on $this->col and $this->idx.
  1845.      * 
  1846.      * @return boolean true if verification succees (PEAR_Error on failure).
  1847.      *
  1848.      * @throws PEAR_Error if
  1849.      *     DB_Table_Manager::verify() returns Error (bubbles up)
  1850.      *
  1851.      * @access public
  1852.      * 
  1853.      * @see DB_Table_Manager::verify()
  1854.      */
  1855.     function verify()
  1856.     {
  1857.         return DB_Table_Manager::verify(
  1858.             $this->db, $this->table, $this->col, $this->idx
  1859.         );
  1860.     }
  1861.     
  1862.     
  1863.     /**
  1864.      * Checks if a value validates against the DB_Table data type for a
  1865.      * given column. This only checks that it matches the data type; it
  1866.      * does not do extended validation.
  1867.      * 
  1868.      * @param array $val A value to check against the column's DB_Table
  1869.      * data type.
  1870.      * 
  1871.      * @param array $col A column name from $this->col.
  1872.      * 
  1873.      * @return boolean True if $val validates against data type, false if not
  1874.      *
  1875.      * @throws PEAR_Error if
  1876.      *     Invalid column type in $this->col (DB_TABLE_ERR_VALIDATE_TYPE)
  1877.      *
  1878.      * @access public
  1879.      * 
  1880.      * @see DB_Table_Valid
  1881.      */
  1882.     function isValid($val, $col)
  1883.     {
  1884.         // is the value null?
  1885.         if (is_null($val)) {
  1886.             // is the column required?
  1887.             if ($this->isRequired($col)) {
  1888.                 // yes, so not valid
  1889.                 return false;
  1890.             } else {
  1891.                 // not required, so it's valid
  1892.                 return true;
  1893.             }
  1894.         }
  1895.         
  1896.         // make sure we have the validation class
  1897.         include_once 'DB/Table/Valid.php';
  1898.         
  1899.         // validate values per the column type.  we use sqlite
  1900.         // as the single authentic list of allowed column types,
  1901.         // regardless of the actual rdbms being used.
  1902.         $map = array_keys($GLOBALS['_DB_TABLE']['type']['sqlite']);
  1903.         
  1904.         // is the column type on the map?
  1905.         if (! in_array($this->col[$col]['type'], $map)) {
  1906.             return $this->throwError(
  1907.                 DB_TABLE_ERR_VALIDATE_TYPE,
  1908.                 "'$col' ('{$this->col[$col]['type']}')"
  1909.             );
  1910.         }
  1911.         
  1912.         // validate for the type
  1913.         switch ($this->col[$col]['type']) {
  1914.         
  1915.         case 'char':
  1916.         case 'varchar':
  1917.             $result = DB_Table_Valid::isChar(
  1918.                 $val,
  1919.                 $this->col[$col]['size']
  1920.             );
  1921.             break;
  1922.         
  1923.         case 'decimal':
  1924.             $result = DB_Table_Valid::isDecimal(
  1925.                 $val,
  1926.                 $this->col[$col]['size'],
  1927.                 $this->col[$col]['scope']
  1928.             );
  1929.             break;
  1930.             
  1931.         default:
  1932.             $result = call_user_func(
  1933.                 array(
  1934.                     'DB_Table_Valid',
  1935.                     'is' . ucwords($this->col[$col]['type'])
  1936.                 ),
  1937.                 $val
  1938.             );
  1939.             break;
  1940.  
  1941.         }
  1942.         
  1943.         // have we passed the check so far, and should we
  1944.         // also check for allowed values?
  1945.         if ($result && isset($this->col[$col]['qf_vals'])) {
  1946.             $keys = array_keys($this->col[$col]['qf_vals']);
  1947.             
  1948.             $result = in_array(
  1949.                 $val,
  1950.                 array_keys($this->col[$col]['qf_vals'])
  1951.             );
  1952.         }
  1953.         
  1954.         return $result;
  1955.     }
  1956.     
  1957.     
  1958.     /**
  1959.      * Is a specific column required to be set and non-null?
  1960.      * 
  1961.      * @param mixed $column The column to check against.
  1962.      * @return boolean      True if required, false if not.
  1963.      * @access public
  1964.      */
  1965.     function isRequired($column)
  1966.     {
  1967.         if (isset($this->col[$column]['require']) &&
  1968.             $this->col[$column]['require'] == true) {
  1969.             return true;
  1970.         } else {
  1971.             return false;
  1972.         }
  1973.     }
  1974.     
  1975.     
  1976.     /**
  1977.      * 
  1978.      * Creates and returns a QuickForm object based on table columns.
  1979.      *
  1980.      * @param array $columns A sequential array of column names to use in
  1981.      * the form; if null, uses all columns.
  1982.      *
  1983.      * @param string $array_name By default, the form will use the names
  1984.      * of the columns as the names of the form elements.  If you pass
  1985.      * $array_name, the column names will become keys in an array named
  1986.      * for this parameter.
  1987.      * 
  1988.      * @param array $args An associative array of optional arguments to
  1989.      * pass to the QuickForm object.  The keys are...
  1990.      *
  1991.      * 'formName' : String, name of the form; defaults to the name of this
  1992.      * table.
  1993.      * 
  1994.      * 'method' : String, form method; defaults to 'post'.
  1995.      * 
  1996.      * 'action' : String, form action; defaults to
  1997.      * $_SERVER['REQUEST_URI'].
  1998.      * 
  1999.      * 'target' : String, form target target; defaults to '_self'
  2000.      * 
  2001.      * 'attributes' : Associative array, extra attributes for <form>
  2002.      * tag; the key is the attribute name and the value is attribute
  2003.      * value.
  2004.      * 
  2005.      * 'trackSubmit' : Boolean, whether to track if the form was
  2006.      * submitted by adding a special hidden field
  2007.      * 
  2008.      * @param string $clientValidate By default, validation will match
  2009.      * the 'qf_client' value from the column definition.  However, if
  2010.      * you set $clientValidate to true or false, this will override the
  2011.      * value from the column definition.
  2012.      *
  2013.      * @param array $formFilters An array with filter function names or
  2014.      * callbacks that will be applied to all form elements.
  2015.      *
  2016.      * @return object HTML_QuickForm
  2017.      * 
  2018.      * @access public
  2019.      *
  2020.      * @see HTML_QuickForm
  2021.      * @see DB_Table_QuickForm
  2022.      */
  2023.     function &getForm($columns = null, $array_name = null, $args = array(),
  2024.         $clientValidate = null, $formFilters = null)
  2025.     {
  2026.         include_once 'DB/Table/QuickForm.php';
  2027.         $coldefs = $this->_getFormColDefs($columns);
  2028.         $form =& DB_Table_QuickForm::getForm($coldefs, $array_name, $args,
  2029.             $clientValidate, $formFilters);
  2030.         return $form;
  2031.     }
  2032.     
  2033.     
  2034.     /**
  2035.      * Adds elements and rules to a pre-existing HTML_QuickForm object.
  2036.      * 
  2037.      * By default, the form will use the names of the columns as the names 
  2038.      * of the form elements.  If you pass $array_name, the column names 
  2039.      * will become keys in an array named for this parameter.
  2040.      *
  2041.      * @param object &$form      An HTML_QuickForm object.
  2042.      * 
  2043.      * @param array $columns     A sequential array of column names to use in
  2044.      *                           the form; if null, uses all columns.
  2045.      *
  2046.      * @param string $array_name Name of array of column names
  2047.      *
  2048.      * @param clientValidate
  2049.      * 
  2050.      * @return void
  2051.      * 
  2052.      * @access public
  2053.      * 
  2054.      * @see HTML_QuickForm
  2055.      * 
  2056.      * @see DB_Table_QuickForm
  2057.      */
  2058.     function addFormElements(&$form, $columns = null, $array_name = null,
  2059.         $clientValidate = null)
  2060.     {
  2061.         include_once 'DB/Table/QuickForm.php';
  2062.         $coldefs = $this->_getFormColDefs($columns);
  2063.         DB_Table_QuickForm::addElements($form, $coldefs, $array_name);
  2064.         DB_Table_QuickForm::addRules($form, $coldefs, $array_name, 
  2065.            $clientValidate);
  2066.     }
  2067.  
  2068.  
  2069.     /**
  2070.      * Adds static form elements like 'header', 'static', 'submit' or 'reset' 
  2071.      * to a pre-existing HTML_QuickForm object. The form elements needs to be
  2072.      * defined in a property called $frm.
  2073.      * 
  2074.      * @param object &$form An HTML_QuickForm object.
  2075.      * @return void
  2076.      * @access public
  2077.      * 
  2078.      * @see HTML_QuickForm
  2079.      * @see DB_Table_QuickForm
  2080.      */
  2081.     function addStaticFormElements(&$form)
  2082.     {
  2083.         include_once 'DB/Table/QuickForm.php';
  2084.         DB_Table_QuickForm::addStaticElements($form, $this->frm);
  2085.     }
  2086.  
  2087.  
  2088.     /**
  2089.      * 
  2090.      * Creates and returns an array of QuickForm elements based on an
  2091.      * array of DB_Table column names.
  2092.      * 
  2093.      * @param array $columns A sequential array of column names to use in
  2094.      * the form; if null, uses all columns.
  2095.      * 
  2096.      * @param string $array_name By default, the form will use the names
  2097.      * of the columns as the names of the form elements.  If you pass
  2098.      * $array_name, the column names will become keys in an array named
  2099.      * for this parameter.
  2100.      * 
  2101.      * @return array An array of HTML_QuickForm_Element objects.
  2102.      * 
  2103.      * @access public
  2104.      * 
  2105.      * @see HTML_QuickForm
  2106.      * @see DB_Table_QuickForm
  2107.      */
  2108.     function &getFormGroup($columns = null, $array_name = null)
  2109.     {
  2110.         include_once 'DB/Table/QuickForm.php';
  2111.         $coldefs = $this->_getFormColDefs($columns);
  2112.         $group =& DB_Table_QuickForm::getGroup($coldefs, $array_name);
  2113.         return $group;
  2114.     }
  2115.     
  2116.     
  2117.     /**
  2118.      * Creates and returns a single QuickForm element based on a DB_Table
  2119.      * column name.
  2120.      * 
  2121.      * @param string $column   A DB_Table column name.
  2122.      * @param string $elemname The name to use for the generated QuickForm
  2123.      *                         element.
  2124.      * 
  2125.      * @return object HTML_QuickForm_Element
  2126.      * 
  2127.      * @access public
  2128.      * 
  2129.      * @see HTML_QuickForm
  2130.      * @see DB_Table_QuickForm
  2131.      */
  2132.     
  2133.     function &getFormElement($column, $elemname)
  2134.     {
  2135.         include_once 'DB/Table/QuickForm.php';
  2136.         $coldef = $this->_getFormColDefs($column);
  2137.         DB_Table_QuickForm::fixColDef($coldef[$column], $elemname);
  2138.         $element =& DB_Table_QuickForm::getElement($coldef[$column],
  2139.             $elemname);
  2140.         return $element;
  2141.     }
  2142.  
  2143.     /**
  2144.      * Creates and returns an array of QuickForm elements based on a DB_Table
  2145.      * column name.
  2146.      * 
  2147.      * @author Ian Eure <ieure@php.net>
  2148.      * 
  2149.      * @param array $cols        Array of DB_Table column names
  2150.      * @param string $array_name The name to use for the generated QuickForm
  2151.      *                           elements.
  2152.      * @return object HTML_QuickForm_Element
  2153.      * 
  2154.      * @access public
  2155.      * 
  2156.      * @see HTML_QuickForm
  2157.      * @see DB_Table_QuickForm
  2158.      */
  2159.     function &getFormElements($cols, $array_name = null)
  2160.     {
  2161.         include_once 'DB/Table/QuickForm.php';
  2162.         $elements =& DB_Table_QuickForm::getElements($cols, $array_name);
  2163.         return $elements;
  2164.     }
  2165.     
  2166.     
  2167.     /**
  2168.      * Creates a column definition array suitable for DB_Table_QuickForm.
  2169.      * 
  2170.      * @param string|array $column_set A string column name, a sequential
  2171.      * array of columns names, or an associative array where the key is a
  2172.      * column name and the value is the default value for the generated
  2173.      * form element.  If null, uses all columns for this class.
  2174.      * 
  2175.      * @return array An array of column defintions suitable for passing
  2176.      *               to DB_Table_QuickForm.
  2177.      *
  2178.      * @access public
  2179.      * 
  2180.      */
  2181.     function _getFormColDefs($column_set = null)
  2182.     {
  2183.         if (is_null($column_set)) {
  2184.             // no columns or columns+values; just return the $this->col
  2185.             // array.
  2186.             return $this->getColumns($column_set);
  2187.         }
  2188.         
  2189.         // check to see if the keys are sequential integers.  if so,
  2190.         // the $column_set is just a list of columns.
  2191.         settype($column_set, 'array');
  2192.         $keys = array_keys($column_set);
  2193.         $all_integer = true;
  2194.         foreach ($keys as $val) {
  2195.             if (! is_integer($val)) {
  2196.                 $all_integer = false;
  2197.                 break;
  2198.             }
  2199.         }
  2200.         
  2201.         if ($all_integer) {
  2202.         
  2203.             // the column_set is just a list of columns; get back the $this->col
  2204.             // array elements matching this list.
  2205.             $coldefs = $this->getColumns($column_set);
  2206.             
  2207.         } else {
  2208.             
  2209.             // the columns_set is an associative array where the key is a
  2210.             // column name and the value is the form element value.
  2211.             $coldefs = $this->getColumns($keys);
  2212.             foreach ($coldefs as $key => $val) {
  2213.                 $coldefs[$key]['qf_setvalue'] = $column_set[$key];
  2214.             }
  2215.             
  2216.         }
  2217.         
  2218.         return $coldefs;
  2219.     }
  2220.  
  2221.     /**
  2222.      * Returns XML string representation of the table
  2223.      *
  2224.      * @param  string $indent string of whitespace
  2225.      * @return string XML string
  2226.      * @access public
  2227.      */
  2228.     function toXML($indent = '') {
  2229.         require_once 'DB/Table/XML.php';
  2230.         $s = array();
  2231.         $s[] = DB_Table_XML::openTag('table', $indent);
  2232.         $s[] = DB_Table_XML::lineElement('name', $this->table, $indent);
  2233.         $s[] = DB_Table_XML::openTag('declaration', $indent);
  2234.         // Column declarations
  2235.         foreach ($this->col as $name => $col) {
  2236.             $type     = (isset($col['type'])) ? $col['type'] : null;
  2237.             $size     = (isset($col['size'])) ? $col['size'] : null;
  2238.             $scope    = (isset($col['scope'])) ? $col['scope'] : null;
  2239.             $require  = (isset($col['require'])) ? $col['require'] : null;
  2240.             $default  = (isset($col['set default'])) ? $col['set default'] : null;
  2241.             $line = '   ' . $name . '  ' . $type;
  2242.             $s[] = DB_Table_XML::openTag('field', $indent);
  2243.             $s[] = DB_Table_XML::lineElement('name', $name, $indent);
  2244.             $s[] = DB_Table_XML::lineElement('type', $type, $indent);
  2245.             if ($size) {
  2246.                 $s[] = DB_Table_XML::lineElement('length', $size, $indent);
  2247.             }
  2248.             if ($require) {
  2249.                 $require = (int) $require;
  2250.                 $s[] = DB_Table_XML::lineElement('notnull', $require, $indent);
  2251.             }
  2252.             if (!($default === null)) {
  2253.                $s[] = DB_Table_XML::lineElement('set default', $default, $indent);
  2254.             }
  2255.             if ($this->auto_inc_col == $name) {
  2256.                $s[] = DB_Table_XML::lineElement('autoincrement', '1', $indent);
  2257.             }
  2258.             $s[] = DB_Table_XML::closeTag('field', $indent);
  2259.         }
  2260.         // Index declarations
  2261.         foreach ($this->idx as $name => $idx) {
  2262.             $s[] = DB_Table_XML::openTag('index', $indent);
  2263.             $cols = $idx['cols'];
  2264.             $type = $idx['type'];
  2265.             if (is_string($name)) {
  2266.                 $s[] = DB_Table_XML::lineElement('name', $name, $indent);
  2267.             }
  2268.             if ($type == 'primary') {
  2269.                 $s[] = DB_Table_XML::lineElement('primary', '1', $indent);
  2270.             } elseif ($type == 'unique') {
  2271.                 $s[] = DB_Table_XML::lineElement('unique', '1', $indent);
  2272.             }
  2273.             if (is_string($cols)) {
  2274.                 $cols = array($cols);
  2275.             }
  2276.             foreach ($cols as $col) {
  2277.                 $s[] = DB_Table_XML::lineElement('field', $col, $indent);
  2278.             }
  2279.             $s[] = DB_Table_XML::closeTag('index', $indent);
  2280.         }
  2281.         // Foreign key references (if $this->_database is not null)
  2282.         if ($this->_database) {
  2283.             if (isset($this->_database->_ref[$this->table])) {
  2284.                 $refs = $this->_database->_ref[$this->table];
  2285.                 foreach ($refs as $rtable => $def) {
  2286.                     $fkey = $def['fkey']; // foreign key of referencing table
  2287.                     $rkey = $def['rkey']; // referenced/primary key
  2288.                     if (is_string($fkey)) {
  2289.                         $fkey = array($fkey);
  2290.                     }
  2291.                     if (is_string($rkey)) {
  2292.                         $rkey = array($rkey);
  2293.                     }
  2294.                     $on_delete = $def['on_delete']; // on-delete action
  2295.                     $on_update = $def['on_update']; // on-update action
  2296.                     $s[] = DB_Table_XML::openTag('foreign', $indent);
  2297.                     foreach ($fkey as $fcol) {
  2298.                         $s[] = DB_Table_XML::lineElement('field', $fcol, $indent);
  2299.                     }
  2300.                     $s[] = DB_Table_XML::openTag('references', $indent);
  2301.                     $s[] = DB_Table_XML::lineElement('table', $rtable, $indent);
  2302.                     if ($rkey) {
  2303.                         foreach ($rkey as $rcol) {
  2304.                             $s[] = DB_Table_XML::lineElement('field', $rcol,
  2305.                                                              $indent);
  2306.                         }
  2307.                     }
  2308.                     $s[] = DB_Table_XML::closeTag('references', $indent);
  2309.                     if ($on_delete) {
  2310.                         $s[] = DB_Table_XML::lineElement('ondelete', $on_delete,
  2311.                                                          $indent);
  2312.                     }
  2313.                     if ($on_update) {
  2314.                         $s[] = DB_Table_XML::lineElement('onupdate', $on_update,
  2315.                                                          $indent);
  2316.                     }
  2317.                     $s[] = DB_Table_XML::closeTag('foreign', $indent);
  2318.                 }
  2319.             }
  2320.         }
  2321.         $s[] = DB_Table_XML::closeTag('declaration', $indent);
  2322.         $s[] = DB_Table_XML::closeTag('table', $indent);
  2323.         return implode("\n", $s);
  2324.     }
  2325.  
  2326. }
  2327. ?>
  2328.