home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / DataObject.php < prev    next >
Encoding:
PHP Script  |  2004-03-24  |  105.6 KB  |  3,239 lines

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PHP Version 4                                                        |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1997-2003 The PHP Group                                |
  6. // +----------------------------------------------------------------------+
  7. // | This source file is subject to version 2.02 of the PHP license,      |
  8. // | that is bundled with this package in the file LICENSE, and is        |
  9. // | available at through the world-wide-web at                           |
  10. // | http://www.php.net/license/2_02.txt.                                 |
  11. // | If you did not receive a copy of the PHP license and are unable to   |
  12. // | obtain it through the world-wide-web, please send a note to          |
  13. // | license@php.net so we can mail you a copy immediately.               |
  14. // +----------------------------------------------------------------------+
  15. // | Author:  Alan Knowles <alan@akbkhome.com>
  16. // +----------------------------------------------------------------------+
  17. /**
  18.  * Object Based Database Query Builder and data store
  19.  *
  20.  * @package  DB_DataObject
  21.  * @category DB
  22.  *
  23.  * $Id: DataObject.php,v 1.208 2004/01/29 09:53:18 alan_k Exp $
  24.  */
  25.  
  26. /* =====================================================================================
  27. *
  28. *        !!!!!!!!!!!!!               W A R N I N G                !!!!!!!!!!!
  29. *
  30. *     THIS MAY SEGFAULT PHP IF YOU ARE USING THE ZEND OPTIMIZER (to fix it, just add 
  31. *     "define('DB_DATAOBJECT_NO_OVERLOAD',true);" before you include this file.
  32. *     reducing the optimization level may also solve the segfault.
  33. *  =====================================================================================
  34. */
  35.  
  36.  
  37.  
  38.  
  39. /**
  40.  * Needed classes
  41.  */
  42. require_once 'DB.php';
  43. require_once 'PEAR.php';
  44.  
  45. /**
  46.  * these are constants for the get_table array
  47.  * user to determine what type of escaping is required around the object vars.
  48.  */
  49. define('DB_DATAOBJECT_INT',  1);  // does not require ''
  50. define('DB_DATAOBJECT_STR',  2);  // requires ''
  51.  
  52. define('DB_DATAOBJECT_DATE', 4);  // is date #TODO
  53. define('DB_DATAOBJECT_TIME', 8);  // is time #TODO
  54. define('DB_DATAOBJECT_BOOL', 16); // is boolean #TODO
  55. define('DB_DATAOBJECT_TXT',  32); // is long text #TODO
  56. define('DB_DATAOBJECT_BLOB', 64); // is blob type
  57.  
  58.  
  59. define('DB_DATAOBJECT_NOTNULL', 128);           // not null col.
  60. define('DB_DATAOBJECT_MYSQLTIMESTAMP'   , 256);           // mysql timestamps (ignored by update/insert)
  61. /*
  62.  * Define this before you include DataObjects.php to  disable overload - if it segfaults due to Zend optimizer..
  63.  */
  64. //define('DB_DATAOBJECT_NO_OVERLOAD',true)  
  65.  
  66.  
  67. /**
  68.  * Theses are the standard error codes, most methods will fail silently - and return false
  69.  * to access the error message either use $table->_lastError
  70.  * or $last_error = PEAR::getStaticProperty('DB_DataObject','lastError');
  71.  * the code is $last_error->code, and the message is $last_error->message (a standard PEAR error)
  72.  */
  73.  
  74. define('DB_DATAOBJECT_ERROR_INVALIDARGS',   -1);  // wrong args to function
  75. define('DB_DATAOBJECT_ERROR_NODATA',        -2);  // no data available
  76. define('DB_DATAOBJECT_ERROR_INVALIDCONFIG', -3);  // something wrong with the config
  77. define('DB_DATAOBJECT_ERROR_NOCLASS',       -4);  // no class exists
  78. define('DB_DATAOBJECT_ERROR_NOAFFECTEDROWS',-5);  // no rows where affected by update/insert/delete
  79. define('DB_DATAOBJECT_ERROR_NOTSUPPORTED'  ,-6);  // limit queries on unsuppored databases
  80. define('DB_DATAOBJECT_ERROR_INVALID_CALL'  ,-7);  // overlad getter/setter failure
  81.  
  82. /**
  83.  * Used in methods like delete() and count() to specify that the method should
  84.  * build the condition only out of the whereAdd's and not the object parameters.
  85.  */
  86. define('DB_DATAOBJECT_WHEREADD_ONLY', true);
  87.  
  88. /**
  89.  *
  90.  * storage for connection and result objects,
  91.  * it is done this way so that print_r()'ing the is smaller, and
  92.  * it reduces the memory size of the object.
  93.  * -- future versions may use $this->_connection = & PEAR object..
  94.  *   although will need speed tests to see how this affects it.
  95.  * - includes sub arrays
  96.  *   - connections = md5 sum mapp to pear db object
  97.  *   - results     = [id] => map to pear db object
  98.  *   - ini         = mapping of database to ini file results
  99.  *   - links       = mapping of database to links file
  100.  *   - lasterror   = pear error objects for last error event.
  101.  *   - config      = aliased view of PEAR::getStaticPropery('DB_DataObject','options') * done for performance.
  102.  *   - array of loaded classes by autoload method - to stop it doing file access request over and over again!
  103.  */
  104. $GLOBALS['_DB_DATAOBJECT']['RESULTS'] = array();
  105. $GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'] = array();
  106. $GLOBALS['_DB_DATAOBJECT']['INI'] = array();
  107. $GLOBALS['_DB_DATAOBJECT']['LINKS'] = array();
  108. $GLOBALS['_DB_DATAOBJECT']['LASTERROR'] = null;
  109. $GLOBALS['_DB_DATAOBJECT']['CONFIG'] = array();
  110. $GLOBALS['_DB_DATAOBJECT']['CACHE'] = array();
  111. $GLOBALS['_DB_DATAOBJECT']['OVERLOADED'] = false;
  112. $GLOBALS['_DB_DATAOBJECT']['QUERYENDTIME'] = 0;
  113.  
  114.  
  115. // this will be horrifically slow!!!!
  116. // NOTE: Overload SEGFAULTS ON PHP4 + Zend Optimizer (see define before..)
  117. // these two are BC/FC handlers for call in PHP4/5
  118.  
  119. if ( substr(phpversion(),0,1) == 5) {
  120.     class DB_DataObject_Overload {
  121.         function __call($method,$args) {
  122.             $return = null;
  123.             $this->_call($method,$args,$return);
  124.             return $return;
  125.         }
  126.     }
  127. } else {
  128.     class DB_DataObject_Overload {
  129.         function __call($method,$args,&$return) {
  130.             return $this->_call($method,$args,$return);;
  131.         }
  132.     }
  133.  
  134. }
  135.  
  136.  /**
  137.  * The main "DB_DataObject" class is really a base class for your own tables classes
  138.  *
  139.  * // Set up the class by creating an ini file (refer to the manual for more details
  140.  * [DB_DataObject]
  141.  * database         = mysql:/username:password@host/database
  142.  * schema_location = /home/myapplication/database
  143.  * class_location  = /home/myapplication/DBTables/
  144.  * clase_prefix    = DBTables_
  145.  *
  146.  *
  147.  * //Start and initialize...................... - dont forget the &
  148.  * $config = parse_ini_file('example.ini',true);
  149.  * $options = &PEAR::setStaticProperty('DB_DataObject','options');
  150.  * $options = $config['DB_DataObject'];
  151.  *
  152.  * // example of a class (that does not use the 'auto generated tables data')
  153.  * class mytable extends DB_DataObject {
  154.  *     // mandatory - set the table
  155.  *     var $_database_dsn = "mysql://username:password@localhost/database";
  156.  *     var $__table = "mytable";
  157.  *     function table() {
  158.  *         return array(
  159.  *             'id' => 1, // integer or number
  160.  *             'name' => 2, // string
  161.  *        );
  162.  *     }
  163.  *     function keys() {
  164.  *         return array('id');
  165.  *     }
  166.  * }
  167.  *
  168.  * // use in the application
  169.  *
  170.  *
  171.  * Simple get one row
  172.  *
  173.  * $instance = new mytable;
  174.  * $instance->get("id",12);
  175.  * echo $instance->somedata;
  176.  *
  177.  *
  178.  * Get multiple rows
  179.  *
  180.  * $instance = new mytable;
  181.  * $instance->whereAdd("ID > 12");
  182.  * $instance->whereAdd("ID < 14");
  183.  * $instance->find();
  184.  * while ($instance->fetch()) {
  185.  *     echo $instance->somedata;
  186.  * }
  187.  *
  188.  * @package  DB_DataObject
  189.  * @author   Alan Knowles <alan@akbkhome.com>
  190.  * @since    PHP 4.0
  191.  */
  192.  
  193. Class DB_DataObject extends DB_DataObject_Overload
  194. {
  195.    /**
  196.     * The Version - use this to check feature changes
  197.     *
  198.     * @access   private
  199.     * @var      string
  200.     */
  201.     var $_DB_DataObject_version = "1.5.3";
  202.  
  203.     /**
  204.      * The Database table (used by table extends)
  205.      *
  206.      * @access  private
  207.      * @var     string
  208.      */
  209.     var $__table = '';  // database table
  210.  
  211.     /**
  212.      * The Number of rows returned from a query
  213.      *
  214.      * @access  public
  215.      * @var     int
  216.      */
  217.     var $N = 0;  // Number of rows returned from a query
  218.  
  219.  
  220.     /* ============================================================= */
  221.     /*                      Major Public Methods                     */
  222.     /* (designed to be optionally then called with parent::method()) */
  223.     /* ============================================================= */
  224.  
  225.  
  226.     /**
  227.      * Get a result using key, value.
  228.      *
  229.      * for example
  230.      * $object->get("ID",1234);
  231.      * Returns Number of rows located (usually 1) for success,
  232.      * and puts all the table columns into this classes variables
  233.      *
  234.      * see the fetch example on how to extend this.
  235.      *
  236.      * if no value is entered, it is assumed that $key is a value
  237.      * and get will then use the first key in keys()
  238.      * to obtain the key.
  239.      *
  240.      * @param   string  $k column
  241.      * @param   string  $v value
  242.      * @access  public
  243.      * @return  int     No. of rows
  244.      */
  245.     function get($k = null, $v = null)
  246.     {
  247.         global $_DB_DATAOBJECT;
  248.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  249.             DB_DataObject::_loadConfig();
  250.         }
  251.         $keys = array();
  252.         
  253.         if ($v === null) {
  254.             $v = $k;
  255.             $keys = $this->keys();
  256.             if (!$keys) {
  257.                 DB_DataObject::raiseError("No Keys available for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  258.                 return false;
  259.             }
  260.             $k = $keys[0];
  261.         }
  262.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  263.             $this->debug("$k $v " .print_r($keys,true), "GET");
  264.         }
  265.         
  266.         if ($v === null) {
  267.             DB_DataObject::raiseError("No Value specified for get", DB_DATAOBJECT_ERROR_INVALIDARGS);
  268.             return false;
  269.         }
  270.         $this->$k = $v;
  271.         return $this->find(1);
  272.     }
  273.  
  274.     /**
  275.      * An autoloading, caching static get method  using key, value (based on get)
  276.      *
  277.      * Usage:
  278.      * $object = DB_DataObject::staticGet("DbTable_mytable",12);
  279.      * or
  280.      * $object =  DB_DataObject::staticGet("DbTable_mytable","name","fred");
  281.      *
  282.      * or write it into your extended class:
  283.      * function &staticGet($k,$v=NULL) { return DB_DataObject::staticGet("This_Class",$k,$v);  }
  284.      *
  285.      * @param   string  $class class name
  286.      * @param   string  $k     column (or value if using keys)
  287.      * @param   string  $v     value (optional)
  288.      * @access  public
  289.      * @return  object
  290.      */
  291.     function &staticGet($class, $k, $v = null)
  292.     {
  293.         $lclass = strtolower($class);
  294.         global $_DB_DATAOBJECT;
  295.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  296.             DB_DataObject::_loadConfig();
  297.         }
  298.  
  299.         
  300.  
  301.         $key = "$k:$v";
  302.         if ($v === null) {
  303.             $key = $k;
  304.         }
  305.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  306.             DB_DataObject::debug("$class $key","STATIC GET - TRY CACHE");
  307.         }
  308.         if (@$_DB_DATAOBJECT['CACHE'][$lclass][$key]) {
  309.             return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
  310.         }
  311.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  312.             DB_DataObject::debug("$class $key","STATIC GET - NOT IN CACHE");
  313.         }
  314.  
  315.         $obj = DB_DataObject::factory(substr($class,strlen($_DB_DATAOBJECT['CONFIG']['class_prefix'])));
  316.         if (PEAR::isError($obj)) {
  317.             DB_DataObject::raiseError("could not autoload $class", DB_DATAOBJECT_ERROR_NOCLASS);
  318.             return false;
  319.         }
  320.         
  321.         if (!@$_DB_DATAOBJECT['CACHE'][$lclass]) {
  322.             $_DB_DATAOBJECT['CACHE'][$lclass] = array();
  323.         }
  324.         if (!$obj->get($k,$v)) {
  325.             DB_DataObject::raiseError("No Data return from get $k $v", DB_DATAOBJECT_ERROR_NODATA);
  326.             return false;
  327.         }
  328.         $_DB_DATAOBJECT['CACHE'][$lclass][$key] = $obj;
  329.         return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
  330.     }
  331.  
  332.     /**
  333.      * find results, either normal or crosstable
  334.      *
  335.      * for example
  336.      *
  337.      * $object = new mytable();
  338.      * $object->ID = 1;
  339.      * $object->find();
  340.      *
  341.      *
  342.      * will set $object->N to number of rows, and expects next command to fetch rows
  343.      * will return $object->N
  344.      *
  345.      * @param   boolean $n Fetch first result
  346.      * @access  public
  347.      * @return  int
  348.      */
  349.     function find($n = false)
  350.     {
  351.         global $_DB_DATAOBJECT;
  352.         if (!isset($this->_query)) {
  353.             DB_DataObject::raiseError(
  354.                 "You cannot do two queries on the same object (copy it before finding)", 
  355.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  356.             return false;
  357.         }
  358.         
  359.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  360.             DB_DataObject::_loadConfig();
  361.         }
  362.  
  363.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  364.             $this->debug($n, "__find",1);
  365.         }
  366.         if (!$this->__table) {
  367.             echo "NO \$__table SPECIFIED in class definition";
  368.             exit;
  369.         }
  370.         $this->N = 0;
  371.         $this->_build_condition($this->table()) ;
  372.         
  373.         $quoteEntities = @$_DB_DATAOBJECT['CONFIG']['quote_entities'];
  374.         $DB =& $this->getDatabaseConnection();
  375.  
  376.         $this->_query('SELECT ' .
  377.             $this->_query['data_select'] .
  378.             ' FROM ' . ($quoteEntities ? $DB->quoteEntity($this->__table) : $this->__table) . " " .
  379.             $this->_join .
  380.             $this->_query['condition'] . ' '.
  381.             $this->_query['group_by']  . ' '.
  382.             $this->_query['having']    . ' '.
  383.             $this->_query['order_by']  . ' '.
  384.             
  385.             $this->_query['limit']); // is select
  386.         
  387.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  388.             $this->debug("CHECK autofetchd $n", "__find", 1);
  389.         }
  390.         // unset the 
  391.         
  392.         
  393.         if ($n && $this->N > 0 ) {
  394.              if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  395.                 $this->debug("ABOUT TO AUTOFETCH", "__find", 1);
  396.             }
  397.             $this->fetch() ;
  398.         }
  399.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  400.             $this->debug("DONE", "__find", 1);
  401.         }
  402.         
  403.         return $this->N;
  404.     }
  405.  
  406.     /**
  407.      * fetches next row into this objects var's
  408.      *
  409.      * returns 1 on success 0 on failure
  410.      *
  411.      *
  412.      *
  413.      * Example
  414.      * $object = new mytable();
  415.      * $object->name = "fred";
  416.      * $object->find();
  417.      * $store = array();
  418.      * while ($object->fetch()) {
  419.      *   echo $this->ID;
  420.      *   $store[] = $object; // builds an array of object lines.
  421.      * }
  422.      *
  423.      * to add features to a fetch
  424.      * function fetch () {
  425.      *    $ret = parent::fetch();
  426.      *    $this->date_formated = date('dmY',$this->date);
  427.      *    return $ret;
  428.      * }
  429.      *
  430.      * @access  public
  431.      * @return  boolean on success
  432.      */
  433.     function fetch()
  434.     {
  435.  
  436.         global $_DB_DATAOBJECT;
  437.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  438.             DB_DataObject::_loadConfig();
  439.         }
  440.         if (!@$this->N) {
  441.             if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  442.                 $this->debug("No data returned from FIND (eg. N is 0)","FETCH", 3);
  443.             }
  444.             return false;
  445.         }
  446.         $result = &$_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid];
  447.         $array = $result->fetchRow(DB_FETCHMODE_ASSOC);
  448.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  449.             $this->debug(serialize($array),"FETCH");
  450.         }
  451.  
  452.         if (!is_array($array)) {
  453.             if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  454.                 $t= explode(' ',microtime());
  455.             
  456.                 $this->debug("Last Data Fetch'ed after " . 
  457.                         ($t[0]+$t[1]- $_DB_DATAOBJECT['QUERYENDTIME']  ) . 
  458.                         " seconds",
  459.                     "FETCH", 1);
  460.             }
  461.  
  462.             // this is probably end of data!!
  463.             //DB_DataObject::raiseError("fetch: no data returned", DB_DATAOBJECT_ERROR_NODATA);
  464.             return false;
  465.         }
  466.  
  467.         foreach($array as $k=>$v) {
  468.             $kk = str_replace(".", "_", $k);
  469.             $kk = str_replace(" ", "_", $kk);
  470.              if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  471.                 $this->debug("$kk = ". $array[$k], "fetchrow LINE", 3);
  472.             }
  473.             $this->$kk = $array[$k];
  474.         }
  475.         
  476.         // set link flag
  477.         $this->_link_loaded=false;
  478.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  479.             $this->debug("{$this->__table} DONE", "fetchrow",2);
  480.         }
  481.         if (isset($this->_query) && !@$_DB_DATAOBJECT['CONFIG']['keep_query_after_fetch']) {
  482.             unset($this->_query);
  483.         }
  484.         return true;
  485.     }
  486.  
  487.     /**
  488.      * Adds a condition to the WHERE statement, defaults to AND
  489.      *
  490.      * $object->whereAdd(); //reset or cleaer ewhwer
  491.      * $object->whereAdd("ID > 20");
  492.      * $object->whereAdd("age > 20","OR");
  493.      *
  494.      * @param    string  $cond  condition
  495.      * @param    string  $logic optional logic "OR" (defaults to "AND")
  496.      * @access   public
  497.      * @return   none|PEAR::Error - invalid args only
  498.      */
  499.     function whereAdd($cond = false, $logic = 'AND')
  500.     {
  501.         if (!isset($this->_query)) {
  502.             DB_DataObject::raiseError(
  503.                 "You cannot do two queries on the same object (copy it before finding)", 
  504.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  505.             return false;
  506.         }
  507.         
  508.         if ($cond === false) {
  509.             $this->_query['condition'] = '';
  510.             return;
  511.         }
  512.         // check input...= 0 or '   ' == error!
  513.         if (!trim($cond)) {
  514.             return DB_DataObject::raiseError("WhereAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  515.         }
  516.         if ($this->_query['condition']) {
  517.             $this->_query['condition'] .= " {$logic} {$cond}";
  518.             return;
  519.         }
  520.         $this->_query['condition'] = " WHERE {$cond}";
  521.     }
  522.  
  523.     /**
  524.      * Adds a order by condition
  525.      *
  526.      * $object->orderBy(); //clears order by
  527.      * $object->orderBy("ID");
  528.      * $object->orderBy("ID,age");
  529.      *
  530.      * @param  string $order  Order
  531.      * @access public
  532.      * @return none|PEAR::Error - invalid args only
  533.      */
  534.     function orderBy($order = false)
  535.     {
  536.         if (!isset($this->_query)) {
  537.             DB_DataObject::raiseError(
  538.                 "You cannot do two queries on the same object (copy it before finding)", 
  539.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  540.             return false;
  541.         }
  542.         if ($order === false) {
  543.             $this->_query['order_by'] = '';
  544.             return;
  545.         }
  546.         // check input...= 0 or '    ' == error!
  547.         if (!trim($order)) {
  548.             return DB_DataObject::raiseError("orderBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  549.         }
  550.         
  551.         if (!$this->_query['order_by']) {
  552.             $this->_query['order_by'] = " ORDER BY {$order} ";
  553.             return;
  554.         }
  555.         $this->_query['order_by'] .= " , {$order}";
  556.     }
  557.  
  558.     /**
  559.      * Adds a group by condition
  560.      *
  561.      * $object->groupBy(); //reset the grouping
  562.      * $object->groupBy("ID DESC");
  563.      * $object->groupBy("ID,age");
  564.      *
  565.      * @param  string  $group  Grouping
  566.      * @access public
  567.      * @return none|PEAR::Error - invalid args only
  568.      */
  569.     function groupBy($group = false)
  570.     {
  571.         if (!isset($this->_query)) {
  572.             DB_DataObject::raiseError(
  573.                 "You cannot do two queries on the same object (copy it before finding)", 
  574.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  575.             return false;
  576.         }
  577.         if ($group === false) {
  578.             $this->_query['group_by'] = '';
  579.             return;
  580.         }
  581.         // check input...= 0 or '    ' == error!
  582.         if (!trim($group)) {
  583.             return DB_DataObject::raiseError("groupBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  584.         }
  585.         
  586.         
  587.         if (!$this->_query['group_by']) {
  588.             $this->_query['group_by'] = " GROUP BY {$group} ";
  589.             return;
  590.         }
  591.         $this->_query['group_by'] .= " , {$group}";
  592.     }
  593.  
  594.     /**
  595.      * Adds a having clause
  596.      *
  597.      * $object->having(); //reset the grouping
  598.      * $object->having("sum(value) > 0 ");
  599.      *
  600.      * @param  string  $having  condition
  601.      * @access public
  602.      * @return none|PEAR::Error - invalid args only
  603.      */
  604.     function having($having = false)
  605.     {
  606.         if (!isset($this->_query)) {
  607.             DB_DataObject::raiseError(
  608.                 "You cannot do two queries on the same object (copy it before finding)", 
  609.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  610.             return false;
  611.         }
  612.         if ($having === false) {
  613.             $this->_query['having'] = '';
  614.             return;
  615.         }
  616.         // check input...= 0 or '    ' == error!
  617.         if (!trim($having)) {
  618.             return DB_DataObject::raiseError("Having: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  619.         }
  620.         
  621.         
  622.         if (!$this->_query['having']) {
  623.             $this->_query['having'] = " HAVING {$having} ";
  624.             return;
  625.         }
  626.         $this->_query['having'] .= " , {$having}";
  627.     }
  628.  
  629.     /**
  630.      * Sets the Limit
  631.      *
  632.      * $boject->limit(); // clear limit
  633.      * $object->limit(12);
  634.      * $object->limit(12,10);
  635.      *
  636.      * Note this will emit an error on databases other than mysql/postgress
  637.      * as there is no 'clean way' to implement it. - you should consider refering to
  638.      * your database manual to decide how you want to implement it.
  639.      *
  640.      * @param  string $a  limit start (or number), or blank to reset
  641.      * @param  string $b  number
  642.      * @access public
  643.      * @return none|PEAR::Error - invalid args only
  644.      */
  645.     function limit($a = null, $b = null)
  646.     {
  647.         if (!isset($this->_query)) {
  648.             DB_DataObject::raiseError(
  649.                 "You cannot do two queries on the same object (copy it before finding)", 
  650.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  651.             return false;
  652.         }
  653.         
  654.         if ($a === null) {
  655.            $this->_query['limit'] = '';
  656.            return;
  657.         }
  658.         // check input...= 0 or '    ' == error!
  659.         if ((!is_int($a) && ((string)((int)$a) !== (string)$a)) 
  660.             || (($b !== null) && (!is_int($b) && ((string)((int)$b) !== (string)$b)))) {
  661.             return DB_DataObject::raiseError("limit: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  662.         }
  663.  
  664.         $db = $this->getDatabaseConnection();
  665.  
  666.         if (($db->features['limit'] == 'alter') && ($db->phptype != 'oci8')) {
  667.             if ($b === null) {
  668.                $this->_query['limit'] = " LIMIT $a";
  669.                return;
  670.             }
  671.              
  672.             $this->_query['limit'] = $db->modifyLimitQuery('',$a,$b);
  673.             
  674.         } else {
  675.             DB_DataObject::raiseError(
  676.                 "DB_DataObjects only supports mysql and postgres limit queries at present, \n".
  677.                 "Refer to your Database manual to find out how to do limit queries manually.\n",
  678.                 DB_DATAOBJECT_ERROR_NOTSUPPORTED, PEAR_ERROR_DIE);
  679.         }
  680.     }
  681.  
  682.     /**
  683.      * Adds a select columns
  684.      *
  685.      * $object->selectAdd(); // resets select to nothing!
  686.      * $object->selectAdd("*"); // default select
  687.      * $object->selectAdd("unixtime(DATE) as udate");
  688.      * $object->selectAdd("DATE");
  689.      *
  690.      * @param  string  $k
  691.      * @access public
  692.      * @return void
  693.      */
  694.     function selectAdd($k = null)
  695.     {
  696.         if (!isset($this->_query)) {
  697.             DB_DataObject::raiseError(
  698.                 "You cannot do two queries on the same object (copy it before finding)", 
  699.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  700.             return false;
  701.         }
  702.         if ($k === null) {
  703.             $this->_query['data_select'] = '';
  704.             return;
  705.         }
  706.         
  707.         // check input...= 0 or '    ' == error!
  708.         if (!trim($k)) {
  709.             return DB_DataObject::raiseError("selectAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  710.         }
  711.         
  712.         if ($this->_query['data_select'])
  713.             $this->_query['data_select'] .= ', ';
  714.         $this->_query['data_select'] .= " $k ";
  715.     }
  716.     /**
  717.      * Adds multiple Columns or objects to select with formating.
  718.      *
  719.      * $object->selectAs(null); // adds "table.colnameA as colnameA,table.colnameB as colnameB,......"
  720.      *                      // note with null it will also clear the '*' default select
  721.      * $object->selectAs(array('a','b'),'%s_x'); // adds "a as a_x, b as b_x"
  722.      * $object->selectAs(array('a','b'),'ddd_%s','ccc'); // adds "ccc.a as ddd_a, ccc.b as ddd_b"
  723.      * $object->selectAdd($object,'prefix_%s'); // calls $object->get_table and adds it all as
  724.      *                  objectTableName.colnameA as prefix_colnameA
  725.      *
  726.      * @param  array|object|null the array or object to take column names from.
  727.      * @param  string           format in sprintf format (use %s for the colname)
  728.      * @param  string           table name eg. if you have joinAdd'd or send $from as an array.
  729.      * @access public
  730.      * @return void
  731.      */
  732.     function selectAs($from = null,$format = '%s',$tableName=false)
  733.     {
  734.         global $_DB_DATAOBJECT;
  735.         
  736.         if (!isset($this->_query)) {
  737.             DB_DataObject::raiseError(
  738.                 "You cannot do two queries on the same object (copy it before finding)", 
  739.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  740.             return false;
  741.         }
  742.         
  743.         if ($from === null) {
  744.             // blank the '*' 
  745.             $this->selectAdd();
  746.             $from = $this;
  747.         }
  748.         
  749.         
  750.         $table = $this->__table;
  751.         if (is_object($from)) {
  752.             $table = $from->__table;
  753.             $from = array_keys($from->table());
  754.         }
  755.         
  756.         if ($tableName !== false) {
  757.             $table = $tableName;
  758.         }
  759.         $s = '%s';
  760.         if (@$_DB_DATAOBJECT['CONFIG']['quote_entities']) {
  761.             $DB     = &$this->getDatabaseConnection();
  762.             $table  = $DB->quoteEntity($table);
  763.             $s      = $DB->quoteEntity($k);
  764.             $format = $DB->quoteEntity($format);
  765.         }
  766.         foreach ($from as $k) {
  767.             $this->selectAdd(sprintf("{$s}.{$s} as {$format}",$table,$k,$k));
  768.         }
  769.         $this->_query['data_select'] .= "\n";
  770.     }
  771.     /**
  772.      * Insert the current objects variables into the database
  773.      *
  774.      * Returns the ID of the inserted element - mysql specific = fixme?
  775.      *
  776.      * for example
  777.      *
  778.      * Designed to be extended
  779.      *
  780.      * $object = new mytable();
  781.      * $object->name = "fred";
  782.      * echo $object->insert();
  783.      *
  784.      * @access public
  785.      * @return  mixed|false key value or false on failure
  786.      */
  787.     function insert()
  788.     {
  789.         global $_DB_DATAOBJECT;
  790.         $quoteEntities  = @$_DB_DATAOBJECT['CONFIG']['quote_entities'];
  791.         // we need to write to the connection (For nextid) - so us the real
  792.         // one not, a copyied on (as ret-by-ref fails with overload!)
  793.         
  794.         $this->getDatabaseConnection(); 
  795.         $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  796.          
  797.         $items = $this->table();
  798.         if (!$items) {
  799.             DB_DataObject::raiseError("insert:No table definition for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  800.             return false;
  801.         }
  802.         $options= &$_DB_DATAOBJECT['CONFIG'];
  803.  
  804.  
  805.         $datasaved = 1;
  806.         $leftq     = '';
  807.         $rightq    = '';
  808.      
  809.         @list($key,$useNative,$seq) = $this->sequenceKey();
  810.         $dbtype    = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn["phptype"];
  811.          
  812.          
  813.         // nativeSequences or Sequences..     
  814.  
  815.         // big check for using sequences
  816.         
  817.         if (($key !== false) && !$useNative) { 
  818.             if (!$seq) {
  819.                 $this->$key = $DB->nextId($this->__table);
  820.             } else {
  821.                 $f = $DB->getOption('seqname_format');
  822.                 $DB->setOption('seqname_format','%s');
  823.                 $this->$key =  $DB->nextId($seq);
  824.                 $DB->setOption('seqname_format',$f);
  825.             }
  826.         }
  827.  
  828.  
  829.  
  830.         foreach($items as $k => $v) {
  831.             
  832.             // if we are using autoincrement - skip the column...
  833.             if ($key && ($k == $key) && $useNative) {
  834.                 continue;
  835.             }
  836.         
  837.             
  838.             if (!isset($this->$k)) {
  839.                 continue;
  840.             }
  841.             // dont insert data into mysql timestamps 
  842.             // use query() if you really want to do this!!!!
  843.             if ($v & DB_DATAOBJECT_MYSQLTIMESTAMP) {
  844.                 continue;
  845.             }
  846.             
  847.             if ($leftq) {
  848.                 $leftq  .= ', ';
  849.                 $rightq .= ', ';
  850.             }
  851.             
  852.             $leftq .= ($quoteEntities ? ($DB->quoteEntity($k) . ' ')  : "$k ");
  853.             
  854.             if (is_a($this->$k,'db_dataobject_cast')) {
  855.                 $value = $this->$k->toString($v,$dbtype);
  856.                 if (PEAR::isError($value)) {
  857.                     DB_DataObject::raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
  858.                     return false;
  859.                 }
  860.                 $rightq .=  $value;
  861.                 continue;
  862.             }
  863.             
  864.  
  865.             if (strtolower($this->$k) === 'null') {
  866.                 $rightq .= " NULL ";
  867.                 continue;
  868.             }
  869.  
  870.             if ($v & DB_DATAOBJECT_STR) {
  871.                 $rightq .= $DB->quote($this->$k) . " ";
  872.                 continue;
  873.             }
  874.             if (is_numeric($this->$k)) {
  875.                 $rightq .=" {$this->$k} ";
  876.                 continue;
  877.             }
  878.             // at present we only cast to integers
  879.             // - V2 may store additional data about float/int
  880.             $rightq .= ' ' . intval($this->$k) . ' ';
  881.  
  882.         }
  883.         
  884.         
  885.         if ($leftq || $useNative) {
  886.             $table = ($quoteEntities ? $DB->quoteEntity($this->__table)    : $this->__table);
  887.             
  888.             $r = $this->_query("INSERT INTO {$table} ($leftq) VALUES ($rightq) ");
  889.             
  890.             if (PEAR::isError($r)) {
  891.                 DB_DataObject::raiseError($r);
  892.                 return false;
  893.             }
  894.             if ($r < 1) {
  895.                 DB_DataObject::raiseError('No Data Affected By insert',DB_DATAOBJECT_ERROR_NOAFFECTEDROWS);
  896.                 return false;
  897.             }
  898.            
  899.             // now do we have an integer key!
  900.             
  901.             if ($key && $useNative) {
  902.                 switch ($dbtype) {
  903.                     case 'mysql':
  904.                         $this->$key = mysql_insert_id(
  905.                             $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->connection
  906.                         );
  907.                         break;
  908.                     case 'mssql':
  909.                         // note this is not really thread safe - you should wrapp it with 
  910.                         // transactions = eg.
  911.                         // $db->query('BEGIN');
  912.                         // $db->insert();
  913.                         // $db->query('COMMIT');
  914.                         
  915.                         $mssql_key = $DB->getOne("SELECT @@IDENTITY");
  916.                         if (PEAR::isError($mssql_key)) {
  917.                             DB_DataObject::raiseError($r);
  918.                             return false;
  919.                         }
  920.                         $this->$key = $mssql_key;
  921.                         break; 
  922.                         
  923.                     case 'pgsql':
  924.                         if (!$seq) {
  925.                             $seq = $DB->getSequenceName($this->__table );
  926.                         }
  927.                         $pgsql_key = $DB->getOne("SELECT last_value FROM ".$seq);
  928.                         if (PEAR::isError($pgsql_key)) {
  929.                             DB_DataObject::raiseError($r);
  930.                             return false;
  931.                         }
  932.                         $this->$key = $pgsql_key;
  933.                         break;
  934.                 }
  935.                         
  936.             }
  937.  
  938.             $this->_clear_cache();
  939.             if ($key) {
  940.                 return $this->$key;
  941.             }
  942.             return true;
  943.         }
  944.         DB_DataObject::raiseError("insert: No Data specifed for query", DB_DATAOBJECT_ERROR_NODATA);
  945.         return false;
  946.     }
  947.  
  948.     /**
  949.      * Updates  current objects variables into the database
  950.      * uses the keys() to decide how to update
  951.      * Returns the  true on success
  952.      *
  953.      * for example
  954.      *
  955.      * $object = new mytable();
  956.      * $object->get("ID",234);
  957.      * $object->email="testing@test.com";
  958.      * if(!$object->update())
  959.      *   echo "UPDATE FAILED";
  960.      *
  961.      * to only update changed items :
  962.      * $dataobject->get(132);
  963.      * $original = $dataobject; // clone/copy it..
  964.      * $dataobject->setFrom($_POST);
  965.      * if ($dataobject->validate()) {
  966.      *    $dataobject->update($original);
  967.      * } // otherwise an error...
  968.      *
  969.      *
  970.      * @param object dataobject (optional) - used to only update changed items.
  971.      * @access public
  972.      * @return  int rows affected or false on failure
  973.      */
  974.     function update($dataObject = false)
  975.     {
  976.         global $_DB_DATAOBJECT;
  977.         // connect will load the config!
  978.         $this->_connect();
  979.         
  980.         
  981.         $original_query = isset($this->_query) ? $this->_query : null;
  982.         
  983.         $items = $this->table();
  984.         $keys  = $this->keys();
  985.         
  986.          
  987.         if (!$items) {
  988.             DB_DataObject::raiseError("update:No table definition for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  989.             return false;
  990.         }
  991.         $datasaved = 1;
  992.         $settings  = '';
  993.  
  994.         $DB            = &$this->getDatabaseConnection();
  995.         $dbtype        = $DB->dsn["phptype"];
  996.         $quoteEntities = @$_DB_DATAOBJECT['CONFIG']['quote_entities'];
  997.         
  998.         foreach($items as $k => $v) {
  999.             if (!isset($this->$k)) {
  1000.                 continue;
  1001.             }
  1002.             // dont write things that havent changed..
  1003.             if (($dataObject !== false) && (@$dataObject->$k == $this->$k)) {
  1004.                 continue;
  1005.             }
  1006.             
  1007.             // beta testing.. - dont write keys to left.!!!
  1008.             if (in_array($k,$keys)) {
  1009.                 continue;
  1010.             }
  1011.             
  1012.              // dont insert data into mysql timestamps 
  1013.             // use query() if you really want to do this!!!!
  1014.             if ($v & DB_DATAOBJECT_MYSQLTIMESTAMP) {
  1015.                 continue;
  1016.             }
  1017.             
  1018.             
  1019.             if ($settings)  {
  1020.                 $settings .= ', ';
  1021.             }
  1022.             
  1023.             $kSql = ($quoteEntities ? $DB->quoteEntity($k) : $k);
  1024.             
  1025.             if (is_a($this->$k,'db_dataobject_cast')) {
  1026.                 $value = $this->$k->toString($v,$dbtype);
  1027.                 if (PEAR::isError($value)) {
  1028.                     DB_DataObject::raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
  1029.                     return false;
  1030.                 }
  1031.                 $settings .= "$kSql = $value ";
  1032.                 continue;
  1033.             }
  1034.             
  1035.             /* special values ... at least null is handled...*/
  1036.             if (strtolower($this->$k) === 'null') {
  1037.                 $settings .= "$kSql = NULL ";
  1038.                 continue;
  1039.             }
  1040.  
  1041.             if ($v & DB_DATAOBJECT_STR) {
  1042.                 $settings .= "$kSql = ". $DB->quote($this->$k) . ' ';
  1043.                 continue;
  1044.             }
  1045.             if (is_numeric($this->$k)) {
  1046.                 $settings .= "$kSql = {$this->$k} ";
  1047.                 continue;
  1048.             }
  1049.             // at present we only cast to integers
  1050.             // - V2 may store additional data about float/int
  1051.             $settings .= "$kSql = " . intval($this->$k) . ' ';
  1052.         }
  1053.  
  1054.         
  1055.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1056.             $this->debug("got keys as ".serialize($keys),3);
  1057.         }
  1058.         
  1059.         $this->_build_condition($items,$keys);
  1060.         //  echo " $settings, $this->condition ";
  1061.         if ($settings && isset($this->_query) && $this->_query['condition']) {
  1062.             
  1063.             $table = ($quoteEntities ? $DB->quoteEntity($this->__table) : $this->__table);
  1064.         
  1065.             $r = $this->_query("UPDATE  {$table}  SET {$settings} {$this->_query['condition']} ");
  1066.             
  1067.             // restore original query conditions.
  1068.             $this->_query = $original_query;
  1069.             
  1070.             if (PEAR::isError($r)) {
  1071.                 $this->raiseError($r);
  1072.                 return false;
  1073.             }
  1074.             if ($r < 1) {
  1075.                 DB_DataObject::raiseError('No Data Affected By update',DB_DATAOBJECT_ERROR_NOAFFECTEDROWS);
  1076.                 return false;
  1077.             }
  1078.  
  1079.             $this->_clear_cache();
  1080.             return $r;
  1081.         }
  1082.         // restore original query conditions.
  1083.         $this->_query = $original_query;
  1084.         
  1085.         DB_DataObject::raiseError(
  1086.             "update: No Data specifed for query $settings , {$this->_query['condition']}", 
  1087.             DB_DATAOBJECT_ERROR_NODATA);
  1088.         return false;
  1089.     }
  1090.  
  1091.     /**
  1092.      * Deletes items from table which match current objects variables
  1093.      *
  1094.      * Returns the true on success
  1095.      *
  1096.      * for example
  1097.      *
  1098.      * Designed to be extended
  1099.      *
  1100.      * $object = new mytable();
  1101.      * $object->ID=123;
  1102.      * echo $object->delete(); // builds a conditon
  1103.      * $object = new mytable();
  1104.      * $object->whereAdd('age > 12');
  1105.      * $object->delete(true); // use the condition
  1106.      *
  1107.      * @param bool $useWhere (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then
  1108.      *             we will build the condition only using the whereAdd's.  Default is to
  1109.      *             build the condition only using the object parameters.
  1110.      *
  1111.      * @access public
  1112.      * @return bool True on success
  1113.      */
  1114.     function delete($useWhere = false)
  1115.     {
  1116.         global $_DB_DATAOBJECT;
  1117.         // connect will load the config!
  1118.         $DB             = &$this->getDatabaseConnection();
  1119.         $quoteEntities  = @$_DB_DATAOBJECT['CONFIG']['quote_entities'];
  1120.         
  1121.         if (!$useWhere) {
  1122.  
  1123.             $keys = $this->keys();
  1124.             $this->_query = array(); // as it's probably unset!
  1125.             $this->_query['condition'] = ''; // default behaviour not to use where condition
  1126.             $this->_build_condition($this->table(),$keys);
  1127.             // if primary keys are not set then use data from rest of object.
  1128.             if (!$this->_query['condition']) {
  1129.                 $this->_build_condition($this->table(),array(),$keys);
  1130.             }
  1131.         }
  1132.  
  1133.         // don't delete without a condition
  1134.         if (isset($this->_query) && $this->_query['condition']) {
  1135.         
  1136.             $table = ($quoteEntities ? $DB->quoteEntity($this->__table) : $this->__table);
  1137.         
  1138.             $r = $this->_query("DELETE FROM {$table} {$this->_query['condition']}");
  1139.             
  1140.             if (PEAR::isError($r)) {
  1141.                 $this->raiseError($r);
  1142.                 return false;
  1143.             }
  1144.             if ($r < 1) {
  1145.                 DB_DataObject::raiseError('No Data Affected By delete',DB_DATAOBJECT_ERROR_NOAFFECTEDROWS);
  1146.                 return false;
  1147.             }
  1148.             $this->_clear_cache();
  1149.             return $r;
  1150.         } else {
  1151.             DB_DataObject::raiseError("delete: No condition specifed for query", DB_DATAOBJECT_ERROR_NODATA);
  1152.             return false;
  1153.         }
  1154.     }
  1155.  
  1156.     /**
  1157.      * fetches a specific row into this object variables
  1158.      *
  1159.      * Not recommended - better to use fetch()
  1160.      *
  1161.      * Returens true on success
  1162.      *
  1163.      * @param  int   $row  row
  1164.      * @access public
  1165.      * @return boolean true on success
  1166.      */
  1167.     function fetchRow($row = null)
  1168.     {
  1169.         global $_DB_DATAOBJECT;
  1170.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  1171.             DB_DataObject::_loadConfig();
  1172.         }
  1173.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1174.             $this->debug("{$this->__table} $row of {$this->N}", "fetchrow",3);
  1175.         }
  1176.         if (!$this->__table) {
  1177.             DB_DataObject::raiseError("fetchrow: No table", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  1178.             return false;
  1179.         }
  1180.         if ($row === null) {
  1181.             DB_DataObject::raiseError("fetchrow: No row specified", DB_DATAOBJECT_ERROR_INVALIDARGS);
  1182.             return false;
  1183.         }
  1184.         if (!$this->N) {
  1185.             DB_DataObject::raiseError("fetchrow: No results avaiable", DB_DATAOBJECT_ERROR_NODATA);
  1186.             return false;
  1187.         }
  1188.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1189.             $this->debug("{$this->__table} $row of {$this->N}", "fetchrow",3);
  1190.         }
  1191.  
  1192.  
  1193.         $result = &$_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid];
  1194.         $array  = $result->fetchrow(DB_FETCHMODE_ASSOC,$row);
  1195.         if (!is_array($array)) {
  1196.             DB_DataObject::raiseError("fetchrow: No results available", DB_DATAOBJECT_ERROR_NODATA);
  1197.             return false;
  1198.         }
  1199.  
  1200.         foreach($array as $k => $v) {
  1201.             $kk = str_replace(".", "_", $k);
  1202.             if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1203.                 $this->debug("$kk = ". $array[$k], "fetchrow LINE", 3);
  1204.             }
  1205.             $this->$kk = $array[$k];
  1206.         }
  1207.  
  1208.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1209.             $this->debug("{$this->__table} DONE", "fetchrow", 3);
  1210.         }
  1211.         return true;
  1212.     }
  1213.  
  1214.     /**
  1215.      * Find the number of results from a simple query
  1216.      *
  1217.      * for example
  1218.      *
  1219.      * $object = new mytable();
  1220.      * $object->name = "fred";
  1221.      * echo $object->count();
  1222.      *
  1223.      * @param bool $whereAddOnly (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then
  1224.      *             we will build the condition only using the whereAdd's.  Default is to
  1225.      *             build the condition using the object parameters as well.
  1226.      *
  1227.      * @access public
  1228.      * @return int
  1229.      */
  1230.     function count($whereAddOnly = false)
  1231.     {
  1232.         global $_DB_DATAOBJECT;
  1233.         
  1234.         
  1235.         $t = $this->__clone();
  1236.         
  1237.         $quoteEntities = @$_DB_DATAOBJECT['CONFIG']['quote_entities'];
  1238.         
  1239.         $items   = $t->table();
  1240.         if (!isset($t->_query)) {
  1241.             DB_DataObject::raiseError(
  1242.                 "You cannot do run count after you have run fetch()", 
  1243.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  1244.             return false;
  1245.         }
  1246.         $DB    = $t->getDatabaseConnection();
  1247.  
  1248.         if (!$whereAddOnly && $items)  {
  1249.             foreach ($items as $key => $val) {
  1250.                 if (isset($t->$key))  {
  1251.                     $kSql = $quoteEntities ? $DB->quoteEntity($key) : $key;
  1252.                     $t->whereAdd($kSql . ' = ' . $DB->quote($t->$key));
  1253.                 }
  1254.             }
  1255.         }
  1256.         $keys = $this->keys();
  1257.  
  1258.         if (!$keys[0]) {
  1259.             echo 'CAN NOT COUNT WITHOUT PRIMARY KEYS';
  1260.             exit;
  1261.         }
  1262.         
  1263.         $table   = ($quoteEntities ? $DB->quoteEntity($this->__table) : $this->__table);
  1264.         $key_col = ($quoteEntities ? $DB->quoteEntity($keys[0]) : $keys[0]);
  1265.         $as      = ($quoteEntities ? $DB->quoteEntity('DATAOBJECT_NUM') : 'DATAOBJECT_NUM');
  1266.         $r = $t->_query(
  1267.             "SELECT count({$table}.{$key_col}) as $as
  1268.                 FROM $table {$t->_join} {$t->_query['condition']}");
  1269.         if (PEAR::isError($r)) {
  1270.             return false;
  1271.         }
  1272.          
  1273.         $result  = &$_DB_DATAOBJECT['RESULTS'][$t->_DB_resultid];
  1274.         $l = $result->fetchRow();
  1275.         return $l[0];
  1276.     }
  1277.  
  1278.     /**
  1279.      * sends raw query to database
  1280.      *
  1281.      * Since _query has to be a private 'non overwriteable method', this is a relay
  1282.      *
  1283.      * @param  string  $string  SQL Query
  1284.      * @access public
  1285.      * @return void or DB_Error
  1286.      */
  1287.     function query($string)
  1288.     {
  1289.         return $this->_query($string);
  1290.     }
  1291.  
  1292.  
  1293.     /**
  1294.      * an escape wrapper around quote ..
  1295.      * can be used when adding manual queries =
  1296.      * eg.
  1297.      * $object->query("select * from xyz where abc like '". $object->quote($_GET['name']) . "'");
  1298.      *
  1299.      * @param  string  $string  SQL Query
  1300.      * @access public
  1301.      * @return void or PEAR_Error
  1302.      */
  1303.     function escape($string)
  1304.     {
  1305.         global $_DB_DATAOBJECT;
  1306.         $DB = &$this->getDatabaseConnection();
  1307.         return substr($DB->quote($string),1,-1);
  1308.     }
  1309.  
  1310.     /* ==================================================== */
  1311.     /*        Major Private Vars                            */
  1312.     /* ==================================================== */
  1313.  
  1314.     /**
  1315.      * The Database connection dsn (as described in the PEAR DB)
  1316.      * only used really if you are writing a very simple application/test..
  1317.      * try not to use this - it is better stored in configuration files..
  1318.      *
  1319.      * @access  private
  1320.      * @var     string
  1321.      */
  1322.     var $_database_dsn = '';
  1323.  
  1324.     /**
  1325.      * The Database connection id (md5 sum of databasedsn)
  1326.      *
  1327.      * @access  private
  1328.      * @var     string
  1329.      */
  1330.     var $_database_dsn_md5 = '';
  1331.  
  1332.     /**
  1333.      * The Database name
  1334.      * created in __connection
  1335.      *
  1336.      * @access  private
  1337.      * @var  string
  1338.      */
  1339.     var $_database = '';
  1340.  
  1341.     
  1342.     
  1343.     /**
  1344.      * The QUERY rules
  1345.      * This replaces alot of the private variables 
  1346.      * used to build a query, it is unset after find() is run.
  1347.      * 
  1348.      *
  1349.      *
  1350.      * @access  private
  1351.      * @var     array
  1352.      */
  1353.     var $_query = array(
  1354.         'condition'   => '', // the WHERE condition
  1355.         'group_by'    => '', // the GROUP BY condition
  1356.         'order_by'    => '', // the ORDER BY condition
  1357.         'having'      => '', // the HAVING condition
  1358.         'limit'       => '', // the LIMIT condition
  1359.         'data_select' => '*', // the columns to be SELECTed
  1360.     );
  1361.         
  1362.     
  1363.   
  1364.  
  1365.     /**
  1366.      * Database result id (references global $_DB_DataObject[results]
  1367.      *
  1368.      * @access  private
  1369.      * @var     integer
  1370.      */
  1371.     var $_DB_resultid; // database result object
  1372.  
  1373.  
  1374.     /* ============================================================== */
  1375.     /*  Table definition layer (started of very private but 'came out'*/
  1376.     /* ============================================================== */
  1377.  
  1378.     /**
  1379.      * Autoload or manually load the table definitions
  1380.      *
  1381.      *
  1382.      * usage :
  1383.      * DB_DataObject::databaseStructure(  'databasename',
  1384.      *                                    parse_ini_file('mydb.ini',true), 
  1385.      *                                    parse_ini_file('mydb.link.ini',true)); 
  1386.      *
  1387.      * obviously you dont have to use ini files.. (just return array similar to ini files..)
  1388.      *  
  1389.      * It should append to the table structure array 
  1390.      *
  1391.      *     
  1392.      * @param optional string  name of database to assign / read
  1393.      * @param optional array   structure of database, and keys
  1394.      * @param optional array  table links
  1395.      *
  1396.      * @access public
  1397.      * @return true or PEAR:error on wrong paramenters.. or false if no file exists..
  1398.      *              or the array(tablename => array(column_name=>type)) if called with 1 argument.. (databasename)
  1399.      */
  1400.     function databaseStructure()
  1401.     {
  1402.  
  1403.         global $_DB_DATAOBJECT;
  1404.         
  1405.         // Assignment code 
  1406.         
  1407.         if ($args = func_get_args()) {
  1408.         
  1409.             if (count($args) == 1) {
  1410.                 
  1411.                 // this returns all the tables and their structure..
  1412.                 
  1413.                 $x = new DB_DataObject;
  1414.                 $x->_database = $args[0];
  1415.                 $DB  = $x->getDatabaseConnection();
  1416.                 $tables = $DB->getListOf('tables');
  1417.                 require_once 'DB/DataObject/Generator.php';
  1418.                 foreach($tables as $table) {
  1419.                     $y = new DB_DataObject_Generator;
  1420.                     $y->fillTableSchema($x->_database,$table);
  1421.                 }
  1422.                 return $_DB_DATAOBJECT['INI'][$x->_database];            
  1423.             } else {
  1424.         
  1425.                 $_DB_DATAOBJECT['INI'][$args[0]] = isset($_DB_DATAOBJECT['INI'][$args[0]]) ?
  1426.                     $_DB_DATAOBJECT['INI'][$args[0]] + $args[1] : $args[1];
  1427.                 
  1428.                 if (isset($args[1])) {
  1429.                     $_DB_DATAOBJECT['LINKS'][$args[0]] = isset($_DB_DATAOBJECT['LINKS'][$args[0]]) ?
  1430.                         $_DB_DATAOBJECT['LINKS'][$args[0]] + $args[2] : $args[2];
  1431.                 }
  1432.                 return true;
  1433.             }
  1434.           
  1435.         }
  1436.         
  1437.         
  1438.         
  1439.         
  1440.         
  1441.         // loaded already?
  1442.         if (!empty($_DB_DATAOBJECT['INI'][$this->_database])) {
  1443.             // database loaded - but this is table is not available..
  1444.             if (empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
  1445.                 require_once 'DB/DataObject/Generator.php';
  1446.                 $x = new DB_DataObject_Generator;
  1447.                 $x->fillTableSchema($this->_database,$this->__table);
  1448.             }
  1449.             return true;
  1450.         }
  1451.         
  1452.         
  1453.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  1454.             DB_DataObject::_loadConfig();
  1455.         }
  1456.         
  1457.         // if you supply this with arguments, then it will take those
  1458.         // as the database and links array...
  1459.         if (!@$_DB_DATAOBJECT['CONFIG']['schema_location']) {
  1460.             return true;
  1461.         }
  1462.         $location = $_DB_DATAOBJECT['CONFIG']['schema_location'];
  1463.  
  1464.         $ini   = $location . "/{$this->_database}.ini";
  1465.  
  1466.         if (isset($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"])) {
  1467.             $ini = $_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"];
  1468.         }
  1469.         $links = str_replace('.ini','.links.ini',$ini);
  1470.         
  1471.         if (file_exists($ini)) {
  1472.             $_DB_DATAOBJECT['INI'][$this->_database] = parse_ini_file($ini, true);    
  1473.         }
  1474.         
  1475.         
  1476.         if (empty($_DB_DATAOBJECT['LINKS'][$this->_database]) && file_exists($links)) {
  1477.             /* not sure why $links = ... here  - TODO check if that works */
  1478.             $_DB_DATAOBJECT['LINKS'][$this->_database] = parse_ini_file($links, true);
  1479.         }
  1480.         
  1481.         // now have we loaded the structure.. - if not try building it..
  1482.         
  1483.         if (empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
  1484.             require_once 'DB/DataObject/Generator.php';
  1485.             $x = new DB_DataObject_Generator;
  1486.             $x->fillTableSchema($this->_database,$this->__table);
  1487.         }
  1488.         
  1489.         
  1490.         return true;
  1491.     }
  1492.  
  1493.  
  1494.  
  1495.  
  1496.     /**
  1497.      * Return or assign the name of the current table
  1498.      *
  1499.      *
  1500.      * @param   string optinal table name to set
  1501.      * @access public
  1502.      * @return string The name of the current table
  1503.      */
  1504.     function tableName()
  1505.     {
  1506.         $args = func_get_args();
  1507.         if (count($args)) {
  1508.             $this->__table = $args[0];
  1509.         }
  1510.         return $this->__table;
  1511.     }
  1512.     
  1513.     /**
  1514.      * Return or assign the name of the current database
  1515.      *
  1516.      * @param   string optional database name to set
  1517.      * @access public
  1518.      * @return string The name of the current database
  1519.      */
  1520.     function database()
  1521.     {
  1522.         $args = func_get_args();
  1523.         if (count($args)) {
  1524.             $this->_database = $args[0];
  1525.         }
  1526.         return $this->_database;
  1527.     }
  1528.   
  1529.     /**
  1530.      * get/set an associative array of table columns
  1531.      *
  1532.      * @access public
  1533.      * @param  array key=>type array
  1534.      * @return array (associative)
  1535.      */
  1536.     function table()
  1537.     {
  1538.         
  1539.         // for temporary storage of database fields..
  1540.         // note this is not declared as we dont want to bloat the print_r output
  1541.         $args = func_get_args();
  1542.         if (count($args)) {
  1543.             $this->_database_fields = $args[0];
  1544.         }
  1545.         if (isset($this->_database_fields)) {
  1546.             return $this->_database_fields;
  1547.         }
  1548.         
  1549.         
  1550.         global $_DB_DATAOBJECT;
  1551.         if (!@$this->_database) {
  1552.             $this->_connect();
  1553.         }
  1554.         
  1555.         $this->databaseStructure();
  1556.  
  1557.         
  1558.         $ret = array();
  1559.         if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
  1560.             $ret =  $_DB_DATAOBJECT['INI'][$this->_database][$this->__table];
  1561.         }
  1562.         
  1563.         return $ret;
  1564.     }
  1565.  
  1566.     /**
  1567.      * get/set an  array of table primary keys
  1568.      *
  1569.      * set usage: $do->keys('id','code');
  1570.      *
  1571.      * This is defined in the table definition if it gets it wrong,
  1572.      * or you do not want to use ini tables, you can override this.
  1573.      * @param  string optional set the key
  1574.      * @param  *   optional  set more keys
  1575.      * @access private
  1576.      * @return array
  1577.      */
  1578.     function keys()
  1579.     {
  1580.         // for temporary storage of database fields..
  1581.         // note this is not declared as we dont want to bloat the print_r output
  1582.         $args = func_get_args();
  1583.         if (count($args)) {
  1584.             $this->_database_keys = $args;
  1585.         }
  1586.         if (isset($this->_database_keys)) {
  1587.             return $this->_database_keys;
  1588.         }
  1589.         
  1590.         global $_DB_DATAOBJECT;
  1591.         if (!@$this->_database) {
  1592.             $this->_connect();
  1593.         }
  1594.         $this->databaseStructure();
  1595.         
  1596.         if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
  1597.             return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]);
  1598.         }
  1599.         return array();
  1600.     }
  1601.     /**
  1602.      * get/set an  sequence key
  1603.      *
  1604.      * by default it returns the first key from keys()
  1605.      * set usage: $do->sequenceKey('id',true);
  1606.      *
  1607.      * override this to return array(false,false) if table has no real sequence key.
  1608.      *
  1609.      * @param  string  optional the key sequence/autoinc. key
  1610.      * @param  boolean optional use native increment. default false 
  1611.      * @param  false|string optional native sequence name
  1612.      * @access private
  1613.      * @return array (column,use_native,sequence_name)
  1614.      */
  1615.     function sequenceKey()
  1616.     {
  1617.         global $_DB_DATAOBJECT;
  1618.         // for temporary storage of database fields..
  1619.         // note this is not declared as we dont want to bloat the print_r output
  1620.         $args = func_get_args();
  1621.         if (count($args)) {
  1622.             $args[1] = isset($args[1]) ? $args[1] : false;
  1623.             $args[2] = isset($args[2]) ? $args[2] : false;
  1624.             $this->_databaseSequenceKeys = $args;
  1625.         }
  1626.         if (isset($this->_databaseSequenceKeys )) {
  1627.             return $this->_databaseSequenceKeys;
  1628.         }
  1629.         
  1630.         $keys = $this->keys();
  1631.         if (!$keys) {
  1632.             return array(false,false,false);;
  1633.         }
  1634.         $table = $this->table();
  1635.         $dbtype    = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'];
  1636.         
  1637.         $usekey = $keys[0];
  1638.         
  1639.         
  1640.         
  1641.         $seqname = false;
  1642.         
  1643.         if (@$_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table]) {
  1644.             $usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
  1645.             if (strpos($usekey,':') !== false) {
  1646.                 list($usekey,$seqname) = explode(':',$usekey);
  1647.             }
  1648.         }  
  1649.         
  1650.         
  1651.         // if the key is not an integer - then it's not a sequence or native
  1652.         if (!($table[$usekey] & DB_DATAOBJECT_INT)) {
  1653.                 return array(false,false,false);
  1654.         }
  1655.  
  1656.         if (@$_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys']) {
  1657.             $ignore =  $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'];
  1658.             if (is_string($ignore) && (strtoupper($ignore) == 'ALL')) {
  1659.                 return array(false,false,$seqname);
  1660.             }
  1661.             if (is_string($ignore)) {
  1662.                 $ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'] = explode(',',$ignore);
  1663.             }
  1664.             if (in_array($this->__table,$ignore)) {
  1665.                 return array(false,false,$seqname);
  1666.             }
  1667.         }
  1668.         
  1669.         
  1670.         $realkeys = $_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"];
  1671.         
  1672.         
  1673.         //echo "--keys--\n";
  1674.         //print_R(array($realkeys[$usekey], count($keys))) ;
  1675.         //echo "\n-/keys--\n";
  1676.         
  1677.         // if you are using an old ini file - go back to old behaviour...
  1678.         if (is_numeric($realkeys[$usekey])) {
  1679.             $realkeys[$usekey] = 'N';
  1680.         }
  1681.         
  1682.         // multiple unique primary keys without a native sequence...
  1683.         if (($realkeys[$usekey] == 'K') && (count($keys) > 1)) {
  1684.             return array(false,false,$seqname);
  1685.         }
  1686.         // use native sequence keys...
  1687.         // technically postgres native here...
  1688.         // we need to get the new improved tabledata sorted out first.
  1689.         
  1690.         if (    in_array($dbtype , array( 'mysql', 'mssql')) && 
  1691.                 ($table[$usekey] & DB_DATAOBJECT_INT) && 
  1692.                 (@$realkeys[$usekey] == 'N')
  1693.                 ) {
  1694.             return array($usekey,true,$seqname);
  1695.         }
  1696.         // I assume it's going to try and be a nextval DB sequence.. (not native)
  1697.         
  1698.         return array($usekey,false,$seqname);
  1699.     }
  1700.     
  1701.     
  1702.     
  1703.     /* =========================================================== */
  1704.     /*  Major Private Methods - the core part!              */
  1705.     /* =========================================================== */
  1706.  
  1707.  
  1708.     
  1709.     /**
  1710.      * clear the cache values for this class  - normally done on insert/update etc.
  1711.      *
  1712.      * @access private
  1713.      * @return void
  1714.      */
  1715.     function _clear_cache()
  1716.     {
  1717.         global $_DB_DATAOBJECT;
  1718.         
  1719.         $class = get_class($this);
  1720.         
  1721.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1722.             $this->debug("Clearing Cache for ".$class,1);
  1723.         }
  1724.         
  1725.         if (@$_DB_DATAOBJECT['CACHE'][$class]) {
  1726.             unset($_DB_DATAOBJECT['CACHE'][$class]);
  1727.         }
  1728.     }
  1729.  
  1730.     /**
  1731.      * connects to the database
  1732.      *
  1733.      *
  1734.      * TODO: tidy this up - This has grown to support a number of connection options like
  1735.      *  a) dynamic changing of ini file to change which database to connect to
  1736.      *  b) multi data via the table_{$table} = dsn ini option
  1737.      *  c) session based storage.
  1738.      *
  1739.      * @access private
  1740.      * @return true | PEAR::error
  1741.      */
  1742.     function _connect()
  1743.     {
  1744.         global $_DB_DATAOBJECT;
  1745.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  1746.             DB_DataObject::_loadConfig();
  1747.         }
  1748.  
  1749.         // is it already connected ?
  1750.  
  1751.         if ($this->_database_dsn_md5 && @$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]) {
  1752.             if (PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  1753.                 return DB_DataObject::raiseError(
  1754.                         $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->message,
  1755.                         $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->code, PEAR_ERROR_DIE
  1756.                 );
  1757.                  
  1758.             }
  1759.  
  1760.             if (!$this->_database) {
  1761.                 $this->_database = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
  1762.             }
  1763.             
  1764.         }
  1765.  
  1766.         // it's not currently connected!
  1767.         // try and work out what to use for the dsn !
  1768.  
  1769.         $options= &$_DB_DATAOBJECT['CONFIG'];
  1770.         $dsn = @$this->_database_dsn;
  1771.  
  1772.         if (!$dsn) {
  1773.             if (!$this->_database) {
  1774.                 $this->_database = @$options["table_{$this->__table}"];
  1775.             }
  1776.             if (@$this->_database && @$options["database_{$this->_database}"])  {
  1777.                 $dsn = $options["database_{$this->_database}"];
  1778.             } else if ($options['database']) {
  1779.                 $dsn = $options['database'];
  1780.             }
  1781.         }
  1782.  
  1783.         $this->_database_dsn_md5 = md5($dsn);
  1784.  
  1785.         if (@$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]) {
  1786.             if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1787.                 $this->debug("USING CACHED CONNECTION", "CONNECT",3);
  1788.             }
  1789.             if (!$this->_database) {
  1790.                 $this->_database = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn["database"];
  1791.             }
  1792.             return true;
  1793.         }
  1794.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1795.             $this->debug("NEW CONNECTION", "CONNECT",3);
  1796.             /* actualy make a connection */
  1797.             $this->debug("{$dsn} {$this->_database_dsn_md5}", "CONNECT",3);
  1798.         }
  1799.  
  1800.         $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = DB::connect($dsn);
  1801.         
  1802.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1803.             $this->debug(serialize($_DB_DATAOBJECT['CONNECTIONS']), "CONNECT",5);
  1804.         }
  1805.         if (PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  1806.             return DB_DataObject::raiseError(
  1807.                         $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->message,
  1808.                         $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->code, PEAR_ERROR_DIE
  1809.             );
  1810.  
  1811.         }
  1812.  
  1813.         if (!$this->_database) {
  1814.             $this->_database = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn["database"];
  1815.         }
  1816.         
  1817.         // Oracle need to optimize for portibility - not sure exactly what this does though :)
  1818.         $c = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1819.         if ($c->phptype == 'oci8') {
  1820.             $c->setOption('optimize','portability');
  1821.  
  1822.         }
  1823.         
  1824.         
  1825.         
  1826.         
  1827.         
  1828.  
  1829.         return true;
  1830.     }
  1831.  
  1832.     /**
  1833.      * sends query to database - this is the private one that must work 
  1834.      *   - internal functions use this rather than $this->query()
  1835.      *
  1836.      * @param  string  $string
  1837.      * @access private
  1838.      * @return mixed none or PEAR_Error
  1839.      */
  1840.     function _query($string)
  1841.     {
  1842.         global $_DB_DATAOBJECT;
  1843.         $this->_connect();
  1844.         
  1845.  
  1846.         $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1847.  
  1848.         $options = &$_DB_DATAOBJECT['CONFIG'];
  1849.  
  1850.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1851.             $this->debug($string,$log="QUERY");
  1852.             
  1853.         }
  1854.         
  1855.         if (strtoupper($string) == 'BEGIN') {
  1856.             $DB->autoCommit(false);
  1857.             // db backend adds begin anyway from now on..
  1858.             return true;
  1859.         }
  1860.         if (strtoupper($string) == 'COMMIT') {
  1861.             $DB->commit();
  1862.             $DB->autoCommit(true);
  1863.             return true;
  1864.         }
  1865.         
  1866.         if (strtoupper($string) == 'ROLLBACK') {
  1867.             $DB->rollback();
  1868.             $DB->autoCommit(true);
  1869.             return true;
  1870.         }
  1871.         
  1872.  
  1873.         if (@$options['debug_ignore_updates'] &&
  1874.             (strtolower(substr(trim($string), 0, 6)) != 'select') &&
  1875.             (strtolower(substr(trim($string), 0, 4)) != 'show') &&
  1876.             (strtolower(substr(trim($string), 0, 8)) != 'describe')) {
  1877.  
  1878.             $this->debug('Disabling Update as you are in debug mode');
  1879.             return DB_DataObject::raiseError("Disabling Update as you are in debug mode", null) ;
  1880.  
  1881.         }
  1882.         if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 1) {
  1883.             // this will only work when PEAR:DB supports it.
  1884.             //$this->debug($DB->getAll('explain ' .$string,DB_FETCHMODE_ASSOC), $log="sql",2);
  1885.         }
  1886.         
  1887.         // some sim
  1888.         $t= explode(' ',microtime());
  1889.         $_DB_DATAOBJECT['QUERYENDTIME'] = $time = $t[0]+$t[1];
  1890.          
  1891.         $result = $DB->query($string);
  1892.         
  1893.         
  1894.        
  1895.  
  1896.         if (DB::isError($result)) {
  1897.             if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1898.                 DB_DataObject::debug($string, "SENT");
  1899.                 
  1900.             }
  1901.             return $result;
  1902.         }
  1903.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1904.             $t= explode(' ',microtime());
  1905.             $_DB_DATAOBJECT['QUERYENDTIME'] = $t[0]+$t[1];
  1906.             $this->debug('QUERY DONE IN  '.($t[0]+$t[1]-$time)." seconds", 'query',1);
  1907.         }
  1908.         switch (strtolower(substr(trim($string),0,6))) {
  1909.             case 'insert':
  1910.             case 'update':
  1911.             case 'delete':
  1912.                 return $DB->affectedRows();;
  1913.         }
  1914.         // lets hope that copying the result object is OK!
  1915.         $_DB_resultid  = count($_DB_DATAOBJECT['RESULTS']); // add to the results stuff...
  1916.         $_DB_DATAOBJECT['RESULTS'][$_DB_resultid] = $result; 
  1917.         $this->_DB_resultid = $_DB_resultid;
  1918.         
  1919.         $this->N = 0;
  1920.         if (@$_DB_DATAOBJECT['CONFIG']['debug']) {
  1921.             $this->debug(serialize($result), 'RESULT',5);
  1922.         }
  1923.         if (method_exists($result, 'numrows')) {
  1924.             $this->N = $result->numrows();
  1925.         }
  1926.     }
  1927.  
  1928.     /**
  1929.      * Builds the WHERE based on the values of of this object
  1930.      *
  1931.      * @param   mixed   $keys
  1932.      * @param   array   $filter (used by update to only uses keys in this filter list).
  1933.      * @param   array   $negative_filter (used by delete to prevent deleting using the keys mentioned..)
  1934.      * @access  private
  1935.      * @return  string
  1936.      */
  1937.     function _build_condition($keys, $filter = array(),$negative_filter=array())
  1938.     {
  1939.         global $_DB_DATAOBJECT;
  1940.         $DB             = &$this->getDatabaseConnection();
  1941.         $quoteEntities  = @$_DB_DATAOBJECT['CONFIG']['quote_entities'];
  1942.         // if we dont have query vars.. - reset them.
  1943.         if (!isset($this->_query)) {
  1944.             $x = new DB_DataObject;
  1945.             $this->_query= $x->_query;
  1946.         }
  1947.  
  1948.         foreach($keys as $k => $v) {
  1949.             // index keys is an indexed array
  1950.             /* these filter checks are a bit suspicious..
  1951.                 - need to check that update really wants to work this way */
  1952.  
  1953.             if ($filter) {
  1954.                 if (!in_array($k, $filter)) {
  1955.                     continue;
  1956.                 }
  1957.             }
  1958.             if ($negative_filter) {
  1959.                 if (in_array($k, $negative_filter)) {
  1960.                     continue;
  1961.                 }
  1962.             }
  1963.             if (!isset($this->$k)) {
  1964.                 continue;
  1965.             }
  1966.             
  1967.             $kSql = $quoteEntities 
  1968.                 ? ( $DB->quoteEntity($this->__table) . '.' . $DB->quoteEntity($k) )  
  1969.                 : "{$this->__table}.{$k}";
  1970.              
  1971.              
  1972.             
  1973.             if (is_a($this->$k,'db_dataobject_cast')) {
  1974.                 $value = $this->$k->toString($v,$dbtype);
  1975.                 if (PEAR::isError($value)) {
  1976.                     DB_DataObject::raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
  1977.                     return false;
  1978.                 }
  1979.                 if ($value == 'NULL') {
  1980.                     $value = 'IS NULL';
  1981.                 }
  1982.                 $this->whereAdd(" $kSql = $value");
  1983.                 continue;
  1984.             }
  1985.             
  1986.             if (strtolower($this->$k) === 'null') {
  1987.                 $this->whereAdd(" $kSql  IS NULL");
  1988.                 continue;
  1989.             }
  1990.             
  1991.  
  1992.             if ($v & DB_DATAOBJECT_STR) {
  1993.                 $this->whereAdd(" $kSql  = " . $DB->quote($this->$k) );
  1994.                 continue;
  1995.             }
  1996.             if (is_numeric($this->$k)) {
  1997.                 $this->whereAdd(" $kSql = {$this->$k}");
  1998.                 continue;
  1999.             }
  2000.             /* this is probably an error condition! */
  2001.             $this->whereAdd(" $kSql = 0");
  2002.         }
  2003.     }
  2004.  
  2005.     /**
  2006.      * autoload Class relating to a table
  2007.      * (depreciated - use ::factory)
  2008.      *
  2009.      * @param  string  $table  table
  2010.      * @access private
  2011.      * @return string classname on Success
  2012.      */
  2013.     function staticAutoloadTable($table)
  2014.     {
  2015.         global $_DB_DATAOBJECT;
  2016.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2017.             DB_DataObject::_loadConfig();
  2018.         }
  2019.         $class = $_DB_DATAOBJECT['CONFIG']['class_prefix'] . preg_replace('/[^A-Z]/i','_',ucfirst($table));
  2020.         $class = (class_exists($class)) ? $class  : DB_DataObject::_autoloadClass($class);
  2021.         return $class;
  2022.     }
  2023.     
  2024.     
  2025.      /**
  2026.      * classic factory method for loading a table class
  2027.      * usage: $do = DB_DataObject::factory('person')
  2028.      * WARNING - this may emit a include error if the file does not exist..
  2029.      * use @ to silence it (if you are sure it is acceptable)
  2030.      * eg. $do = @DB_DataObject::factory('person')
  2031.      *
  2032.      * table name will eventually be databasename/table
  2033.      * - and allow modular dataobjects to be written..
  2034.      * (this also helps proxy creation)
  2035.      *
  2036.      *
  2037.      * @param  string  $table  table
  2038.      * @access private
  2039.      * @return DataObject|PEAR_Error 
  2040.      */
  2041.     
  2042.     
  2043.  
  2044.     function factory($table) {
  2045.         global $_DB_DATAOBJECT;
  2046.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2047.             DB_DataObject::_loadConfig();
  2048.         }
  2049.         $class = $_DB_DATAOBJECT['CONFIG']['class_prefix'] . preg_replace('/[^A-Z]/i','_',ucfirst($table));
  2050.         
  2051.         $class = (class_exists($class)) ? $class  : DB_DataObject::_autoloadClass($class);
  2052.         
  2053.         // proxy = full|light
  2054.         if (!$class && isset($_DB_DATAOBJECT['CONFIG']['proxy'])) { 
  2055.             $proxyMethod = 'getProxy'.$_DB_DATAOBJECT['CONFIG']['proxy'];
  2056.             
  2057.             require_once 'DB/DataObject/Generator.php';
  2058.             $d = new DB_DataObject;
  2059.            
  2060.             $d->__table = $table;
  2061.             $d->_connect();
  2062.             
  2063.             $x = new DB_DataObject_Generator;
  2064.             return $x->$proxyMethod( $d->_database, $table);
  2065.         }
  2066.         
  2067.         if (!$class) {
  2068.             return DB_DataObject::raiseError(
  2069.                 "factory could not find class $class from $table",
  2070.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2071.         }
  2072.  
  2073.         return new $class;
  2074.     }
  2075.     /**
  2076.      * autoload Class
  2077.      *
  2078.      * @param  string  $class  Class
  2079.      * @access private
  2080.      * @return string classname on Success
  2081.      */
  2082.     function _autoloadClass($class)
  2083.     {
  2084.         global $_DB_DATAOBJECT;
  2085.         
  2086.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2087.             DB_DataObject::_loadConfig();
  2088.         }
  2089.         $table   = substr($class,strlen($_DB_DATAOBJECT['CONFIG']['class_prefix']));
  2090.  
  2091.         // only include the file if it exists - and barf badly if it has parse errors :)
  2092.         if (@$_DB_DATAOBJECT['CONFIG']['proxy'] && empty($_DB_DATAOBJECT['CONFIG']['class_location'])) {
  2093.             return false;
  2094.         }
  2095.         
  2096.         $file = $_DB_DATAOBJECT['CONFIG']['class_location'].'/'.preg_replace('/[^A-Z]/i','_',ucfirst($table)).".php";
  2097.         
  2098.         if (!file_exists($file)) {
  2099.             DB_DataObject::raiseError(
  2100.                 "autoload:Could not find class {$class} using class_location value", 
  2101.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2102.             return false;
  2103.         }
  2104.         
  2105.         include_once $file;
  2106.         
  2107.         
  2108.         
  2109.         
  2110.         if (!class_exists($class)) {
  2111.             DB_DataObject::raiseError(
  2112.                 "autoload:Could not autoload {$class}", 
  2113.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2114.             return false;
  2115.         }
  2116.         return $class;
  2117.     }
  2118.     
  2119.     
  2120.     
  2121.     /**
  2122.      * Have the links been loaded?
  2123.      * if they have it contains a array of those variables.
  2124.      *
  2125.      * @access  private
  2126.      * @var     boolean | array
  2127.      */
  2128.     var $_link_loaded = false;
  2129.     
  2130.     /**
  2131.     * Get the links associate array  as defined by the links.ini file.
  2132.     * 
  2133.     *
  2134.     * Experimental... - 
  2135.     * Should look a bit like
  2136.     *       [local_col_name] => "related_tablename:related_col_name"
  2137.     * 
  2138.     * 
  2139.     * @return   array    key value of 
  2140.     * @access   public
  2141.     * @see      DB_DataObject::getLinks(), DB_DataObject::getLink()
  2142.     */
  2143.     
  2144.     function links()
  2145.     {
  2146.         global $_DB_DATAOBJECT;
  2147.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2148.             DB_DataObject::_loadConfig();
  2149.         }
  2150.         if (isset($_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table])) {
  2151.             return $_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table];
  2152.         }
  2153.         return false;
  2154.     }
  2155.     /**
  2156.      * load related objects
  2157.      *
  2158.      * There are two ways to use this, one is to set up a <dbname>.links.ini file
  2159.      * into a static property named <dbname>.links and specifies the table joins,
  2160.      * the other highly dependent on naming columns 'correctly' :)
  2161.      * using colname = xxxxx_yyyyyy
  2162.      * xxxxxx = related table; (yyyyy = user defined..)
  2163.      * looks up table xxxxx, for value id=$this->xxxxx
  2164.      * stores it in $this->_xxxxx_yyyyy
  2165.      * you can change what object vars the links are stored in by 
  2166.      * changeing the format parameter
  2167.      *
  2168.      *
  2169.      * @param  string format (default _%s) where %s is the table name.
  2170.      * @author Tim White <tim@cyface.com>
  2171.      * @access public
  2172.      * @return boolean , true on success
  2173.      */
  2174.     function getLinks($format = '_%s')
  2175.     {
  2176.         global $_DB_DATAOBJECT;
  2177.         // get table will load the options.
  2178.         if ($this->_link_loaded) {
  2179.             return true;
  2180.         }
  2181.         $this->_link_loaded = false;
  2182.         $cols  = $this->table();
  2183.         if (!isset($_DB_DATAOBJECT['LINKS'][$this->_database])) {
  2184.             return false;
  2185.         }
  2186.         $links = &$_DB_DATAOBJECT['LINKS'][$this->_database];
  2187.         /* if you define it in the links.ini file with no entries */
  2188.         if (isset($links[$this->__table]) && (!@$links[$this->__table])) {
  2189.             return false;
  2190.         }
  2191.         $loaded = array();
  2192.         if (@$links[$this->__table]) {
  2193.             
  2194.             foreach($links[$this->__table] as $key => $match) {
  2195.                 list($table,$link) = explode(':', $match);
  2196.                 $k = sprintf($format, str_replace('.', '_', $key));
  2197.                 // makes sure that '.' is the end of the key;
  2198.                 if ($p = strpos($key,'.')) {
  2199.                       $key = substr($key, 0, $p);
  2200.                 }
  2201.                 
  2202.                 $this->$k = $this->getLink($key, $table, $link);
  2203.                 if (is_object($this->$k)) {
  2204.                     $loaded[] = $k; 
  2205.                 }
  2206.             }
  2207.             $this->_link_loaded = $loaded;
  2208.             return true;
  2209.         }
  2210.         foreach (array_keys($cols) as $key) {
  2211.             if (!($p = strpos($key, '_'))) {
  2212.                 continue;
  2213.             }
  2214.             // does the table exist.
  2215.             $k =sprintf($format, $key);
  2216.             $this->$k = $this->getLink($key);
  2217.             if (is_object($this->$k)) {
  2218.                 $loaded[] = $k; 
  2219.             }
  2220.         }
  2221.         $this->_link_loaded = $loaded;
  2222.         return true;
  2223.     }
  2224.  
  2225.     /**
  2226.      * return name from related object
  2227.      *
  2228.      * There are two ways to use this, one is to set up a <dbname>.links.ini file
  2229.      * into a static property named <dbname>.links and specifies the table joins,
  2230.      * the other is highly dependant on naming columns 'correctly' :)
  2231.      *
  2232.      * NOTE: the naming convention is depreciated!!! - use links.ini
  2233.      *
  2234.      * using colname = xxxxx_yyyyyy
  2235.      * xxxxxx = related table; (yyyyy = user defined..)
  2236.      * looks up table xxxxx, for value id=$this->xxxxx
  2237.      * stores it in $this->_xxxxx_yyyyy
  2238.      *
  2239.      * you can also use $this->getLink('thisColumnName','otherTable','otherTableColumnName')
  2240.      *
  2241.      *
  2242.      * @param string $row    either row or row.xxxxx
  2243.      * @param string $table  name of table to look up value in
  2244.      * @param string $link   name of column in other table to match
  2245.      * @author Tim White <tim@cyface.com>
  2246.      * @access public
  2247.      * @return mixed object on success
  2248.      */
  2249.     function &getLink($row, $table = null, $link = false)
  2250.     {
  2251.         /* see if autolinking is available
  2252.          * This will do a recursive call!
  2253.          */
  2254.         global $_DB_DATAOBJECT;
  2255.  
  2256.         $this->table(); /* make sure the links are loaded */
  2257.  
  2258.         if ($table === null) {
  2259.             $links = array();
  2260.             if (isset($_DB_DATAOBJECT['LINKS'][$this->_database])) {
  2261.                 $links = &$_DB_DATAOBJECT['LINKS'][$this->_database];
  2262.             }
  2263.  
  2264.             if (isset($links[$this->__table])) {
  2265.                 if (@$links[$this->__table][$row]) {
  2266.                     list($table,$link) = explode(':', $links[$this->__table][$row]);
  2267.                     if ($p = strpos($row,".")) {
  2268.                         $row = substr($row,0,$p);
  2269.                     }
  2270.                     return $r = &$this->getLink($row,$table,$link);
  2271.                 } else {
  2272.                     DB_DataObject::raiseError(
  2273.                         "getLink: $row is not defined as a link (normally this is ok)", 
  2274.                         DB_DATAOBJECT_ERROR_NODATA);
  2275.                         
  2276.                     return false; // technically a possible error condition?
  2277.                 }
  2278.             } else { // use the old _ method
  2279.                 if (!($p = strpos($row, '_'))) {
  2280.                     return null;
  2281.                 }
  2282.                 $table = substr($row, 0, $p);
  2283.                 return $r = &$this->getLink($row, $table);
  2284.             }
  2285.         }
  2286.         if (!isset($this->$row)) {
  2287.             DB_DataObject::raiseError("getLink: row not set $row", DB_DATAOBJECT_ERROR_NODATA);
  2288.             return false;
  2289.         }
  2290.         
  2291.         // check to see if we know anything about this table..
  2292.         
  2293.         $obj = $this->factory($table);
  2294.         
  2295.         if (PEAR::isError($obj)) {
  2296.             DB_DataObject::raiseError(
  2297.                 "getLink:Could not find class for row $row, table $table", 
  2298.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2299.             return false;
  2300.         }
  2301.         if ($link) {
  2302.             if ($obj->get($link, $this->$row)) {
  2303.                 return $obj;
  2304.             } else {
  2305.                 return false;
  2306.             }
  2307.         }
  2308.         
  2309.         if ($obj->get($this->$row)) {
  2310.             return $obj;
  2311.         }
  2312.         return false;
  2313.     }
  2314.  
  2315.     /**
  2316.      * IS THIS SUPPORTED/USED ANYMORE???? 
  2317.      *return a list of options for a linked table
  2318.      *
  2319.      * This is highly dependant on naming columns 'correctly' :)
  2320.      * using colname = xxxxx_yyyyyy
  2321.      * xxxxxx = related table; (yyyyy = user defined..)
  2322.      * looks up table xxxxx, for value id=$this->xxxxx
  2323.      * stores it in $this->_xxxxx_yyyyy
  2324.      *
  2325.      * @access public
  2326.      * @return array of results (empty array on failure)
  2327.      */
  2328.     function &getLinkArray($row, $table = null)
  2329.     {
  2330.         global $_DB_DATAOBJECT;
  2331.  
  2332.         $this->table(); /* make sure the links are loaded */
  2333.  
  2334.  
  2335.         $ret = array();
  2336.         if (!$table) {
  2337.             $links = array();
  2338.             if (isset($_DB_DATAOBJECT['LINKS'][$this->_database])) {
  2339.                 $links = &$_DB_DATAOBJECT['LINKS'][$this->_database];
  2340.             }
  2341.  
  2342.             if (@$links[$this->__table][$row]) {
  2343.                 list($table,$link) = explode(':',$links[$this->__table][$row]);
  2344.             } else {
  2345.                 if (!($p = strpos($row,'_'))) {
  2346.                     return $ret;
  2347.                 }
  2348.                 $table = substr($row,0,$p);
  2349.             }
  2350.         }
  2351.         $c  = $this->factory($table);
  2352.         if (PEAR::isError($c)) {
  2353.             DB_DataObject::raiseError("getLinkArray:Could not find class for row $row, table $table", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2354.             return $ret;
  2355.         }
  2356.  
  2357.         // if the user defined method list exists - use it...
  2358.         if (method_exists($c, 'listFind')) {
  2359.             $c->listFind($this->id);
  2360.         } else {
  2361.             $c->find();
  2362.         }
  2363.         while ($c->fetch()) {
  2364.             $ret[] = $c;
  2365.         }
  2366.         return $ret;
  2367.     }
  2368.  
  2369.     /**
  2370.      * The JOIN condition
  2371.      *
  2372.      * @access  private
  2373.      * @var     string
  2374.      */
  2375.     var $_join = '';
  2376.  
  2377.     /**
  2378.      * joinAdd - adds another dataobject to this, building a joined query.
  2379.      *
  2380.      * example (requires links.ini to be set up correctly)
  2381.      * // get all the images for product 24
  2382.      * $i = new DataObject_Image();
  2383.      * $pi = new DataObjects_Product_image();
  2384.      * $pi->product_id = 24; // set the product id to 24
  2385.      * $i->joinAdd($pi); // add the product_image connectoin
  2386.      * $i->find();
  2387.      * while ($i->fetch()) {
  2388.      *     // do stuff
  2389.      * }
  2390.      * // an example with 2 joins
  2391.      * // get all the images linked with products or productgroups
  2392.      * $i = new DataObject_Image();
  2393.      * $pi = new DataObject_Product_image();
  2394.      * $pgi = new DataObject_Productgroup_image();
  2395.      * $i->joinAdd($pi);
  2396.      * $i->joinAdd($pgi);
  2397.      * $i->find();
  2398.      * while ($i->fetch()) {
  2399.      *     // do stuff
  2400.      * }
  2401.      *
  2402.      *
  2403.      * @param    optional $obj       object |array    the joining object (no value resets the join)
  2404.      *                                          If you use an array here it should be in the format:
  2405.      *                                          array('local_column','remotetable:remote_column');
  2406.      *                                          if remotetable does not have a definition, you should
  2407.      *                                          use @ to hide the include error message..
  2408.      *                                      
  2409.      *
  2410.      * @param    optional $joinType  string     'LEFT'|'INNER'|'RIGHT'|'' Inner is default, '' indicates 
  2411.      *                                          just select ... from a,b,c with no join and 
  2412.      *                                          links are added as where items.
  2413.      *
  2414.      * @param    optional $joinAs    string     if you want to select the table as anther name
  2415.      *                                          useful when you want to select multiple columsn
  2416.      *                                          from a secondary table.
  2417.      
  2418.      * @param    optional $joinCol   string     The column on This objects table to match (needed
  2419.      *                                          if this table links to the child object in 
  2420.      *                                          multiple places eg.
  2421.      *                                          user->friend (is a link to another user)
  2422.      *                                          user->mother (is a link to another user..)
  2423.      *
  2424.      * @return   none
  2425.      * @access   public
  2426.      * @author   Stijn de Reede      <sjr@gmx.co.uk>
  2427.      */
  2428.     function joinAdd($obj = false, $joinType='INNER', $joinAs=false, $joinCol=false)
  2429.     {
  2430.         global $_DB_DATAOBJECT;
  2431.         if ($obj === false) {
  2432.             $this->_join = '';
  2433.             return;
  2434.         }
  2435.         
  2436.         // support for array as first argument 
  2437.         // this assumes that you dont have a links.ini for the specified table.
  2438.         // and it doesnt exist as am extended dataobject!! - experimental.
  2439.         
  2440.         $ofield = false; // object field
  2441.         $tfield = false; // this field
  2442.         $toTable = false;
  2443.         if (is_array($obj)) {
  2444.             $tfield = $obj[0];
  2445.             list($toTable,$ofield) = explode(':',$obj[1]);
  2446.             $obj = DB_DataObject::factory($toTable);
  2447.             if (!$obj) {
  2448.                 $obj = new DB_DataObject;
  2449.                 $obj->__table = $toTable;
  2450.             }
  2451.             // set the table items to nothing.. - eg. do not try and match
  2452.             // things in the child table...???
  2453.             $items = array();
  2454.         }
  2455.         
  2456.         if (!is_object($obj)) {
  2457.             DB_DataObject::raiseError("joinAdd: called without an object", DB_DATAOBJECT_ERROR_NODATA,PEAR_ERROR_DIE);
  2458.         }
  2459.         /*  make sure $this->_database is set.  */
  2460.         $DB = &$this->getDatabaseConnection();
  2461.  
  2462.         $this->table(); /* make sure the links are loaded */
  2463.  
  2464.         $links = array();
  2465.         if (isset($_DB_DATAOBJECT['LINKS'][$this->_database])) {
  2466.             $links = &$_DB_DATAOBJECT['LINKS'][$this->_database];
  2467.         }
  2468.  
  2469.         
  2470.          /* look up the links for obj table */
  2471.  
  2472.         if (!$ofield && isset($links[$obj->__table])) {
  2473.             foreach ($links[$obj->__table] as $k => $v) {
  2474.                 /* link contains {this column} = {linked table}:{linked column} */
  2475.                 $ar = explode(':', $v);
  2476.                 if ($ar[0] == $this->__table) {
  2477.                     
  2478.                     // you have explictly specified the column
  2479.                     // and the col is listed here..
  2480.                     // not sure if 1:1 table could cause probs here..
  2481.                     
  2482.                     if ($joinCol !== false) {
  2483.                         DB_DataObject::raiseError( 
  2484.                             "joinAdd: You cannot target a join column in the " .
  2485.                             "'link from' table ({$obj->__table}). " . 
  2486.                             "Either remove the fourth argument to joinAdd() ".
  2487.                             "({$joinCol}), or alter your links.ini file.",
  2488.                             DB_DATAOBJECT_ERROR_NODATA);
  2489.                         return false;
  2490.                     }
  2491.                 
  2492.                     $ofield = $k;
  2493.                     $tfield = $ar[1];
  2494.                     break;
  2495.                 }
  2496.             }
  2497.         }
  2498.  
  2499.         /* otherwise see if there are any links from this table to the obj. */
  2500.  
  2501.         if (($ofield === false) &&isset($links[$this->__table])) {
  2502.             foreach ($links[$this->__table] as $k => $v) {
  2503.                 /* link contains {this column} = {linked table}:{linked column} */
  2504.                 $ar = explode(':', $v);
  2505.                 if ($ar[0] == $obj->__table) {
  2506.                     if ($joinCol !== false) {
  2507.                         if ($k == $joinCol) {
  2508.                             $tfield = $k;
  2509.                             $ofield = $ar[1];
  2510.                             break;
  2511.                         } else {
  2512.                             continue;
  2513.                         }
  2514.                     } else {
  2515.                         $tfield = $k;
  2516.                         $ofield = $ar[1];
  2517.                         break;
  2518.                     }
  2519.                 }
  2520.             }
  2521.         }
  2522.         
  2523.         /* did I find a conneciton between them? */
  2524.  
  2525.         if ($ofield === false) {
  2526.             DB_DataObject::raiseError(
  2527.                 "joinAdd: {$obj->__table} has no link with {$this->__table}",
  2528.                 DB_DATAOBJECT_ERROR_NODATA);
  2529.             return false;
  2530.         }
  2531.         $joinType = strtoupper($joinType);
  2532.         if ($joinAs === false) {
  2533.             $joinAs = $obj->__table;
  2534.         }
  2535.         
  2536.         $quoteEntities = @$_DB_DATAOBJECT['CONFIG']['quote_entities'];        
  2537.         
  2538.         $objTable = $quoteEntities ? $DB->quoteEntity($objTable) : $obj->__table ;
  2539.         
  2540.         
  2541.         // nested (join of joined objects..)
  2542.         $appendJoin = '';
  2543.         if ($obj->_join) {
  2544.             // postgres allows nested queries, with ()'s
  2545.             // not sure what the results are with other databases..
  2546.             // may be unpredictable..
  2547.             if (in_array($DB->dsn["phptype"],array('pgsql'))) {
  2548.                 $objTable = "($objTable {$obj->_join})";
  2549.             } else {
  2550.                 $appendJoin = $obj->_join;
  2551.             }
  2552.         }
  2553.         
  2554.         
  2555.         $table = $this->__table;
  2556.         
  2557.  
  2558.         if ($quoteEntities) {
  2559.             $joinAs   = $DB->quoteEntity($joinAs);
  2560.             $table    = $DB->quoteEntity($table);     
  2561.             $ofield   = $DB->quoteEntity($ofield);    
  2562.             $tfield   = $DB->quoteEntity($tfield);    
  2563.         }
  2564.         
  2565.         $fullJoinAs = '';
  2566.         if ($obj->__table != $joinAs) {
  2567.             $fullJoinAs = "AS {$joinAs}";
  2568.         }
  2569.         
  2570.         switch ($joinType) {
  2571.             case 'INNER':
  2572.             case 'LEFT': 
  2573.             case 'RIGHT': // others??? .. cross, left outer, right outer, natural..?
  2574.                 $this->_join .= "\n {$joinType} JOIN {$objTable}  {$fullJoinAs}".
  2575.                                 " ON {$joinAs}.{$ofield}={$table}.{$tfield} {$appendJoin} ";
  2576.                 break;
  2577.             case '': // this is just a standard multitable select..
  2578.                 $this->_join .= "\n , {$objTable} {$fullJoinAs} {$appendJoin}";
  2579.                 $this->whereAdd("{$joinAs}.{$ofield}={$table}.{$tfield}");
  2580.         }
  2581.          
  2582.         // if obj only a dataobject - eg. no extended class has been defined..
  2583.         // it obvioulsy cant work out what child elements might exist...
  2584.         // untill we get on the fly querying of tables..
  2585.         if ( get_class($obj) == 'db_dataobject') {
  2586.             return true;
  2587.         }
  2588.          
  2589.         /* now add where conditions for anything that is set in the object */
  2590.     
  2591.     
  2592.     
  2593.         $items = $obj->table();
  2594.         // will return an array if no items..
  2595.         
  2596.         // only fail if we where expecting it to work (eg. not joined on a array)
  2597.         
  2598.         
  2599.         
  2600.         if (!$items) {
  2601.             DB_DataObject::raiseError(
  2602.                 "joinAdd: No table definition for {$obj->__table}", 
  2603.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2604.             return false;
  2605.         }
  2606.  
  2607.         foreach($items as $k => $v) {
  2608.             if (!isset($obj->$k)) {
  2609.                 continue;
  2610.             }
  2611.             
  2612.             $kSql = ($quoteEntities ? $DB->quoteEntity($k) : $k);
  2613.             
  2614.             
  2615.             if ($v & DB_DATAOBJECT_STR) {
  2616.                 $this->whereAdd("{$joinAs}.{$kSql} = " . $DB->quote($obj->$k));
  2617.                 continue;
  2618.             }
  2619.             if (is_numeric($obj->$k)) {
  2620.                 $this->whereAdd("{$joinAs}.{$kSql} = {$obj->$k}");
  2621.                 continue;
  2622.             }
  2623.             /* this is probably an error condition! */
  2624.             $this->whereAdd("{$joinAs}.{$kSql} = 0");
  2625.         }
  2626.         
  2627.         // and finally merge the whereAdd from the child..
  2628.         if (!$obj->_query['condition']) {
  2629.             return true;
  2630.         }
  2631.         $cond = preg_replace('/^\sWHERE/i','',$obj->_query['condition']);
  2632.         
  2633.         $this->whereAdd("($cond)");
  2634.         return true;
  2635.  
  2636.     }
  2637.  
  2638.     /**
  2639.      * Copies items that are in the table definitions from an
  2640.      * array or object into the current object
  2641.      * will not override key values.
  2642.      *
  2643.      *
  2644.      * @param    array | object  $from
  2645.      * @param    string  $format eg. map xxxx_name to $object->name using 'xxxx_%s' (defaults to %s - eg. name -> $object->name
  2646.      * @access   public
  2647.      * @return   true on success or array of key=>setValue error message
  2648.      */
  2649.     function setFrom(&$from, $format = '%s')
  2650.     {
  2651.         global $_DB_DATAOBJECT;
  2652.         $keys  = $this->keys();
  2653.         $items = $this->table();
  2654.         if (!$items) {
  2655.             DB_DataObject::raiseError(
  2656.                 "setFrom:Could not find table definition for {$this->__table}", 
  2657.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2658.             return;
  2659.         }
  2660.         $overload_return = array();
  2661.         foreach (array_keys($items) as $k) {
  2662.             if (in_array($k,$keys)) {
  2663.                 continue; // dont overwrite keys
  2664.             }
  2665.             if (!$k) {
  2666.                 continue; // ignore empty keys!!! what
  2667.             }
  2668.             if (is_object($from) && isset($from->{sprintf($format,$k)})) {
  2669.                 $kk = (strtolower($k) == 'from') ? '_from' : $k;
  2670.                 if (method_exists($this,'set'.$kk)) {
  2671.                     $ret = $this->{'set'.$kk}($from->{sprintf($format,$k)});
  2672.                     if (is_string($ret)) {
  2673.                         $overload_return[$k] = $ret;
  2674.                     }
  2675.                     continue;
  2676.                 }
  2677.                 $this->$k = $from->{sprintf($format,$k)};
  2678.                 continue;
  2679.             }
  2680.             
  2681.             if (is_object($from)) {
  2682.                 continue;
  2683.             }
  2684.             
  2685.             if (!isset($from[sprintf($format,$k)])) {
  2686.                 continue;
  2687.             }
  2688.             if (is_object($from[sprintf($format,$k)])) {
  2689.                 continue;
  2690.             }
  2691.             if (is_array($from[sprintf($format,$k)])) {
  2692.                 continue;
  2693.             }
  2694.             $kk = (strtolower($k) == 'from') ? '_from' : $k;
  2695.             if (method_exists($this,'set'. $kk)) {
  2696.                 $ret =  $this->{'set'.$kk}($from[sprintf($format,$k)]);
  2697.                 if (is_string($ret)) {
  2698.                     $overload_return[$k] = $ret;
  2699.                 }
  2700.                 continue;
  2701.             }
  2702.             $ret = $this->fromValue($k,$from[sprintf($format,$k)]);
  2703.             if ($ret !== true)  {
  2704.                 $overload_return[$k] = 'Not A Valid Value';
  2705.             }
  2706.             //$this->$k = $from[sprintf($format,$k)];
  2707.         }
  2708.         if ($overload_return) {
  2709.             return $overload_return;
  2710.         }
  2711.         return true;
  2712.     }
  2713.  
  2714.     /**
  2715.      * Returns an associative array from the current data
  2716.      * (kind of oblivates the idea behind DataObjects, but
  2717.      * is usefull if you use it with things like QuickForms.
  2718.      *
  2719.      * you can use the format to return things like user[key]
  2720.      * by sending it $object->toArray('user[%s]')
  2721.      *
  2722.      * will also return links converted to arrays.
  2723.      *
  2724.      * @param   string sprintf format for array
  2725.      * @access   public
  2726.      * @return   array of key => value for row
  2727.      */
  2728.  
  2729.     function toArray($format = '%s')
  2730.     {
  2731.         global $_DB_DATAOBJECT;
  2732.         $ret = array();
  2733.  
  2734.         foreach($this->table() as $k=>$v) {
  2735.              
  2736.             if (!isset($this->$k)) {
  2737.                 $ret[sprintf($format,$k)] = '';
  2738.                 continue;
  2739.             }
  2740.             // call the overloaded getXXXX() method.
  2741.             if (method_exists($this,'get'.$k)) {
  2742.                 $ret[sprintf($format,$k)] = $this->{'get'.$k}();
  2743.                 continue;
  2744.             }
  2745.             // should this call toValue() ???
  2746.             $ret[sprintf($format,$k)] = $this->$k;
  2747.         }
  2748.         if (!$this->_link_loaded) {
  2749.             return $ret;
  2750.         }
  2751.         foreach($this->_link_loaded as $k) {
  2752.             $ret[sprintf($format,$k)] = $this->$k->toArray();
  2753.         
  2754.         }
  2755.         
  2756.         return $ret;
  2757.     }
  2758.  
  2759.     /**
  2760.      * validate - override this to set up your validation rules
  2761.      *
  2762.      * validate the current objects values either just testing strings/numbers or
  2763.      * using the user defined validate{Row name}() methods.
  2764.      * will attempt to call $this->validate{column_name}() - expects true = ok  false = ERROR
  2765.      * you can the use the validate Class from your own methods.
  2766.      *
  2767.      * @access  public
  2768.      * @return  array of validation results or true
  2769.      */
  2770.     function validate()
  2771.     {
  2772.         require_once 'Validate.php';
  2773.         $table = &$this->table();
  2774.         $ret   = array();
  2775.  
  2776.         foreach($table as $key => $val) {
  2777.             // ignore things that are not set. ?
  2778.             if (!isset($this->$key)) {
  2779.                 continue;
  2780.             }
  2781.             // call user defined validation
  2782.             $method = "Validate" . ucfirst($key);
  2783.             if (method_exists($this, $method)) {
  2784.                 $ret[$key] = $this->$method();
  2785.                 continue;
  2786.             }
  2787.             // if the string is empty.. assume it is ok..
  2788.             if (!strlen($this->$key)) {
  2789.                 continue;
  2790.             }
  2791.             
  2792.             switch ($val) {
  2793.                 case  DB_DATAOBJECT_STR:
  2794.                     $ret[$key] = Validate::string($this->$key, VALIDATE_PUNCTUATION . VALIDATE_NAME);
  2795.                     continue;
  2796.                 case  DB_DATAOBJECT_INT:
  2797.                     $ret[$key] = Validate::number($this->$key, array('decimal'=>'.'));
  2798.                     continue;
  2799.             }
  2800.         }
  2801.  
  2802.         foreach ($ret as $key => $val) {
  2803.             if ($val == false) return $ret;
  2804.         }
  2805.         return true; // everything is OK.
  2806.     }
  2807.  
  2808.     /**
  2809.      * Gets the DB object related to an object - so you can use funky peardb stuf with it :)
  2810.      *
  2811.      * @access public
  2812.      * @return object The DB connection
  2813.      */
  2814.     function &getDatabaseConnection()
  2815.     {
  2816.         global $_DB_DATAOBJECT;
  2817.  
  2818.         if (($e = $this->_connect()) !== true) {
  2819.             return $e;
  2820.         }
  2821.         if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  2822.             return  false;
  2823.         }
  2824.         return $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  2825.     }
  2826.  
  2827.  
  2828.     /**
  2829.      * Gets the DB result object related to the objects active query
  2830.      *  - so you can use funky pear stuff with it - like pager for example.. :)
  2831.      *
  2832.      * @access public
  2833.      * @return object The DB result object
  2834.      */
  2835.      
  2836.     function &getDatabaseResult()
  2837.     {
  2838.         global $_DB_DATAOBJECT;
  2839.         $this->_connect();
  2840.         if (!isset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) {
  2841.             return  false;
  2842.         }
  2843.         return $_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid];
  2844.     }
  2845.  
  2846.     /**
  2847.      * Overload Extension support
  2848.      *  - enables setCOLNAME/getCOLNAME
  2849.      *  if you define a set/get method for the item it will be called.
  2850.      * otherwise it will just return/set the value.
  2851.      * NOTE this currently means that a few Names are NO-NO's 
  2852.      * eg. links,link,linksarray, from, Databaseconnection,databaseresult
  2853.      *
  2854.      * note 
  2855.      *  - set is automatically called by setFrom.
  2856.      *   - get is automatically called by toArray()
  2857.      *  
  2858.      * setters return true on success. = strings on failure
  2859.      * getters return the value!
  2860.      *
  2861.      * this fires off trigger_error - if any problems.. pear_error, 
  2862.      * has problems with 4.3.2RC2 here
  2863.      *
  2864.      * @access public
  2865.      * @return true?
  2866.      * @see overload
  2867.      */
  2868.  
  2869.     
  2870.     function _call($method,$params,&$return) {
  2871.         
  2872.         //$this->debug("ATTEMPTING OVERLOAD? $method");
  2873.         
  2874.         // ignore constructors : - mm
  2875.         if ($method == get_class($this)) {
  2876.             return true;
  2877.         }
  2878.         $type = strtolower(substr($method,0,3));
  2879.         $class = get_class($this);
  2880.         if (($type != 'set') && ($type != 'get')) {
  2881.             return false;
  2882.         }
  2883.          
  2884.         
  2885.         
  2886.         // deal with naming conflick of setFrom = this is messy ATM!
  2887.         
  2888.         if (strtolower($method) == 'set_from') {
  2889.             $this->from = $params[0];
  2890.             return $return = true;
  2891.         }
  2892.         
  2893.         $element = substr($method,3);
  2894.         if ($element{0} == '_') {
  2895.             return false;
  2896.         }
  2897.          
  2898.         
  2899.         // dont you just love php's case insensitivity!!!!
  2900.         
  2901.         $array =  array_keys(get_class_vars($class));
  2902.         
  2903.         if (!in_array($element,$array)) {
  2904.             // munge case
  2905.             foreach($array as $k) {
  2906.                 $case[strtolower($k)] = $k;
  2907.             }
  2908.             // does it really exist?
  2909.             if (!isset($case[$element])) {
  2910.                 return false;            
  2911.             }
  2912.             // use the mundged case
  2913.             $element = $case[$element]; // real case !
  2914.         }
  2915.         
  2916.         
  2917.         if ($type == 'get') {
  2918.             $return = $this->toValue($element,isset($params[0]) ? $params[0] : null);
  2919.             return true;
  2920.         }
  2921.         
  2922.         
  2923.         $return = $this->fromValue($element, $params[0]);
  2924.          
  2925.         return true;
  2926.             
  2927.           
  2928.     }
  2929.         
  2930.     
  2931.     /**
  2932.     * standard set* implementation.
  2933.     *
  2934.     * takes data and uses it to set dates/strings etc.
  2935.     * normally called from __call..  
  2936.     *
  2937.     * Current supports
  2938.     *   date      = using (standard time format, or unixtimestamp).... so you could create a method :
  2939.     *               function setLastread($string) { $this->fromValue('lastread',strtotime($string)); }
  2940.     *
  2941.     *   time      = using strtotime 
  2942.     *   datetime  = using  same as date - accepts iso standard or unixtimestamp.
  2943.     *   string    = typecast only..
  2944.     * 
  2945.     * TODO: add formater:: eg. d/m/Y for date! ???
  2946.     *
  2947.     * @param   string       column of database
  2948.     * @param   mixed        value to assign
  2949.     
  2950.     *
  2951.     * @return   true| false     (False on error)
  2952.     * @access   public 
  2953.     * @see      DB_DataObject::_call
  2954.     */
  2955.   
  2956.     
  2957.     function fromValue($col,$value) 
  2958.     {
  2959.         $cols = $this->table();
  2960.         // dont know anything about this col..
  2961.         if (!isset($cols[$col])) {
  2962.             $this->$col = $value;
  2963.             return true;
  2964.         }
  2965.         //echo "FROM VALUE $col, {$cols[$col]}, $value\n";
  2966.         switch (true) {
  2967.         
  2968.             case ((strtolower($value) == 'null') && (!($cols[$col] & DB_DATAOBJECT_NOTNULL))):
  2969.             case (is_object($value) && is_a($value,'DB_DataObject_Cast')): 
  2970.             
  2971.                 $this->$col = $value;
  2972.                 return true;
  2973.                 
  2974.             // fail on setting null on a not null field..
  2975.             case ((strtolower($value) == 'null') && ($cols[$col] & DB_DATAOBJECT_NOTNULL)):
  2976.                 return false;
  2977.         
  2978.             case (($cols[$col] & DB_DATAOBJECT_DATE) &&  ($cols[$col] & DB_DATAOBJECT_TIME)):
  2979.                 
  2980.                 if (is_numeric($value)) {
  2981.                     $this->$col = date('Y-m-d H:i:s', $value);
  2982.                     return true;
  2983.                 }
  2984.               
  2985.                 // eak... - no way to validate date time otherwise...
  2986.                 $this->$col = (string) $value;
  2987.                 return true;
  2988.             
  2989.             case ($cols[$col] & DB_DATAOBJECT_DATE):
  2990.                 if (is_numeric($value)) {
  2991.                     $this->$col = date('Y-m-d',$value);
  2992.                     return true;
  2993.                 }
  2994.                 // try date!!!!
  2995.                 require_once 'Date.php';
  2996.                 $x = new Date($value);
  2997.                 $this->$col = $x->format("%Y-%m-%d");
  2998.                 return true;
  2999.             
  3000.             case ($cols[$col] & DB_DATAOBJECT_TIME):
  3001.                 $guess = strtotime($value);
  3002.                 if ($guess != -1) {
  3003.                      $this->$col = date('H:i:s', $guess);
  3004.                     return $return = true;
  3005.                 }
  3006.                 // otherwise an error in type...
  3007.                 return false;
  3008.             
  3009.            
  3010.             
  3011.             case ($cols[$col] & DB_DATAOBJECT_STR):
  3012.                 
  3013.                 $this->$col = (string) $value;
  3014.                 return true;
  3015.                 
  3016.             // todo : floats numerics and ints...
  3017.             default:
  3018.                 $this->$col = $value;
  3019.                 return true;
  3020.         }
  3021.     
  3022.     
  3023.     
  3024.     }
  3025.      /**
  3026.     * standard get* implementation.
  3027.     *
  3028.     *  with formaters..
  3029.     * supported formaters:  
  3030.     *   date/time : %d/%m/%Y (eg. php strftime) or pear::Date 
  3031.     *   numbers   : %02d (eg. sprintf)
  3032.     *  NOTE you will get unexpected results with times like 0000-00-00 !!!
  3033.     *
  3034.     *
  3035.     * 
  3036.     * @param   string       column of database
  3037.     * @param   format       foramt
  3038.     *
  3039.     * @return   true     Description
  3040.     * @access   public 
  3041.     * @see      DB_DataObject::_call(),strftime(),Date::format()
  3042.     */
  3043.     function toValue($col,$format = null) 
  3044.     {
  3045.         if (is_null($format)) {
  3046.             return $this->$col;
  3047.         }
  3048.         $cols = $this->table();
  3049.         switch (true) {
  3050.             case (($cols[$col] & DB_DATAOBJECT_DATE) &&  ($cols[$col] & DB_DATAOBJECT_TIME)):
  3051.                 $guess = strtotime($this->$col);
  3052.                 if ($guess != -1) {
  3053.                     return strftime($format, $guess);
  3054.                 }
  3055.                 // eak... - no way to validate date time otherwise...
  3056.                 return $this->$col;
  3057.             case ($cols[$col] & DB_DATAOBJECT_DATE):
  3058.                  
  3059.                 $guess = strtotime($this->$col);
  3060.                 if ($guess > -1) {
  3061.                    
  3062.                     return strftime($format,$guess);
  3063.                 }
  3064.                 // try date!!!!
  3065.                 require_once 'Date.php';
  3066.                 $x = new Date($this->$col);
  3067.                 // this is broken - date does not work the same way..
  3068.                 return $x->format($format);
  3069.                 
  3070.             case ($cols[$col] & DB_DATAOBJECT_TIME):
  3071.                 $guess = strtotime($this->$col);
  3072.                 if ($guess > -1) {
  3073.                     return strftime($format, $guess);
  3074.                 }
  3075.                 // otherwise an error in type...
  3076.                 return $this->$col;
  3077.                 
  3078.             case ($cols[$col] &  DB_DATAOBJECT_MYSQLTIMESTAMP):
  3079.                 require_once 'Date.php';
  3080.                 
  3081.                 $x = new Date($this->$col);
  3082.                 
  3083.                 return $x->format($format);
  3084.             
  3085.             
  3086.             default:
  3087.                 return sprintf($format,$this->col);
  3088.         }
  3089.             
  3090.  
  3091.     }
  3092.     
  3093.     
  3094.     /* ----------------------- Debugger ------------------ */
  3095.  
  3096.     /**
  3097.      * Debugger. - use this in your extended classes to output debugging information.
  3098.      *
  3099.      * Uses DB_DataObject::DebugLevel(x) to turn it on
  3100.      *
  3101.      * @param    string $message - message to output
  3102.      * @param    string $logtype - bold at start
  3103.      * @param    string $level   - output level
  3104.      * @access   public
  3105.      * @return   none
  3106.      */
  3107.     function debug($message, $logtype = 0, $level = 1)
  3108.     {
  3109.         global $_DB_DATAOBJECT;
  3110.  
  3111.         if (DB_DataObject::debugLevel()<$level) {
  3112.             return;
  3113.         }
  3114.         $class = isset($this) ? get_class($this) : __CLASS__;
  3115.         if (!is_string($message)) {
  3116.             $message = print_r($message,true);
  3117.         }
  3118.         if (!ini_get('html_errors')) {
  3119.             echo "$class   : $logtype       : $message\n";
  3120.             flush();
  3121.             return;
  3122.         }
  3123.         if (!is_string($message)) {
  3124.             $message = print_r($message,true);
  3125.         }
  3126.         echo "<code><B>$class: $logtype:</B> $message</code><BR>\n";
  3127.         flush();
  3128.     }
  3129.  
  3130.     /**
  3131.      * sets and returns debug level
  3132.      * eg. DB_DataObject::debugLevel(4);
  3133.      *
  3134.      * @param   int     $v  level
  3135.      * @access  public
  3136.      * @return  none
  3137.      */
  3138.     function debugLevel($v = null)
  3139.     {
  3140.         global $_DB_DATAOBJECT;
  3141.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  3142.             DB_DataObject::_loadConfig();
  3143.         }
  3144.         if ($v !== null) {
  3145.             $_DB_DATAOBJECT['CONFIG']['debug']  = $v;
  3146.         }
  3147.         return @$_DB_DATAOBJECT['CONFIG']['debug'];
  3148.     }
  3149.  
  3150.     /**
  3151.      * Last Error that has occured
  3152.      * - use $this->_lastError or
  3153.      * $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
  3154.      *
  3155.      * @access  public
  3156.      * @var     object PEAR_Error (or false)
  3157.      */
  3158.     var $_lastError = false;
  3159.  
  3160.     /**
  3161.      * Default error handling is to create a pear error, but never return it.
  3162.      * if you need to handle errors you should look at setting the PEAR_Error callback
  3163.      * this is due to the fact it would wreck havoc on the internal methods!
  3164.      *
  3165.      * @param  int $message    message
  3166.      * @param  int $type       type
  3167.      * @param  int $behaviour  behaviour (die or continue!);
  3168.      * @access public
  3169.      * @return error object
  3170.      */
  3171.     function raiseError($message, $type = null, $behaviour = null)
  3172.     {
  3173.         global $_DB_DATAOBJECT;
  3174.         
  3175.         if ($behaviour == PEAR_ERROR_DIE && @$_DB_DATAOBJECT['CONFIG']['dont_die']) {
  3176.             $behaviour = null;
  3177.         }
  3178.         
  3179.         if (PEAR::isError($message)) {
  3180.             $error = $message;
  3181.         } else {
  3182.             $error = PEAR::raiseError($message, $type, $behaviour);
  3183.         }
  3184.         // this will never work totally with PHP's object model.
  3185.         // as this is passed on static calls (like staticGet in our case)
  3186.  
  3187.         if (@is_object($this) && is_subclass_of($this,'db_dataobject')) {
  3188.             $this->_lastError = $error;
  3189.         }
  3190.  
  3191.         $_DB_DATAOBJECT['LASTERROR'] = $error;
  3192.  
  3193.         // no checks for production here?.......
  3194.         DB_DataObject::debug($message,"ERROR",1);
  3195.         return $error;
  3196.     }
  3197.  
  3198.     /**
  3199.      * Define the global $_DB_DATAOBJECT['CONFIG'] as an alias to  PEAR::getStaticProperty('DB_DataObject','options');
  3200.      *
  3201.      * After Profiling DB_DataObject, I discoved that the debug calls where taking
  3202.      * considerable time (well 0.1 ms), so this should stop those calls happening. as
  3203.      * all calls to debug are wrapped with direct variable queries rather than actually calling the funciton
  3204.      * THIS STILL NEEDS FURTHER INVESTIGATION
  3205.      *
  3206.      * @access   public
  3207.      * @return   object an error object
  3208.      */
  3209.     function _loadConfig()
  3210.     {
  3211.         global $_DB_DATAOBJECT;
  3212.  
  3213.         $_DB_DATAOBJECT['CONFIG'] = &PEAR::getStaticProperty('DB_DataObject','options');
  3214.  
  3215.  
  3216.     }
  3217.     
  3218.     
  3219.     /* ---- LEGACY BC METHODS - NOT DOCUMENTED - See Documentation on New Methods. ---*/
  3220.     
  3221.     function _get_table() { return $this->table(); }
  3222.     function _get_keys()  { return $this->keys();  }
  3223.     
  3224.     
  3225.     
  3226.     
  3227. }
  3228. // technially 4.3.2RC1 was broken!!
  3229. // looks like 4.3.3 may have problems too....
  3230. if (!defined('DB_DATAOBJECT_NO_OVERLOAD')) {
  3231.  
  3232.     if ((phpversion() != '4.3.2-RC1') && (version_compare( phpversion(), "4.3.1") > 0)) {
  3233.         if (version_compare( phpversion(), "5") < 0) {
  3234.            overload('DB_DataObject');
  3235.         } 
  3236.         $GLOBALS['_DB_DATAOBJECT']['OVERLOADED'] = true;
  3237.     }
  3238. }
  3239.