home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / DB / Table.php < prev   
Encoding:
PHP Script  |  2005-12-02  |  61.0 KB  |  2,168 lines

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