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

  1. <?php
  2. /**
  3.  * Object Based Database Query Builder and data store
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  8.  * that is available through the world-wide-web at the following URI:
  9.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  10.  * the PHP License and are unable to obtain it through the web, please
  11.  * send a note to license@php.net so we can mail you a copy immediately.
  12.  *
  13.  * @category   Database
  14.  * @package    DB_DataObject
  15.  * @author     Alan Knowles <alan@akbkhome.com>
  16.  * @copyright  1997-2006 The PHP Group
  17.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  18.  * @version    CVS: $Id: DataObject.php,v 1.439 2008/01/30 02:14:06 alan_k Exp $
  19.  * @link       http://pear.php.net/package/DB_DataObject
  20.  */
  21.   
  22.  
  23. /* =========================================================================== 
  24.  *
  25.  *    !!!!!!!!!!!!!               W A R N I N G                !!!!!!!!!!!
  26.  *
  27.  *  THIS MAY SEGFAULT PHP IF YOU ARE USING THE ZEND OPTIMIZER (to fix it, 
  28.  *  just add "define('DB_DATAOBJECT_NO_OVERLOAD',true);" before you include 
  29.  *  this file. reducing the optimization level may also solve the segfault.
  30.  *  ===========================================================================
  31.  */
  32.  
  33. /**
  34.  * The main "DB_DataObject" class is really a base class for your own tables classes
  35.  *
  36.  * // Set up the class by creating an ini file (refer to the manual for more details
  37.  * [DB_DataObject]
  38.  * database         = mysql:/username:password@host/database
  39.  * schema_location = /home/myapplication/database
  40.  * class_location  = /home/myapplication/DBTables/
  41.  * clase_prefix    = DBTables_
  42.  *
  43.  *
  44.  * //Start and initialize...................... - dont forget the &
  45.  * $config = parse_ini_file('example.ini',true);
  46.  * $options = &PEAR::getStaticProperty('DB_DataObject','options');
  47.  * $options = $config['DB_DataObject'];
  48.  *
  49.  * // example of a class (that does not use the 'auto generated tables data')
  50.  * class mytable extends DB_DataObject {
  51.  *     // mandatory - set the table
  52.  *     var $_database_dsn = "mysql://username:password@localhost/database";
  53.  *     var $__table = "mytable";
  54.  *     function table() {
  55.  *         return array(
  56.  *             'id' => 1, // integer or number
  57.  *             'name' => 2, // string
  58.  *        );
  59.  *     }
  60.  *     function keys() {
  61.  *         return array('id');
  62.  *     }
  63.  * }
  64.  *
  65.  * // use in the application
  66.  *
  67.  *
  68.  * Simple get one row
  69.  *
  70.  * $instance = new mytable;
  71.  * $instance->get("id",12);
  72.  * echo $instance->somedata;
  73.  *
  74.  *
  75.  * Get multiple rows
  76.  *
  77.  * $instance = new mytable;
  78.  * $instance->whereAdd("ID > 12");
  79.  * $instance->whereAdd("ID < 14");
  80.  * $instance->find();
  81.  * while ($instance->fetch()) {
  82.  *     echo $instance->somedata;
  83.  * }
  84.  
  85.  
  86. /**
  87.  * Needed classes
  88.  * - we use getStaticProperty from PEAR pretty extensively (cant remove it ATM)
  89.  */
  90.  
  91. require_once 'PEAR.php';
  92.  
  93. /**
  94.  * We are duping fetchmode constants to be compatible with
  95.  * both DB and MDB2
  96.  */
  97. define('DB_DATAOBJECT_FETCHMODE_ORDERED',1); 
  98. define('DB_DATAOBJECT_FETCHMODE_ASSOC',2);
  99.  
  100.  
  101.  
  102.  
  103.  
  104. /**
  105.  * these are constants for the get_table array
  106.  * user to determine what type of escaping is required around the object vars.
  107.  */
  108. define('DB_DATAOBJECT_INT',  1);  // does not require ''
  109. define('DB_DATAOBJECT_STR',  2);  // requires ''
  110.  
  111. define('DB_DATAOBJECT_DATE', 4);  // is date #TODO
  112. define('DB_DATAOBJECT_TIME', 8);  // is time #TODO
  113. define('DB_DATAOBJECT_BOOL', 16); // is boolean #TODO
  114. define('DB_DATAOBJECT_TXT',  32); // is long text #TODO
  115. define('DB_DATAOBJECT_BLOB', 64); // is blob type
  116.  
  117.  
  118. define('DB_DATAOBJECT_NOTNULL', 128);           // not null col.
  119. define('DB_DATAOBJECT_MYSQLTIMESTAMP'   , 256);           // mysql timestamps (ignored by update/insert)
  120. /*
  121.  * Define this before you include DataObjects.php to  disable overload - if it segfaults due to Zend optimizer..
  122.  */
  123. //define('DB_DATAOBJECT_NO_OVERLOAD',true)  
  124.  
  125.  
  126. /**
  127.  * Theses are the standard error codes, most methods will fail silently - and return false
  128.  * to access the error message either use $table->_lastError
  129.  * or $last_error = PEAR::getStaticProperty('DB_DataObject','lastError');
  130.  * the code is $last_error->code, and the message is $last_error->message (a standard PEAR error)
  131.  */
  132.  
  133. define('DB_DATAOBJECT_ERROR_INVALIDARGS',   -1);  // wrong args to function
  134. define('DB_DATAOBJECT_ERROR_NODATA',        -2);  // no data available
  135. define('DB_DATAOBJECT_ERROR_INVALIDCONFIG', -3);  // something wrong with the config
  136. define('DB_DATAOBJECT_ERROR_NOCLASS',       -4);  // no class exists
  137. define('DB_DATAOBJECT_ERROR_INVALID_CALL'  ,-7);  // overlad getter/setter failure
  138.  
  139. /**
  140.  * Used in methods like delete() and count() to specify that the method should
  141.  * build the condition only out of the whereAdd's and not the object parameters.
  142.  */
  143. define('DB_DATAOBJECT_WHEREADD_ONLY', true);
  144.  
  145. /**
  146.  *
  147.  * storage for connection and result objects,
  148.  * it is done this way so that print_r()'ing the is smaller, and
  149.  * it reduces the memory size of the object.
  150.  * -- future versions may use $this->_connection = & PEAR object..
  151.  *   although will need speed tests to see how this affects it.
  152.  * - includes sub arrays
  153.  *   - connections = md5 sum mapp to pear db object
  154.  *   - results     = [id] => map to pear db object
  155.  *   - resultseq   = sequence id for results & results field
  156.  *   - resultfields = [id] => list of fields return from query (for use with toArray())
  157.  *   - ini         = mapping of database to ini file results
  158.  *   - links       = mapping of database to links file
  159.  *   - lasterror   = pear error objects for last error event.
  160.  *   - config      = aliased view of PEAR::getStaticPropery('DB_DataObject','options') * done for performance.
  161.  *   - array of loaded classes by autoload method - to stop it doing file access request over and over again!
  162.  */
  163. $GLOBALS['_DB_DATAOBJECT']['RESULTS']   = array();
  164. $GLOBALS['_DB_DATAOBJECT']['RESULTSEQ'] = 1;
  165. $GLOBALS['_DB_DATAOBJECT']['RESULTFIELDS'] = array();
  166. $GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'] = array();
  167. $GLOBALS['_DB_DATAOBJECT']['INI'] = array();
  168. $GLOBALS['_DB_DATAOBJECT']['LINKS'] = array();
  169. $GLOBALS['_DB_DATAOBJECT']['SEQUENCE'] = array();
  170. $GLOBALS['_DB_DATAOBJECT']['LASTERROR'] = null;
  171. $GLOBALS['_DB_DATAOBJECT']['CONFIG'] = array();
  172. $GLOBALS['_DB_DATAOBJECT']['CACHE'] = array();
  173. $GLOBALS['_DB_DATAOBJECT']['OVERLOADED'] = false;
  174. $GLOBALS['_DB_DATAOBJECT']['QUERYENDTIME'] = 0;
  175.  
  176.  
  177.  
  178. // this will be horrifically slow!!!!
  179. // NOTE: Overload SEGFAULTS ON PHP4 + Zend Optimizer (see define before..)
  180. // these two are BC/FC handlers for call in PHP4/5
  181.  
  182. if ( substr(phpversion(),0,1) == 5) {
  183.     class DB_DataObject_Overload 
  184.     {
  185.         function __call($method,$args) 
  186.         {
  187.             $return = null;
  188.             $this->_call($method,$args,$return);
  189.             return $return;
  190.         }
  191.         function __sleep() 
  192.         {
  193.             return array_keys(get_object_vars($this)) ; 
  194.         }
  195.     }
  196. } else {
  197.     if (version_compare(phpversion(),'4.3.10','eq') && !defined('DB_DATAOBJECT_NO_OVERLOAD')) {
  198.         trigger_error(
  199.             "overload does not work with PHP4.3.10, either upgrade 
  200.             (snaps.php.net) or more recent version 
  201.             or define DB_DATAOBJECT_NO_OVERLOAD as per the manual.
  202.             ",E_USER_ERROR);
  203.     }
  204.  
  205.     if (!function_exists('clone')) {
  206.         // emulate clone  - as per php_compact, slow but really the correct behaviour..
  207.         eval('function clone($t) { $r = $t; if (method_exists($r,"__clone")) { $r->__clone(); } return $r; }');
  208.     }
  209.     eval('
  210.         class DB_DataObject_Overload {
  211.             function __call($method,$args,&$return) {
  212.                 return $this->_call($method,$args,$return); 
  213.             }
  214.         }
  215.     ');
  216. }
  217.  
  218.     
  219.  
  220.  
  221.  
  222.  
  223.  /*
  224.  *
  225.  * @package  DB_DataObject
  226.  * @author   Alan Knowles <alan@akbkhome.com>
  227.  * @since    PHP 4.0
  228.  */
  229.  
  230. class DB_DataObject extends DB_DataObject_Overload
  231. {
  232.    /**
  233.     * The Version - use this to check feature changes
  234.     *
  235.     * @access   private
  236.     * @var      string
  237.     */
  238.     var $_DB_DataObject_version = "1.8.8";
  239.  
  240.     /**
  241.      * The Database table (used by table extends)
  242.      *
  243.      * @access  private
  244.      * @var     string
  245.      */
  246.     var $__table = '';  // database table
  247.  
  248.     /**
  249.      * The Number of rows returned from a query
  250.      *
  251.      * @access  public
  252.      * @var     int
  253.      */
  254.     var $N = 0;  // Number of rows returned from a query
  255.  
  256.     /* ============================================================= */
  257.     /*                      Major Public Methods                     */
  258.     /* (designed to be optionally then called with parent::method()) */
  259.     /* ============================================================= */
  260.  
  261.  
  262.     /**
  263.      * Get a result using key, value.
  264.      *
  265.      * for example
  266.      * $object->get("ID",1234);
  267.      * Returns Number of rows located (usually 1) for success,
  268.      * and puts all the table columns into this classes variables
  269.      *
  270.      * see the fetch example on how to extend this.
  271.      *
  272.      * if no value is entered, it is assumed that $key is a value
  273.      * and get will then use the first key in keys()
  274.      * to obtain the key.
  275.      *
  276.      * @param   string  $k column
  277.      * @param   string  $v value
  278.      * @access  public
  279.      * @return  int     No. of rows
  280.      */
  281.     function get($k = null, $v = null)
  282.     {
  283.         global $_DB_DATAOBJECT;
  284.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  285.             DB_DataObject::_loadConfig();
  286.         }
  287.         $keys = array();
  288.         
  289.         if ($v === null) {
  290.             $v = $k;
  291.             $keys = $this->keys();
  292.             if (!$keys) {
  293.                 $this->raiseError("No Keys available for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  294.                 return false;
  295.             }
  296.             $k = $keys[0];
  297.         }
  298.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  299.             $this->debug("$k $v " .print_r($keys,true), "GET");
  300.         }
  301.         
  302.         if ($v === null) {
  303.             $this->raiseError("No Value specified for get", DB_DATAOBJECT_ERROR_INVALIDARGS);
  304.             return false;
  305.         }
  306.         $this->$k = $v;
  307.         return $this->find(1);
  308.     }
  309.  
  310.     /**
  311.      * An autoloading, caching static get method  using key, value (based on get)
  312.      *
  313.      * Usage:
  314.      * $object = DB_DataObject::staticGet("DbTable_mytable",12);
  315.      * or
  316.      * $object =  DB_DataObject::staticGet("DbTable_mytable","name","fred");
  317.      *
  318.      * or write it into your extended class:
  319.      * function &staticGet($k,$v=NULL) { return DB_DataObject::staticGet("This_Class",$k,$v);  }
  320.      *
  321.      * @param   string  $class class name
  322.      * @param   string  $k     column (or value if using keys)
  323.      * @param   string  $v     value (optional)
  324.      * @access  public
  325.      * @return  object
  326.      */
  327.     function &staticGet($class, $k, $v = null)
  328.     {
  329.         $lclass = strtolower($class);
  330.         global $_DB_DATAOBJECT;
  331.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  332.             DB_DataObject::_loadConfig();
  333.         }
  334.  
  335.         
  336.  
  337.         $key = "$k:$v";
  338.         if ($v === null) {
  339.             $key = $k;
  340.         }
  341.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  342.             DB_DataObject::debug("$class $key","STATIC GET - TRY CACHE");
  343.         }
  344.         if (!empty($_DB_DATAOBJECT['CACHE'][$lclass][$key])) {
  345.             return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
  346.         }
  347.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  348.             DB_DataObject::debug("$class $key","STATIC GET - NOT IN CACHE");
  349.         }
  350.  
  351.         $obj = DB_DataObject::factory(substr($class,strlen($_DB_DATAOBJECT['CONFIG']['class_prefix'])));
  352.         if (PEAR::isError($obj)) {
  353.             DB_DataObject::raiseError("could not autoload $class", DB_DATAOBJECT_ERROR_NOCLASS);
  354.             $r = false;
  355.             return $r;
  356.         }
  357.         
  358.         if (!isset($_DB_DATAOBJECT['CACHE'][$lclass])) {
  359.             $_DB_DATAOBJECT['CACHE'][$lclass] = array();
  360.         }
  361.         if (!$obj->get($k,$v)) {
  362.             DB_DataObject::raiseError("No Data return from get $k $v", DB_DATAOBJECT_ERROR_NODATA);
  363.             
  364.             $r = false;
  365.             return $r;
  366.         }
  367.         $_DB_DATAOBJECT['CACHE'][$lclass][$key] = $obj;
  368.         return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
  369.     }
  370.  
  371.     /**
  372.      * find results, either normal or crosstable
  373.      *
  374.      * for example
  375.      *
  376.      * $object = new mytable();
  377.      * $object->ID = 1;
  378.      * $object->find();
  379.      *
  380.      *
  381.      * will set $object->N to number of rows, and expects next command to fetch rows
  382.      * will return $object->N
  383.      *
  384.      * @param   boolean $n Fetch first result
  385.      * @access  public
  386.      * @return  mixed (number of rows returned, or true if numRows fetching is not supported)
  387.      */
  388.     function find($n = false)
  389.     {
  390.         global $_DB_DATAOBJECT;
  391.         if ($this->_query === false) {
  392.             $this->raiseError(
  393.                 "You cannot do two queries on the same object (copy it before finding)", 
  394.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  395.             return false;
  396.         }
  397.         
  398.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  399.             DB_DataObject::_loadConfig();
  400.         }
  401.  
  402.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  403.             $this->debug($n, "find",1);
  404.         }
  405.         if (!$this->__table) {
  406.             // xdebug can backtrace this!
  407.             trigger_error("NO \$__table SPECIFIED in class definition",E_USER_ERROR);
  408.         }
  409.         $this->N = 0;
  410.         $query_before = $this->_query;
  411.         $this->_build_condition($this->table()) ;
  412.         
  413.         $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  414.         $this->_connect();
  415.         $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  416.        
  417.         /* We are checking for method modifyLimitQuery as it is PEAR DB specific */
  418.         $sql = 'SELECT ' .
  419.             $this->_query['data_select'] . " \n" .
  420.             ' FROM ' . ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table) . " \n" .
  421.             $this->_join . " \n" .
  422.             $this->_query['condition'] . " \n" .
  423.             $this->_query['group_by']  . " \n" .
  424.             $this->_query['having']    . " \n" .
  425.             $this->_query['order_by']  . " \n";
  426.         
  427.         if ((!isset($_DB_DATAOBJECT['CONFIG']['db_driver'])) || 
  428.             ($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB')) {
  429.             /* PEAR DB specific */
  430.         
  431.             if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) {
  432.                 $sql = $DB->modifyLimitQuery($sql,$this->_query['limit_start'], $this->_query['limit_count']);
  433.             }
  434.         } else {
  435.             /* theoretically MDB2! */
  436.             if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) {
  437.                 $DB->setLimit($this->_query['limit_count'],$this->_query['limit_start']);
  438.             }
  439.         }
  440.         
  441.         
  442.         $this->_query($sql);
  443.         
  444.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  445.             $this->debug("CHECK autofetchd $n", "find", 1);
  446.         }
  447.         
  448.         // find(true)
  449.         
  450.         $ret = $this->N;
  451.         if (!$ret && !empty($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) {     
  452.             // clear up memory if nothing found!?
  453.             unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]);
  454.         }
  455.         
  456.         if ($n && $this->N > 0 ) {
  457.             if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  458.                 $this->debug("ABOUT TO AUTOFETCH", "find", 1);
  459.             }
  460.             $fs = $this->fetch();
  461.             // if fetch returns false (eg. failed), then the backend doesnt support numRows (eg. ret=true)
  462.             // - hence find() also returns false..
  463.             $ret = ($ret === true) ? $fs : $ret;
  464.         }
  465.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  466.             $this->debug("DONE", "find", 1);
  467.         }
  468.         $this->_query = $query_before;
  469.         return $ret;
  470.     }
  471.  
  472.     /**
  473.      * fetches next row into this objects var's
  474.      *
  475.      * returns 1 on success 0 on failure
  476.      *
  477.      *
  478.      *
  479.      * Example
  480.      * $object = new mytable();
  481.      * $object->name = "fred";
  482.      * $object->find();
  483.      * $store = array();
  484.      * while ($object->fetch()) {
  485.      *   echo $this->ID;
  486.      *   $store[] = $object; // builds an array of object lines.
  487.      * }
  488.      *
  489.      * to add features to a fetch
  490.      * function fetch () {
  491.      *    $ret = parent::fetch();
  492.      *    $this->date_formated = date('dmY',$this->date);
  493.      *    return $ret;
  494.      * }
  495.      *
  496.      * @access  public
  497.      * @return  boolean on success
  498.      */
  499.     function fetch()
  500.     {
  501.  
  502.         global $_DB_DATAOBJECT;
  503.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  504.             DB_DataObject::_loadConfig();
  505.         }
  506.         if (empty($this->N)) {
  507.             if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  508.                 $this->debug("No data returned from FIND (eg. N is 0)","FETCH", 3);
  509.             }
  510.             return false;
  511.         }
  512.         
  513.         if (empty($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]) || 
  514.             !is_object($result = &$_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) 
  515.         {
  516.             if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  517.                 $this->debug('fetched on object after fetch completed (no results found)');
  518.             }
  519.             return false;
  520.         }
  521.         
  522.         
  523.         $array = $result->fetchRow(DB_DATAOBJECT_FETCHMODE_ASSOC);
  524.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  525.             $this->debug(serialize($array),"FETCH");
  526.         }
  527.         
  528.         // fetched after last row..
  529.         if ($array === null) {
  530.             if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  531.                 $t= explode(' ',microtime());
  532.             
  533.                 $this->debug("Last Data Fetch'ed after " . 
  534.                         ($t[0]+$t[1]- $_DB_DATAOBJECT['QUERYENDTIME']  ) . 
  535.                         " seconds",
  536.                     "FETCH", 1);
  537.             }
  538.             // reduce the memory usage a bit... (but leave the id in, so count() works ok on it)
  539.             unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]);
  540.             
  541.             // we need to keep a copy of resultfields locally so toArray() still works
  542.             // however we dont want to keep it in the global cache..
  543.             
  544.             if (!empty($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) {
  545.                 $this->_resultFields = $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid];
  546.                 unset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]);
  547.             }
  548.             // this is probably end of data!!
  549.             //DB_DataObject::raiseError("fetch: no data returned", DB_DATAOBJECT_ERROR_NODATA);
  550.             return false;
  551.         }
  552.         // make sure resultFields is always empty..
  553.         $this->_resultFields = false;
  554.         
  555.         if (!isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) {
  556.             // note: we dont declare this to keep the print_r size down.
  557.             $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]= array_flip(array_keys($array));
  558.         }
  559.         
  560.         foreach($array as $k=>$v) {
  561.             $kk = str_replace(".", "_", $k);
  562.             $kk = str_replace(" ", "_", $kk);
  563.             if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  564.                 $this->debug("$kk = ". $array[$k], "fetchrow LINE", 3);
  565.             }
  566.             $this->$kk = $array[$k];
  567.         }
  568.         
  569.         // set link flag
  570.         $this->_link_loaded=false;
  571.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  572.             $this->debug("{$this->__table} DONE", "fetchrow",2);
  573.         }
  574.         if (($this->_query !== false) &&  empty($_DB_DATAOBJECT['CONFIG']['keep_query_after_fetch'])) {
  575.             $this->_query = false;
  576.         }
  577.         return true;
  578.     }
  579.  
  580.     /**
  581.      * Adds a condition to the WHERE statement, defaults to AND
  582.      *
  583.      * $object->whereAdd(); //reset or cleaer ewhwer
  584.      * $object->whereAdd("ID > 20");
  585.      * $object->whereAdd("age > 20","OR");
  586.      *
  587.      * @param    string  $cond  condition
  588.      * @param    string  $logic optional logic "OR" (defaults to "AND")
  589.      * @access   public
  590.      * @return   string|PEAR::Error - previous condition or Error when invalid args found
  591.      */
  592.     function whereAdd($cond = false, $logic = 'AND')
  593.     {
  594.         // for PHP5.2.3 - there is a bug with setting array properties of an object.
  595.         $_query = $this->_query;
  596.          
  597.         if (!isset($this->_query) || ($_query === false)) {
  598.             return $this->raiseError(
  599.                 "You cannot do two queries on the same object (clone it before finding)", 
  600.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  601.         }
  602.         
  603.         if ($cond === false) {
  604.             $r = $this->_query['condition'];
  605.             $_query['condition'] = '';
  606.             $this->_query = $_query;
  607.             return preg_replace('/^\s+WHERE\s+/','',$r);
  608.         }
  609.         // check input...= 0 or '   ' == error!
  610.         if (!trim($cond)) {
  611.             return $this->raiseError("WhereAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  612.         }
  613.         $r = $_query['condition'];
  614.         if ($_query['condition']) {
  615.             $_query['condition'] .= " {$logic} ( {$cond} )";
  616.             $this->_query = $_query;
  617.             return $r;
  618.         }
  619.         $_query['condition'] = " WHERE ( {$cond} ) ";
  620.         $this->_query = $_query;
  621.         return $r;
  622.     }
  623.  
  624.     /**
  625.      * Adds a order by condition
  626.      *
  627.      * $object->orderBy(); //clears order by
  628.      * $object->orderBy("ID");
  629.      * $object->orderBy("ID,age");
  630.      *
  631.      * @param  string $order  Order
  632.      * @access public
  633.      * @return none|PEAR::Error - invalid args only
  634.      */
  635.     function orderBy($order = false)
  636.     {
  637.         if ($this->_query === false) {
  638.             $this->raiseError(
  639.                 "You cannot do two queries on the same object (copy it before finding)", 
  640.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  641.             return false;
  642.         }
  643.         if ($order === false) {
  644.             $this->_query['order_by'] = '';
  645.             return;
  646.         }
  647.         // check input...= 0 or '    ' == error!
  648.         if (!trim($order)) {
  649.             return $this->raiseError("orderBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  650.         }
  651.         
  652.         if (!$this->_query['order_by']) {
  653.             $this->_query['order_by'] = " ORDER BY {$order} ";
  654.             return;
  655.         }
  656.         $this->_query['order_by'] .= " , {$order}";
  657.     }
  658.  
  659.     /**
  660.      * Adds a group by condition
  661.      *
  662.      * $object->groupBy(); //reset the grouping
  663.      * $object->groupBy("ID DESC");
  664.      * $object->groupBy("ID,age");
  665.      *
  666.      * @param  string  $group  Grouping
  667.      * @access public
  668.      * @return none|PEAR::Error - invalid args only
  669.      */
  670.     function groupBy($group = false)
  671.     {
  672.         if ($this->_query === false) {
  673.             $this->raiseError(
  674.                 "You cannot do two queries on the same object (copy it before finding)", 
  675.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  676.             return false;
  677.         }
  678.         if ($group === false) {
  679.             $this->_query['group_by'] = '';
  680.             return;
  681.         }
  682.         // check input...= 0 or '    ' == error!
  683.         if (!trim($group)) {
  684.             return $this->raiseError("groupBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  685.         }
  686.         
  687.         
  688.         if (!$this->_query['group_by']) {
  689.             $this->_query['group_by'] = " GROUP BY {$group} ";
  690.             return;
  691.         }
  692.         $this->_query['group_by'] .= " , {$group}";
  693.     }
  694.  
  695.     /**
  696.      * Adds a having clause
  697.      *
  698.      * $object->having(); //reset the grouping
  699.      * $object->having("sum(value) > 0 ");
  700.      *
  701.      * @param  string  $having  condition
  702.      * @access public
  703.      * @return none|PEAR::Error - invalid args only
  704.      */
  705.     function having($having = false)
  706.     {
  707.         if ($this->_query === false) {
  708.             $this->raiseError(
  709.                 "You cannot do two queries on the same object (copy it before finding)", 
  710.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  711.             return false;
  712.         }
  713.         if ($having === false) {
  714.             $this->_query['having'] = '';
  715.             return;
  716.         }
  717.         // check input...= 0 or '    ' == error!
  718.         if (!trim($having)) {
  719.             return $this->raiseError("Having: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  720.         }
  721.         
  722.         
  723.         if (!$this->_query['having']) {
  724.             $this->_query['having'] = " HAVING {$having} ";
  725.             return;
  726.         }
  727.         $this->_query['having'] .= " AND {$having}";
  728.     }
  729.  
  730.     /**
  731.      * Sets the Limit
  732.      *
  733.      * $boject->limit(); // clear limit
  734.      * $object->limit(12);
  735.      * $object->limit(12,10);
  736.      *
  737.      * Note this will emit an error on databases other than mysql/postgress
  738.      * as there is no 'clean way' to implement it. - you should consider refering to
  739.      * your database manual to decide how you want to implement it.
  740.      *
  741.      * @param  string $a  limit start (or number), or blank to reset
  742.      * @param  string $b  number
  743.      * @access public
  744.      * @return none|PEAR::Error - invalid args only
  745.      */
  746.     function limit($a = null, $b = null)
  747.     {
  748.         if ($this->_query === false) {
  749.             $this->raiseError(
  750.                 "You cannot do two queries on the same object (copy it before finding)", 
  751.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  752.             return false;
  753.         }
  754.         
  755.         if ($a === null) {
  756.            $this->_query['limit_start'] = '';
  757.            $this->_query['limit_count'] = '';
  758.            return;
  759.         }
  760.         // check input...= 0 or '    ' == error!
  761.         if ((!is_int($a) && ((string)((int)$a) !== (string)$a)) 
  762.             || (($b !== null) && (!is_int($b) && ((string)((int)$b) !== (string)$b)))) {
  763.             return $this->raiseError("limit: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  764.         }
  765.         global $_DB_DATAOBJECT;
  766.         $this->_connect();
  767.         $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  768.         
  769.         $this->_query['limit_start'] = ($b == null) ? 0 : (int)$a;
  770.         $this->_query['limit_count'] = ($b == null) ? (int)$a : (int)$b;
  771.         
  772.     }
  773.  
  774.     /**
  775.      * Adds a select columns
  776.      *
  777.      * $object->selectAdd(); // resets select to nothing!
  778.      * $object->selectAdd("*"); // default select
  779.      * $object->selectAdd("unixtime(DATE) as udate");
  780.      * $object->selectAdd("DATE");
  781.      *
  782.      * to prepend distict:
  783.      * $object->selectAdd('distinct ' . $object->selectAdd());
  784.      *
  785.      * @param  string  $k
  786.      * @access public
  787.      * @return mixed null or old string if you reset it.
  788.      */
  789.     function selectAdd($k = null)
  790.     {
  791.         if ($this->_query === false) {
  792.             $this->raiseError(
  793.                 "You cannot do two queries on the same object (copy it before finding)", 
  794.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  795.             return false;
  796.         }
  797.         if ($k === null) {
  798.             $old = $this->_query['data_select'];
  799.             $this->_query['data_select'] = '';
  800.             return $old;
  801.         }
  802.         
  803.         // check input...= 0 or '    ' == error!
  804.         if (!trim($k)) {
  805.             return $this->raiseError("selectAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS);
  806.         }
  807.         
  808.         if ($this->_query['data_select']) {
  809.             $this->_query['data_select'] .= ', ';
  810.         }
  811.         $this->_query['data_select'] .= " $k ";
  812.     }
  813.     /**
  814.      * Adds multiple Columns or objects to select with formating.
  815.      *
  816.      * $object->selectAs(null); // adds "table.colnameA as colnameA,table.colnameB as colnameB,......"
  817.      *                      // note with null it will also clear the '*' default select
  818.      * $object->selectAs(array('a','b'),'%s_x'); // adds "a as a_x, b as b_x"
  819.      * $object->selectAs(array('a','b'),'ddd_%s','ccc'); // adds "ccc.a as ddd_a, ccc.b as ddd_b"
  820.      * $object->selectAdd($object,'prefix_%s'); // calls $object->get_table and adds it all as
  821.      *                  objectTableName.colnameA as prefix_colnameA
  822.      *
  823.      * @param  array|object|null the array or object to take column names from.
  824.      * @param  string           format in sprintf format (use %s for the colname)
  825.      * @param  string           table name eg. if you have joinAdd'd or send $from as an array.
  826.      * @access public
  827.      * @return void
  828.      */
  829.     function selectAs($from = null,$format = '%s',$tableName=false)
  830.     {
  831.         global $_DB_DATAOBJECT;
  832.         
  833.         if ($this->_query === false) {
  834.             $this->raiseError(
  835.                 "You cannot do two queries on the same object (copy it before finding)", 
  836.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  837.             return false;
  838.         }
  839.         
  840.         if ($from === null) {
  841.             // blank the '*' 
  842.             $this->selectAdd();
  843.             $from = $this;
  844.         }
  845.         
  846.         
  847.         $table = $this->__table;
  848.         if (is_object($from)) {
  849.             $table = $from->__table;
  850.             $from = array_keys($from->table());
  851.         }
  852.         
  853.         if ($tableName !== false) {
  854.             $table = $tableName;
  855.         }
  856.         $s = '%s';
  857.         if (!empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers'])) {
  858.             $this->_connect();
  859.             $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  860.             $s      = $DB->quoteIdentifier($s);
  861.             $format = $DB->quoteIdentifier($format); 
  862.         }
  863.         foreach ($from as $k) {
  864.             $this->selectAdd(sprintf("{$s}.{$s} as {$format}",$table,$k,$k));
  865.         }
  866.         $this->_query['data_select'] .= "\n";
  867.     }
  868.     /**
  869.      * Insert the current objects variables into the database
  870.      *
  871.      * Returns the ID of the inserted element (if auto increment or sequences are used.)
  872.      *
  873.      * for example
  874.      *
  875.      * Designed to be extended
  876.      *
  877.      * $object = new mytable();
  878.      * $object->name = "fred";
  879.      * echo $object->insert();
  880.      *
  881.      * @access public
  882.      * @return mixed false on failure, int when auto increment or sequence used, otherwise true on success
  883.      */
  884.     function insert()
  885.     {
  886.         global $_DB_DATAOBJECT;
  887.         
  888.         // we need to write to the connection (For nextid) - so us the real
  889.         // one not, a copyied on (as ret-by-ref fails with overload!)
  890.         
  891.         if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  892.             $this->_connect();
  893.         }
  894.         
  895.         $quoteIdentifiers  = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  896.         
  897.         $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  898.          
  899.         $items =  isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ?   
  900.             $_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table();
  901.             
  902.         if (!$items) {
  903.             $this->raiseError("insert:No table definition for {$this->__table}",
  904.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  905.             return false;
  906.         }
  907.         $options = &$_DB_DATAOBJECT['CONFIG'];
  908.  
  909.  
  910.         $datasaved = 1;
  911.         $leftq     = '';
  912.         $rightq    = '';
  913.      
  914.         $seqKeys   = isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table]) ?
  915.                         $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] : 
  916.                         $this->sequenceKey();
  917.         
  918.         $key       = isset($seqKeys[0]) ? $seqKeys[0] : false;
  919.         $useNative = isset($seqKeys[1]) ? $seqKeys[1] : false;
  920.         $seq       = isset($seqKeys[2]) ? $seqKeys[2] : false;
  921.         
  922.         $dbtype    = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn["phptype"];
  923.         
  924.          
  925.         // nativeSequences or Sequences..     
  926.  
  927.         // big check for using sequences
  928.         
  929.         if (($key !== false) && !$useNative) { 
  930.         
  931.             if (!$seq) {
  932.                 $keyvalue =  $DB->nextId($this->__table);
  933.             } else {
  934.                 $f = $DB->getOption('seqname_format');
  935.                 $DB->setOption('seqname_format','%s');
  936.                 $keyvalue =  $DB->nextId($seq);
  937.                 $DB->setOption('seqname_format',$f);
  938.             }
  939.             if (PEAR::isError($keyvalue)) {
  940.                 $this->raiseError($keyvalue->toString(), DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  941.                 return false;
  942.             }
  943.             $this->$key = $keyvalue;
  944.         }
  945.  
  946.  
  947.  
  948.         foreach($items as $k => $v) {
  949.             
  950.             // if we are using autoincrement - skip the column...
  951.             if ($key && ($k == $key) && $useNative) {
  952.                 continue;
  953.             }
  954.         
  955.             
  956.             if (!isset($this->$k)) {
  957.                 continue;
  958.             }
  959.             // dont insert data into mysql timestamps 
  960.             // use query() if you really want to do this!!!!
  961.             if ($v & DB_DATAOBJECT_MYSQLTIMESTAMP) {
  962.                 continue;
  963.             }
  964.             
  965.             if ($leftq) {
  966.                 $leftq  .= ', ';
  967.                 $rightq .= ', ';
  968.             }
  969.             
  970.             $leftq .= ($quoteIdentifiers ? ($DB->quoteIdentifier($k) . ' ')  : "$k ");
  971.             
  972.             if (is_a($this->$k,'DB_DataObject_Cast')) {
  973.                 $value = $this->$k->toString($v,$DB);
  974.                 if (PEAR::isError($value)) {
  975.                     $this->raiseError($value->toString() ,DB_DATAOBJECT_ERROR_INVALIDARGS);
  976.                     return false;
  977.                 }
  978.                 $rightq .=  $value;
  979.                 continue;
  980.             }
  981.             
  982.             
  983.  
  984.             if (!isset($options['disable_null_strings']) && is_string($this->$k) && (strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
  985.                 $rightq .= " NULL ";
  986.                 continue;
  987.             }
  988.             // DATE is empty... on a col. that can be null.. 
  989.             // note: this may be usefull for time as well..
  990.             if (!$this->$k && 
  991.                     (($v & DB_DATAOBJECT_DATE) || ($v & DB_DATAOBJECT_TIME)) && 
  992.                     !($v & DB_DATAOBJECT_NOTNULL)) {
  993.                     
  994.                 $rightq .= " NULL ";
  995.                 continue;
  996.             }
  997.               
  998.             
  999.             if ($v & DB_DATAOBJECT_STR) {
  1000.                 $rightq .= $this->_quote((string) (
  1001.                         ($v & DB_DATAOBJECT_BOOL) ? 
  1002.                             // this is thanks to the braindead idea of postgres to 
  1003.                             // use t/f for boolean.
  1004.                             (($this->$k === 'f') ? 0 : (int)(bool) $this->$k) :  
  1005.                             $this->$k
  1006.                     )) . " ";
  1007.                 continue;
  1008.             }
  1009.             if (is_numeric($this->$k)) {
  1010.                 $rightq .=" {$this->$k} ";
  1011.                 continue;
  1012.             }
  1013.             /* flag up string values - only at debug level... !!!??? */
  1014.             if (is_object($this->$k) || is_array($this->$k)) {
  1015.                 $this->debug('ODD DATA: ' .$k . ' ' .  print_r($this->$k,true),'ERROR');
  1016.             }
  1017.             
  1018.             // at present we only cast to integers
  1019.             // - V2 may store additional data about float/int
  1020.             $rightq .= ' ' . intval($this->$k) . ' ';
  1021.  
  1022.         }
  1023.         
  1024.         // not sure why we let empty insert here.. - I guess to generate a blank row..
  1025.         
  1026.         
  1027.         if ($leftq || $useNative) {
  1028.             $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table)    : $this->__table);
  1029.             
  1030.             $r = $this->_query("INSERT INTO {$table} ($leftq) VALUES ($rightq) ");
  1031.  
  1032.             
  1033.             
  1034.             if (PEAR::isError($r)) {
  1035.                 $this->raiseError($r);
  1036.                 return false;
  1037.             }
  1038.             
  1039.             if ($r < 1) {
  1040.                 return 0;
  1041.             }
  1042.             
  1043.             
  1044.             // now do we have an integer key!
  1045.             
  1046.             if ($key && $useNative) {
  1047.                 switch ($dbtype) {
  1048.                     case 'mysql':
  1049.                     case 'mysqli':
  1050.                         $method = "{$dbtype}_insert_id";
  1051.                         $this->$key = $method(
  1052.                             $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->connection
  1053.                         );
  1054.                         break;
  1055.                     
  1056.                     case 'mssql':
  1057.                         // note this is not really thread safe - you should wrapp it with 
  1058.                         // transactions = eg.
  1059.                         // $db->query('BEGIN');
  1060.                         // $db->insert();
  1061.                         // $db->query('COMMIT');
  1062.                         $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
  1063.                         $method = ($db_driver  == 'DB') ? 'getOne' : 'queryOne';
  1064.                         $mssql_key = $DB->$method("SELECT @@IDENTITY");
  1065.                         if (PEAR::isError($mssql_key)) {
  1066.                             $this->raiseError($mssql_key);
  1067.                             return false;
  1068.                         }
  1069.                         $this->$key = $mssql_key;
  1070.                         break; 
  1071.                         
  1072.                     case 'pgsql':
  1073.                         if (!$seq) {
  1074.                             $seq = $DB->getSequenceName(strtolower($this->__table));
  1075.                         }
  1076.                         $db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
  1077.                         $method = ($db_driver  == 'DB') ? 'getOne' : 'queryOne';
  1078.                         $pgsql_key = $DB->$method("SELECT currval('".$seq . "')"); 
  1079.  
  1080.  
  1081.                         if (PEAR::isError($pgsql_key)) {
  1082.                             $this->raiseError($pgsql_key);
  1083.                             return false;
  1084.                         }
  1085.                         $this->$key = $pgsql_key;
  1086.                         break;
  1087.                     
  1088.                     case 'ifx':
  1089.                         $this->$key = array_shift (
  1090.                             ifx_fetch_row (
  1091.                                 ifx_query(
  1092.                                     "select DBINFO('sqlca.sqlerrd1') FROM systables where tabid=1",
  1093.                                     $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->connection,
  1094.                                     IFX_SCROLL
  1095.                                 ), 
  1096.                                 "FIRST"
  1097.                             )
  1098.                         ); 
  1099.                         break;
  1100.                     
  1101.                 }
  1102.                         
  1103.             }
  1104.  
  1105.             if (isset($_DB_DATAOBJECT['CACHE'][strtolower(get_class($this))])) {
  1106.                 $this->_clear_cache();
  1107.             }
  1108.             if ($key) {
  1109.                 return $this->$key;
  1110.             }
  1111.             return true;
  1112.         }
  1113.         $this->raiseError("insert: No Data specifed for query", DB_DATAOBJECT_ERROR_NODATA);
  1114.         return false;
  1115.     }
  1116.  
  1117.     /**
  1118.      * Updates  current objects variables into the database
  1119.      * uses the keys() to decide how to update
  1120.      * Returns the  true on success
  1121.      *
  1122.      * for example
  1123.      *
  1124.      * $object = DB_DataObject::factory('mytable');
  1125.      * $object->get("ID",234);
  1126.      * $object->email="testing@test.com";
  1127.      * if(!$object->update())
  1128.      *   echo "UPDATE FAILED";
  1129.      *
  1130.      * to only update changed items :
  1131.      * $dataobject->get(132);
  1132.      * $original = $dataobject; // clone/copy it..
  1133.      * $dataobject->setFrom($_POST);
  1134.      * if ($dataobject->validate()) {
  1135.      *    $dataobject->update($original);
  1136.      * } // otherwise an error...
  1137.      *
  1138.      * performing global updates:
  1139.      * $object = DB_DataObject::factory('mytable');
  1140.      * $object->status = "dead";
  1141.      * $object->whereAdd('age > 150');
  1142.      * $object->update(DB_DATAOBJECT_WHEREADD_ONLY);
  1143.      *
  1144.      * @param object dataobject (optional) | DB_DATAOBJECT_WHEREADD_ONLY - used to only update changed items.
  1145.      * @access public
  1146.      * @return  int rows affected or false on failure
  1147.      */
  1148.     function update($dataObject = false)
  1149.     {
  1150.         global $_DB_DATAOBJECT;
  1151.         // connect will load the config!
  1152.         $this->_connect();
  1153.         
  1154.         
  1155.         $original_query =  $this->_query;
  1156.         
  1157.         $items =  isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ?   
  1158.             $_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table();
  1159.         
  1160.         // only apply update against sequence key if it is set?????
  1161.         
  1162.         $seq    = $this->sequenceKey();
  1163.         if ($seq[0] !== false) {
  1164.             $keys = array($seq[0]);
  1165.             if (empty($this->{$keys[0]}) && $dataObject !== true) {
  1166.                 $this->raiseError("update: trying to perform an update without 
  1167.                         the key set, and argument to update is not 
  1168.                         DB_DATAOBJECT_WHEREADD_ONLY
  1169.                     ", DB_DATAOBJECT_ERROR_INVALIDARGS);
  1170.                 return false;  
  1171.             }
  1172.         } else {
  1173.             $keys = $this->keys();
  1174.         }
  1175.         
  1176.          
  1177.         if (!$items) {
  1178.             $this->raiseError("update:No table definition for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  1179.             return false;
  1180.         }
  1181.         $datasaved = 1;
  1182.         $settings  = '';
  1183.         $this->_connect();
  1184.         
  1185.         $DB            = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1186.         $dbtype        = $DB->dsn["phptype"];
  1187.         $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  1188.         $options = $_DB_DATAOBJECT['CONFIG'];
  1189.         
  1190.         
  1191.         foreach($items as $k => $v) {
  1192.             if (!isset($this->$k)) {
  1193.                 continue;
  1194.             }
  1195.             // ignore stuff thats 
  1196.           
  1197.             // dont write things that havent changed..
  1198.             if (($dataObject !== false) && isset($dataObject->$k) && ($dataObject->$k === $this->$k)) {
  1199.                 continue;
  1200.             }
  1201.             
  1202.             // - dont write keys to left.!!!
  1203.             if (in_array($k,$keys)) {
  1204.                 continue;
  1205.             }
  1206.             
  1207.              // dont insert data into mysql timestamps 
  1208.             // use query() if you really want to do this!!!!
  1209.             if ($v & DB_DATAOBJECT_MYSQLTIMESTAMP) {
  1210.                 continue;
  1211.             }
  1212.             
  1213.             
  1214.             if ($settings)  {
  1215.                 $settings .= ', ';
  1216.             }
  1217.             
  1218.             $kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k);
  1219.             
  1220.             if (is_a($this->$k,'DB_DataObject_Cast')) {
  1221.                 $value = $this->$k->toString($v,$DB);
  1222.                 if (PEAR::isError($value)) {
  1223.                     $this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
  1224.                     return false;
  1225.                 }
  1226.                 $settings .= "$kSql = $value ";
  1227.                 continue;
  1228.             }
  1229.             
  1230.             // special values ... at least null is handled...
  1231.             if (!isset($options['disable_null_strings']) && (strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
  1232.                 $settings .= "$kSql = NULL ";
  1233.                 continue;
  1234.             }
  1235.             // DATE is empty... on a col. that can be null.. 
  1236.             // note: this may be usefull for time as well..
  1237.             if (!$this->$k && 
  1238.                     (($v & DB_DATAOBJECT_DATE) || ($v & DB_DATAOBJECT_TIME)) && 
  1239.                     !($v & DB_DATAOBJECT_NOTNULL)) {
  1240.                     
  1241.                 $settings .= "$kSql = NULL ";
  1242.                 continue;
  1243.             }
  1244.             
  1245.  
  1246.             if ($v & DB_DATAOBJECT_STR) {
  1247.                 $settings .= "$kSql = ". $this->_quote((string) (
  1248.                         ($v & DB_DATAOBJECT_BOOL) ? 
  1249.                             // this is thanks to the braindead idea of postgres to 
  1250.                             // use t/f for boolean.
  1251.                             (($this->$k === 'f') ? 0 : (int)(bool) $this->$k) :  
  1252.                             $this->$k
  1253.                     )) . ' ';
  1254.                 continue;
  1255.             }
  1256.             if (is_numeric($this->$k)) {
  1257.                 $settings .= "$kSql = {$this->$k} ";
  1258.                 continue;
  1259.             }
  1260.             // at present we only cast to integers
  1261.             // - V2 may store additional data about float/int
  1262.             $settings .= "$kSql = " . intval($this->$k) . ' ';
  1263.         }
  1264.  
  1265.         
  1266.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1267.             $this->debug("got keys as ".serialize($keys),3);
  1268.         }
  1269.         if ($dataObject !== true) {
  1270.             $this->_build_condition($items,$keys);
  1271.         } else {
  1272.             // prevent wiping out of data!
  1273.             if (empty($this->_query['condition'])) {
  1274.                  $this->raiseError("update: global table update not available
  1275.                         do \$do->whereAdd('1=1'); if you really want to do that.
  1276.                     ", DB_DATAOBJECT_ERROR_INVALIDARGS);
  1277.                 return false;
  1278.             }
  1279.         }
  1280.         
  1281.         
  1282.         
  1283.         //  echo " $settings, $this->condition ";
  1284.         if ($settings && isset($this->_query) && $this->_query['condition']) {
  1285.             
  1286.             $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table);
  1287.         
  1288.             $r = $this->_query("UPDATE  {$table}  SET {$settings} {$this->_query['condition']} ");
  1289.             
  1290.             // restore original query conditions.
  1291.             $this->_query = $original_query;
  1292.             
  1293.             if (PEAR::isError($r)) {
  1294.                 $this->raiseError($r);
  1295.                 return false;
  1296.             }
  1297.             if ($r < 1) {
  1298.                 return 0;
  1299.             }
  1300.  
  1301.             $this->_clear_cache();
  1302.             return $r;
  1303.         }
  1304.         // restore original query conditions.
  1305.         $this->_query = $original_query;
  1306.         
  1307.         // if you manually specified a dataobject, and there where no changes - then it's ok..
  1308.         if ($dataObject !== false) {
  1309.             return true;
  1310.         }
  1311.         
  1312.         $this->raiseError(
  1313.             "update: No Data specifed for query $settings , {$this->_query['condition']}", 
  1314.             DB_DATAOBJECT_ERROR_NODATA);
  1315.         return false;
  1316.     }
  1317.  
  1318.     /**
  1319.      * Deletes items from table which match current objects variables
  1320.      *
  1321.      * Returns the true on success
  1322.      *
  1323.      * for example
  1324.      *
  1325.      * Designed to be extended
  1326.      *
  1327.      * $object = new mytable();
  1328.      * $object->ID=123;
  1329.      * echo $object->delete(); // builds a conditon
  1330.      *
  1331.      * $object = new mytable();
  1332.      * $object->whereAdd('age > 12');
  1333.      * $object->limit(1);
  1334.      * $object->orderBy('age DESC');
  1335.      * $object->delete(true); // dont use object vars, use the conditions, limit and order.
  1336.      *
  1337.      * @param bool $useWhere (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then
  1338.      *             we will build the condition only using the whereAdd's.  Default is to
  1339.      *             build the condition only using the object parameters.
  1340.      *
  1341.      * @access public
  1342.      * @return mixed True on success, false on failure, 0 on no data affected
  1343.      */
  1344.     function delete($useWhere = false)
  1345.     {
  1346.         global $_DB_DATAOBJECT;
  1347.         // connect will load the config!
  1348.         $this->_connect();
  1349.         $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1350.         $quoteIdentifiers  = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  1351.         
  1352.         $extra_cond = ' ' . (isset($this->_query['order_by']) ? $this->_query['order_by'] : ''); 
  1353.         
  1354.         if (!$useWhere) {
  1355.  
  1356.             $keys = $this->keys();
  1357.             $this->_query = array(); // as it's probably unset!
  1358.             $this->_query['condition'] = ''; // default behaviour not to use where condition
  1359.             $this->_build_condition($this->table(),$keys);
  1360.             // if primary keys are not set then use data from rest of object.
  1361.             if (!$this->_query['condition']) {
  1362.                 $this->_build_condition($this->table(),array(),$keys);
  1363.             }
  1364.             $extra_cond = '';
  1365.         } 
  1366.             
  1367.  
  1368.         // don't delete without a condition
  1369.         if (($this->_query !== false) && $this->_query['condition']) {
  1370.         
  1371.             $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table);
  1372.             $sql = "DELETE FROM {$table} {$this->_query['condition']}{$extra_cond}";
  1373.             
  1374.             // add limit..
  1375.             
  1376.             if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) {
  1377.                 
  1378.                 if (!isset($_DB_DATAOBJECT['CONFIG']['db_driver']) ||  
  1379.                     ($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB')) {
  1380.                     // pear DB 
  1381.                     $sql = $DB->modifyLimitQuery($sql,$this->_query['limit_start'], $this->_query['limit_count']);
  1382.                     
  1383.                 } else {
  1384.                     // MDB2
  1385.                     $DB->setLimit( $this->_query['limit_count'],$this->_query['limit_start']);
  1386.                 }
  1387.                     
  1388.             }
  1389.             
  1390.             
  1391.             $r = $this->_query($sql);
  1392.             
  1393.             
  1394.             if (PEAR::isError($r)) {
  1395.                 $this->raiseError($r);
  1396.                 return false;
  1397.             }
  1398.             if ($r < 1) {
  1399.                 return 0;
  1400.             }
  1401.             $this->_clear_cache();
  1402.             return $r;
  1403.         } else {
  1404.             $this->raiseError("delete: No condition specifed for query", DB_DATAOBJECT_ERROR_NODATA);
  1405.             return false;
  1406.         }
  1407.     }
  1408.  
  1409.     /**
  1410.      * fetches a specific row into this object variables
  1411.      *
  1412.      * Not recommended - better to use fetch()
  1413.      *
  1414.      * Returens true on success
  1415.      *
  1416.      * @param  int   $row  row
  1417.      * @access public
  1418.      * @return boolean true on success
  1419.      */
  1420.     function fetchRow($row = null)
  1421.     {
  1422.         global $_DB_DATAOBJECT;
  1423.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  1424.             $this->_loadConfig();
  1425.         }
  1426.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1427.             $this->debug("{$this->__table} $row of {$this->N}", "fetchrow",3);
  1428.         }
  1429.         if (!$this->__table) {
  1430.             $this->raiseError("fetchrow: No table", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  1431.             return false;
  1432.         }
  1433.         if ($row === null) {
  1434.             $this->raiseError("fetchrow: No row specified", DB_DATAOBJECT_ERROR_INVALIDARGS);
  1435.             return false;
  1436.         }
  1437.         if (!$this->N) {
  1438.             $this->raiseError("fetchrow: No results avaiable", DB_DATAOBJECT_ERROR_NODATA);
  1439.             return false;
  1440.         }
  1441.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1442.             $this->debug("{$this->__table} $row of {$this->N}", "fetchrow",3);
  1443.         }
  1444.  
  1445.  
  1446.         $result = &$_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid];
  1447.         $array  = $result->fetchrow(DB_DATAOBJECT_FETCHMODE_ASSOC,$row);
  1448.         if (!is_array($array)) {
  1449.             $this->raiseError("fetchrow: No results available", DB_DATAOBJECT_ERROR_NODATA);
  1450.             return false;
  1451.         }
  1452.  
  1453.         foreach($array as $k => $v) {
  1454.             $kk = str_replace(".", "_", $k);
  1455.             if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1456.                 $this->debug("$kk = ". $array[$k], "fetchrow LINE", 3);
  1457.             }
  1458.             $this->$kk = $array[$k];
  1459.         }
  1460.  
  1461.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1462.             $this->debug("{$this->__table} DONE", "fetchrow", 3);
  1463.         }
  1464.         return true;
  1465.     }
  1466.  
  1467.     /**
  1468.      * Find the number of results from a simple query
  1469.      *
  1470.      * for example
  1471.      *
  1472.      * $object = new mytable();
  1473.      * $object->name = "fred";
  1474.      * echo $object->count();
  1475.      * echo $object->count(true);  // dont use object vars.
  1476.      * echo $object->count('distinct mycol');   count distinct mycol.
  1477.      * echo $object->count('distinct mycol',true); // dont use object vars.
  1478.      * echo $object->count('distinct');      // count distinct id (eg. the primary key)
  1479.      *
  1480.      *
  1481.      * @param bool|string  (optional)
  1482.      *                  (true|false => see below not on whereAddonly)
  1483.      *                  (string)
  1484.      *                      "DISTINCT" => does a distinct count on the tables 'key' column
  1485.      *                      otherwise  => normally it counts primary keys - you can use 
  1486.      *                                    this to do things like $do->count('distinct mycol');
  1487.      *                  
  1488.      * @param bool      $whereAddOnly (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then
  1489.      *                  we will build the condition only using the whereAdd's.  Default is to
  1490.      *                  build the condition using the object parameters as well.
  1491.      *                  
  1492.      * @access public
  1493.      * @return int
  1494.      */
  1495.     function count($countWhat = false,$whereAddOnly = false)
  1496.     {
  1497.         global $_DB_DATAOBJECT;
  1498.         
  1499.         if (is_bool($countWhat)) {
  1500.             $whereAddOnly = $countWhat;
  1501.         }
  1502.         
  1503.         $t = clone($this);
  1504.         $items   = $t->table();
  1505.         
  1506.         $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  1507.         
  1508.         
  1509.         if (!isset($t->_query)) {
  1510.             $this->raiseError(
  1511.                 "You cannot do run count after you have run fetch()", 
  1512.                 DB_DATAOBJECT_ERROR_INVALIDARGS);
  1513.             return false;
  1514.         }
  1515.         $this->_connect();
  1516.         $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1517.        
  1518.  
  1519.         if (!$whereAddOnly && $items)  {
  1520.             $t->_build_condition($items);
  1521.         }
  1522.         $keys = $this->keys();
  1523.  
  1524.         if (!$keys[0] && !is_string($countWhat)) {
  1525.             $this->raiseError(
  1526.                 "You cannot do run count without keys - use \$do->keys('id');", 
  1527.                 DB_DATAOBJECT_ERROR_INVALIDARGS,PEAR_ERROR_DIE);
  1528.             return false;
  1529.             
  1530.         }
  1531.         $table   = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table);
  1532.         $key_col = ($quoteIdentifiers ? $DB->quoteIdentifier($keys[0]) : $keys[0]);
  1533.         $as      = ($quoteIdentifiers ? $DB->quoteIdentifier('DATAOBJECT_NUM') : 'DATAOBJECT_NUM');
  1534.         
  1535.         // support distinct on default keys.
  1536.         $countWhat = (strtoupper($countWhat) == 'DISTINCT') ? 
  1537.             "DISTINCT {$table}.{$key_col}" : $countWhat;
  1538.         
  1539.         $countWhat = is_string($countWhat) ? $countWhat : "{$table}.{$key_col}";
  1540.         
  1541.         $r = $t->_query(
  1542.             "SELECT count({$countWhat}) as $as
  1543.                 FROM $table {$t->_join} {$t->_query['condition']}");
  1544.         if (PEAR::isError($r)) {
  1545.             return false;
  1546.         }
  1547.          
  1548.         $result  = &$_DB_DATAOBJECT['RESULTS'][$t->_DB_resultid];
  1549.         $l = $result->fetchRow(DB_DATAOBJECT_FETCHMODE_ORDERED);
  1550.         // free the results - essential on oracle.
  1551.         $t->free();
  1552.         
  1553.         return (int) $l[0];
  1554.     }
  1555.  
  1556.     /**
  1557.      * sends raw query to database
  1558.      *
  1559.      * Since _query has to be a private 'non overwriteable method', this is a relay
  1560.      *
  1561.      * @param  string  $string  SQL Query
  1562.      * @access public
  1563.      * @return void or DB_Error
  1564.      */
  1565.     function query($string)
  1566.     {
  1567.         return $this->_query($string);
  1568.     }
  1569.  
  1570.  
  1571.     /**
  1572.      * an escape wrapper around DB->escapeSimple()
  1573.      * can be used when adding manual queries or clauses
  1574.      * eg.
  1575.      * $object->query("select * from xyz where abc like '". $object->escape($_GET['name']) . "'");
  1576.      *
  1577.      * @param  string  $string  value to be escaped 
  1578.      * @param  bool $likeEscape  escapes % and _ as well. - so like queries can be protected.
  1579.      * @access public
  1580.      * @return string
  1581.      */
  1582.     function escape($string, $likeEscape=false)
  1583.     {
  1584.         global $_DB_DATAOBJECT;
  1585.         $this->_connect();
  1586.         $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1587.         // mdb2 uses escape...
  1588.         $dd = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ? 'DB' : $_DB_DATAOBJECT['CONFIG']['db_driver'];
  1589.         $ret = ($dd == 'DB') ? $DB->escapeSimple($string) : $DB->escape($string);
  1590.         if ($likeEscape) {
  1591.             $ret = str_replace(array('_','%'), array('\_','\%'), $ret);
  1592.         }
  1593.         return $ret;
  1594.         
  1595.     }
  1596.  
  1597.     /* ==================================================== */
  1598.     /*        Major Private Vars                            */
  1599.     /* ==================================================== */
  1600.  
  1601.     /**
  1602.      * The Database connection dsn (as described in the PEAR DB)
  1603.      * only used really if you are writing a very simple application/test..
  1604.      * try not to use this - it is better stored in configuration files..
  1605.      *
  1606.      * @access  private
  1607.      * @var     string
  1608.      */
  1609.     var $_database_dsn = '';
  1610.  
  1611.     /**
  1612.      * The Database connection id (md5 sum of databasedsn)
  1613.      *
  1614.      * @access  private
  1615.      * @var     string
  1616.      */
  1617.     var $_database_dsn_md5 = '';
  1618.  
  1619.     /**
  1620.      * The Database name
  1621.      * created in __connection
  1622.      *
  1623.      * @access  private
  1624.      * @var  string
  1625.      */
  1626.     var $_database = '';
  1627.  
  1628.     
  1629.     
  1630.     /**
  1631.      * The QUERY rules
  1632.      * This replaces alot of the private variables 
  1633.      * used to build a query, it is unset after find() is run.
  1634.      * 
  1635.      *
  1636.      *
  1637.      * @access  private
  1638.      * @var     array
  1639.      */
  1640.     var $_query = array(
  1641.         'condition'   => '', // the WHERE condition
  1642.         'group_by'    => '', // the GROUP BY condition
  1643.         'order_by'    => '', // the ORDER BY condition
  1644.         'having'      => '', // the HAVING condition
  1645.         'limit_start' => '', // the LIMIT condition
  1646.         'limit_count' => '', // the LIMIT condition
  1647.         'data_select' => '*', // the columns to be SELECTed
  1648.     );
  1649.         
  1650.     
  1651.   
  1652.  
  1653.     /**
  1654.      * Database result id (references global $_DB_DataObject[results]
  1655.      *
  1656.      * @access  private
  1657.      * @var     integer
  1658.      */
  1659.     var $_DB_resultid;
  1660.      
  1661.      /**
  1662.      * ResultFields - on the last call to fetch(), resultfields is sent here,
  1663.      * so we can clean up the memory.
  1664.      *
  1665.      * @access  public
  1666.      * @var     array
  1667.      */
  1668.     var $_resultFields = false; 
  1669.  
  1670.  
  1671.     /* ============================================================== */
  1672.     /*  Table definition layer (started of very private but 'came out'*/
  1673.     /* ============================================================== */
  1674.  
  1675.     /**
  1676.      * Autoload or manually load the table definitions
  1677.      *
  1678.      *
  1679.      * usage :
  1680.      * DB_DataObject::databaseStructure(  'databasename',
  1681.      *                                    parse_ini_file('mydb.ini',true), 
  1682.      *                                    parse_ini_file('mydb.link.ini',true)); 
  1683.      *
  1684.      * obviously you dont have to use ini files.. (just return array similar to ini files..)
  1685.      *  
  1686.      * It should append to the table structure array 
  1687.      *
  1688.      *     
  1689.      * @param optional string  name of database to assign / read
  1690.      * @param optional array   structure of database, and keys
  1691.      * @param optional array  table links
  1692.      *
  1693.      * @access public
  1694.      * @return true or PEAR:error on wrong paramenters.. or false if no file exists..
  1695.      *              or the array(tablename => array(column_name=>type)) if called with 1 argument.. (databasename)
  1696.      */
  1697.     function databaseStructure()
  1698.     {
  1699.  
  1700.         global $_DB_DATAOBJECT;
  1701.         
  1702.         // Assignment code 
  1703.         
  1704.         if ($args = func_get_args()) {
  1705.         
  1706.             if (count($args) == 1) {
  1707.                 
  1708.                 // this returns all the tables and their structure..
  1709.                 if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1710.                     $this->debug("Loading Generator as databaseStructure called with args",1);
  1711.                 }
  1712.                 
  1713.                 $x = new DB_DataObject;
  1714.                 $x->_database = $args[0];
  1715.                 $this->_connect();
  1716.                 $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  1717.        
  1718.                 $tables = $DB->getListOf('tables');
  1719.                 class_exists('DB_DataObject_Generator') ? '' : 
  1720.                     require_once 'DB/DataObject/Generator.php';
  1721.                     
  1722.                 foreach($tables as $table) {
  1723.                     $y = new DB_DataObject_Generator;
  1724.                     $y->fillTableSchema($x->_database,$table);
  1725.                 }
  1726.                 return $_DB_DATAOBJECT['INI'][$x->_database];            
  1727.             } else {
  1728.         
  1729.                 $_DB_DATAOBJECT['INI'][$args[0]] = isset($_DB_DATAOBJECT['INI'][$args[0]]) ?
  1730.                     $_DB_DATAOBJECT['INI'][$args[0]] + $args[1] : $args[1];
  1731.                 
  1732.                 if (isset($args[1])) {
  1733.                     $_DB_DATAOBJECT['LINKS'][$args[0]] = isset($_DB_DATAOBJECT['LINKS'][$args[0]]) ?
  1734.                         $_DB_DATAOBJECT['LINKS'][$args[0]] + $args[2] : $args[2];
  1735.                 }
  1736.                 return true;
  1737.             }
  1738.           
  1739.         }
  1740.         
  1741.         
  1742.         
  1743.         if (!$this->_database) {
  1744.             $this->_connect();
  1745.         }
  1746.         
  1747.         // loaded already?
  1748.         if (!empty($_DB_DATAOBJECT['INI'][$this->_database])) {
  1749.             
  1750.             // database loaded - but this is table is not available..
  1751.             if (
  1752.                     empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) 
  1753.                     && !empty($_DB_DATAOBJECT['CONFIG']['proxy'])
  1754.                 ) {
  1755.                 if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1756.                     $this->debug("Loading Generator to fetch Schema",1);
  1757.                 }
  1758.                 class_exists('DB_DataObject_Generator') ? '' : 
  1759.                     require_once 'DB/DataObject/Generator.php';
  1760.                     
  1761.                 
  1762.                 $x = new DB_DataObject_Generator;
  1763.                 $x->fillTableSchema($this->_database,$this->__table);
  1764.             }
  1765.             return true;
  1766.         }
  1767.         
  1768.         
  1769.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  1770.             DB_DataObject::_loadConfig();
  1771.         }
  1772.         
  1773.         // if you supply this with arguments, then it will take those
  1774.         // as the database and links array...
  1775.          
  1776.         $schemas = isset($_DB_DATAOBJECT['CONFIG']['schema_location']) ?
  1777.             array("{$_DB_DATAOBJECT['CONFIG']['schema_location']}/{$this->_database}.ini") :
  1778.             array() ;
  1779.                  
  1780.         if (isset($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"])) {
  1781.             $schemas = is_array($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]) ?
  1782.                 $_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"] :
  1783.                 explode(PATH_SEPARATOR,$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]);
  1784.         }
  1785.                     
  1786.          
  1787.         
  1788.         foreach ($schemas as $ini) {
  1789.              if (file_exists($ini) && is_file($ini)) {
  1790.                 $_DB_DATAOBJECT['INI'][$this->_database] = parse_ini_file($ini, true);
  1791.                 if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { 
  1792.                     if (!is_readable ($ini)) {
  1793.                         $this->debug("ini file is not readable: $ini","databaseStructure",1);
  1794.                     } else {
  1795.                         $this->debug("Loaded ini file: $ini","databaseStructure",1);
  1796.                     }
  1797.                 }
  1798.             } else {
  1799.                 if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  1800.                     $this->debug("Missing ini file: $ini","databaseStructure",1);
  1801.                 }
  1802.             }
  1803.              
  1804.         }
  1805.         // now have we loaded the structure.. 
  1806.         
  1807.         if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
  1808.             return true;
  1809.         }
  1810.         // - if not try building it..
  1811.         if (!empty($_DB_DATAOBJECT['CONFIG']['proxy'])) {
  1812.             class_exists('DB_DataObject_Generator') ? '' : 
  1813.                 require_once 'DB/DataObject/Generator.php';
  1814.                 
  1815.             $x = new DB_DataObject_Generator;
  1816.             $x->fillTableSchema($this->_database,$this->__table);
  1817.             // should this fail!!!???
  1818.             return true;
  1819.         }
  1820.         $this->debug("Cant find database schema: {$this->_database}/{$this->__table} \n".
  1821.                     "in links file data: " . print_r($_DB_DATAOBJECT['INI'],true),"databaseStructure",5);
  1822.         // we have to die here!! - it causes chaos if we dont (including looping forever!)
  1823.         $this->raiseError( "Unable to load schema for database and table (turn debugging up to 5 for full error message)", DB_DATAOBJECT_ERROR_INVALIDARGS, PEAR_ERROR_DIE);
  1824.         return false;
  1825.         
  1826.          
  1827.     }
  1828.  
  1829.  
  1830.  
  1831.  
  1832.     /**
  1833.      * Return or assign the name of the current table
  1834.      *
  1835.      *
  1836.      * @param   string optinal table name to set
  1837.      * @access public
  1838.      * @return string The name of the current table
  1839.      */
  1840.     function tableName()
  1841.     {
  1842.         $args = func_get_args();
  1843.         if (count($args)) {
  1844.             $this->__table = $args[0];
  1845.         }
  1846.         return $this->__table;
  1847.     }
  1848.     
  1849.     /**
  1850.      * Return or assign the name of the current database
  1851.      *
  1852.      * @param   string optional database name to set
  1853.      * @access public
  1854.      * @return string The name of the current database
  1855.      */
  1856.     function database()
  1857.     {
  1858.         $args = func_get_args();
  1859.         if (count($args)) {
  1860.             $this->_database = $args[0];
  1861.         }
  1862.         return $this->_database;
  1863.     }
  1864.   
  1865.     /**
  1866.      * get/set an associative array of table columns
  1867.      *
  1868.      * @access public
  1869.      * @param  array key=>type array
  1870.      * @return array (associative)
  1871.      */
  1872.     function table()
  1873.     {
  1874.         
  1875.         // for temporary storage of database fields..
  1876.         // note this is not declared as we dont want to bloat the print_r output
  1877.         $args = func_get_args();
  1878.         if (count($args)) {
  1879.             $this->_database_fields = $args[0];
  1880.         }
  1881.         if (isset($this->_database_fields)) {
  1882.             return $this->_database_fields;
  1883.         }
  1884.         
  1885.         
  1886.         global $_DB_DATAOBJECT;
  1887.         if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  1888.             $this->_connect();
  1889.         }
  1890.         
  1891.         if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
  1892.             return $_DB_DATAOBJECT['INI'][$this->_database][$this->__table];
  1893.         }
  1894.         
  1895.         $this->databaseStructure();
  1896.  
  1897.         
  1898.         $ret = array();
  1899.         if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
  1900.             $ret =  $_DB_DATAOBJECT['INI'][$this->_database][$this->__table];
  1901.         }
  1902.         
  1903.         return $ret;
  1904.     }
  1905.  
  1906.     /**
  1907.      * get/set an  array of table primary keys
  1908.      *
  1909.      * set usage: $do->keys('id','code');
  1910.      *
  1911.      * This is defined in the table definition if it gets it wrong,
  1912.      * or you do not want to use ini tables, you can override this.
  1913.      * @param  string optional set the key
  1914.      * @param  *   optional  set more keys
  1915.      * @access private
  1916.      * @return array
  1917.      */
  1918.     function keys()
  1919.     {
  1920.         // for temporary storage of database fields..
  1921.         // note this is not declared as we dont want to bloat the print_r output
  1922.         $args = func_get_args();
  1923.         if (count($args)) {
  1924.             $this->_database_keys = $args;
  1925.         }
  1926.         if (isset($this->_database_keys)) {
  1927.             return $this->_database_keys;
  1928.         }
  1929.         
  1930.         global $_DB_DATAOBJECT;
  1931.         if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  1932.             $this->_connect();
  1933.         }
  1934.         if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
  1935.             return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]);
  1936.         }
  1937.         $this->databaseStructure();
  1938.         
  1939.         if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
  1940.             return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]);
  1941.         }
  1942.         return array();
  1943.     }
  1944.     /**
  1945.      * get/set an  sequence key
  1946.      *
  1947.      * by default it returns the first key from keys()
  1948.      * set usage: $do->sequenceKey('id',true);
  1949.      *
  1950.      * override this to return array(false,false) if table has no real sequence key.
  1951.      *
  1952.      * @param  string  optional the key sequence/autoinc. key
  1953.      * @param  boolean optional use native increment. default false 
  1954.      * @param  false|string optional native sequence name
  1955.      * @access private
  1956.      * @return array (column,use_native,sequence_name)
  1957.      */
  1958.     function sequenceKey()
  1959.     {
  1960.         global $_DB_DATAOBJECT;
  1961.           
  1962.         // call setting
  1963.         if (!$this->_database) {
  1964.             $this->_connect();
  1965.         }
  1966.         
  1967.         if (!isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database])) {
  1968.             $_DB_DATAOBJECT['SEQUENCE'][$this->_database] = array();
  1969.         }
  1970.  
  1971.         
  1972.         $args = func_get_args();
  1973.         if (count($args)) {
  1974.             $args[1] = isset($args[1]) ? $args[1] : false;
  1975.             $args[2] = isset($args[2]) ? $args[2] : false;
  1976.             $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = $args;
  1977.         }
  1978.         if (isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table])) {
  1979.             return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table];
  1980.         }
  1981.         // end call setting (eg. $do->sequenceKeys(a,b,c); )
  1982.         
  1983.        
  1984.         
  1985.         
  1986.         $keys = $this->keys();
  1987.         if (!$keys) {
  1988.             return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] 
  1989.                 = array(false,false,false);
  1990.         }
  1991.  
  1992.  
  1993.         $table =  isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ?   
  1994.             $_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table();
  1995.        
  1996.         $dbtype    = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'];
  1997.         
  1998.         $usekey = $keys[0];
  1999.         
  2000.         
  2001.         
  2002.         $seqname = false;
  2003.         
  2004.         if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) {
  2005.             $usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
  2006.             if (strpos($usekey,':') !== false) {
  2007.                 list($usekey,$seqname) = explode(':',$usekey);
  2008.             }
  2009.         }  
  2010.         
  2011.         
  2012.         // if the key is not an integer - then it's not a sequence or native
  2013.         if (empty($table[$usekey]) || !($table[$usekey] & DB_DATAOBJECT_INT)) {
  2014.                 return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,false);
  2015.         }
  2016.         
  2017.         
  2018.         if (!empty($_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'])) {
  2019.             $ignore =  $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'];
  2020.             if (is_string($ignore) && (strtoupper($ignore) == 'ALL')) {
  2021.                 return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname);
  2022.             }
  2023.             if (is_string($ignore)) {
  2024.                 $ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'] = explode(',',$ignore);
  2025.             }
  2026.             if (in_array($this->__table,$ignore)) {
  2027.                 return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname);
  2028.             }
  2029.         }
  2030.         
  2031.         
  2032.         $realkeys = $_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"];
  2033.         
  2034.         // if you are using an old ini file - go back to old behaviour...
  2035.         if (is_numeric($realkeys[$usekey])) {
  2036.             $realkeys[$usekey] = 'N';
  2037.         }
  2038.         
  2039.         // multiple unique primary keys without a native sequence...
  2040.         if (($realkeys[$usekey] == 'K') && (count($keys) > 1)) {
  2041.             return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname);
  2042.         }
  2043.         // use native sequence keys...
  2044.         // technically postgres native here...
  2045.         // we need to get the new improved tabledata sorted out first.
  2046.         
  2047.         if (    in_array($dbtype , array('psql', 'mysql', 'mysqli', 'mssql', 'ifx')) && 
  2048.                 ($table[$usekey] & DB_DATAOBJECT_INT) && 
  2049.                 isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
  2050.                 ) {
  2051.             return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array($usekey,true,$seqname);
  2052.         }
  2053.         // if not a native autoinc, and we have not assumed all primary keys are sequence
  2054.         if (($realkeys[$usekey] != 'N') && 
  2055.             !empty($_DB_DATAOBJECT['CONFIG']['dont_use_pear_sequences'])) {
  2056.             return array(false,false,false);
  2057.         }
  2058.         // I assume it's going to try and be a nextval DB sequence.. (not native)
  2059.         return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array($usekey,false,$seqname);
  2060.     }
  2061.     
  2062.     
  2063.     
  2064.     /* =========================================================== */
  2065.     /*  Major Private Methods - the core part!              */
  2066.     /* =========================================================== */
  2067.  
  2068.  
  2069.     
  2070.     /**
  2071.      * clear the cache values for this class  - normally done on insert/update etc.
  2072.      *
  2073.      * @access private
  2074.      * @return void
  2075.      */
  2076.     function _clear_cache()
  2077.     {
  2078.         global $_DB_DATAOBJECT;
  2079.         
  2080.         $class = strtolower(get_class($this));
  2081.         
  2082.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2083.             $this->debug("Clearing Cache for ".$class,1);
  2084.         }
  2085.         
  2086.         if (!empty($_DB_DATAOBJECT['CACHE'][$class])) {
  2087.             unset($_DB_DATAOBJECT['CACHE'][$class]);
  2088.         }
  2089.     }
  2090.  
  2091.     
  2092.     /**
  2093.      * backend wrapper for quoting, as MDB2 and DB do it differently...
  2094.      *
  2095.      * @access private
  2096.      * @return string quoted
  2097.      */
  2098.     
  2099.     function _quote($str) 
  2100.     {
  2101.         global $_DB_DATAOBJECT;
  2102.         return (empty($_DB_DATAOBJECT['CONFIG']['db_driver']) || 
  2103.                 ($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB'))
  2104.             ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->quoteSmart($str)
  2105.             : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->quote($str);
  2106.     }
  2107.     
  2108.     
  2109.     /**
  2110.      * connects to the database
  2111.      *
  2112.      *
  2113.      * TODO: tidy this up - This has grown to support a number of connection options like
  2114.      *  a) dynamic changing of ini file to change which database to connect to
  2115.      *  b) multi data via the table_{$table} = dsn ini option
  2116.      *  c) session based storage.
  2117.      *
  2118.      * @access private
  2119.      * @return true | PEAR::error
  2120.      */
  2121.     function _connect()
  2122.     {
  2123.         global $_DB_DATAOBJECT;
  2124.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2125.             $this->_loadConfig();
  2126.         }
  2127.         // Set database driver for reference 
  2128.         $db_driver = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ? 'DB' : $_DB_DATAOBJECT['CONFIG']['db_driver'];
  2129.         // is it already connected ?
  2130.  
  2131.         if ($this->_database_dsn_md5 && !empty($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  2132.             if (PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  2133.                 return $this->raiseError(
  2134.                         $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->message,
  2135.                         $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->code, PEAR_ERROR_DIE
  2136.                 );
  2137.                  
  2138.             }
  2139.  
  2140.             if (!$this->_database) {
  2141.                 $this->_database = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
  2142.                 $hasGetDatabase = method_exists($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5], 'getDatabase');
  2143.                 
  2144.                 $this->_database = ($db_driver != 'DB' && $hasGetDatabase)  
  2145.                         ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->getDatabase() 
  2146.                         : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
  2147.  
  2148.                 
  2149.                 
  2150.                 if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite') 
  2151.                     && is_file($this->_database))  {
  2152.                     $this->_database = basename($this->_database);
  2153.                 }
  2154.                 if ($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'ibase')  {
  2155.                     $this->_database = substr(basename($this->_database), 0, -4);
  2156.                 }
  2157.                 
  2158.             }
  2159.             // theoretically we have a md5, it's listed in connections and it's not an error.
  2160.             // so everything is ok!
  2161.             return true;
  2162.             
  2163.         }
  2164.  
  2165.         // it's not currently connected!
  2166.         // try and work out what to use for the dsn !
  2167.  
  2168.         $options= &$_DB_DATAOBJECT['CONFIG'];
  2169.         $dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
  2170.         
  2171.         if (!$dsn) {
  2172.             if (!$this->_database) {
  2173.                 $this->_database = isset($options["table_{$this->__table}"]) ? $options["table_{$this->__table}"] : null;
  2174.             }
  2175.             if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2176.                 $this->debug("Checking for database database_{$this->_database} in options","CONNECT");
  2177.             }
  2178.             
  2179.             if ($this->_database && !empty($options["database_{$this->_database}"]))  {
  2180.                 
  2181.                 $dsn = $options["database_{$this->_database}"];
  2182.             } else if (!empty($options['database'])) {
  2183.                 $dsn = $options['database'];
  2184.             }
  2185.         }
  2186.         
  2187.         // if still no database...
  2188.         if (!$dsn) {
  2189.             return $this->raiseError(
  2190.                 "No database name / dsn found anywhere",
  2191.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG, PEAR_ERROR_DIE
  2192.             );
  2193.                  
  2194.         }
  2195.         
  2196.         
  2197.         if (is_string($dsn)) {
  2198.             $this->_database_dsn_md5 = md5($dsn);
  2199.         } else {
  2200.             /// support array based dsn's
  2201.             $this->_database_dsn_md5 = md5(serialize($dsn));
  2202.         }
  2203.  
  2204.         if (!empty($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  2205.             if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2206.                 $this->debug("USING CACHED CONNECTION", "CONNECT",3);
  2207.             }
  2208.             if (!$this->_database) {
  2209.  
  2210.                 $hasGetDatabase = method_exists($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5], 'getDatabase');
  2211.                 $this->_database = ($db_driver != 'DB' && $hasGetDatabase)  
  2212.                         ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->getDatabase() 
  2213.                         : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
  2214.                 
  2215.                 if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite') 
  2216.                     && is_file($this->_database)) 
  2217.                 {
  2218.                     $this->_database = basename($this->_database);
  2219.                 }
  2220.             }
  2221.             return true;
  2222.         }
  2223.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2224.             $this->debug("NEW CONNECTION", "CONNECT",3);
  2225.             /* actualy make a connection */
  2226.             $this->debug(print_r($dsn,true) ." {$this->_database_dsn_md5}", "CONNECT",3);
  2227.         }
  2228.         
  2229.         // Note this is verbose deliberatly! 
  2230.         
  2231.         if ($db_driver == 'DB') {
  2232.             
  2233.             /* PEAR DB connect */
  2234.             
  2235.             // this allows the setings of compatibility on DB 
  2236.             $db_options = PEAR::getStaticProperty('DB','options');
  2237.             require_once 'DB.php';
  2238.             if ($db_options) {
  2239.                 $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = &DB::connect($dsn,$db_options);
  2240.             } else {
  2241.                 $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = &DB::connect($dsn);
  2242.             }
  2243.             
  2244.         } else {
  2245.             /* assumption is MDB2 */
  2246.             require_once 'MDB2.php';
  2247.             // this allows the setings of compatibility on MDB2 
  2248.             $db_options = PEAR::getStaticProperty('MDB2','options');
  2249.             $db_options = is_array($db_options) ? $db_options : array();
  2250.             $db_options['portability'] = isset($db_options['portability'] )
  2251.                 ? $db_options['portability']  : MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE;
  2252.             $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = &MDB2::connect($dsn,$db_options);
  2253.             
  2254.         }
  2255.         
  2256.         
  2257.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2258.             $this->debug(serialize($_DB_DATAOBJECT['CONNECTIONS']), "CONNECT",5);
  2259.         }
  2260.         if (PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  2261.             $this->debug($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->toString(), "CONNECT FAILED",5);
  2262.             return $this->raiseError(
  2263.                     "Connect failed, turn on debugging to 5 see why",
  2264.                         $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->code, PEAR_ERROR_DIE
  2265.             );
  2266.  
  2267.         }
  2268.  
  2269.         if (!$this->_database) {
  2270.             $hasGetDatabase = method_exists($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5], 'getDatabase');
  2271.             
  2272.             $this->_database = ($db_driver != 'DB' && $hasGetDatabase)  
  2273.                     ? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->getDatabase() 
  2274.                     : $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database'];
  2275.  
  2276.  
  2277.             if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite') 
  2278.                 && is_file($this->_database)) 
  2279.             {
  2280.                 $this->_database = basename($this->_database);
  2281.             }
  2282.         }
  2283.         
  2284.         // Oracle need to optimize for portibility - not sure exactly what this does though :)
  2285.         $c = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  2286.          
  2287.         return true;
  2288.     }
  2289.  
  2290.     /**
  2291.      * sends query to database - this is the private one that must work 
  2292.      *   - internal functions use this rather than $this->query()
  2293.      *
  2294.      * @param  string  $string
  2295.      * @access private
  2296.      * @return mixed none or PEAR_Error
  2297.      */
  2298.     function _query($string)
  2299.     {
  2300.         global $_DB_DATAOBJECT;
  2301.         $this->_connect();
  2302.         
  2303.  
  2304.         $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  2305.  
  2306.         $options = &$_DB_DATAOBJECT['CONFIG'];
  2307.         
  2308.         $_DB_driver = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ? 
  2309.                     'DB':  $_DB_DATAOBJECT['CONFIG']['db_driver'];
  2310.         
  2311.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2312.             $this->debug($string,$log="QUERY");
  2313.             
  2314.         }
  2315.         
  2316.         if (strtoupper($string) == 'BEGIN') {
  2317.             if ($_DB_driver == 'DB') {
  2318.                 $DB->autoCommit(false);
  2319.             } else {
  2320.                 $DB->beginTransaction();
  2321.             }
  2322.             // db backend adds begin anyway from now on..
  2323.             return true;
  2324.         }
  2325.         if (strtoupper($string) == 'COMMIT') {
  2326.             $res = $DB->commit();
  2327.             if ($_DB_driver == 'DB') {
  2328.                 $DB->autoCommit(true);
  2329.             }
  2330.             return $res;
  2331.         }
  2332.         
  2333.         if (strtoupper($string) == 'ROLLBACK') {
  2334.             $DB->rollback();
  2335.             if ($_DB_driver == 'DB') {
  2336.                 $DB->autoCommit(true);
  2337.             }
  2338.             return true;
  2339.         }
  2340.         
  2341.  
  2342.         if (!empty($options['debug_ignore_updates']) &&
  2343.             (strtolower(substr(trim($string), 0, 6)) != 'select') &&
  2344.             (strtolower(substr(trim($string), 0, 4)) != 'show') &&
  2345.             (strtolower(substr(trim($string), 0, 8)) != 'describe')) {
  2346.  
  2347.             $this->debug('Disabling Update as you are in debug mode');
  2348.             return $this->raiseError("Disabling Update as you are in debug mode", null) ;
  2349.  
  2350.         }
  2351.         //if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 1) {
  2352.             // this will only work when PEAR:DB supports it.
  2353.             //$this->debug($DB->getAll('explain ' .$string,DB_DATAOBJECT_FETCHMODE_ASSOC), $log="sql",2);
  2354.         //}
  2355.         
  2356.         // some sim
  2357.         $t= explode(' ',microtime());
  2358.         $_DB_DATAOBJECT['QUERYENDTIME'] = $time = $t[0]+$t[1];
  2359.          
  2360.         
  2361.         if ($_DB_driver == 'DB') {
  2362.             $result = $DB->query($string);
  2363.         } else {
  2364.             switch (strtolower(substr(trim($string),0,6))) {
  2365.             
  2366.                 case 'insert':
  2367.                 case 'update':
  2368.                 case 'delete':
  2369.                     $result = $DB->exec($string);
  2370.                     break;
  2371.                     
  2372.                 default:
  2373.                     $result = $DB->query($string);
  2374.                     break;
  2375.             }
  2376.         }
  2377.         
  2378.        
  2379.  
  2380.         if (is_a($result,'PEAR_Error')) {
  2381.             if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { 
  2382.                 $this->debug($result->toString(), "Query Error",1 );
  2383.             }
  2384.             return $this->raiseError($result);
  2385.         }
  2386.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2387.             $t= explode(' ',microtime());
  2388.             $_DB_DATAOBJECT['QUERYENDTIME'] = $t[0]+$t[1];
  2389.             $this->debug('QUERY DONE IN  '.($t[0]+$t[1]-$time)." seconds", 'query',1);
  2390.         }
  2391.         switch (strtolower(substr(trim($string),0,6))) {
  2392.             case 'insert':
  2393.             case 'update':
  2394.             case 'delete':
  2395.                 if ($_DB_driver == 'DB') {
  2396.                     // pear DB specific
  2397.                     return $DB->affectedRows(); 
  2398.                 }
  2399.                 return $result;
  2400.         }
  2401.         if (is_object($result)) {
  2402.             // lets hope that copying the result object is OK!
  2403.             
  2404.             $_DB_resultid  = $GLOBALS['_DB_DATAOBJECT']['RESULTSEQ']++;
  2405.             $_DB_DATAOBJECT['RESULTS'][$_DB_resultid] = $result; 
  2406.             $this->_DB_resultid = $_DB_resultid;
  2407.         }
  2408.         $this->N = 0;
  2409.         if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2410.             $this->debug(serialize($result), 'RESULT',5);
  2411.         }
  2412.         if (method_exists($result, 'numrows')) {
  2413.             if ($_DB_driver == 'DB') {
  2414.                 $DB->expectError(DB_ERROR_UNSUPPORTED);
  2415.             } else {
  2416.                 $DB->expectError(MDB2_ERROR_UNSUPPORTED);
  2417.             }
  2418.             $this->N = $result->numrows();
  2419.             if (is_a($this->N,'PEAR_Error')) {
  2420.                 $this->N = true;
  2421.             }
  2422.             $DB->popExpect();
  2423.         }
  2424.     }
  2425.  
  2426.     /**
  2427.      * Builds the WHERE based on the values of of this object
  2428.      *
  2429.      * @param   mixed   $keys
  2430.      * @param   array   $filter (used by update to only uses keys in this filter list).
  2431.      * @param   array   $negative_filter (used by delete to prevent deleting using the keys mentioned..)
  2432.      * @access  private
  2433.      * @return  string
  2434.      */
  2435.     function _build_condition($keys, $filter = array(),$negative_filter=array())
  2436.     {
  2437.         global $_DB_DATAOBJECT;
  2438.         $this->_connect();
  2439.         $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  2440.        
  2441.         $quoteIdentifiers  = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  2442.         $options = $_DB_DATAOBJECT['CONFIG'];
  2443.         
  2444.         // if we dont have query vars.. - reset them.
  2445.         if ($this->_query === false) {
  2446.             $x = new DB_DataObject;
  2447.             $this->_query= $x->_query;
  2448.         }
  2449.  
  2450.         foreach($keys as $k => $v) {
  2451.             // index keys is an indexed array
  2452.             /* these filter checks are a bit suspicious..
  2453.                 - need to check that update really wants to work this way */
  2454.  
  2455.             if ($filter) {
  2456.                 if (!in_array($k, $filter)) {
  2457.                     continue;
  2458.                 }
  2459.             }
  2460.             if ($negative_filter) {
  2461.                 if (in_array($k, $negative_filter)) {
  2462.                     continue;
  2463.                 }
  2464.             }
  2465.             if (!isset($this->$k)) {
  2466.                 continue;
  2467.             }
  2468.             
  2469.             $kSql = $quoteIdentifiers 
  2470.                 ? ( $DB->quoteIdentifier($this->__table) . '.' . $DB->quoteIdentifier($k) )  
  2471.                 : "{$this->__table}.{$k}";
  2472.              
  2473.              
  2474.             
  2475.             if (is_a($this->$k,'DB_DataObject_Cast')) {
  2476.                 $dbtype = $DB->dsn["phptype"];
  2477.                 $value = $this->$k->toString($v,$DB);
  2478.                 if (PEAR::isError($value)) {
  2479.                     $this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
  2480.                     return false;
  2481.                 }
  2482.                 if ((strtolower($value) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
  2483.                     $this->whereAdd(" $kSql IS NULL");
  2484.                     continue;
  2485.                 }
  2486.                 $this->whereAdd(" $kSql = $value");
  2487.                 continue;
  2488.             }
  2489.             
  2490.             if (!isset($options['disable_null_strings']) &&  (strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
  2491.                 $this->whereAdd(" $kSql  IS NULL");
  2492.                 continue;
  2493.             }
  2494.             
  2495.  
  2496.             if ($v & DB_DATAOBJECT_STR) {
  2497.                 $this->whereAdd(" $kSql  = " . $this->_quote((string) (
  2498.                         ($v & DB_DATAOBJECT_BOOL) ? 
  2499.                             // this is thanks to the braindead idea of postgres to 
  2500.                             // use t/f for boolean.
  2501.                             (($this->$k === 'f') ? 0 : (int)(bool) $this->$k) :  
  2502.                             $this->$k
  2503.                     )) );
  2504.                 continue;
  2505.             }
  2506.             if (is_numeric($this->$k)) {
  2507.                 $this->whereAdd(" $kSql = {$this->$k}");
  2508.                 continue;
  2509.             }
  2510.             /* this is probably an error condition! */
  2511.             $this->whereAdd(" $kSql = ".intval($this->$k));
  2512.         }
  2513.     }
  2514.  
  2515.     /**
  2516.      * autoload Class relating to a table
  2517.      * (depreciated - use ::factory)
  2518.      *
  2519.      * @param  string  $table  table
  2520.      * @access private
  2521.      * @return string classname on Success
  2522.      */
  2523.     function staticAutoloadTable($table)
  2524.     {
  2525.         global $_DB_DATAOBJECT;
  2526.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2527.             DB_DataObject::_loadConfig();
  2528.         }
  2529.         $p = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
  2530.             $_DB_DATAOBJECT['CONFIG']['class_prefix'] : '';
  2531.         $class = $p . preg_replace('/[^A-Z0-9]/i','_',ucfirst($table));
  2532.         
  2533.         $ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
  2534.         $class = $ce ? $class  : DB_DataObject::_autoloadClass($class);
  2535.         return $class;
  2536.     }
  2537.     
  2538.     
  2539.      /**
  2540.      * classic factory method for loading a table class
  2541.      * usage: $do = DB_DataObject::factory('person')
  2542.      * WARNING - this may emit a include error if the file does not exist..
  2543.      * use @ to silence it (if you are sure it is acceptable)
  2544.      * eg. $do = @DB_DataObject::factory('person')
  2545.      *
  2546.      * table name will eventually be databasename/table
  2547.      * - and allow modular dataobjects to be written..
  2548.      * (this also helps proxy creation)
  2549.      *
  2550.      *
  2551.      * @param  string  $table  tablename (use blank to create a new instance of the same class.)
  2552.      * @access private
  2553.      * @return DataObject|PEAR_Error 
  2554.      */
  2555.     
  2556.     
  2557.  
  2558.     function factory($table = '') {
  2559.         global $_DB_DATAOBJECT;
  2560.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2561.             DB_DataObject::_loadConfig();
  2562.         }
  2563.         
  2564.         if ($table === '') {
  2565.             if (is_a($this,'DB_DataObject') && strlen($this->__table)) {
  2566.                 $table = $this->__table;
  2567.             } else {
  2568.                 return DB_DataObject::raiseError(
  2569.                     "factory did not recieve a table name",
  2570.                     DB_DATAOBJECT_ERROR_INVALIDARGS);
  2571.             }
  2572.         }
  2573.         
  2574.         
  2575.         $p = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
  2576.             $_DB_DATAOBJECT['CONFIG']['class_prefix'] : '';
  2577.         $class = $p . preg_replace('/[^A-Z0-9]/i','_',ucfirst($table));
  2578.         
  2579.         $ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
  2580.         $class = $ce ? $class  : DB_DataObject::_autoloadClass($class);
  2581.         
  2582.         // proxy = full|light
  2583.         if (!$class && isset($_DB_DATAOBJECT['CONFIG']['proxy'])) { 
  2584.             $proxyMethod = 'getProxy'.$_DB_DATAOBJECT['CONFIG']['proxy'];
  2585.             class_exists('DB_DataObject_Generator') ? '' : 
  2586.                     require_once 'DB/DataObject/Generator.php';
  2587.             
  2588.             $d = new DB_DataObject;
  2589.            
  2590.             $d->__table = $table;
  2591.             if (is_a($ret = $d->_connect(), 'PEAR_Error')) {
  2592.                 return $ret;
  2593.             }
  2594.             
  2595.             $x = new DB_DataObject_Generator;
  2596.             return $x->$proxyMethod( $d->_database, $table);
  2597.         }
  2598.         
  2599.         if (!$class) {
  2600.             return DB_DataObject::raiseError(
  2601.                 "factory could not find class $class from $table",
  2602.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2603.         }
  2604.  
  2605.         return new $class;
  2606.     }
  2607.     /**
  2608.      * autoload Class
  2609.      *
  2610.      * @param  string  $class  Class
  2611.      * @access private
  2612.      * @return string classname on Success
  2613.      */
  2614.     function _autoloadClass($class)
  2615.     {
  2616.         global $_DB_DATAOBJECT;
  2617.         
  2618.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2619.             DB_DataObject::_loadConfig();
  2620.         }
  2621.         $class_prefix = empty($_DB_DATAOBJECT['CONFIG']['class_prefix']) ? 
  2622.                 '' : $_DB_DATAOBJECT['CONFIG']['class_prefix'];
  2623.                 
  2624.         $table   = substr($class,strlen($class_prefix));
  2625.  
  2626.         // only include the file if it exists - and barf badly if it has parse errors :)
  2627.         if (!empty($_DB_DATAOBJECT['CONFIG']['proxy']) || empty($_DB_DATAOBJECT['CONFIG']['class_location'])) {
  2628.             return false;
  2629.         }
  2630.         
  2631.         
  2632.         if (strpos($_DB_DATAOBJECT['CONFIG']['class_location'],'%s') !== false) {
  2633.             $file = sprintf($_DB_DATAOBJECT['CONFIG']['class_location'], preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)));
  2634.         } else {
  2635.             $file = $_DB_DATAOBJECT['CONFIG']['class_location'].'/'.preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)).".php";
  2636.         }
  2637.         
  2638.         if (!file_exists($file)) {
  2639.             $found = false;
  2640.             foreach(explode(PATH_SEPARATOR, ini_get('include_path')) as $p) {
  2641.                 if (file_exists("$p/$file")) {
  2642.                     $file = "$p/$file";
  2643.                     $found = true;
  2644.                     break;
  2645.                 }
  2646.             }
  2647.             if (!$found) {
  2648.                 DB_DataObject::raiseError(
  2649.                     "autoload:Could not find class {$class} using class_location value", 
  2650.                     DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2651.                 return false;
  2652.             }
  2653.         }
  2654.         
  2655.         include_once $file;
  2656.         
  2657.         
  2658.         $ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
  2659.         
  2660.         if (!$ce) {
  2661.             DB_DataObject::raiseError(
  2662.                 "autoload:Could not autoload {$class}", 
  2663.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2664.             return false;
  2665.         }
  2666.         return $class;
  2667.     }
  2668.     
  2669.     
  2670.     
  2671.     /**
  2672.      * Have the links been loaded?
  2673.      * if they have it contains a array of those variables.
  2674.      *
  2675.      * @access  private
  2676.      * @var     boolean | array
  2677.      */
  2678.     var $_link_loaded = false;
  2679.     
  2680.     /**
  2681.     * Get the links associate array  as defined by the links.ini file.
  2682.     * 
  2683.     *
  2684.     * Experimental... - 
  2685.     * Should look a bit like
  2686.     *       [local_col_name] => "related_tablename:related_col_name"
  2687.     * 
  2688.     * 
  2689.     * @return   array|null    
  2690.     *           array       = if there are links defined for this table.
  2691.     *           empty array - if there is a links.ini file, but no links on this table
  2692.     *           null        - if no links.ini exists for this database (hence try auto_links).
  2693.     * @access   public
  2694.     * @see      DB_DataObject::getLinks(), DB_DataObject::getLink()
  2695.     */
  2696.     
  2697.     function links()
  2698.     {
  2699.         global $_DB_DATAOBJECT;
  2700.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  2701.             $this->_loadConfig();
  2702.         }
  2703.         // have to connect.. -> otherwise things break later.
  2704.         $this->_connect();
  2705.         
  2706.         if (isset($_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table])) {
  2707.             return $_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table];
  2708.         }
  2709.         
  2710.         
  2711.         
  2712.         
  2713.         
  2714.         // attempt to load links file here..
  2715.         
  2716.         if (!isset($_DB_DATAOBJECT['LINKS'][$this->_database])) {
  2717.             $schemas = isset($_DB_DATAOBJECT['CONFIG']['schema_location']) ?
  2718.                 array("{$_DB_DATAOBJECT['CONFIG']['schema_location']}/{$this->_database}.ini") :
  2719.                 array() ;
  2720.                      
  2721.             if (isset($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"])) {
  2722.                 $schemas = is_array($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]) ?
  2723.                     $_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"] :
  2724.                     explode(PATH_SEPARATOR,$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]);
  2725.             }
  2726.                         
  2727.              
  2728.             
  2729.             foreach ($schemas as $ini) {
  2730.                 
  2731.                 $links =
  2732.                     isset($_DB_DATAOBJECT['CONFIG']["links_{$this->_database}"]) ?
  2733.                         $_DB_DATAOBJECT['CONFIG']["links_{$this->_database}"] :
  2734.                         str_replace('.ini','.links.ini',$ini);
  2735.         
  2736.                 if (empty($_DB_DATAOBJECT['LINKS'][$this->_database]) && file_exists($links) && is_file($links)) {
  2737.                     /* not sure why $links = ... here  - TODO check if that works */
  2738.                     $_DB_DATAOBJECT['LINKS'][$this->_database] = parse_ini_file($links, true);
  2739.                     if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2740.                         $this->debug("Loaded links.ini file: $links","links",1);
  2741.                     }
  2742.                 } else {
  2743.                     if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
  2744.                         $this->debug("Missing links.ini file: $links","links",1);
  2745.                     }
  2746.                 }
  2747.             }
  2748.         }
  2749.         
  2750.         
  2751.         // if there is no link data at all on the file!
  2752.         // we return null.
  2753.         if (!isset($_DB_DATAOBJECT['LINKS'][$this->_database])) {
  2754.             return null;
  2755.         }
  2756.         
  2757.         if (isset($_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table])) {
  2758.             return $_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table];
  2759.         }
  2760.         
  2761.         return array();
  2762.     }
  2763.     /**
  2764.      * load related objects
  2765.      *
  2766.      * There are two ways to use this, one is to set up a <dbname>.links.ini file
  2767.      * into a static property named <dbname>.links and specifies the table joins,
  2768.      * the other highly dependent on naming columns 'correctly' :)
  2769.      * using colname = xxxxx_yyyyyy
  2770.      * xxxxxx = related table; (yyyyy = user defined..)
  2771.      * looks up table xxxxx, for value id=$this->xxxxx
  2772.      * stores it in $this->_xxxxx_yyyyy
  2773.      * you can change what object vars the links are stored in by 
  2774.      * changeing the format parameter
  2775.      *
  2776.      *
  2777.      * @param  string format (default _%s) where %s is the table name.
  2778.      * @author Tim White <tim@cyface.com>
  2779.      * @access public
  2780.      * @return boolean , true on success
  2781.      */
  2782.     function getLinks($format = '_%s')
  2783.     {
  2784.          
  2785.         // get table will load the options.
  2786.         if ($this->_link_loaded) {
  2787.             return true;
  2788.         }
  2789.         $this->_link_loaded = false;
  2790.         $cols  = $this->table();
  2791.         $links = $this->links();
  2792.          
  2793.         $loaded = array();
  2794.         
  2795.         if ($links) {   
  2796.             foreach($links as $key => $match) {
  2797.                 list($table,$link) = explode(':', $match);
  2798.                 $k = sprintf($format, str_replace('.', '_', $key));
  2799.                 // makes sure that '.' is the end of the key;
  2800.                 if ($p = strpos($key,'.')) {
  2801.                       $key = substr($key, 0, $p);
  2802.                 }
  2803.                 
  2804.                 $this->$k = $this->getLink($key, $table, $link);
  2805.                 
  2806.                 if (is_object($this->$k)) {
  2807.                     $loaded[] = $k; 
  2808.                 }
  2809.             }
  2810.             $this->_link_loaded = $loaded;
  2811.             return true;
  2812.         }
  2813.         // this is the autonaming stuff..
  2814.         // it sends the column name down to getLink and lets that sort it out..
  2815.         // if there is a links file then it is not used!
  2816.         // IT IS DEPRECIATED!!!! - USE 
  2817.         if (!is_null($links)) {    
  2818.             return false;
  2819.         }
  2820.         
  2821.         
  2822.         foreach (array_keys($cols) as $key) {
  2823.             if (!($p = strpos($key, '_'))) {
  2824.                 continue;
  2825.             }
  2826.             // does the table exist.
  2827.             $k =sprintf($format, $key);
  2828.             $this->$k = $this->getLink($key);
  2829.             if (is_object($this->$k)) {
  2830.                 $loaded[] = $k; 
  2831.             }
  2832.         }
  2833.         $this->_link_loaded = $loaded;
  2834.         return true;
  2835.     }
  2836.  
  2837.     /**
  2838.      * return name from related object
  2839.      *
  2840.      * There are two ways to use this, one is to set up a <dbname>.links.ini file
  2841.      * into a static property named <dbname>.links and specifies the table joins,
  2842.      * the other is highly dependant on naming columns 'correctly' :)
  2843.      *
  2844.      * NOTE: the naming convention is depreciated!!! - use links.ini
  2845.      *
  2846.      * using colname = xxxxx_yyyyyy
  2847.      * xxxxxx = related table; (yyyyy = user defined..)
  2848.      * looks up table xxxxx, for value id=$this->xxxxx
  2849.      * stores it in $this->_xxxxx_yyyyy
  2850.      *
  2851.      * you can also use $this->getLink('thisColumnName','otherTable','otherTableColumnName')
  2852.      *
  2853.      *
  2854.      * @param string $row    either row or row.xxxxx
  2855.      * @param string $table  name of table to look up value in
  2856.      * @param string $link   name of column in other table to match
  2857.      * @author Tim White <tim@cyface.com>
  2858.      * @access public
  2859.      * @return mixed object on success
  2860.      */
  2861.     function getLink($row, $table = null, $link = false)
  2862.     {
  2863.         
  2864.         
  2865.         // GUESS THE LINKED TABLE.. (if found - recursevly call self)
  2866.         
  2867.         if ($table === null) {
  2868.             $links = $this->links();
  2869.             
  2870.             if (is_array($links)) {
  2871.             
  2872.                 if ($links[$row]) {
  2873.                     list($table,$link) = explode(':', $links[$row]);
  2874.                     if ($p = strpos($row,".")) {
  2875.                         $row = substr($row,0,$p);
  2876.                     }
  2877.                     return $this->getLink($row,$table,$link);
  2878.                     
  2879.                 } 
  2880.                 
  2881.                 $this->raiseError(
  2882.                     "getLink: $row is not defined as a link (normally this is ok)", 
  2883.                     DB_DATAOBJECT_ERROR_NODATA);
  2884.                     
  2885.                 $r = false;
  2886.                 return $r;// technically a possible error condition?
  2887.  
  2888.             }  
  2889.             // use the old _ method - this shouldnt happen if called via getLinks()
  2890.             if (!($p = strpos($row, '_'))) {
  2891.                 $r = null;
  2892.                 return $r; 
  2893.             }
  2894.             $table = substr($row, 0, $p);
  2895.             return $this->getLink($row, $table);
  2896.             
  2897.  
  2898.         }
  2899.         
  2900.         
  2901.         
  2902.         if (!isset($this->$row)) {
  2903.             $this->raiseError("getLink: row not set $row", DB_DATAOBJECT_ERROR_NODATA);
  2904.             return false;
  2905.         }
  2906.         
  2907.         // check to see if we know anything about this table..
  2908.         
  2909.         $obj = $this->factory($table);
  2910.         
  2911.         if (!is_a($obj,'DB_DataObject')) {
  2912.             $this->raiseError(
  2913.                 "getLink:Could not find class for row $row, table $table", 
  2914.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  2915.             return false;
  2916.         }
  2917.         if ($link) {
  2918.             if ($obj->get($link, $this->$row)) {
  2919.                 $obj->free();
  2920.                 return $obj;
  2921.             } 
  2922.             return  false;
  2923.         }
  2924.         
  2925.         if ($obj->get($this->$row)) {
  2926.             $obj->free();
  2927.             return $obj;
  2928.         }
  2929.         return false;
  2930.         
  2931.     }
  2932.  
  2933.     /**
  2934.      * IS THIS SUPPORTED/USED ANYMORE???? 
  2935.      *return a list of options for a linked table
  2936.      *
  2937.      * This is highly dependant on naming columns 'correctly' :)
  2938.      * using colname = xxxxx_yyyyyy
  2939.      * xxxxxx = related table; (yyyyy = user defined..)
  2940.      * looks up table xxxxx, for value id=$this->xxxxx
  2941.      * stores it in $this->_xxxxx_yyyyy
  2942.      *
  2943.      * @access public
  2944.      * @return array of results (empty array on failure)
  2945.      */
  2946.     function &getLinkArray($row, $table = null)
  2947.     {
  2948.         
  2949.         $ret = array();
  2950.         if (!$table) {
  2951.             $links = $this->links();
  2952.             
  2953.             if (is_array($links)) {
  2954.                 if (!isset($links[$row])) {
  2955.                     // failed..
  2956.                     return $ret;
  2957.                 }
  2958.                 list($table,$link) = explode(':',$links[$row]);
  2959.             } else {
  2960.                 if (!($p = strpos($row,'_'))) {
  2961.                     return $ret;
  2962.                 }
  2963.                 $table = substr($row,0,$p);
  2964.             }
  2965.         }
  2966.         
  2967.         $c  = $this->factory($table);
  2968.         
  2969.         if (!is_a($c,'DB_DataObject')) {
  2970.             $this->raiseError(
  2971.                 "getLinkArray:Could not find class for row $row, table $table", 
  2972.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG
  2973.             );
  2974.             return $ret;
  2975.         }
  2976.  
  2977.         // if the user defined method list exists - use it...
  2978.         if (method_exists($c, 'listFind')) {
  2979.             $c->listFind($this->id);
  2980.         } else {
  2981.             $c->find();
  2982.         }
  2983.         while ($c->fetch()) {
  2984.             $ret[] = $c;
  2985.         }
  2986.         return $ret;
  2987.     }
  2988.  
  2989.     /**
  2990.      * The JOIN condition
  2991.      *
  2992.      * @access  private
  2993.      * @var     string
  2994.      */
  2995.     var $_join = '';
  2996.  
  2997.     /**
  2998.      * joinAdd - adds another dataobject to this, building a joined query.
  2999.      *
  3000.      * example (requires links.ini to be set up correctly)
  3001.      * // get all the images for product 24
  3002.      * $i = new DataObject_Image();
  3003.      * $pi = new DataObjects_Product_image();
  3004.      * $pi->product_id = 24; // set the product id to 24
  3005.      * $i->joinAdd($pi); // add the product_image connectoin
  3006.      * $i->find();
  3007.      * while ($i->fetch()) {
  3008.      *     // do stuff
  3009.      * }
  3010.      * // an example with 2 joins
  3011.      * // get all the images linked with products or productgroups
  3012.      * $i = new DataObject_Image();
  3013.      * $pi = new DataObject_Product_image();
  3014.      * $pgi = new DataObject_Productgroup_image();
  3015.      * $i->joinAdd($pi);
  3016.      * $i->joinAdd($pgi);
  3017.      * $i->find();
  3018.      * while ($i->fetch()) {
  3019.      *     // do stuff
  3020.      * }
  3021.      *
  3022.      *
  3023.      * @param    optional $obj       object |array    the joining object (no value resets the join)
  3024.      *                                          If you use an array here it should be in the format:
  3025.      *                                          array('local_column','remotetable:remote_column');
  3026.      *                                          if remotetable does not have a definition, you should
  3027.      *                                          use @ to hide the include error message..
  3028.      *                                      
  3029.      *
  3030.      * @param    optional $joinType  string | array
  3031.      *                                          'LEFT'|'INNER'|'RIGHT'|'' Inner is default, '' indicates 
  3032.      *                                          just select ... from a,b,c with no join and 
  3033.      *                                          links are added as where items.
  3034.      *                                          
  3035.      *                                          If second Argument is array, it is assumed to be an associative
  3036.      *                                          array with arguments matching below = eg.
  3037.      *                                          'joinType' => 'INNER',
  3038.      *                                          'joinAs' => '...'
  3039.      *                                          'joinCol' => ....
  3040.      *                                          'useWhereAsOn' => false,
  3041.      *
  3042.      * @param    optional $joinAs    string     if you want to select the table as anther name
  3043.      *                                          useful when you want to select multiple columsn
  3044.      *                                          from a secondary table.
  3045.      
  3046.      * @param    optional $joinCol   string     The column on This objects table to match (needed
  3047.      *                                          if this table links to the child object in 
  3048.      *                                          multiple places eg.
  3049.      *                                          user->friend (is a link to another user)
  3050.      *                                          user->mother (is a link to another user..)
  3051.      *
  3052.      *           optional 'useWhereAsOn' bool   default false;
  3053.      *                                          convert the where argments from the object being added
  3054.      *                                          into ON arguments.
  3055.      * 
  3056.      * 
  3057.      * @return   none
  3058.      * @access   public
  3059.      * @author   Stijn de Reede      <sjr@gmx.co.uk>
  3060.      */
  3061.     function joinAdd($obj = false, $joinType='INNER', $joinAs=false, $joinCol=false)
  3062.     {
  3063.         global $_DB_DATAOBJECT;
  3064.         if ($obj === false) {
  3065.             $this->_join = '';
  3066.             return;
  3067.         }
  3068.          
  3069.  
  3070.         $useWhereAsOn = false;
  3071.         // support for 2nd argument as an array of options
  3072.         if (is_array($joinType)) {
  3073.             // new options can now go in here... (dont forget to document them)
  3074.             $useWhereAsOn = !empty($joinType['useWhereAsOn']);
  3075.             $joinCol      = isset($joinType['joinCol'])  ? $joinType['joinCol']  : $joinCol;
  3076.             $joinAs       = isset($joinType['joinAs'])   ? $joinType['joinAs']   : $joinAs;
  3077.             $joinType     = isset($joinType['joinType']) ? $joinType['joinType'] : 'INNER';
  3078.         }
  3079.         // support for array as first argument 
  3080.         // this assumes that you dont have a links.ini for the specified table.
  3081.         // and it doesnt exist as am extended dataobject!! - experimental.
  3082.         
  3083.         $ofield = false; // object field
  3084.         $tfield = false; // this field
  3085.         $toTable = false;
  3086.         if (is_array($obj)) {
  3087.             $tfield = $obj[0];
  3088.             list($toTable,$ofield) = explode(':',$obj[1]);
  3089.             $obj = DB_DataObject::factory($toTable);
  3090.             
  3091.             if (!$obj || is_a($obj,'PEAR_Error')) {
  3092.                 $obj = new DB_DataObject;
  3093.                 $obj->__table = $toTable;
  3094.             }
  3095.             $obj->_connect();
  3096.             // set the table items to nothing.. - eg. do not try and match
  3097.             // things in the child table...???
  3098.             $items = array();
  3099.         }
  3100.         
  3101.         if (!is_object($obj) || !is_a($obj,'DB_DataObject')) {
  3102.             return $this->raiseError("joinAdd: called without an object", DB_DATAOBJECT_ERROR_NODATA,PEAR_ERROR_DIE);
  3103.         }
  3104.         /*  make sure $this->_database is set.  */
  3105.         $this->_connect();
  3106.         $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  3107.        
  3108.  
  3109.         
  3110.         
  3111.          /* look up the links for obj table */
  3112.         //print_r($obj->links());
  3113.         if (!$ofield && ($olinks = $obj->links())) {
  3114.             
  3115.             foreach ($olinks as $k => $v) {
  3116.                 /* link contains {this column} = {linked table}:{linked column} */
  3117.                 $ar = explode(':', $v);
  3118.                 
  3119.                 // Feature Request #4266 - Allow joins with multiple keys
  3120.                 
  3121.                 $links_key_array = strpos($k,',');
  3122.                 if ($links_key_array !== false) {
  3123.                     $k = explode(',', $k);
  3124.                 }
  3125.                 
  3126.                 $ar_array = strpos($ar[1],',');
  3127.                 if ($ar_array !== false) {
  3128.                     $ar[1] = explode(',', $ar[1]);
  3129.                 }
  3130.              
  3131.                 if ($ar[0] == $this->__table) {
  3132.                     
  3133.                     // you have explictly specified the column
  3134.                     // and the col is listed here..
  3135.                     // not sure if 1:1 table could cause probs here..
  3136.                     
  3137.                     if ($joinCol !== false) {
  3138.                         $this->raiseError( 
  3139.                             "joinAdd: You cannot target a join column in the " .
  3140.                             "'link from' table ({$obj->__table}). " . 
  3141.                             "Either remove the fourth argument to joinAdd() ".
  3142.                             "({$joinCol}), or alter your links.ini file.",
  3143.                             DB_DATAOBJECT_ERROR_NODATA);
  3144.                         return false;
  3145.                     }
  3146.                 
  3147.                     $ofield = $k;
  3148.                     $tfield = $ar[1];
  3149.                     break;
  3150.                 }
  3151.             }
  3152.         }
  3153.  
  3154.         /* otherwise see if there are any links from this table to the obj. */
  3155.         //print_r($this->links());
  3156.         if (($ofield === false) && ($links = $this->links())) {
  3157.             foreach ($links as $k => $v) {
  3158.                 /* link contains {this column} = {linked table}:{linked column} */
  3159.                 $ar = explode(':', $v);
  3160.                 // Feature Request #4266 - Allow joins with multiple keys
  3161.                 if (strpos($k, ',') !== false) {
  3162.                     $k = explode(',', $k);
  3163.                 }
  3164.                 if (strpos($ar[1], ',') !== false) {
  3165.                     $ar[1] = explode(',', $ar[1]);
  3166.                 }
  3167.  
  3168.                 if ($ar[0] == $obj->__table) {
  3169.                     if ($joinCol !== false) {
  3170.                         if ($k == $joinCol) {
  3171.                             $tfield = $k;
  3172.                             $ofield = $ar[1];
  3173.                             break;
  3174.                         } else {
  3175.                             continue;
  3176.                         }
  3177.                     } else {
  3178.                         $tfield = $k;
  3179.                         $ofield = $ar[1];
  3180.                         break;
  3181.                     }
  3182.                 }
  3183.             }
  3184.         }
  3185.         // finally if these two table have column names that match do a join by default on them
  3186.  
  3187.         if (($ofield === false) && $joinCol) {
  3188.             $ofield = $joinCol;
  3189.             $tfield = $joinCol;
  3190.  
  3191.         }
  3192.         /* did I find a conneciton between them? */
  3193.  
  3194.         if ($ofield === false) {
  3195.             $this->raiseError(
  3196.                 "joinAdd: {$obj->__table} has no link with {$this->__table}",
  3197.                 DB_DATAOBJECT_ERROR_NODATA);
  3198.             return false;
  3199.         }
  3200.         $joinType = strtoupper($joinType);
  3201.         
  3202.         // we default to joining as the same name (this is remvoed later..)
  3203.         
  3204.         if ($joinAs === false) {
  3205.             $joinAs = $obj->__table;
  3206.         }
  3207.         
  3208.         $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
  3209.         $options = $_DB_DATAOBJECT['CONFIG'];
  3210.         
  3211.         // not sure  how portable adding database prefixes is..
  3212.         $objTable = $quoteIdentifiers ? 
  3213.                 $DB->quoteIdentifier($obj->__table) : 
  3214.                  $obj->__table ;
  3215.                 
  3216.         $dbPrefix  = '';
  3217.         if (strlen($obj->_database) && in_array($DB->dsn['phptype'],array('mysql','mysqli'))) {
  3218.             $dbPrefix = ($quoteIdentifiers
  3219.                          ? $DB->quoteIdentifier($obj->_database)
  3220.                          : $obj->_database) . '.';    
  3221.         }
  3222.         
  3223.         // if they are the same, then dont add a prefix...                
  3224.         if ($obj->_database == $this->_database) {
  3225.            $dbPrefix = '';
  3226.         }
  3227.         // as far as we know only mysql supports database prefixes..
  3228.         // prefixing the database name is now the default behaviour,
  3229.         // as it enables joining mutiple columns from multiple databases...
  3230.          
  3231.             // prefix database (quoted if neccessary..)
  3232.         $objTable = $dbPrefix . $objTable;
  3233.        
  3234.         $cond = '';
  3235.  
  3236.         // if obj only a dataobject - eg. no extended class has been defined..
  3237.         // it obvioulsy cant work out what child elements might exist...
  3238.         // until we get on the fly querying of tables..
  3239.         // note: we have already checked that it is_a(db_dataobject earlier)
  3240.         if ( strtolower(get_class($obj)) != 'db_dataobject') {
  3241.                  
  3242.             // now add where conditions for anything that is set in the object 
  3243.         
  3244.         
  3245.         
  3246.             $items = $obj->table();
  3247.             // will return an array if no items..
  3248.             
  3249.             // only fail if we where expecting it to work (eg. not joined on a array)
  3250.              
  3251.             if (!$items) {
  3252.                 $this->raiseError(
  3253.                     "joinAdd: No table definition for {$obj->__table}", 
  3254.                     DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  3255.                 return false;
  3256.             }
  3257.  
  3258.             foreach($items as $k => $v) {
  3259.                 if (!isset($obj->$k)) {
  3260.                     continue;
  3261.                 }
  3262.                 
  3263.                 $kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k);
  3264.                 
  3265.                 
  3266.                 if ($v & DB_DATAOBJECT_STR) {
  3267.                     $obj->whereAdd("{$joinAs}.{$kSql} = " . $this->_quote((string) (
  3268.                             ($v & DB_DATAOBJECT_BOOL) ? 
  3269.                                 // this is thanks to the braindead idea of postgres to 
  3270.                                 // use t/f for boolean.
  3271.                                 (($obj->$k === 'f') ? 0 : (int)(bool) $obj->$k) :  
  3272.                                 $obj->$k
  3273.                         )));
  3274.                     continue;
  3275.                 }
  3276.                 if (is_numeric($obj->$k)) {
  3277.                     $obj->whereAdd("{$joinAs}.{$kSql} = {$obj->$k}");
  3278.                     continue;
  3279.                 }
  3280.                             
  3281.                 if (is_a($obj->$k,'DB_DataObject_Cast')) {
  3282.                     $value = $obj->$k->toString($v,$DB);
  3283.                     if (PEAR::isError($value)) {
  3284.                         $this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
  3285.                         return false;
  3286.                     }
  3287.                     if (!isset($options['disable_null_strings']) && strtolower($value) === 'null') {
  3288.                         $obj->whereAdd("{$joinAs}.{$kSql} IS NULL");
  3289.                         continue;
  3290.                     } else {
  3291.                         $obj->whereAdd("{$joinAs}.{$kSql} = $value");
  3292.                         continue;
  3293.                     }
  3294.                 }
  3295.                 
  3296.                 
  3297.                 /* this is probably an error condition! */
  3298.                 $obj->whereAdd("{$joinAs}.{$kSql} = 0");
  3299.             }
  3300.             if ($this->_query === false) {
  3301.                 $this->raiseError(
  3302.                     "joinAdd can not be run from a object that has had a query run on it,
  3303.                     clone the object or create a new one and use setFrom()", 
  3304.                     DB_DATAOBJECT_ERROR_INVALIDARGS);
  3305.                 return false;
  3306.             }
  3307.         }
  3308.  
  3309.         // and finally merge the whereAdd from the child..
  3310.         if ($obj->_query['condition']) {
  3311.             $cond = preg_replace('/^\sWHERE/i','',$obj->_query['condition']);
  3312.  
  3313.             if (!$useWhereAsOn) {
  3314.                 $this->whereAdd($cond);
  3315.             }
  3316.         }
  3317.     
  3318.         
  3319.         
  3320.         
  3321.         // nested (join of joined objects..)
  3322.         $appendJoin = '';
  3323.         if ($obj->_join) {
  3324.             // postgres allows nested queries, with ()'s
  3325.             // not sure what the results are with other databases..
  3326.             // may be unpredictable..
  3327.             if (in_array($DB->dsn["phptype"],array('pgsql'))) {
  3328.                 $objTable = "($objTable {$obj->_join})";
  3329.             } else {
  3330.                 $appendJoin = $obj->_join;
  3331.             }
  3332.         }
  3333.         
  3334.   
  3335.         // fix for #2216
  3336.         // add the joinee object's conditions to the ON clause instead of the WHERE clause
  3337.         if ($useWhereAsOn && strlen($cond)) {
  3338.             $appendJoin = ' AND ' . $cond . ' ' . $appendJoin;
  3339.         }
  3340.                
  3341.         
  3342.         
  3343.         $table = $this->__table;
  3344.         
  3345.         if ($quoteIdentifiers) {
  3346.             $joinAs   = $DB->quoteIdentifier($joinAs);
  3347.             $table    = $DB->quoteIdentifier($table);     
  3348.             $ofield   = (is_array($ofield)) ? array_map(array($DB, 'quoteIdentifier'), $ofield) : $DB->quoteIdentifier($ofield);
  3349.             $tfield   = (is_array($tfield)) ? array_map(array($DB, 'quoteIdentifier'), $tfield) : $DB->quoteIdentifier($tfield); 
  3350.         }
  3351.         // add database prefix if they are different databases
  3352.        
  3353.         
  3354.         $fullJoinAs = '';
  3355.         $addJoinAs  = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->__table) : $obj->__table) != $joinAs;
  3356.         if ($addJoinAs) {
  3357.             // join table a AS b - is only supported by a few databases and is probably not needed
  3358.             // , however since it makes the whole Statement alot clearer we are leaving it in
  3359.             // for those databases.
  3360.             $fullJoinAs = in_array($DB->dsn["phptype"],array('mysql','mysqli','pgsql')) ? "AS {$joinAs}" :  $joinAs;
  3361.         } else {
  3362.             // if 
  3363.             $joinAs = $dbPrefix . $joinAs;
  3364.         }
  3365.         
  3366.         
  3367.         switch ($joinType) {
  3368.             case 'INNER':
  3369.             case 'LEFT': 
  3370.             case 'RIGHT': // others??? .. cross, left outer, right outer, natural..?
  3371.                 
  3372.                 // Feature Request #4266 - Allow joins with multiple keys
  3373.                 $this->_join .= "\n {$joinType} JOIN {$objTable} {$fullJoinAs}";
  3374.                 if (is_array($ofield)) {
  3375.                     $key_count = count($ofield);
  3376.                     for($i = 0; $i < $key_count; $i++) {
  3377.                         if ($i == 0) {
  3378.                             $this->_join .= " ON ({$joinAs}.{$ofield[$i]}={$table}.{$tfield[$i]}) ";
  3379.                         }
  3380.                         else {
  3381.                             $this->_join .= " AND {$joinAs}.{$ofield[$i]}={$table}.{$tfield[$i]} ";
  3382.                         }
  3383.                     }
  3384.                     $this->_join .= ' ' . $appendJoin . ' ';
  3385.                 } else {
  3386.                     $this->_join .= " ON ({$joinAs}.{$ofield}={$table}.{$tfield}) {$appendJoin} ";
  3387.                 }
  3388.  
  3389.                 break;
  3390.                 
  3391.             case '': // this is just a standard multitable select..
  3392.                 $this->_join .= "\n , {$objTable} {$fullJoinAs} {$appendJoin}";
  3393.                 $this->whereAdd("{$joinAs}.{$ofield}={$table}.{$tfield}");
  3394.         }
  3395.          
  3396.          
  3397.         return true;
  3398.  
  3399.     }
  3400.  
  3401.     /**
  3402.      * Copies items that are in the table definitions from an
  3403.      * array or object into the current object
  3404.      * will not override key values.
  3405.      *
  3406.      *
  3407.      * @param    array | object  $from
  3408.      * @param    string  $format eg. map xxxx_name to $object->name using 'xxxx_%s' (defaults to %s - eg. name -> $object->name
  3409.      * @param    boolean  $skipEmpty (dont assign empty values if a column is empty (eg. '' / 0 etc...)
  3410.      * @access   public
  3411.      * @return   true on success or array of key=>setValue error message
  3412.      */
  3413.     function setFrom($from, $format = '%s', $skipEmpty=false)
  3414.     {
  3415.         global $_DB_DATAOBJECT;
  3416.         $keys  = $this->keys();
  3417.         $items = $this->table();
  3418.         if (!$items) {
  3419.             $this->raiseError(
  3420.                 "setFrom:Could not find table definition for {$this->__table}", 
  3421.                 DB_DATAOBJECT_ERROR_INVALIDCONFIG);
  3422.             return;
  3423.         }
  3424.         $overload_return = array();
  3425.         foreach (array_keys($items) as $k) {
  3426.             if (in_array($k,$keys)) {
  3427.                 continue; // dont overwrite keys
  3428.             }
  3429.             if (!$k) {
  3430.                 continue; // ignore empty keys!!! what
  3431.             }
  3432.             if (is_object($from) && isset($from->{sprintf($format,$k)})) {
  3433.                 $kk = (strtolower($k) == 'from') ? '_from' : $k;
  3434.                 if (method_exists($this,'set'.$kk)) {
  3435.                     $ret = $this->{'set'.$kk}($from->{sprintf($format,$k)});
  3436.                     if (is_string($ret)) {
  3437.                         $overload_return[$k] = $ret;
  3438.                     }
  3439.                     continue;
  3440.                 }
  3441.                 $this->$k = $from->{sprintf($format,$k)};
  3442.                 continue;
  3443.             }
  3444.             
  3445.             if (is_object($from)) {
  3446.                 continue;
  3447.             }
  3448.             
  3449.             if (empty($from[$k]) && $skipEmpty) {
  3450.                 continue;
  3451.             }
  3452.             
  3453.             if (!isset($from[sprintf($format,$k)])) {
  3454.                 continue;
  3455.             }
  3456.            
  3457.             $kk = (strtolower($k) == 'from') ? '_from' : $k;
  3458.             if (method_exists($this,'set'. $kk)) {
  3459.                 $ret =  $this->{'set'.$kk}($from[sprintf($format,$k)]);
  3460.                 if (is_string($ret)) {
  3461.                     $overload_return[$k] = $ret;
  3462.                 }
  3463.                 continue;
  3464.             }
  3465.             if (is_object($from[sprintf($format,$k)])) {
  3466.                 continue;
  3467.             }
  3468.             if (is_array($from[sprintf($format,$k)])) {
  3469.                 continue;
  3470.             }
  3471.             $ret = $this->fromValue($k,$from[sprintf($format,$k)]);
  3472.             if ($ret !== true)  {
  3473.                 $overload_return[$k] = 'Not A Valid Value';
  3474.             }
  3475.             //$this->$k = $from[sprintf($format,$k)];
  3476.         }
  3477.         if ($overload_return) {
  3478.             return $overload_return;
  3479.         }
  3480.         return true;
  3481.     }
  3482.  
  3483.     /**
  3484.      * Returns an associative array from the current data
  3485.      * (kind of oblivates the idea behind DataObjects, but
  3486.      * is usefull if you use it with things like QuickForms.
  3487.      *
  3488.      * you can use the format to return things like user[key]
  3489.      * by sending it $object->toArray('user[%s]')
  3490.      *
  3491.      * will also return links converted to arrays.
  3492.      *
  3493.      * @param   string  sprintf format for array
  3494.      * @param   bool    empty only return elemnts that have a value set.
  3495.      *
  3496.      * @access   public
  3497.      * @return   array of key => value for row
  3498.      */
  3499.  
  3500.     function toArray($format = '%s', $hideEmpty = false) 
  3501.     {
  3502.         global $_DB_DATAOBJECT;
  3503.         $ret = array();
  3504.         $rf = ($this->_resultFields !== false) ? $this->_resultFields : 
  3505.                 (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]) ? $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid] : false);
  3506.         $ar = ($rf !== false) ?
  3507.             array_merge($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid],$this->table()) :
  3508.             $this->table();
  3509.  
  3510.         foreach($ar as $k=>$v) {
  3511.              
  3512.             if (!isset($this->$k)) {
  3513.                 if (!$hideEmpty) {
  3514.                     $ret[sprintf($format,$k)] = '';
  3515.                 }
  3516.                 continue;
  3517.             }
  3518.             // call the overloaded getXXXX() method. - except getLink and getLinks
  3519.             if (method_exists($this,'get'.$k) && !in_array(strtolower($k),array('links','link'))) {
  3520.                 $ret[sprintf($format,$k)] = $this->{'get'.$k}();
  3521.                 continue;
  3522.             }
  3523.             // should this call toValue() ???
  3524.             $ret[sprintf($format,$k)] = $this->$k;
  3525.         }
  3526.         if (!$this->_link_loaded) {
  3527.             return $ret;
  3528.         }
  3529.         foreach($this->_link_loaded as $k) {
  3530.             $ret[sprintf($format,$k)] = $this->$k->toArray();
  3531.         
  3532.         }
  3533.         
  3534.         return $ret;
  3535.     }
  3536.  
  3537.     /**
  3538.      * validate the values of the object (usually prior to inserting/updating..)
  3539.      *
  3540.      * Note: This was always intended as a simple validation routine.
  3541.      * It lacks understanding of field length, whether you are inserting or updating (and hence null key values)
  3542.      *
  3543.      * This should be moved to another class: DB_DataObject_Validate 
  3544.      *      FEEL FREE TO SEND ME YOUR VERSION FOR CONSIDERATION!!!
  3545.      *
  3546.      * Usage:
  3547.      * if (is_array($ret = $obj->validate())) { ... there are problems with the data ... }
  3548.      *
  3549.      * Logic:
  3550.      *   - defaults to only testing strings/numbers if numbers or strings are the correct type and null values are correct
  3551.      *   - validate Column methods : "validate{ROWNAME}()"  are called if they are defined.
  3552.      *            These methods should return 
  3553.      *                  true = everything ok
  3554.      *                  false|object = something is wrong!
  3555.      * 
  3556.      *   - This method loads and uses the PEAR Validate Class.
  3557.      *
  3558.      *
  3559.      * @access  public
  3560.      * @return  array of validation results (where key=>value, value=false|object if it failed) or true (if they all succeeded)
  3561.      */
  3562.     function validate()
  3563.     {
  3564.         global $_DB_DATAOBJECT;
  3565.         require_once 'Validate.php';
  3566.         $table = $this->table();
  3567.         $ret   = array();
  3568.         $seq   = $this->sequenceKey();
  3569.         $options = $_DB_DATAOBJECT['CONFIG'];
  3570.         foreach($table as $key => $val) {
  3571.             
  3572.             
  3573.             // call user defined validation always...
  3574.             $method = "Validate" . ucfirst($key);
  3575.             if (method_exists($this, $method)) {
  3576.                 $ret[$key] = $this->$method();
  3577.                 continue;
  3578.             }
  3579.             
  3580.             // if not null - and it's not set.......
  3581.             
  3582.             if (!isset($this->$key) && ($val & DB_DATAOBJECT_NOTNULL)) {
  3583.                 // dont check empty sequence key values..
  3584.                 if (($key == $seq[0]) && ($seq[1] == true)) {
  3585.                     continue;
  3586.                 }
  3587.                 $ret[$key] = false;
  3588.                 continue;
  3589.             }
  3590.             
  3591.             
  3592.             if (!isset($options['disable_null_strings']) && is_string($this->$key) && (strtolower($this->$key) == 'null')) {
  3593.                 if ($val & DB_DATAOBJECT_NOTNULL) {
  3594.                     $this->debug("'null' field used for '$key', but it is defined as NOT NULL", 'VALIDATION', 4);
  3595.                     $ret[$key] = false;
  3596.                     continue;
  3597.                 }
  3598.                 continue;
  3599.             }
  3600.  
  3601.             // ignore things that are not set. ?
  3602.            
  3603.             if (!isset($this->$key)) {
  3604.                 continue;
  3605.             }
  3606.             
  3607.             // if the string is empty.. assume it is ok..
  3608.             if (!is_object($this->$key) && !is_array($this->$key) && !strlen((string) $this->$key)) {
  3609.                 continue;
  3610.             }
  3611.             
  3612.             // dont try and validate cast objects - assume they are problably ok..
  3613.             if (is_object($this->$key) && is_a($this->$key,'DB_DataObject_Cast')) {
  3614.                 continue;
  3615.             }
  3616.             // at this point if you have set something to an object, and it's not expected
  3617.             // the Validate will probably break!!... - rightly so! (your design is broken, 
  3618.             // so issuing a runtime error like PEAR_Error is probably not appropriate..
  3619.             
  3620.             switch (true) {
  3621.                 // todo: date time.....
  3622.                 case  ($val & DB_DATAOBJECT_STR):
  3623.                     $ret[$key] = Validate::string($this->$key, VALIDATE_PUNCTUATION . VALIDATE_NAME);
  3624.                     continue;
  3625.                 case  ($val & DB_DATAOBJECT_INT):
  3626.                     $ret[$key] = Validate::number($this->$key, array('decimal'=>'.'));
  3627.                     continue;
  3628.             }
  3629.         }
  3630.         // if any of the results are false or an object (eg. PEAR_Error).. then return the array..
  3631.         foreach ($ret as $key => $val) {
  3632.             if ($val !== true) {
  3633.                 return $ret;
  3634.             }
  3635.         }
  3636.         return true; // everything is OK.
  3637.     }
  3638.  
  3639.     /**
  3640.      * Gets the DB object related to an object - so you can use funky peardb stuf with it :)
  3641.      *
  3642.      * @access public
  3643.      * @return object The DB connection
  3644.      */
  3645.     function &getDatabaseConnection()
  3646.     {
  3647.         global $_DB_DATAOBJECT;
  3648.  
  3649.         if (($e = $this->_connect()) !== true) {
  3650.             return $e;
  3651.         }
  3652.         if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  3653.             $r = false;
  3654.             return $r;
  3655.         }
  3656.         return $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
  3657.     }
  3658.  
  3659.  
  3660.     /**
  3661.      * Gets the DB result object related to the objects active query
  3662.      *  - so you can use funky pear stuff with it - like pager for example.. :)
  3663.      *
  3664.      * @access public
  3665.      * @return object The DB result object
  3666.      */
  3667.      
  3668.     function &getDatabaseResult()
  3669.     {
  3670.         global $_DB_DATAOBJECT;
  3671.         $this->_connect();
  3672.         if (!isset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) {
  3673.             $r = false;
  3674.             return $r;
  3675.         }
  3676.         return $_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid];
  3677.     }
  3678.  
  3679.     /**
  3680.      * Overload Extension support
  3681.      *  - enables setCOLNAME/getCOLNAME
  3682.      *  if you define a set/get method for the item it will be called.
  3683.      * otherwise it will just return/set the value.
  3684.      * NOTE this currently means that a few Names are NO-NO's 
  3685.      * eg. links,link,linksarray, from, Databaseconnection,databaseresult
  3686.      *
  3687.      * note 
  3688.      *  - set is automatically called by setFrom.
  3689.      *   - get is automatically called by toArray()
  3690.      *  
  3691.      * setters return true on success. = strings on failure
  3692.      * getters return the value!
  3693.      *
  3694.      * this fires off trigger_error - if any problems.. pear_error, 
  3695.      * has problems with 4.3.2RC2 here
  3696.      *
  3697.      * @access public
  3698.      * @return true?
  3699.      * @see overload
  3700.      */
  3701.  
  3702.     
  3703.     function _call($method,$params,&$return) {
  3704.         
  3705.         //$this->debug("ATTEMPTING OVERLOAD? $method");
  3706.         // ignore constructors : - mm
  3707.         if (strtolower($method) == strtolower(get_class($this))) {
  3708.             return true;
  3709.         }
  3710.         $type = strtolower(substr($method,0,3));
  3711.         $class = get_class($this);
  3712.         if (($type != 'set') && ($type != 'get')) {
  3713.             return false;
  3714.         }
  3715.          
  3716.         
  3717.         
  3718.         // deal with naming conflick of setFrom = this is messy ATM!
  3719.         
  3720.         if (strtolower($method) == 'set_from') {
  3721.             $return = $this->toValue('from',isset($params[0]) ? $params[0] : null);
  3722.             return  true;
  3723.         }
  3724.         
  3725.         $element = substr($method,3);
  3726.         
  3727.         // dont you just love php's case insensitivity!!!!
  3728.         
  3729.         $array =  array_keys(get_class_vars($class));
  3730.         /* php5 version which segfaults on 5.0.3 */
  3731.         if (class_exists('ReflectionClass')) {
  3732.             $reflection = new ReflectionClass($class);
  3733.             $array = array_keys($reflection->getdefaultProperties());
  3734.         }
  3735.         
  3736.         if (!in_array($element,$array)) {
  3737.             // munge case
  3738.             foreach($array as $k) {
  3739.                 $case[strtolower($k)] = $k;
  3740.             }
  3741.             if ((substr(phpversion(),0,1) == 5) && isset($case[strtolower($element)])) {
  3742.                 trigger_error("PHP5 set/get calls should match the case of the variable",E_USER_WARNING);
  3743.                 $element = strtolower($element);
  3744.             }
  3745.             
  3746.             // does it really exist?
  3747.             if (!isset($case[$element])) {
  3748.                 return false;            
  3749.             }
  3750.             // use the mundged case
  3751.             $element = $case[$element]; // real case !
  3752.         }
  3753.         
  3754.         
  3755.         if ($type == 'get') {
  3756.             $return = $this->toValue($element,isset($params[0]) ? $params[0] : null);
  3757.             return true;
  3758.         }
  3759.         
  3760.         
  3761.         $return = $this->fromValue($element, $params[0]);
  3762.          
  3763.         return true;
  3764.             
  3765.           
  3766.     }
  3767.         
  3768.     
  3769.     /**
  3770.     * standard set* implementation.
  3771.     *
  3772.     * takes data and uses it to set dates/strings etc.
  3773.     * normally called from __call..  
  3774.     *
  3775.     * Current supports
  3776.     *   date      = using (standard time format, or unixtimestamp).... so you could create a method :
  3777.     *               function setLastread($string) { $this->fromValue('lastread',strtotime($string)); }
  3778.     *
  3779.     *   time      = using strtotime 
  3780.     *   datetime  = using  same as date - accepts iso standard or unixtimestamp.
  3781.     *   string    = typecast only..
  3782.     * 
  3783.     * TODO: add formater:: eg. d/m/Y for date! ???
  3784.     *
  3785.     * @param   string       column of database
  3786.     * @param   mixed        value to assign
  3787.     *
  3788.     * @return   true| false     (False on error)
  3789.     * @access   public 
  3790.     * @see      DB_DataObject::_call
  3791.     */
  3792.   
  3793.     
  3794.     function fromValue($col,$value) 
  3795.     {
  3796.         global $_DB_DATAOBJECT;
  3797.         $options = $_DB_DATAOBJECT['CONFIG'];
  3798.         $cols = $this->table();
  3799.         // dont know anything about this col..
  3800.         if (!isset($cols[$col])) {
  3801.             $this->$col = $value;
  3802.             return true;
  3803.         }
  3804.         //echo "FROM VALUE $col, {$cols[$col]}, $value\n";
  3805.         switch (true) {
  3806.             // set to null and column is can be null...
  3807.             case (!isset($options['disable_null_strings']) && (strtolower($value) == 'null') && (!($cols[$col] & DB_DATAOBJECT_NOTNULL))):
  3808.             case (is_object($value) && is_a($value,'DB_DataObject_Cast')): 
  3809.                 $this->$col = $value;
  3810.                 return true;
  3811.                 
  3812.             // fail on setting null on a not null field..
  3813.             case (!isset($options['disable_null_strings']) && (strtolower($value) == 'null') && ($cols[$col] & DB_DATAOBJECT_NOTNULL)):
  3814.                 return false;
  3815.         
  3816.             case (($cols[$col] & DB_DATAOBJECT_DATE) &&  ($cols[$col] & DB_DATAOBJECT_TIME)):
  3817.                 // empty values get set to '' (which is inserted/updated as NULl
  3818.                 if (!$value) {
  3819.                     $this->$col = '';
  3820.                 }
  3821.             
  3822.                 if (is_numeric($value)) {
  3823.                     $this->$col = date('Y-m-d H:i:s', $value);
  3824.                     return true;
  3825.                 }
  3826.               
  3827.                 // eak... - no way to validate date time otherwise...
  3828.                 $this->$col = (string) $value;
  3829.                 return true;
  3830.             
  3831.             case ($cols[$col] & DB_DATAOBJECT_DATE):
  3832.                 // empty values get set to '' (which is inserted/updated as NULl
  3833.                  
  3834.                 if (!$value) {
  3835.                     $this->$col = '';
  3836.                     return true; 
  3837.                 }
  3838.             
  3839.                 if (is_numeric($value)) {
  3840.                     $this->$col = date('Y-m-d',$value);
  3841.                     return true;
  3842.                 }
  3843.                  
  3844.                 // try date!!!!
  3845.                 require_once 'Date.php';
  3846.                 $x = new Date($value);
  3847.                 $this->$col = $x->format("%Y-%m-%d");
  3848.                 return true;
  3849.             
  3850.             case ($cols[$col] & DB_DATAOBJECT_TIME):
  3851.                 // empty values get set to '' (which is inserted/updated as NULl
  3852.                 if (!$value) {
  3853.                     $this->$col = '';
  3854.                 }
  3855.             
  3856.                 $guess = strtotime($value);
  3857.                 if ($guess != -1) {
  3858.                      $this->$col = date('H:i:s', $guess);
  3859.                     return $return = true;
  3860.                 }
  3861.                 // otherwise an error in type...
  3862.                 return false;
  3863.             
  3864.             case ($cols[$col] & DB_DATAOBJECT_STR):
  3865.                 
  3866.                 $this->$col = (string) $value;
  3867.                 return true;
  3868.                 
  3869.             // todo : floats numerics and ints...
  3870.             default:
  3871.                 $this->$col = $value;
  3872.                 return true;
  3873.         }
  3874.     
  3875.     
  3876.     
  3877.     }
  3878.      /**
  3879.     * standard get* implementation.
  3880.     *
  3881.     *  with formaters..
  3882.     * supported formaters:  
  3883.     *   date/time : %d/%m/%Y (eg. php strftime) or pear::Date 
  3884.     *   numbers   : %02d (eg. sprintf)
  3885.     *  NOTE you will get unexpected results with times like 0000-00-00 !!!
  3886.     *
  3887.     *
  3888.     * 
  3889.     * @param   string       column of database
  3890.     * @param   format       foramt
  3891.     *
  3892.     * @return   true     Description
  3893.     * @access   public 
  3894.     * @see      DB_DataObject::_call(),strftime(),Date::format()
  3895.     */
  3896.     function toValue($col,$format = null) 
  3897.     {
  3898.         if (is_null($format)) {
  3899.             return $this->$col;
  3900.         }
  3901.         $cols = $this->table();
  3902.         switch (true) {
  3903.             case (($cols[$col] & DB_DATAOBJECT_DATE) &&  ($cols[$col] & DB_DATAOBJECT_TIME)):
  3904.                 if (!$this->$col) {
  3905.                     return '';
  3906.                 }
  3907.                 $guess = strtotime($this->$col);
  3908.                 if ($guess != -1) {
  3909.                     return strftime($format, $guess);
  3910.                 }
  3911.                 // eak... - no way to validate date time otherwise...
  3912.                 return $this->$col;
  3913.             case ($cols[$col] & DB_DATAOBJECT_DATE):
  3914.                 if (!$this->$col) {
  3915.                     return '';
  3916.                 } 
  3917.                 $guess = strtotime($this->$col);
  3918.                 if ($guess != -1) {
  3919.                     return strftime($format,$guess);
  3920.                 }
  3921.                 // try date!!!!
  3922.                 require_once 'Date.php';
  3923.                 $x = new Date($this->$col);
  3924.                 return $x->format($format);
  3925.                 
  3926.             case ($cols[$col] & DB_DATAOBJECT_TIME):
  3927.                 if (!$this->$col) {
  3928.                     return '';
  3929.                 }
  3930.                 $guess = strtotime($this->$col);
  3931.                 if ($guess > -1) {
  3932.                     return strftime($format, $guess);
  3933.                 }
  3934.                 // otherwise an error in type...
  3935.                 return $this->$col;
  3936.                 
  3937.             case ($cols[$col] &  DB_DATAOBJECT_MYSQLTIMESTAMP):
  3938.                 if (!$this->$col) {
  3939.                     return '';
  3940.                 }
  3941.                 require_once 'Date.php';
  3942.                 
  3943.                 $x = new Date($this->$col);
  3944.                 
  3945.                 return $x->format($format);
  3946.             
  3947.              
  3948.             case ($cols[$col] &  DB_DATAOBJECT_BOOL):
  3949.                 
  3950.                 if ($cols[$col] &  DB_DATAOBJECT_STR) {
  3951.                     // it's a 't'/'f' !
  3952.                     return ($this->$col === 't');
  3953.                 }
  3954.                 return (bool) $this->$col;
  3955.             
  3956.                
  3957.             default:
  3958.                 return sprintf($format,$this->col);
  3959.         }
  3960.             
  3961.  
  3962.     }
  3963.     
  3964.     
  3965.     /* ----------------------- Debugger ------------------ */
  3966.  
  3967.     /**
  3968.      * Debugger. - use this in your extended classes to output debugging information.
  3969.      *
  3970.      * Uses DB_DataObject::DebugLevel(x) to turn it on
  3971.      *
  3972.      * @param    string $message - message to output
  3973.      * @param    string $logtype - bold at start
  3974.      * @param    string $level   - output level
  3975.      * @access   public
  3976.      * @return   none
  3977.      */
  3978.     function debug($message, $logtype = 0, $level = 1)
  3979.     {
  3980.         global $_DB_DATAOBJECT;
  3981.  
  3982.         if (empty($_DB_DATAOBJECT['CONFIG']['debug'])  || 
  3983.             (is_numeric($_DB_DATAOBJECT['CONFIG']['debug']) &&  $_DB_DATAOBJECT['CONFIG']['debug'] < $level)) {
  3984.             return;
  3985.         }
  3986.         // this is a bit flaky due to php's wonderfull class passing around crap..
  3987.         // but it's about as good as it gets..
  3988.         $class = (isset($this) && is_a($this,'DB_DataObject')) ? get_class($this) : 'DB_DataObject';
  3989.         
  3990.         if (!is_string($message)) {
  3991.             $message = print_r($message,true);
  3992.         }
  3993.         if (!is_numeric( $_DB_DATAOBJECT['CONFIG']['debug']) && is_callable( $_DB_DATAOBJECT['CONFIG']['debug'])) {
  3994.             return call_user_func($_DB_DATAOBJECT['CONFIG']['debug'], $class, $message, $logtype, $level);
  3995.         }
  3996.         
  3997.         if (!ini_get('html_errors')) {
  3998.             echo "$class   : $logtype       : $message\n";
  3999.             flush();
  4000.             return;
  4001.         }
  4002.         if (!is_string($message)) {
  4003.             $message = print_r($message,true);
  4004.         }
  4005.         $colorize = ($logtype == 'ERROR') ? '<font color="red">' : '<font>';
  4006.         echo "<code>{$colorize}<B>$class: $logtype:</B> ". nl2br(htmlspecialchars($message)) . "</font></code><BR>\n";
  4007.     }
  4008.  
  4009.     /**
  4010.      * sets and returns debug level
  4011.      * eg. DB_DataObject::debugLevel(4);
  4012.      *
  4013.      * @param   int     $v  level
  4014.      * @access  public
  4015.      * @return  none
  4016.      */
  4017.     function debugLevel($v = null)
  4018.     {
  4019.         global $_DB_DATAOBJECT;
  4020.         if (empty($_DB_DATAOBJECT['CONFIG'])) {
  4021.             DB_DataObject::_loadConfig();
  4022.         }
  4023.         if ($v !== null) {
  4024.             $r = isset($_DB_DATAOBJECT['CONFIG']['debug']) ? $_DB_DATAOBJECT['CONFIG']['debug'] : 0;
  4025.             $_DB_DATAOBJECT['CONFIG']['debug']  = $v;
  4026.             return $r;
  4027.         }
  4028.         return isset($_DB_DATAOBJECT['CONFIG']['debug']) ? $_DB_DATAOBJECT['CONFIG']['debug'] : 0;
  4029.     }
  4030.  
  4031.     /**
  4032.      * Last Error that has occured
  4033.      * - use $this->_lastError or
  4034.      * $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
  4035.      *
  4036.      * @access  public
  4037.      * @var     object PEAR_Error (or false)
  4038.      */
  4039.     var $_lastError = false;
  4040.  
  4041.     /**
  4042.      * Default error handling is to create a pear error, but never return it.
  4043.      * if you need to handle errors you should look at setting the PEAR_Error callback
  4044.      * this is due to the fact it would wreck havoc on the internal methods!
  4045.      *
  4046.      * @param  int $message    message
  4047.      * @param  int $type       type
  4048.      * @param  int $behaviour  behaviour (die or continue!);
  4049.      * @access public
  4050.      * @return error object
  4051.      */
  4052.     function raiseError($message, $type = null, $behaviour = null)
  4053.     {
  4054.         global $_DB_DATAOBJECT;
  4055.         
  4056.         if ($behaviour == PEAR_ERROR_DIE && !empty($_DB_DATAOBJECT['CONFIG']['dont_die'])) {
  4057.             $behaviour = null;
  4058.         }
  4059.         $error = &PEAR::getStaticProperty('DB_DataObject','lastError');
  4060.         
  4061.         // this will never work totally with PHP's object model.
  4062.         // as this is passed on static calls (like staticGet in our case)
  4063.  
  4064.         if (isset($this) && is_object($this) && is_subclass_of($this,'db_dataobject')) {
  4065.             $this->_lastError = $error;
  4066.         }
  4067.  
  4068.         $_DB_DATAOBJECT['LASTERROR'] = $error;
  4069.  
  4070.         // no checks for production here?....... - we log  errors before we throw them.
  4071.         DB_DataObject::debug($message,'ERROR',1);
  4072.         
  4073.         
  4074.         if (PEAR::isError($message)) {
  4075.             $error = $message;
  4076.         } else {
  4077.             require_once 'DB/DataObject/Error.php';
  4078.             $error = PEAR::raiseError($message, $type, $behaviour,
  4079.                             $opts=null, $userinfo=null, 'DB_DataObject_Error'
  4080.                         );
  4081.         }
  4082.    
  4083.         return $error;
  4084.     }
  4085.  
  4086.     /**
  4087.      * Define the global $_DB_DATAOBJECT['CONFIG'] as an alias to  PEAR::getStaticProperty('DB_DataObject','options');
  4088.      *
  4089.      * After Profiling DB_DataObject, I discoved that the debug calls where taking
  4090.      * considerable time (well 0.1 ms), so this should stop those calls happening. as
  4091.      * all calls to debug are wrapped with direct variable queries rather than actually calling the funciton
  4092.      * THIS STILL NEEDS FURTHER INVESTIGATION
  4093.      *
  4094.      * @access   public
  4095.      * @return   object an error object
  4096.      */
  4097.     function _loadConfig()
  4098.     {
  4099.         global $_DB_DATAOBJECT;
  4100.  
  4101.         $_DB_DATAOBJECT['CONFIG'] = &PEAR::getStaticProperty('DB_DataObject','options');
  4102.  
  4103.  
  4104.     }
  4105.      /**
  4106.      * Free global arrays associated with this object.
  4107.      *
  4108.      *
  4109.      * @access   public
  4110.      * @return   none
  4111.      */
  4112.     function free() 
  4113.     {
  4114.         global $_DB_DATAOBJECT;
  4115.           
  4116.         if (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) {
  4117.             unset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]);
  4118.         }
  4119.         if (isset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) {     
  4120.             unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]);
  4121.         }
  4122.         // clear the staticGet cache as well.
  4123.         $this->_clear_cache();
  4124.         // this is a huge bug in DB!
  4125.         if (isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
  4126.             $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->num_rows = array();
  4127.         }
  4128.         
  4129.     }
  4130.     
  4131.     
  4132.     /* ---- LEGACY BC METHODS - NOT DOCUMENTED - See Documentation on New Methods. ---*/
  4133.     
  4134.     function _get_table() { return $this->table(); }
  4135.     function _get_keys()  { return $this->keys();  }
  4136.     
  4137.     
  4138.     
  4139.     
  4140. }
  4141. // technially 4.3.2RC1 was broken!!
  4142. // looks like 4.3.3 may have problems too....
  4143. if (!defined('DB_DATAOBJECT_NO_OVERLOAD')) {
  4144.  
  4145.     if ((phpversion() != '4.3.2-RC1') && (version_compare( phpversion(), "4.3.1") > 0)) {
  4146.         if (version_compare( phpversion(), "5") < 0) {
  4147.            overload('DB_DataObject');
  4148.         } 
  4149.         $GLOBALS['_DB_DATAOBJECT']['OVERLOADED'] = true;
  4150.     }
  4151. }
  4152.  
  4153.