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 / QueryTool / Query.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  93.0 KB  |  2,792 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3.  
  4. /**
  5.  * Contains the DB_QueryTool_Query class
  6.  *
  7.  * PHP versions 4 and 5
  8.  *
  9.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  10.  * that is available through the world-wide-web at the following URI:
  11.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  12.  * the PHP License and are unable to obtain it through the web, please
  13.  * send a note to license@php.net so we can mail you a copy immediately.
  14.  *
  15.  * @category  Database
  16.  * @package   DB_QueryTool
  17.  * @author    Wolfram Kriesing <wk@visionp.de>
  18.  * @author    Paolo Panto <wk@visionp.de>
  19.  * @author    Lorenzo Alberton <l dot alberton at quipo dot it>
  20.  * @copyright 2003-2007 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton
  21.  * @license   http://www.php.net/license/3_0.txt  PHP License 3.0
  22.  * @version   CVS: $Id: Query.php,v 1.77 2008/01/12 13:08:27 quipo Exp $
  23.  * @link      http://pear.php.net/package/DB_QueryTool
  24.  */
  25.  
  26. /**
  27.  * require the PEAR and DB classes
  28.  */
  29. require_once 'PEAR.php';
  30. require_once 'DB.php';
  31.  
  32. /**
  33.  * DB_QueryTool_Query class
  34.  *
  35.  * This class should be extended
  36.  *
  37.  * @category  Database
  38.  * @package   DB_QueryTool
  39.  * @author    Wolfram Kriesing <wk@visionp.de>
  40.  * @author    Paolo Panto <wk@visionp.de>
  41.  * @author    Lorenzo Alberton <l dot alberton at quipo dot it>
  42.  * @copyright 2003-2007 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton
  43.  * @license   http://www.php.net/license/3_0.txt  PHP License 3.0
  44.  * @link      http://pear.php.net/package/DB_QueryTool
  45.  */
  46. class DB_QueryTool_Query
  47. {
  48.     // {{{ class vars
  49.  
  50.     /**
  51.      * @var string  the name of the primary column
  52.      */
  53.     var $primaryCol = 'id';
  54.  
  55.     /**
  56.      * @var string  the current table the class works on
  57.      */
  58.     var $table      = '';
  59.  
  60.     /**
  61.      * @var string  the name of the sequence for this table
  62.      */
  63.     var $sequenceName = null;
  64.  
  65.     /**
  66.      * @var object  the db-object, a PEAR::DB instance
  67.      */
  68.     var $db = null;
  69.  
  70.     /**
  71.      * @var string  the where condition
  72.      * @access private
  73.      */
  74.     var $_where = '';
  75.  
  76.     /**
  77.      * @var string  the order condition
  78.      * @access private
  79.      */
  80.     var $_order = '';
  81.  
  82.     /**
  83.      * @var    string  the having definition
  84.      * @access private
  85.      */
  86.     var $_having = '';
  87.  
  88.     /**
  89.      * @var array   contains the join content
  90.      *              the key is the join type, for now we have 'default' and 'left'
  91.      *              inside each key 'table' contains the table
  92.      *                          key 'where' contains the where clause for the join
  93.      * @access private
  94.      */
  95.     var $_join = array();
  96.  
  97.     /**
  98.      * @var    string  which column to index the result by
  99.      * @access private
  100.      */
  101.     var $_index = null;
  102.  
  103.     /**
  104.      * @var    string  the group-by clause
  105.      * @access private
  106.      */
  107.     var $_group = '';
  108.  
  109.     /**
  110.      * @var    array   the limit
  111.      * @access private
  112.      */
  113.     var $_limit = array();
  114.  
  115.     /**
  116.      * @var    string  type of result to return
  117.      * @access private
  118.      */
  119.     var $_resultType = 'none';
  120.  
  121.     /**
  122.      * @var    array   the metadata temporary saved
  123.      * @access private
  124.      */
  125.     var $_metadata = array();
  126.  
  127.     /**
  128.      * @var    string
  129.      * @access private
  130.      */
  131.     var $_lastQuery = null;
  132.  
  133.     /**
  134.      * @var    string   the rows that shall be selected
  135.      * @access private
  136.      */
  137.     var $_select = '*';
  138.  
  139.     /**
  140.      * @var    string   the rows that shall not be selected
  141.      * @access private
  142.      */
  143.     var $_dontSelect = '';
  144.  
  145.     /**
  146.      * @var array  this array saves different modes in which this class works
  147.      *             i.e. 'raw' means no quoting before saving/updating data
  148.      * @access private
  149.      */
  150.     var $options = array(
  151.         'raw'      =>  false,
  152.         'verbose'  =>  true,    // set this to false in a productive environment
  153.                                 // it will produce error-logs if set to true
  154.         'useCache' =>  false,
  155.         'logFile'  =>  false,
  156.     );
  157.  
  158.     /**
  159.      * this array contains information about the tables
  160.      * those are
  161.      * - 'name' => the real table name
  162.      * - 'shortName' => the short name used, so that when moving the table i.e.
  163.      *                  onto a provider's db and u have to rename the tables to
  164.      *                  longer names this name will be relevant, i.e. when
  165.      *                  autoJoining, i.e. a table name on your local machine is:
  166.      *                  'user' but online it has to be 'applName_user' then the
  167.      *                  shortName will be used to determine if a column refers to
  168.      *                  another table, if the colName is 'user_id', it knows the
  169.      *                  shortName 'user' refers to the table 'applName_user'
  170.      */
  171.     var $tableSpec = array();
  172.  
  173.     /**
  174.      * this is the regular expression that shall be used to find a table's shortName
  175.      * in a column name, the string found by using this regular expression will be removed
  176.      * from the column name and it will be checked if it is a table name
  177.      * i.e. the default '/_id$/' would find the table name 'user' from the column name 'user_id'
  178.      *
  179.      * @access private
  180.      */
  181.     var $_tableNameToShortNamePreg = '/^.*_/';
  182.  
  183.     /**
  184.      * @var array this array caches queries that have already been built once
  185.      *            to reduce the execution time
  186.      * @access private
  187.      */
  188.     var $_queryCache = array();
  189.  
  190.     /**
  191.      * The object that contains the log-instance
  192.      * @access private
  193.      */
  194.     var $_logObject = null;
  195.  
  196.     /**
  197.      * Some internal data the logging needs
  198.      * @access private
  199.      */
  200.     var $_logData = array();
  201.  
  202.     // }}}
  203.     // {{{ __construct()
  204.  
  205.     /**
  206.      * this is the constructor, as it will be implemented in ZE2 (php5)
  207.      *
  208.      * @param object $dsn     db-object
  209.      * @param array  $options options array
  210.      *
  211.      * @return void
  212.      * @version 2002/04/02
  213.      * @author Wolfram Kriesing <wk@visionp.de>
  214.      * @access public
  215.      */
  216.     /*
  217.     function __construct($dsn=false, $options=array())
  218.     {
  219.         if (!isset($options['autoConnect'])) {
  220.             $autoConnect = true;
  221.         } else {
  222.             $autoConnect = $options['autoConnect'];
  223.         }
  224.         if (isset($options['errorCallback'])) {
  225.             $this->setErrorCallback($options['errorCallback']);
  226.         }
  227.         if (isset($options['errorSetCallback'])) {
  228.             $this->setErrorSetCallback($options['errorSetCallback']);
  229.         }
  230.         if (isset($options['errorLogCallback'])) {
  231.             $this->setErrorLogCallback($options['errorLogCallback']);
  232.         }
  233.  
  234.         if ($autoConnect && $dsn) {
  235.             $this->connect($dsn, $options);
  236.         }
  237.         //we would need to parse the dsn first ... i dont feel like now :-)
  238.         // oracle has all column names in upper case
  239.         //FIXXXME make the class work only with upper case when we work with oracle
  240.         //if ($this->db->phptype=='oci8' && !$this->primaryCol) {
  241.         //    $this->primaryCol = 'ID';
  242.         //}
  243.  
  244.         if (is_null($this->sequenceName)) {
  245.             $this->sequenceName = $this->table;
  246.         }
  247.     }
  248.     */
  249.  
  250.     // }}}
  251.     // {{{ DB_QueryTool_Query()
  252.  
  253.     /**
  254.      * Constructor
  255.      *
  256.      * @param mixed $dsn     DSN string, DSN array or DB object
  257.      * @param array $options database options
  258.      *
  259.      * @version 2002/04/02
  260.      * @author Wolfram Kriesing <wk@visionp.de>
  261.      * @access public
  262.      */
  263.     function DB_QueryTool_Query($dsn = false, $options = array())
  264.     {
  265.         //$this->__construct($dsn, $options);
  266.         if (!isset($options['autoConnect'])) {
  267.             $autoConnect = true;
  268.         } else {
  269.             $autoConnect = $options['autoConnect'];
  270.             unset($options['autoConnect']);
  271.         }
  272.         if (isset($options['errorCallback'])) {
  273.             $this->setErrorCallback($options['errorCallback']);
  274.             unset($options['errorCallback']);
  275.         }
  276.         if (isset($options['errorSetCallback'])) {
  277.             $this->setErrorSetCallback($options['errorSetCallback']);
  278.             unset($options['errorSetCallback']);
  279.         }
  280.         if (isset($options['errorLogCallback'])) {
  281.             $this->setErrorLogCallback($options['errorLogCallback']);
  282.             unset($options['errorLogCallback']);
  283.         }
  284.         if ($autoConnect && $dsn) {
  285.             $this->connect($dsn, $options);
  286.         }
  287.         if (is_null($this->sequenceName)) {
  288.             $this->sequenceName = $this->table;
  289.         }
  290.     }
  291.  
  292.     // }}}
  293.     // {{{ connect()
  294.  
  295.     /**
  296.      * use this method if you want to connect manually
  297.      *
  298.      * @param mixed $dsn     DSN string, DSN array or DB object
  299.      * @param array $options database options
  300.      *
  301.      * @return void
  302.      */
  303.     function connect($dsn, $options = array())
  304.     {
  305.         if (is_object($dsn)) {
  306.             $res = $this->db =& $dsn;
  307.         } else {
  308.             $res = $this->db = DB::connect($dsn, $options);
  309.         }
  310.         if (PEAR::isError($res)) {
  311.             // FIXXME what shall we do here?
  312.             $this->_errorLog($res->getUserInfo());
  313.         } else {
  314.             $this->db->setFetchMode(DB_FETCHMODE_ASSOC);
  315.         }
  316.     }
  317.  
  318.     // }}}
  319.     // {{{ getDbInstance()
  320.  
  321.     /**
  322.      * Get the current DB instance
  323.      *
  324.      * @return reference to current DB instance
  325.      */
  326.     function &getDbInstance()
  327.     {
  328.         return $this->db;
  329.     }
  330.  
  331.     // }}}
  332.     // {{{ setDbInstance()
  333.  
  334.     /**
  335.      * Setup using an existing connection.
  336.      * this also sets the DB_FETCHMODE_ASSOC since this class
  337.      * needs this to be set!
  338.      *
  339.      * @param object &$dbh a reference to an existing DB-object
  340.      *
  341.      * @return void
  342.      */
  343.     function setDbInstance(&$dbh)
  344.     {
  345.         $this->db =& $dbh;
  346.         $this->db->setFetchMode(DB_FETCHMODE_ASSOC);
  347.     }
  348.  
  349.     // }}}
  350.     // {{{ get()
  351.  
  352.     /**
  353.      * get the data of a single entry
  354.      * if the second parameter is only one column the result will be returned
  355.      * directly not as an array!
  356.      *
  357.      * @param integer $id     the id of the element to retrieve
  358.      * @param string  $column if this is given only one row shall be returned,
  359.      *                        directly, not an array
  360.      *
  361.      * @return mixed (1) an array of the retrieved data
  362.      *               (2) if the second parameter is given and its only one column,
  363.      *                   only this column's data will be returned
  364.      *               (3) false in case of failure
  365.      * @version 2002/03/05
  366.      * @author Wolfram Kriesing <wk@visionp.de>
  367.      * @access public
  368.      */
  369.     function get($id, $column = '')
  370.     {
  371.         if (is_string($id)) {
  372.             $id = trim($id);
  373.         }
  374.         $column = trim($column);
  375.         $table = $this->table;
  376.         $getMethod = 'getRow';
  377.         if ($column && !strpos($column, ',')) {   // if only one column shall be selected
  378.             $getMethod = 'getOne';
  379.         }
  380.         // we dont use 'setSelect' here, since this changes the setup of the class, we
  381.         // build the query directly
  382.         // if $column is '' then _buildSelect selects '*' anyway, so that's the same behaviour as before
  383.         $query['select'] = $this->_buildSelect($column);
  384.         $query['where']  = $this->_buildWhere(
  385.             $this->_quoteIdentifier($this->table).'.'.$this->_quoteIdentifier($this->primaryCol) .'='. $this->_quote($id));
  386.         $queryString = $this->_buildSelectQuery($query);
  387.  
  388.         return $this->returnResult($this->execute($queryString, $getMethod));
  389.     }
  390.  
  391.     // }}}
  392.     // {{{ getMultiple()
  393.  
  394.     /**
  395.      * gets the data of the given ids
  396.      *
  397.      * @param array  $ids    this is an array of ids to retrieve
  398.      * @param string $column the column to search in for
  399.      *
  400.      * @return mixed an array of the retrieved data, or false in case of failure
  401.      *               when failing an error is set in $this->_error
  402.      * @version 2002/04/23
  403.      * @author Wolfram Kriesing <wk@visionp.de>
  404.      * @access public
  405.      */
  406.     function getMultiple($ids, $column = '')
  407.     {
  408.         $col = $this->primaryCol;
  409.         if ($column) {
  410.             $col = $column;
  411.         }
  412.         // FIXXME if $ids has no table.col syntax and we are using joins, the table better be put in front!!!
  413.         $ids = $this->_quoteArray($ids);
  414.  
  415.         $query['where'] = $this->_buildWhere($col.' IN ('.implode(',', $ids).')');
  416.         $queryString    = $this->_buildSelectQuery($query);
  417.  
  418.         return $this->returnResult($this->execute($queryString));
  419.     }
  420.  
  421.     // }}}
  422.     // {{{ getOne()
  423.  
  424.     /**
  425.      * get the first value of the first row
  426.      *
  427.      * @return mixed (1) a scalar value in case of success
  428.      *               (2) false in case of failure
  429.      * @access public
  430.      */
  431.     function getOne()
  432.     {
  433.         $queryString = $this->getQueryString();
  434.         return $this->execute($queryString, 'getOne');
  435.     }
  436.  
  437.     // }}}
  438.     // {{{ getAll()
  439.  
  440.     /**
  441.      * get all entries from the DB
  442.      * for sorting use setOrder!!!, the last 2 parameters are deprecated
  443.      *
  444.      * @param int    $from   to start from
  445.      * @param int    $count  the number of rows to show
  446.      * @param string $method the DB-method to use, i dont know if we should leave this param here ...
  447.      *
  448.      * @return mixed an array of the retrieved data, or false in case of failure
  449.      *               when failing an error is set in $this->_error
  450.      * @version 2002/03/05
  451.      * @author Wolfram Kriesing <wk@visionp.de>
  452.      * @access public
  453.      */
  454.     function getAll($from = 0, $count = 0, $method = 'getAll')
  455.     {
  456.         $query = array();
  457.         if ($count) {
  458.             $query = array('limit' => array($from, $count));
  459.             //echo '<br/>'.$this->_buildSelectQuery($query).'<br/>';
  460.         }
  461.         return $this->returnResult($this->execute($this->_buildSelectQuery($query), $method));
  462.     }
  463.  
  464.     // }}}
  465.     // {{{ getCol()
  466.  
  467.     /**
  468.      * this method only returns one column, so the result will be a one dimensional array
  469.      * this does also mean that using setSelect() should be set to *one* column, the one you want to
  470.      * have returned a most common use case for this could be:
  471.      *      $table->setSelect('id');
  472.      *      $ids = $table->getCol();
  473.      * OR
  474.      *      $ids = $table->getCol('id');
  475.      * so ids will be an array with all the id's
  476.      *
  477.      * @param string $column the column that shall be retrieved
  478.      * @param int    $from   to start from
  479.      * @param int    $count  the number of rows to show
  480.      *
  481.      * @return mixed an array of the retrieved data, or false in case of failure
  482.      *               when failing an error is set in $this->_error
  483.      * @version 2003/02/25
  484.      * @author Wolfram Kriesing <wk@visionp.de>
  485.      * @access public
  486.      */
  487.     function getCol($column = null, $from = 0, $count = 0)
  488.     {
  489.         $query = array();
  490.         if (!is_null($column)) {
  491.             // by using _buildSelect() I can be sure that the table name will not be ambiguous
  492.             // i.e. in a join, where all the joined tables have a col 'id'
  493.             // _buildSelect() will put the proper table name in front in case there is none
  494.             $query['select'] = $this->_buildSelect(trim($column));
  495.         }
  496.         if ($count) {
  497.             $query['limit'] = array($from,$count);
  498.         }
  499.         $res = $this->returnResult($this->execute($this->_buildSelectQuery($query), 'getCol'));
  500.         return ($res === false) ? array() : $res;
  501.     }
  502.  
  503.     // }}}
  504.     // {{{ getCount()
  505.  
  506.     /**
  507.      * get the number of entries
  508.      *
  509.      * @return mixed an array of the retrieved data, or false in case of failure
  510.      *               when failing an error is set in $this->_error
  511.      * @version 2002/04/02
  512.      * @author Wolfram Kriesing <wk@visionp.de>
  513.      * @access public
  514.      */
  515.     function getCount()
  516.     {
  517.         /* the following query works on mysql
  518.         SELECT count(DISTINCT image.id) FROM image2tree
  519.         RIGHT JOIN image ON image.id = image2tree.image_id
  520.         the reason why this is needed - i just wanted to get the number of rows that do exist if the result is grouped by image.id
  521.         the following query is what i tried first, but that returns the number of rows that have been grouped together
  522.         for each image.id
  523.         SELECT count(*) FROM image2tree
  524.         RIGHT JOIN image ON image.id = image2tree.image_id GROUP BY image.id
  525.  
  526.         so that's why we do the following, i am not sure if that is standard SQL and absolutley correct!!!
  527.         */
  528.  
  529.         //FIXXME see comment above if this is absolutely correct!!!
  530.         if ($group = $this->_buildGroup()) {
  531.             $query['select'] = 'COUNT(DISTINCT '.$group.')';
  532.             $query['group'] = '';
  533.         } else {
  534.             $query['select'] = 'COUNT(*)';
  535.         }
  536.  
  537.         $query['order'] = ''; // order is not of importance and might freak up
  538.                               // the special group-handling up there, 
  539.                               // since the order-col is not be known
  540.         /* FIXXME use the following line, but watch out, then it has to be used 
  541.            in every method, or this value will be used always, simply try calling 
  542.            getCount and getAll afterwards, getAll will return the count :-)
  543.            if getAll doesn't use setSelect!!!
  544.         */
  545.         //$this->setSelect('count(*)');
  546.         $queryString = $this->_buildSelectQuery($query, true);
  547.         return ($res = $this->execute($queryString, 'getOne')) ? $res : 0;
  548.     }
  549.  
  550.     // }}}
  551.     // {{{ getDefaultValues()
  552.  
  553.     /**
  554.      * return an empty element where all the array elements do already exist
  555.      * corresponding to the columns in the DB
  556.      *
  557.      * @return array an empty, or pre-initialized element
  558.      * @version2002/04/05
  559.      * @author Wolfram Kriesing <wk@visionp.de>
  560.      * @access public
  561.      */
  562.     function getDefaultValues()
  563.     {
  564.         $ret = array();
  565.         // here we read all the columns from the DB and initialize them
  566.         // with '' to prevent PHP-warnings in case we use error_reporting=E_ALL
  567.         foreach ($this->metadata() as $aCol=>$x) {
  568.             $ret[$aCol] = '';
  569.         }
  570.         return $ret;
  571.     }
  572.  
  573.     // }}}
  574.     // {{{ getEmptyElement()
  575.  
  576.     /**
  577.      * this is just for BC
  578.      *
  579.      * @return void
  580.      * @deprecated
  581.      */
  582.     function getEmptyElement()
  583.     {
  584.         $this->getDefaultValues();
  585.     }
  586.  
  587.     // }}}
  588.     // {{{ getQueryString()
  589.  
  590.     /**
  591.      * Render the current query and return it as a string.
  592.      *
  593.      * @return string the current query
  594.      */
  595.     function getQueryString()
  596.     {
  597.         $ret = $this->_buildSelectQuery();
  598.         if (is_string($ret)) {
  599.             $ret = trim($ret);
  600.         }
  601.         return $ret;
  602.     }
  603.  
  604.     // }}}
  605.     // {{{ _floatToStringNoLocale()
  606.  
  607.     /**
  608.      * If a double number was "localized", restore its decimal separator to "."
  609.      *
  610.      * @param string $float fload value as string
  611.      *
  612.      * @return string
  613.      * @see http://pear.php.net/bugs/bug.php?id=3021
  614.      * @access private
  615.      */
  616.     function _floatToStringNoLocale($float)
  617.     {
  618.         $precision = strlen($float) - strlen(intval($float));
  619.         if ($precision) {
  620.             --$precision; // don't count decimal seperator
  621.         }
  622.         return number_format($float, $precision, '.', '');
  623.     }
  624.  
  625.     // }}}
  626.     // {{{ _localeSafeImplode()
  627.  
  628.     /**
  629.      * New "implode()" implementation to bypass float locale formatting:
  630.      * the SQL decimal separator is and must be ".".  Always.
  631.      *
  632.      * @param string $glue  glue string
  633.      * @param array  $array array to implode
  634.      *
  635.      * @return string
  636.      * @access private
  637.      */
  638.     function _localeSafeImplode($glue, $array)
  639.     {
  640.         $str = '';
  641.         foreach ($array as $value) {
  642.             if ($str !== '') {
  643.                 $str .= $glue;
  644.             }
  645.             $str .= is_double($value) ? $this->_floatToStringNoLocale($value) : $value;
  646.         }
  647.         return $str;
  648.     }
  649.  
  650.     // }}}
  651.     // {{{ save()
  652.  
  653.     /**
  654.      * save data, calls either update or add
  655.      * if the primaryCol is given in the data this method knows that the
  656.      * data passed to it are meant to be updated (call 'update'), otherwise it will
  657.      * call the method 'add'.
  658.      * If you dont like this behaviour simply stick with the methods 'add'
  659.      * and 'update' and ignore this one here.
  660.      * This method is very useful when you have validation checks that have to
  661.      * be done for both adding and updating, then you can simply overwrite this
  662.      * method and do the checks in here, and both cases will be validated first.
  663.      *
  664.      * @param array $data contains the new data that shall be saved in the DB
  665.      *
  666.      * @return mixed   the data returned by either add or update-method
  667.      * @version 2002/03/11
  668.      * @author Wolfram Kriesing <wk@visionp.de>
  669.      * @access public
  670.      */
  671.     function save($data)
  672.     {
  673.         if (isset($data[$this->primaryCol]) && $data[$this->primaryCol]) {
  674.             return $this->update($data);
  675.         }
  676.         return $this->add($data);
  677.     }
  678.  
  679.     // }}}
  680.     // {{{ update()
  681.  
  682.     /**
  683.      * update the member data of a data set
  684.      *
  685.      * @param array $newData contains the new data that shall be saved in the DB
  686.      *                       the id has to be given in the field with the key 'ID'
  687.      *
  688.      * @return mixed true on success, or false otherwise
  689.      * @version 2002/03/06
  690.      * @author Wolfram Kriesing <wk@visionp.de>
  691.      * @access public
  692.      */
  693.     function update($newData)
  694.     {
  695.         $query = array();
  696.         $raw   = $this->getOption('raw');
  697.         // do only set the 'where' part in $query, if a primary column is given
  698.         // if not the default 'where' clause is used
  699.         if (isset($newData[$this->primaryCol])) {
  700.             $query['where'] = $this->_quoteIdentifier($this->primaryCol) . '=' . $this->_quote($newData[$this->primaryCol]);
  701.         }
  702.         $newData = $this->_checkColumns($newData, 'update');
  703.         $values  = array();
  704.         foreach ($newData as $key => $aData) {         // quote the data
  705.             //$values[] = "{$this->table}.$key=". $this->_quote($aData);
  706.             $values[] = $this->_quoteIdentifier($key) . '=' . $this->_quote($aData);
  707.         }
  708.  
  709.         $query['set'] = $this->_localeSafeImplode(',', $values);
  710.         //FIXXXME _buildUpdateQuery() seems to take joins into account, whcih is bullshit here
  711.         $updateString = $this->_buildUpdateQuery($query);
  712.         //print '$updateString = '.$updateString;
  713.         return $this->execute($updateString, 'query') ? true : false;
  714.     }
  715.  
  716.     // }}}
  717.     // {{{ add()
  718.  
  719.     /**
  720.      * add a new member in the DB
  721.      *
  722.      * @param array $newData contains the new data that shall be saved in the DB
  723.      *
  724.      * @return mixed   the inserted id on success, or false otherwise
  725.      * @version 2002/04/02
  726.      * @author Wolfram Kriesing <wk@visionp.de>
  727.      * @access public
  728.      */
  729.     function add($newData)
  730.     {
  731.         // if no primary col is given, get next sequence value
  732.         if (empty($newData[$this->primaryCol])) {
  733.             if ($this->primaryCol) {
  734.                 // do only use the sequence if a primary column is given
  735.                 // otherwise the data are written as given
  736.                 $id = $this->db->nextId($this->sequenceName);
  737.                 $newData[$this->primaryCol] = (int)$id;
  738.             } else {
  739.                 // if no primary col is given return true on success
  740.                 $id = true;
  741.             }
  742.         } else {
  743.             $id = $newData[$this->primaryCol];
  744.         }
  745.  
  746.         //unset($newData[$this->primaryCol]);
  747.  
  748.         $newData = $this->_checkColumns($newData, 'add');
  749.         $newData = $this->_quoteArray($newData);
  750.         
  751.         //quoting the columns
  752.         $tmpData = array();
  753.         foreach ($newData as $key=>$val) {
  754.             $tmpData[$this->_quoteIdentifier($key)] = $val;
  755.         }
  756.         $newData = $tmpData;
  757.         unset($tmpData);
  758.  
  759.         $query = sprintf(
  760.             'INSERT INTO %s (%s) VALUES (%s)',
  761.             $this->table,
  762.             implode(', ', array_keys($newData)),
  763.             $this->_localeSafeImplode(', ', $newData)
  764.         );
  765.         //echo $query;
  766.         return $this->execute($query, 'query') ? $id : false;
  767.     }
  768.  
  769.     // }}}
  770.     // {{{ addMultiple()
  771.  
  772.     /**
  773.      * adds multiple new members in the DB
  774.      *
  775.      * @param array $data contains an array of new data that shall be saved in the DB
  776.      *                      the key-value pairs have to be the same for all the data!!!
  777.      *
  778.      * @return mixed the inserted ids on success, or false otherwise
  779.      * @version 2002/07/17
  780.      * @author Wolfram Kriesing <wk@visionp.de>
  781.      * @access public
  782.      */
  783.     function addMultiple($data)
  784.     {
  785.         if (!sizeof($data)) {
  786.             return false;
  787.         }
  788.         $ret = true;
  789.         // the inserted ids which will be returned or if no primaryCol is given
  790.         // we return true by default
  791.         $retIds = $this->primaryCol ? array() : true;
  792.         $dbtype = $this->db->phptype;
  793.         if ($dbtype == 'mysql') { //Optimise for MySQL
  794.             $allData = array();     // each row that will be inserted
  795.             foreach ($data as $key => $aData) {
  796.                 $aData = $this->_checkColumns($aData, 'add');
  797.                 $aData = $this->_quoteArray($aData);
  798.  
  799.                 if (empty($aData[$this->primaryCol])) {
  800.                     if ($this->primaryCol) {
  801.                         // do only use the sequence if a primary column is given
  802.                         // otherwise the data are written as given
  803.                         $retIds[] = $id = (int)$this->db->nextId($this->sequenceName);
  804.                         $aData[$this->primaryCol] = $id;
  805.                     }
  806.                 } else {
  807.                     $retIds[] = $aData[$this->primaryCol];
  808.                 }
  809.                 $allData[] = '('.$this->_localeSafeImplode(', ', $aData).')';
  810.             }
  811.  
  812.             //quoting the columns
  813.             $tmpData = array();
  814.             foreach ($aData as $key=>$val) {
  815.                 $tmpData[$this->_quoteIdentifier($key)] = $val;
  816.             }
  817.             $newData = $tmpData;
  818.             unset($tmpData);
  819.  
  820.             $query = sprintf(
  821.                 'INSERT INTO %s (%s) VALUES %s',
  822.                 $this->table,
  823.                 implode(', ', array_keys($aData)),
  824.                 $this->_localeSafeImplode(', ', $allData)
  825.             );
  826.             return $this->execute($query, 'query') ? $retIds : false;
  827.         }
  828.         
  829.         //Executing for every entry the add method
  830.         foreach ($data as $entity) {
  831.             if ($ret) {
  832.                 $res = $this->add($entity);
  833.                 if (!$res) {
  834.                     $ret = false;
  835.                 } else {
  836.                     $retIds[] = $res;
  837.                 }
  838.             }
  839.         }
  840.         //Setting the return value to the array with ids
  841.         if ($ret) {
  842.             $ret = $retIds;
  843.         }
  844.         return $ret;
  845.     }
  846.  
  847.     // }}}
  848.     // {{{ remove()
  849.  
  850.     /**
  851.      * removes a member from the DB
  852.      *
  853.      * @param mixed  $data     - integer/string: the value of the column that shall be removed
  854.      *                         - array: multiple columns that shall be matched,
  855.      *                                  (the second parameter will be ignored)
  856.      * @param string $whereCol the column to match the data against, only if $data is not an array
  857.      *
  858.      * @return boolean
  859.      * @version 2002/04/08
  860.      * @author Wolfram Kriesing <wk@visionp.de>
  861.      * @access public
  862.      */
  863.     function remove($data, $whereCol = '')
  864.     {
  865.         $raw = $this->getOption('raw');
  866.  
  867.         if (is_array($data)) {
  868.             //FIXXME check $data if it only contains columns that really exist in the table
  869.             $wheres = array();
  870.             foreach ($data as $key => $val) {
  871.                 if (is_null($val)) {
  872.                     $wheres[] = $this->_quoteIdentifier($key) .' IS NULL';
  873.                 } else {
  874.                     $wheres[] = $this->_quoteIdentifier($key) .'='. $this->_quote($val);
  875.                 }
  876.             }
  877.             $whereClause = implode(' AND ', $wheres);
  878.         } else {
  879.             if (empty($whereCol)) {
  880.                 $whereCol = $this->primaryCol;
  881.             }
  882.             $whereClause = $this->_quoteIdentifier($whereCol) .'='. $this->_quote($data);
  883.         }
  884.  
  885.         $query = 'DELETE FROM '. $this->table .' WHERE '. $whereClause;
  886.         return $this->execute($query, 'query') ? true : false;
  887.         // i think this method should return the ID's that it removed, 
  888.         // this way we could simply use the result for further actions that depend
  889.         // on those id ... or? make stuff easier, see ignaz::imail::remove
  890.     }
  891.  
  892.     // }}}
  893.     // {{{ removeAll()
  894.  
  895.     /**
  896.      * empty a table
  897.      *
  898.      * @return resultSet or false on error [execute() result]
  899.      * @version 2002/06/17
  900.      * @author Wolfram Kriesing <wk@visionp.de>
  901.      * @access public
  902.      */
  903.     function removeAll()
  904.     {
  905.         $query = 'DELETE FROM '.$this->table;
  906.         return $this->execute($query, 'query') ? true : false;
  907.     }
  908.  
  909.     // }}}
  910.     // {{{ removeMultiple()
  911.  
  912.     /**
  913.      * remove the datasets with the given ids
  914.      *
  915.      * @param array  $ids     the ids to remove
  916.      * @param string $colName the name of the column containing the ids (default: PK)
  917.      *
  918.      * @return resultSet or false on error [execute() result]
  919.      * @version 2002/04/24
  920.      * @author Wolfram Kriesing <wk@visionp.de>
  921.      * @access public
  922.      */
  923.     function removeMultiple($ids, $colName = '')
  924.     {
  925.         if (empty($colName)) {
  926.             $colName = $this->primaryCol;
  927.         }
  928.         $ids = $this->_quoteArray($ids);
  929.  
  930.         $query = sprintf('DELETE FROM %s WHERE %s IN (%s)',
  931.             $this->table,
  932.             $colName,
  933.             $this->_localeSafeImplode(',', $ids)
  934.         );
  935.         return $this->execute($query, 'query') ? true : false;
  936.     }
  937.  
  938.     // }}}
  939.     // {{{ removePrimary()
  940.  
  941.     /**
  942.      * removes a member from the DB and calls the remove methods of the given objects
  943.      * so all rows in another table that refer to this table are erased too
  944.      *
  945.      * @param integer $id               the value of the primary key
  946.      * @param string  $colName          the column name of the tables with the foreign keys
  947.      * @param object  $atLeastOneObject just for convinience, so nobody forgets to call
  948.      *                                  this method with at least one object as a parameter
  949.      *
  950.      * @return boolean
  951.      * @version 2002/04/08
  952.      * @author Wolfram Kriesing <wk@visionp.de>
  953.      * @access public
  954.      */
  955.     function removePrimary($id, $colName, $atLeastOneObject)
  956.     {
  957.         $argCounter = 2;    // we have 2 parameters that need to be given at least
  958.         // func_get_arg returns false and a warning if there are no more parameters, so
  959.         // we suppress the warning and check for false
  960.         while ($object = @func_get_arg($argCounter++)) {
  961.             //FIXXXME let $object also simply be a table name
  962.             if (!$object->remove($id, $colName)) {
  963.                 //FIXXXME do this better
  964.                 $this->_errorSet("Error removing '$colName=$id' from table {$object->table}.");
  965.                 return false;
  966.             }
  967.         }
  968.  
  969.         return ($this->remove($id) ? true : false);
  970.     }
  971.  
  972.     // }}}
  973.     // {{{ setLimit()
  974.  
  975.     /**
  976.      * sets query limits
  977.      *
  978.      * @param integer $from  start index
  979.      * @param integer $count number of results
  980.      *
  981.      * @return void
  982.      * @access public
  983.      */
  984.     function setLimit($from = 0, $count = 0)
  985.     {
  986.         if (0 == $from && 0 == $count) {
  987.             $this->_limit = array();
  988.         } else {
  989.             $this->_limit = array($from, $count);
  990.         }
  991.     }
  992.  
  993.     // }}}
  994.     // {{{ getLimit()
  995.  
  996.     /**
  997.      * gets query limits
  998.      *
  999.      * @return array (start index, number of results)
  1000.      * @access public
  1001.      */
  1002.     function getLimit()
  1003.     {
  1004.         return $this->_limit;
  1005.     }
  1006.  
  1007.     // }}}
  1008.     // {{{ setWhere()
  1009.  
  1010.     /**
  1011.      * sets the WHERE condition which is used for the current instance
  1012.      *
  1013.      * @param string $whereCondition the WHERE condition, this can be complete like 'X=7 AND Y=8'
  1014.      *
  1015.      * @return void
  1016.      * @version 2002/04/16
  1017.      * @author Wolfram Kriesing <wk@visionp.de>
  1018.      * @access public
  1019.      */
  1020.     function setWhere($whereCondition = '')
  1021.     {
  1022.         $this->_where = $whereCondition;
  1023.         //FIXXME parse the where condition and replace ambigious column names, 
  1024.         // such as "name='Deutschland'" with "country.name='Deutschland'"
  1025.         // then the users dont have to write that explicitly and can use the same
  1026.         // name as in the setOrder i.e. setOrder('name,_net_name,_netPrefix_prefix');
  1027.     }
  1028.  
  1029.     // }}}
  1030.     // {{{ getWhere()
  1031.  
  1032.     /**
  1033.      * gets the WHERE condition which is used for the current instance
  1034.      *
  1035.      * @return string the WHERE condition, this can be complete like 'X=7 AND Y=8'
  1036.      * @version 2002/04/22
  1037.      * @author Wolfram Kriesing <wk@visionp.de>
  1038.      * @access public
  1039.      */
  1040.     function getWhere()
  1041.     {
  1042.         return $this->_where;
  1043.     }
  1044.  
  1045.     // }}}
  1046.     // {{{ addWhere()
  1047.  
  1048.     /**
  1049.      * only adds a string to the WHERE clause
  1050.      *
  1051.      * @param string $where         the WHERE clause to add to the existing one
  1052.      * @param string $connectString the condition for how to concatenate the
  1053.      *                              new WHERE clause to the existing one [default AND]
  1054.      *
  1055.      * @return void
  1056.      * @version 2002/07/22
  1057.      * @author Wolfram Kriesing <wk@visionp.de>
  1058.      * @access public
  1059.      */
  1060.     function addWhere($where, $connectString = 'AND')
  1061.     {
  1062.         if ($this->getWhere()) {
  1063.             $where = $this->getWhere().' '.$connectString.' '.$where;
  1064.         }
  1065.         $this->setWhere($where);
  1066.     }
  1067.  
  1068.     // }}}
  1069.     // {{{ addWhereSearch()
  1070.  
  1071.     /**
  1072.      * add a WHERE-LIKE clause which works like a search for the given string
  1073.      * i.e. calling it like this:
  1074.      *     $this->addWhereSearch('name', 'otto hans')
  1075.      * produces a where clause like this one
  1076.      *     LOWER(name) LIKE "%otto%hans%"
  1077.      * so the search finds the given string
  1078.      *
  1079.      * @param string $column        the column to search in for
  1080.      * @param string $string        the string to search for
  1081.      * @param string $connectString the condition for how to concatenate the
  1082.      *                              new WHERE clause to the existing one [default AND]
  1083.      *
  1084.      * @return void
  1085.      * @version 2002/08/14
  1086.      * @author Wolfram Kriesing <wk@visionp.de>
  1087.      * @access public
  1088.      */
  1089.     function addWhereSearch($column, $string, $connectString = 'AND')
  1090.     {
  1091.         // if the column doesn't contain a tablename use the current table name
  1092.         // in case it is a defined column to prevent ambiguous rows
  1093.         if (strpos($column, '.') === false) {
  1094.             $meta = $this->metadata();
  1095.             if (isset($meta[$column])) {
  1096.                 $column = $this->table .'.'. trim($column);
  1097.             }
  1098.         }
  1099.  
  1100.         $string = $this->db->quoteSmart('%'.str_replace(' ', '%', strtolower($string)).'%');
  1101.         $this->addWhere("LOWER($column) LIKE $string", $connectString);
  1102.     }
  1103.  
  1104.     // }}}
  1105.     // {{{ setOrder()
  1106.  
  1107.     /**
  1108.      * sets the ORDER BY condition which is used for the current instance
  1109.      *
  1110.      * @param string  $orderCondition the ORDER BY condition
  1111.      * @param boolean $desc           sorting order (TRUE => ASC, FALSE => DESC)
  1112.      *
  1113.      * @return void
  1114.      * @version 2002/05/16
  1115.      * @author Wolfram Kriesing <wk@visionp.de>
  1116.      * @access public
  1117.      */
  1118.     function setOrder($orderCondition = '', $desc = false)
  1119.     {
  1120.         $this->_order = $orderCondition .($desc ? ' DESC' : '');
  1121.     }
  1122.  
  1123.     // }}}
  1124.     // {{{ addOrder()
  1125.  
  1126.     /**
  1127.      * Add a ORDER BY parameter to the query.
  1128.      *
  1129.      * @param string  $orderCondition the ORDER BY condition
  1130.      * @param boolean $desc           sorting order (TRUE => ASC, FALSE => DESC)
  1131.      *
  1132.      * @return void
  1133.      * @version 2003/05/28
  1134.      * @author Wolfram Kriesing <wk@visionp.de>
  1135.      * @access public
  1136.      */
  1137.     function addOrder($orderCondition = '', $desc = false)
  1138.     {
  1139.         $order = $orderCondition .($desc ? ' DESC' : '');
  1140.         if ($this->_order) {
  1141.             $this->_order = $this->_order.','.$order;
  1142.         } else {
  1143.             $this->_order = $order;
  1144.         }
  1145.     }
  1146.  
  1147.     // }}}
  1148.     // {{{ getOrder()
  1149.  
  1150.     /**
  1151.      * gets the ORDER BY condition which is used for the current instance
  1152.      *
  1153.      * @return string the ORDER BY condition, this can be complete like 'ID,TIMESTAMP DESC'
  1154.      * @version 2002/05/16
  1155.      * @author Wolfram Kriesing <wk@visionp.de>
  1156.      * @access public
  1157.      */
  1158.     function getOrder()
  1159.     {
  1160.         return $this->_order;
  1161.     }
  1162.  
  1163.     // }}}
  1164.     // {{{ setHaving()
  1165.  
  1166.     /**
  1167.      * sets the HAVING definition
  1168.      *
  1169.      * @param string $having the HAVING definition
  1170.      *
  1171.      * @return void
  1172.      * @version 2003/06/05
  1173.      * @author Johannes Schaefer <johnschaefer@gmx.de>
  1174.      * @access public
  1175.      */
  1176.     function setHaving($having = '')
  1177.     {
  1178.         $this->_having = $having;
  1179.     }
  1180.  
  1181.     // }}}
  1182.     // {{{ getHaving()
  1183.  
  1184.     /**
  1185.      * gets the HAVING definition which is used for the current instance
  1186.      *
  1187.      * @return string the HAVING definition
  1188.      * @version 2003/06/05
  1189.      * @author Johannes Schaefer <johnschaefer@gmx.de>
  1190.      * @access public
  1191.      */
  1192.     function getHaving()
  1193.     {
  1194.         return $this->_having;
  1195.     }
  1196.  
  1197.     // }}}
  1198.     // {{{ addHaving()
  1199.  
  1200.     /**
  1201.      * Extend the current HAVING clause. This is very useful, when you are building
  1202.      * this clause from different places and don't want to overwrite the currently
  1203.      * set HAVING clause, but extend it.
  1204.      *
  1205.      * @param string $what          this is a HAVING clause, i.e. 'column'
  1206.      *                              or 'table.column' or 'MAX(column)'
  1207.      * @param string $connectString the connection string [default ' AND ']
  1208.      *
  1209.      * @return void
  1210.      * @access public
  1211.      */
  1212.     function addHaving($what = '*', $connectString = ' AND ')
  1213.     {
  1214.         if ($this->_having) {
  1215.             $this->_having = $this->_having.$connectString.$what;
  1216.         } else {
  1217.             $this->_having = $what;
  1218.         }
  1219.     }
  1220.  
  1221.     // }}}
  1222.     // {{{ setJoin()
  1223.  
  1224.     /**
  1225.      * sets a join-condition
  1226.      *
  1227.      * @param string|array $table    the table(s) to join on the current table
  1228.      * @param string       $where    the where clause for the join
  1229.      * @param string       $joinType type of the table join
  1230.      *
  1231.      * @return void
  1232.      * @version 2002/06/10
  1233.      * @author Wolfram Kriesing <wk@visionp.de>
  1234.      * @access public
  1235.      */
  1236.     function setJoin($table = null, $where = null, $joinType = 'default')
  1237.     {
  1238.         //FIXXME make it possible to pass a table name as a string like this too 
  1239.         // 'user u' where u is the string that can be used to refer to this table 
  1240.         // in a where/order or whatever condition
  1241.         // this way it will be possible to join tables with itself, like 
  1242.         // setJoin(array('user u','user u1'))
  1243.         // this wouldnt work yet, but for doing so we would need to change the 
  1244.         // _build methods too!!! because they use getJoin('tables') and this simply
  1245.         // returns all the tables in use but don't take care of the mentioned syntax
  1246.         if (is_null($table) || is_null($where)) {   
  1247.             // remove the join if not sufficient parameters are given
  1248.             $this->_join[$joinType] = array();
  1249.             return;
  1250.         }
  1251.         /* this causes problems if we use the order-by, since it doenst know the name to order it by ... :-)
  1252.         // replace the table names with the internal name used for the join
  1253.         // this way we can also join one table multiple times if it will be implemented one day
  1254.         $this->_join[$table] = preg_replace('/'.$table.'/','j1',$where);
  1255.         */
  1256.         $this->_join[$joinType][$table] = $where;
  1257.     }
  1258.  
  1259.     // }}}
  1260.     // {{{ setLeftJoin()
  1261.  
  1262.     /**
  1263.      * if you do a left join on $this->table you will get all entries
  1264.      * from $this->table, also if there are no entries for them in the joined table
  1265.      * if both parameters are not given the left-join will be removed
  1266.      * NOTE: be sure to only use either a right or a left join
  1267.      *
  1268.      * @param string $table the table(s) to be left-joined
  1269.      * @param string $where the where clause for the join
  1270.      *
  1271.      * @return void
  1272.      * @version 2002/07/22
  1273.      * @author Wolfram Kriesing <wk@visionp.de>
  1274.      * @access public
  1275.      */
  1276.     function setLeftJoin($table = null, $where = null)
  1277.     {
  1278.         $this->setJoin($table, $where, 'left');
  1279.     }
  1280.  
  1281.     // }}}
  1282.     // {{{ addLeftJoin()
  1283.  
  1284.     /**
  1285.      * add a LEFT JOIN clause
  1286.      *
  1287.      * @param string $table the table(s) to be left-joined
  1288.      * @param string $where the where clause for the join
  1289.      * @param string $type  join type
  1290.      *
  1291.      * @return void
  1292.      * @access public
  1293.      */
  1294.     function addLeftJoin($table, $where, $type = 'left')
  1295.     {
  1296.         // init value, to prevent E_ALL-warning
  1297.         if (!isset($this->_join[$type]) || !$this->_join[$type]) {
  1298.             $this->_join[$type] = array();
  1299.         }
  1300.         $this->_join[$type][$table] = $where;
  1301.     }
  1302.  
  1303.     // }}}
  1304.     // {{{ setRightJoin()
  1305.  
  1306.     /**
  1307.      * see setLeftJoin for further explaination on what a left/right join is
  1308.      * NOTE: be sure to only use either a right or a left join
  1309.      * //FIXXME check if the above sentence is necessary and if sql doesn't allow the use of both
  1310.      *
  1311.      * @param string $table the table(s) to be right-joined
  1312.      * @param string $where the where clause for the join
  1313.      *
  1314.      * @return void
  1315.      * @version 2002/09/04
  1316.      * @author Wolfram Kriesing <wk@visionp.de>
  1317.      * @see setLeftJoin()
  1318.      * @access public
  1319.      */
  1320.     function setRightJoin($table = null, $where = null)
  1321.     {
  1322.         $this->setJoin($table, $where, 'right');
  1323.     }
  1324.  
  1325.     // }}}
  1326.     // {{{ getJoin()
  1327.  
  1328.     /**
  1329.      * gets the join-condition
  1330.      *
  1331.      * @param string $what [null|''|'table'|'tables'|'right'|'left'|'inner']
  1332.      *
  1333.      * @return array gets the join parameters
  1334.      * @access public
  1335.      */
  1336.     function getJoin($what = null)
  1337.     {
  1338.         // if the user requests all the join data or if the join is empty, return it
  1339.         if (is_null($what) || empty($this->_join)) {
  1340.             return $this->_join;
  1341.         }
  1342.  
  1343.         $ret = array();
  1344.         switch (strtolower($what)) {
  1345.         case 'table':
  1346.         case 'tables':
  1347.             foreach ($this->_join as $aJoin) {
  1348.                 if (count($aJoin)) {
  1349.                     $ret = array_merge($ret, array_keys($aJoin));
  1350.                 }
  1351.             }
  1352.             break;
  1353.         case 'inner':   // return inner-join data only
  1354.         case 'right':   // return right-join data only
  1355.         case 'left':    // return left join data only
  1356.         default:
  1357.             if (isset($this->_join[$what]) && count($this->_join[$what])) {
  1358.                 $ret = array_merge($ret, $this->_join[$what]);
  1359.             }
  1360.             break;
  1361.         }
  1362.         return $ret;
  1363.     }
  1364.  
  1365.     // }}}
  1366.     // {{{ addJoin()
  1367.  
  1368.     /**
  1369.      * adds a table and a where clause that shall be used for the join
  1370.      * instead of calling
  1371.      *     setJoin(array(table1,table2),'<where clause1> AND <where clause2>')
  1372.      * you can also call
  1373.      *     setJoin(table1,'<where clause1>')
  1374.      *     addJoin(table2,'<where clause2>')
  1375.      * or where it makes more sense is to build a query which is made out of a
  1376.      * left join and a standard join
  1377.      *     setLeftJoin(table1,'<where clause1>')
  1378.      *     // results in ... FROM $this->table LEFT JOIN table ON <where clause1>
  1379.      *     addJoin(table2,'<where clause2>')
  1380.      *     // results in ...  FROM $this->table,table2 LEFT JOIN table ON <where clause1> WHERE <where clause2>
  1381.      *
  1382.      * @param string $table the table to be joined
  1383.      * @param string $where the where clause for the join
  1384.      * @param string $type  the join type
  1385.      *
  1386.      * @return void
  1387.      * @access public
  1388.      */
  1389.     function addJoin($table, $where, $type = 'default')
  1390.     {
  1391.         if ($table == $this->table) {
  1392.             return;  //skip. Self joins are not supported.
  1393.         }
  1394.         // init value, to prevent E_ALL-warning
  1395.         if (!array_key_exists($type, $this->_join)) {
  1396.             $this->_join[$type] = array();
  1397.         }
  1398.         $this->_join[$type][$table] = $where;
  1399.     }
  1400.  
  1401.     // }}}
  1402.     // {{{ setTable()
  1403.  
  1404.     /**
  1405.      * sets the table this class is currently working on
  1406.      *
  1407.      * @param string $table the table name
  1408.      *
  1409.      * @return void
  1410.      * @version 2002/07/11
  1411.      * @author Wolfram Kriesing <wk@visionp.de>
  1412.      * @access public
  1413.      */
  1414.     function setTable($table)
  1415.     {
  1416.         $this->table = $table;
  1417.     }
  1418.  
  1419.     // }}}
  1420.     // {{{ getTable()
  1421.  
  1422.     /**
  1423.      * gets the table this class is currently working on
  1424.      *
  1425.      * @return string the table name
  1426.      * @version 2002/07/11
  1427.      * @author Wolfram Kriesing <wk@visionp.de>
  1428.      * @access public
  1429.      */
  1430.     function getTable()
  1431.     {
  1432.         return $this->table;
  1433.     }
  1434.  
  1435.     // }}}
  1436.     // {{{ setGroup()
  1437.  
  1438.     /**
  1439.      * sets the group-by condition
  1440.      *
  1441.      * @param string $group the group condition
  1442.      *
  1443.      * @return void
  1444.      * @version 2002/07/22
  1445.      * @author Wolfram Kriesing <wk@visionp.de>
  1446.      * @access public
  1447.      */
  1448.     function setGroup($group = '')
  1449.     {
  1450.         $this->_group = $group;
  1451.         //FIXXME parse the condition and replace ambiguous column names, such as
  1452.         // "name='Deutschland'" with "country.name='Deutschland'"
  1453.         // then the users don't have to write that explicitly and can use the same
  1454.         // name as in the setOrder i.e. setOrder('name,_net_name,_netPrefix_prefix');
  1455.     }
  1456.  
  1457.     // }}}
  1458.     // {{{ getGroup()
  1459.  
  1460.     /**
  1461.      * gets the group condition which is used for the current instance
  1462.      *
  1463.      * @return string the group condition
  1464.      * @version 2002/07/22
  1465.      * @author Wolfram Kriesing <wk@visionp.de>
  1466.      * @access public
  1467.      */
  1468.     function getGroup()
  1469.     {
  1470.         return $this->_group;
  1471.     }
  1472.  
  1473.     // }}}
  1474.     // {{{ setSelect()
  1475.  
  1476.     /**
  1477.      * limit the result to return only the columns given in $what
  1478.      *
  1479.      * @param string $what fields that shall be selected
  1480.      *
  1481.      * @return void
  1482.      * @access public
  1483.      */
  1484.     function setSelect($what = '*')
  1485.     {
  1486.         $this->_select = $what;
  1487.     }
  1488.  
  1489.     // }}}
  1490.     // {{{ addSelect()
  1491.  
  1492.     /**
  1493.      * add a string to the select part of the query
  1494.      * Add a string to the select-part of the query and connects it to an existing
  1495.      * string using the $connectString, which by default is a comma.
  1496.      * (SELECT xxx FROM - xxx is the select-part of a query)
  1497.      *
  1498.      * @param string $what          the string that shall be added to the select-part
  1499.      * @param string $connectString the string to connect the new string with the existing one
  1500.      *
  1501.      * @return void
  1502.      * @version 2003/01/08
  1503.      * @author Wolfram Kriesing <wk@visionp.de>
  1504.      * @access public
  1505.      */
  1506.     function addSelect($what = '*', $connectString = ',')
  1507.     {
  1508.         // if the select string is not empty add the string, otherwise simply set it
  1509.         if ($this->_select) {
  1510.             $this->_select = $this->_select.$connectString.$what;
  1511.         } else {
  1512.             $this->_select = $what;
  1513.         }
  1514.     }
  1515.  
  1516.     // }}}
  1517.     // {{{ getSelect()
  1518.  
  1519.     /**
  1520.      * Get the SELECT clause
  1521.      *
  1522.      * @return string
  1523.      * @access public
  1524.      */
  1525.     function getSelect()
  1526.     {
  1527.         return $this->_select;
  1528.     }
  1529.  
  1530.     // }}}
  1531.     // {{{ setDontSelect()
  1532.  
  1533.     /**
  1534.      * Do not select the given column
  1535.      *
  1536.      * @param string $what column to ignore
  1537.      *
  1538.      * @return void
  1539.      * @access public
  1540.      */
  1541.     function setDontSelect($what = '')
  1542.     {
  1543.         $this->_dontSelect = $what;
  1544.     }
  1545.  
  1546.     // }}}
  1547.     // {{{ getDontSelect()
  1548.  
  1549.     /**
  1550.      * Get the column(s) to be ignored
  1551.      *
  1552.      * @return string
  1553.      * @access public
  1554.      */
  1555.     function getDontSelect()
  1556.     {
  1557.         return $this->_dontSelect;
  1558.     }
  1559.  
  1560.     // }}}
  1561.     // {{{ reset()
  1562.  
  1563.     /**
  1564.      * reset all the set* settings; with no parameter given, it resets them all.
  1565.      *
  1566.      * @param array $what clauses to reset [select|dontSelect|group|having|limit
  1567.      *                    |where|index|order|join|leftJoin|rightJoin]
  1568.      *
  1569.      * @return void
  1570.      * @version 2002/09/16
  1571.      * @author Wolfram Kriesing <wk@visionp.de>
  1572.      * @access public
  1573.      */
  1574.     function reset($what = array())
  1575.     {
  1576.         if (!sizeof($what)) {
  1577.             $what = array(
  1578.                 'select',
  1579.                 'dontSelect',
  1580.                 'group',
  1581.                 'having',
  1582.                 'limit',
  1583.                 'where',
  1584.                 'index',
  1585.                 'order',
  1586.                 'join',
  1587.                 'leftJoin',
  1588.                 'rightJoin'
  1589.             );
  1590.         }
  1591.  
  1592.         foreach ($what as $aReset) {
  1593.             $this->{'set'.ucfirst($aReset)}();
  1594.         }
  1595.     }
  1596.  
  1597.     // }}}
  1598.     // {{{ setOption()
  1599.  
  1600.     /**
  1601.      * set mode the class shall work in
  1602.      * currently we have the modes:
  1603.      * 'raw'   does not quote the data before building the query
  1604.      *
  1605.      * @param string $option the mode to be set
  1606.      * @param mixed  $value  the value of the mode
  1607.      *
  1608.      * @return void
  1609.      * @version 2002/09/17
  1610.      * @author Wolfram Kriesing <wk@visionp.de>
  1611.      * @access public
  1612.      */
  1613.     function setOption($option, $value)
  1614.     {
  1615.         $this->options[strtolower($option)] = $value;
  1616.     }
  1617.  
  1618.     // }}}
  1619.     // {{{ getOption()
  1620.  
  1621.     /**
  1622.      * Get the option value
  1623.      *
  1624.      * @param string $option name of the option to retrieve
  1625.      *
  1626.      * @return string value of the option
  1627.      * @access public
  1628.      */
  1629.     function getOption($option)
  1630.     {
  1631.         return $this->options[strtolower($option)];
  1632.     }
  1633.  
  1634.     // }}}
  1635.     // {{{ _quoteIdentifier()
  1636.  
  1637.     /**
  1638.      * Quotes an identifier (table or field name). This wrapper is needed to
  1639.      * comply with the $raw parameter and to override DB_ibase::quoteIdentifier().
  1640.      *
  1641.      * @param string $var var to quote
  1642.      *
  1643.      * @return string quoted identifier
  1644.      * @access private
  1645.      */
  1646.     function _quoteIdentifier($var)
  1647.     {
  1648.         if (!$this->getOption('raw') && $this->db->phptype != 'ibase') {
  1649.             return $this->db->quoteIdentifier($var);
  1650.         }
  1651.         return $var;
  1652.     }
  1653.  
  1654.     // }}}
  1655.     // {{{ _quoteArray()
  1656.  
  1657.     /**
  1658.      * quotes all the data in this array if we are not in raw mode!
  1659.      *
  1660.      * @param array $data data to quote
  1661.      *
  1662.      * @return array
  1663.      * @access private
  1664.      */
  1665.     function _quoteArray($data)
  1666.     {
  1667.         if (!$this->getOption('raw')) {
  1668.             foreach ($data as $key => $val) {
  1669.                 $data[$key] = $this->db->quoteSmart($val);
  1670.             }
  1671.         }
  1672.         return $data;
  1673.     }
  1674.  
  1675.     // }}}
  1676.     // {{{ _quote()
  1677.  
  1678.     /**
  1679.      * quotes all the data in this array|string if we are not in raw mode!
  1680.      *
  1681.      * @param mixed $data data to quote
  1682.      *
  1683.      * @return mixed
  1684.      * @access private
  1685.      */
  1686.     function _quote($data)
  1687.     {
  1688.         if ($this->getOption('raw')) {
  1689.             return $data;
  1690.         }
  1691.         switch (gettype($data)) {
  1692.         case 'array':
  1693.             return $this->_quoteArray($data);
  1694.         default:
  1695.             return $this->db->quoteSmart($data);
  1696.         }
  1697.     }
  1698.  
  1699.     // }}}
  1700.     // {{{ _checkColumns()
  1701.  
  1702.     /**
  1703.      * checks if the columns which are given as the array's indexes really exist
  1704.      * if not it will be unset anyway
  1705.      *
  1706.      * @param array  $newData array data whose keys needs checking
  1707.      * @param string $method  method name
  1708.      *
  1709.      * @return array
  1710.      * @version 2002/04/16
  1711.      * @author Wolfram Kriesing <wk@visionp.de>
  1712.      * @access public
  1713.      */
  1714.     function _checkColumns($newData, $method = 'unknown')
  1715.     {
  1716.         if (!$meta = $this->metadata()) {
  1717.             // if no metadata available, return data as given
  1718.             return $newData;
  1719.         }
  1720.         foreach ($newData as $colName => $x) {
  1721.             if (!isset($meta[$colName])) {
  1722.                 $this->_errorLog("$method, column {$this->table}.$colName doesn't exist, value was removed before '$method'", __LINE__);
  1723.                 unset($newData[$colName]);
  1724.             } else {
  1725.                 // if the current column exists, check the length too, not to write content that is too long
  1726.                 // prevent DB-errors here
  1727.                 // do only check the data length if this field is given
  1728.                 if (isset($meta[$colName]['len']) && ($meta[$colName]['len'] != -1)
  1729.                     && (($oldLength=strlen($newData[$colName])) > $meta[$colName]['len'])
  1730.                     && !is_numeric($newData[$colName])
  1731.                 ) {
  1732.                     $this->_errorLog("_checkColumns, had to trim column '$colName' from $oldLength to ".
  1733.                                         $meta[$colName]['DATA_LENGTH'].' characters.', __LINE__);
  1734.                     $newData[$colName] = substr($newData[$colName], 0, $meta[$colName]['len']);
  1735.                 }
  1736.             }
  1737.         }
  1738.         return $newData;
  1739.     }
  1740.  
  1741.     // }}}
  1742.     // {{{ debug()
  1743.  
  1744.     /**
  1745.      * overwrite this method and i.e. print the query $string
  1746.      * to see the final query
  1747.      *
  1748.      * @param string $string the query mostly
  1749.      *
  1750.      * @return void
  1751.      * @access public
  1752.      */
  1753.     function debug($string)
  1754.     {
  1755.     }
  1756.  
  1757.     //
  1758.     //
  1759.     //  ONLY ORACLE SPECIFIC, not very nice since it is DB dependent, but we need it!!!
  1760.     //
  1761.     //
  1762.  
  1763.     // }}}
  1764.     // {{{ metadata()
  1765.  
  1766.     /**
  1767.      * !!!! query COPIED FROM db_oci8.inc - from PHPLIB !!!!
  1768.      *
  1769.      * @param string $table table name
  1770.      *
  1771.      * @return resultSet or false on error
  1772.      * @version  2001/09
  1773.      * @see db_oci8.inc - PHPLIB
  1774.      * @access public
  1775.      */
  1776.     function metadata($table = '')
  1777.     {
  1778.         // is there an alias in the table name, then we have something like this: 'user ua'
  1779.         // cut of the alias and return the table name
  1780.         if (strpos($table, ' ') !== false) {
  1781.             $split = explode(' ', trim($table));
  1782.             $table = $split[0];
  1783.         }
  1784.  
  1785.         $full = false;
  1786.         if (empty($table)) {
  1787.             $table = $this->table;
  1788.         }
  1789.         // to prevent multiple selects for the same metadata
  1790.         if (isset($this->_metadata[$table])) {
  1791.             return $this->_metadata[$table];
  1792.         }
  1793.         // FIXXXME use oci8 implementation of newer PEAR::DB-version
  1794.         if ($this->db->phptype == 'oci8') {
  1795.             $count = 0;
  1796.             $id    = 0;
  1797.             $res   = array();
  1798.  
  1799.             //# This is a RIGHT OUTER JOIN: "(+)", if you want to see, what
  1800.             //# this query results try the following:
  1801.             //// $table = new Table; $this->db = new my_DB_Sql; // you have to make
  1802.             ////                                          // your own class
  1803.             //// $table->show_results($this->db->query(see query vvvvvv))
  1804.             ////
  1805.             $query = "SELECT T.column_name,
  1806.                              T.table_name,
  1807.                              T.data_type,
  1808.                              T.data_length,
  1809.                              T.data_precision,
  1810.                              T.data_scale,
  1811.                              T.nullable,
  1812.                              T.char_col_decl_length,
  1813.                              I.index_name
  1814.                         FROM ALL_TAB_COLUMNS T,
  1815.                              ALL_IND_COLUMNS I
  1816.                        WHERE T.column_name = I.column_name (+)
  1817.                          AND T.table_name = I.table_name (+)
  1818.                          AND T.table_name = UPPER('$table')
  1819.                     ORDER BY T.column_id";
  1820.             $res = $this->db->getAll($query);
  1821.  
  1822.             if (PEAR::isError($res)) {
  1823.                 //$this->_errorSet($res->getMessage());
  1824.                 // i think we only need to log here, since this method is never used
  1825.                 // directly for the user's functionality, which means if it fails it
  1826.                 // is most probably an appl error
  1827.                 $this->_errorLog($res->getUserInfo());
  1828.                 return false;
  1829.             }
  1830.             foreach ($res as $key => $val) {
  1831.                 $res[$key]['name'] = $val['COLUMN_NAME'];
  1832.             }
  1833.         } else {
  1834.             if (!is_object($this->db)) {
  1835.                 return false;
  1836.             }
  1837.             $res = $this->db->tableinfo($table);
  1838.             if (PEAR::isError($res)) {
  1839.                 //var_dump($res);
  1840.                 //echo '<div style="border:1px solid red; background: yellow">'.$res->getUserInfo().'<br/><pre>'; print_r(debug_backtrace()); echo '</pre></div>';
  1841.                 $this->_errorSet($res->getUserInfo());
  1842.                 return false;
  1843.             }
  1844.         }
  1845.  
  1846.         $ret = array();
  1847.         foreach ($res as $key => $val) {
  1848.             $ret[$val['name']] = $val;
  1849.         }
  1850.         $this->_metadata[$table] = $ret;
  1851.         return $ret;
  1852.     }
  1853.  
  1854.     // }}}
  1855.  
  1856.     //
  1857.     //  methods for building the query
  1858.     //
  1859.  
  1860.     // {{{ _prependTableName()
  1861.  
  1862.     /**
  1863.      * replace 'column' by 'table.column' if the column is defined for the table
  1864.      *
  1865.      * @param string $fieldlist comma-separated field list
  1866.      * @param string $table     table name
  1867.      *
  1868.      * @return string
  1869.      * @see http://pear.php.net/bugs/bug.php?id=9734
  1870.      * @access private
  1871.      */
  1872.     function _prependTableName($fieldlist, $table)
  1873.     {
  1874.         if (!$meta = $this->metadata($table)) {
  1875.             return $fieldlist;
  1876.         }
  1877.         $fields = explode(',', $fieldlist);
  1878.         foreach (array_keys($meta) as $column) {
  1879.             //$fieldlist = preg_replace('/(^\s*|\s+|,)'.$column.'\s*(,)?/U', "$1{$table}.$column$2", $fieldlist);
  1880.             $pattern = '/^'.$column.'\b.*/U';
  1881.             foreach (array_keys($fields) as $k) {
  1882.                 $fields[$k] = trim($fields[$k]);
  1883.                 if (!strpos($fields[$k], '.') && preg_match($pattern, $fields[$k])) {
  1884.                     $fields[$k] = $this->_quoteIdentifier($table).'.'.$this->_quoteIdentifier($fields[$k]);
  1885.                 }
  1886.             }
  1887.         }
  1888.         return implode(',', $fields);
  1889.     }
  1890.  
  1891.     // }}}
  1892.     // {{{ _buildFrom()
  1893.  
  1894.     /**
  1895.      * build the from string
  1896.      *
  1897.      * @return string  the string added after FROM
  1898.      * @access private
  1899.      */
  1900.     function _buildFrom()
  1901.     {
  1902.         $this_table = $from = $this->_quoteIdentifier($this->table);
  1903.         $join = $this->getJoin();
  1904.  
  1905.         if (!$join) {  // no join set
  1906.             return $from;
  1907.         }
  1908.         // handle the standard join thingy
  1909.         if (isset($join['default']) && count($join['default'])) {
  1910.             foreach (array_keys($join['default']) as $joined_tbl) {
  1911.                 $from .= ','.$this->_quoteIdentifier($joined_tbl);
  1912.             }
  1913.         }
  1914.  
  1915.         // handle left/right/inner joins
  1916.         foreach (array('left', 'right', 'inner') as $joinType) {
  1917.             if (isset($join[$joinType]) && count($join[$joinType])) {
  1918.                 foreach ($join[$joinType] as $table => $condition) {
  1919.                     // replace the _TABLENAME_COLUMNNAME by TABLENAME.COLUMNNAME
  1920.                     // since oracle doesn't work with the _TABLENAME_COLUMNNAME which
  1921.                     // I think is strange
  1922.                     // FIXXME i think this should become deprecated since the
  1923.                     // setWhere should not be used like this: '_table_column' but 'table.column'
  1924.                     $regExp = '/_('.$table.')_([^\s]+)/';
  1925.                     $where = preg_replace($regExp, '$1.$2', $condition);
  1926.  
  1927.                     // add the table name before any column that has no table prefix
  1928.                     // since this might cause "unambiguous column" errors
  1929.                     if ($meta = $this->metadata()) {
  1930.                         foreach ($meta as $aCol => $x) {
  1931.                             // this covers the LIKE,IN stuff: 'name LIKE "%you%"'  'id IN (2,3,4,5)'
  1932.                             $condition = preg_replace('/\s'.$aCol.'\s/', " {$this_table}.$aCol ", $condition);
  1933.                             // replace also the column names which are behind a '='
  1934.                             // and do this also if the aCol is at the end of the where clause
  1935.                             // that's what the $ is for
  1936.                             $condition = preg_replace('/=\s*'.$aCol.'(\s|$)/', "={$this_table}.$aCol ", $condition);
  1937.                             // replace if colName is first and possibly also if at the beginning of the where-string
  1938.                             $condition = preg_replace('/(^\s*|\s+)'.$aCol.'\s*=/', "$1{$this_table}.$aCol=", $condition);
  1939.                         }
  1940.                     }
  1941.                     $from .= ' '.strtoupper($joinType).' JOIN '.$this->_quoteIdentifier($table).' ON '.$condition;
  1942.                 }
  1943.             }
  1944.         }
  1945.         return $from;
  1946.     }
  1947.  
  1948.     // }}}
  1949.     // {{{ getTableShortName()
  1950.  
  1951.     /**
  1952.      * Gets the short name for a table
  1953.      *
  1954.      * get the short name for a table, this is needed to properly build the
  1955.      * 'AS' parts in the select query
  1956.      *
  1957.      * @param string $table the real table name
  1958.      *
  1959.      * @return string the table's short name
  1960.      * @access public
  1961.      */
  1962.     function getTableShortName($table)
  1963.     {
  1964.         $tableSpec = $this->getTableSpec(false);
  1965.         if (isset($tableSpec[$table]['shortName']) && $tableSpec[$table]['shortName']) {
  1966.             //print "$table ... ".$tableSpec[$table]['shortName'].'<br />';
  1967.             return $tableSpec[$table]['shortName'];
  1968.         }
  1969.  
  1970.         $possibleTableShortName = preg_replace($this->_tableNameToShortNamePreg, '', $table);
  1971.         //print "$table ... $possibleTableShortName<br />";
  1972.         return $possibleTableShortName;
  1973.     }
  1974.  
  1975.     // }}}
  1976.     // {{{ getTableSpec()
  1977.  
  1978.     /**
  1979.      * gets the tableSpec either indexed by the short name or the name
  1980.      * returns the array for the tables given as parameter or if no
  1981.      * parameter given for all tables that exist in the tableSpec
  1982.      *
  1983.      * @param boolean $shortNameIndexed if true the table is returned indexed by
  1984.      *                                  the shortName otherwise indexed by the name
  1985.      * @param array   $tables           table names (not the short names!)
  1986.      *
  1987.      * @return array the tableSpec indexed
  1988.      * @access public
  1989.      */
  1990.     function getTableSpec($shortNameIndexed = true, $tables = array())
  1991.     {
  1992.         $newSpec = array();
  1993.         foreach ($this->tableSpec as $aSpec) {
  1994.             if (0 == sizeof($tables) || in_array($aSpec['name'], $tables)) {
  1995.                 if ($shortNameIndexed) {
  1996.                     $newSpec[$aSpec['shortName']] = $aSpec;
  1997.                 } else {
  1998.                     $newSpec[$aSpec['name']]      = $aSpec;
  1999.                 }
  2000.             }
  2001.         }
  2002.         return $newSpec;
  2003.     }
  2004.  
  2005.     // }}}
  2006.     // {{{ _buildSelect()
  2007.  
  2008.     /**
  2009.      *   build the 'SELECT <what> FROM ... 'for a select
  2010.      *
  2011.      * @param string $what if given use this string
  2012.      *
  2013.      * @return string the what-clause
  2014.      * @version 2002/07/11
  2015.      * @author Wolfram Kriesing <wk@visionp.de>
  2016.      * @access private
  2017.      */
  2018.     function _buildSelect($what = null)
  2019.     {
  2020.         // what has preference, that means if what is set it is used
  2021.         // this is only because the methods like 'get' pass an individually built value, which
  2022.         // is supposed to be used, but usually it's generically build using the 'getSelect' values
  2023.         if (empty($what) && $this->getSelect()) {
  2024.             $what = $this->getSelect();
  2025.         }
  2026.         
  2027.         //
  2028.         // replace all the '*' by the real column names, and take care of the dontSelect-columns!
  2029.         //
  2030.         $dontSelect = $this->getDontSelect();
  2031.         $dontSelect = $dontSelect ? explode(',', $dontSelect) : array(); // make sure dontSelect is an array
  2032.         
  2033.         // here we will replace all the '*' and 'table.*' by all the columns that this table
  2034.         // contains. we do this so we can easily apply the 'dontSelect' values.
  2035.         // and so we can also handle queries like: 'SELECT *,count() FROM ' and 'SELECT table.*,x FROM ' too
  2036.         if (false !== strpos($what, '*')) {
  2037.             // subpattern 1 get all the table names, that are written like this: 'table.*' including '*'
  2038.             // for '*' the tablename will be ''
  2039.             preg_match_all('/([^,]*)(\.)?\*\s*(,|$)/U', $what, $res);
  2040.             //echo '<pre>'; print "$what ... "; print_r($res); print "</pre><hr />";
  2041.             $selectAllFromTables = array_unique($res[1]); // make the table names unique, so we do it all just once for each table
  2042.             //echo '<pre>'; print "$what ... "; print_r($selectAllFromTables); print "</pre><hr />";
  2043.             $tables = array();
  2044.             if (in_array('', $selectAllFromTables)) { // was there a '*' ?
  2045.                 // get all the tables that we need to process, depending on if joined or not
  2046.                 $tables = $this->getJoin() ?
  2047.                     array_merge($this->getJoin('tables'), array($this->table)) : // get the joined tables and this->table
  2048.                     array($this->table);        // create an array with only this->table
  2049.             } else {
  2050.                 $tables = $selectAllFromTables;
  2051.             }
  2052.             //echo '<br />'; print_r($tables);
  2053.             $cols = array();
  2054.             foreach ($tables as $aTable) {      // go thru all the tables and get all columns for each, and handle 'dontSelect'
  2055.                 //echo '<br />$this->metadata('.$aTable.')';
  2056.                 //echo '<br /><pre>';var_dump($this->metadata($aTable)); echo '</pre>';
  2057.                 if ($meta = $this->metadata($aTable)) {
  2058.                     foreach ($meta as $colName => $x) {
  2059.                         // handle the dontSelect's
  2060.                         if (in_array($colName, $dontSelect) || in_array("$aTable.$colName", $dontSelect)) {
  2061.                             continue;
  2062.                         }
  2063.  
  2064.                         // build the AS clauses
  2065.                         // put " around them to enable use of reserved words, i.e. SELECT table.option as option FROM...
  2066.                         // and 'option' actually is a reserved word, at least in mysql
  2067.                         // but don't do this for ibase because it doesn't work!
  2068.                         if ($aTable == $this->table) {
  2069.                             $cols[$aTable][] = $this->table. '.' .$colName . ' AS '. $this->_quoteIdentifier($colName);
  2070.                         } else {
  2071.                             //with ibase, don't quote aliases, and prepend the 
  2072.                             //joined table cols alias with "t_" because an alias
  2073.                             //starting with just "_" triggers an "invalid token" error
  2074.                             $short_alias = ($this->db->phptype == 'ibase' ? 't_' : '_') . $this->getTableShortName($aTable) .'_'. $colName;
  2075.                             $cols[$aTable][] = $aTable. '.' .$colName . ' AS '. $this->_quoteIdentifier($short_alias);
  2076.                         }
  2077.                     }
  2078.                 }
  2079.             }
  2080.  
  2081.             // put the extracted select back in the $what
  2082.             // that means replace 'table.*' by the i.e. 'table.id AS _table_id'
  2083.             // or if it is the table of this class replace 'table.id AS id'
  2084.             if (in_array('', $selectAllFromTables)) {
  2085.                 $allCols = array();
  2086.                 foreach ($cols as $aTable) {
  2087.                     $allCols[] = implode(',', $aTable);
  2088.                 }
  2089.                 $what = preg_replace('/(^|,)\*($|,)/', '$1'.implode(',', $allCols).'$2', $what);
  2090.                 // remove all the 'table.*' since we have selected all anyway (because there was a '*' in the select)
  2091.                 $what = preg_replace('/[^,]*(\.)?\*\s*(,|$)/U', '', $what);
  2092.             } else {
  2093.                 foreach ($cols as $tableName => $aTable) {
  2094.                     if (is_array($aTable) && sizeof($aTable)) {
  2095.                         // replace all the 'table.*' by their select of each column
  2096.                         $what = preg_replace('/(^|,)\s*'.$tableName.'\.\*\s*($|,)/', '$1'.implode(',', $aTable).'$2', $what);
  2097.                     }
  2098.                 }
  2099.             }
  2100.         }
  2101.  
  2102.         if ($this->getJoin()) {
  2103.             // replace all 'column' by '$this->table.column' to prevent ambiguous errors
  2104.             $metadata = $this->metadata();
  2105.             if (is_array($metadata)) {
  2106.                 foreach ($metadata as $aCol => $x) {
  2107.                     // handle ',id as xid,MAX(id),id' etc.
  2108.                     // FIXXME do this better!!!
  2109.                     $what = preg_replace("/(^|,|\()(\s*)$aCol(\)|\s|,|as|$)/i",
  2110.                         // $2 is actually just to keep the spaces, is not really
  2111.                         // necessary, but this way the test works independent of this functionality here
  2112.                         "$1$2{$this->table}.$aCol$3",
  2113.                         $what
  2114.                     );
  2115.                 }
  2116.             }
  2117.             // replace all 'joinedTable.columnName' by '_joinedTable_columnName'
  2118.             // this actually only has an effect if there was no 'table.*' for 'table'
  2119.             // if that was there, then it has already been done before
  2120.             foreach ($this->getJoin('tables') as $aTable) {
  2121.                 if ($meta = $this->metadata($aTable)) {
  2122.                     foreach ($meta as $aCol => $x) {
  2123.                         // don't put the 'AS' behind it if there is already one
  2124.                         if (preg_match("/$aTable.$aCol\s*as/i", $what)) {
  2125.                             continue;
  2126.                         }
  2127.                         // this covers a ' table.colName ' surrounded by spaces, and replaces it by ' table.colName AS _table_colName'
  2128.                         $what = preg_replace('/\s'.$aTable.'.'.$aCol.'\s/', " $aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol ", $what);
  2129.                         // replace also the column names which are behind a ','
  2130.                         // and do this also if the aCol is at the end that's what the $ is for
  2131.                         $what = preg_replace('/,\s*'.$aTable.'.'.$aCol.'(,|\s|$)/', ",$aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol$1", $what);
  2132.                         // replace if colName is first and possibly also if at the beginning of the where-string
  2133.                         $what = preg_replace('/(^\s*|\s+)'.$aTable.'.'.$aCol.'\s*,/', "$1$aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol,", $what);
  2134.                     }
  2135.                 }
  2136.             }
  2137.         }
  2138.  
  2139.         // quotations of columns
  2140.         $columns    = explode(',', $what);
  2141.         $identifier = substr($this->_quoteIdentifier(''), 0, 1);
  2142.         for ($i=0; $i<sizeof($columns); $i++) {
  2143.             $column = trim($columns[$i]);
  2144.             // Uppercasing "as"
  2145.             $column = str_replace(' as ', ' AS ', $column);
  2146.             if (strpos($column, ' AS ') !== false) {
  2147.                 $column = explode(' AS ', $column);
  2148.                 if ((strpos($column[0], '(') !== false) || (strpos($column[0], ')') !== false)) {
  2149.                     //do not quote function calls, COUNT(), etc.
  2150.                 } elseif (strpos($column[0], '.') !== false) {
  2151.                     $column[0] = explode('.', $column[0]);
  2152.                     $column[0][0] = $this->_quoteIdentifier($column[0][0]);
  2153.                     $column[0][1] = $this->_quoteIdentifier($column[0][1]);
  2154.                     $column[0] = implode('.', $column[0]);
  2155.                 } else {
  2156.                     $column[0] = $this->_quoteIdentifier($column[0]);
  2157.                 }
  2158.                 $column = implode(' AS ', $column);
  2159.             } else {
  2160.                 if ((strpos($column, '(') !== false) || (strpos($column, ')') !== false)) {
  2161.                     //do not quote function calls, COUNT(), etc.
  2162.                 } elseif (strpos($column, '.') !== false) {
  2163.                     $column = explode('.', $column);
  2164.                     $column[0] = $this->_quoteIdentifier($column[0]);
  2165.                     $column[1] = $this->_quoteIdentifier($column[1]);
  2166.                     $column = implode('.', $column);
  2167.                 } else {
  2168.                     $column = $this->_quoteIdentifier($column);
  2169.                 }
  2170.             }
  2171.             /*
  2172.             // Clean up if a function was used in the query
  2173.             if (substr($column, -2) == ')'.$identifier) {
  2174.                 $column = substr($column, 0, -2).$identifier.')';
  2175.                 // Some like spaces in the function
  2176.                 while (strpos($column, ' '.$identifier) !== false) {
  2177.                     $column = str_replace(' '.$identifier, $identifier.' ', $column);
  2178.                 }
  2179.             }
  2180.             */
  2181.             $columns[$i] = $column;
  2182.         }
  2183.         return implode(',', $columns);
  2184.     }
  2185.  
  2186.     // }}}
  2187.     // {{{ _buildWhere()
  2188.  
  2189.     /**
  2190.      * Build WHERE clause
  2191.      *
  2192.      * @param string $where WHERE clause
  2193.      *
  2194.      * @return string $where WHERE clause after processing
  2195.      * @access private
  2196.      */
  2197.     function _buildWhere($where = '')
  2198.     {
  2199.         $where = trim($where);
  2200.         $originalWhere = $this->getWhere();
  2201.         if ($originalWhere) {
  2202.             if (!empty($where)) {
  2203.                 $where = $originalWhere.' AND '.$where;
  2204.             } else {
  2205.                 $where = $originalWhere;
  2206.             }
  2207.         }
  2208.         $where = trim($where);
  2209.  
  2210.         if ($join = $this->getJoin()) {     // is join set?
  2211.             // only those where conditions in the default-join have to be added here
  2212.             // left-join conditions are added behind 'ON', the '_buildJoin()' does that
  2213.             if (isset($join['default']) && count($join['default'])) {
  2214.                 // we have to add this join-where clause here
  2215.                 // since at least in mysql a query like: select * from tableX JOIN tableY ON ...
  2216.                 // doesnt work, may be that's even SQL-standard...
  2217.                 if (!empty($where)) {
  2218.                     $where = implode(' AND ', $join['default']).' AND '.$where;
  2219.                 } else {
  2220.                     $where = implode(' AND ', $join['default']);
  2221.                 }
  2222.             }
  2223.             // replace the _TABLENAME_COLUMNNAME by TABLENAME.COLUMNNAME
  2224.             // since oracle doesnt work with the _TABLENAME_COLUMNNAME which i think is strange
  2225.             // FIXXME i think this should become deprecated since the setWhere should not be used like this: '_table_column' but 'table.column'
  2226.             $regExp = '/_('.implode('|', $this->getJoin('tables')).')_([^\s]+)/';
  2227.             $where = preg_replace($regExp, '$1.$2', $where);
  2228.             // add the table name before any column that has no table prefix
  2229.             // since this might cause "unambigious column" errors
  2230.             if ($meta = $this->metadata()) {
  2231.                 foreach ($meta as $aCol => $x) {
  2232.                     // this covers the LIKE,IN stuff: 'name LIKE "%you%"'  'id IN (2,3,4,5)'
  2233.                     $where = preg_replace('/\s'.$aCol.'\s/', " {$this->table}.$aCol ", $where);
  2234.                     // replace also the column names which are behind a '='
  2235.                     // and do this also if the aCol is at the end of the where clause
  2236.                     // that's what the $ is for
  2237.                     $where = preg_replace('/([=<>])\s*'.$aCol.'(\s|$)/', "$1{$this->table}.$aCol ", $where);
  2238.                     // replace if colName is first and possibly also if at the beginning of the where-string
  2239.                     $where = preg_replace('/(^\s*|\s+)'.$aCol.'\s*([=<>])/', "$1{$this->table}.$aCol$2", $where);
  2240.                 }
  2241.             }
  2242.         }
  2243.         return $where;
  2244.     }
  2245.  
  2246.     // }}}
  2247.     // {{{ _buildOrder()
  2248.  
  2249.     /**
  2250.      * Build the "ORDER BY" clause, replace 'column' by 'table.column'.
  2251.      *
  2252.      * @return string the rendered "ORDER BY" clause
  2253.      * @version 2007/01/10
  2254.      * @author Lorenzo Alberton <l.alberton@quipo.it>
  2255.      * @access private
  2256.      */
  2257.     function _buildOrder()
  2258.     {
  2259.         return $this->_prependTableName($this->getOrder(), $this->table);
  2260.     }
  2261.  
  2262.     // }}}
  2263.     // {{{ _buildGroup()
  2264.  
  2265.     /**
  2266.      * Build the "GROUP BY" clause, replace 'column' by 'table.column'.
  2267.      *
  2268.      * @return string the rendered "GROUP BY" clause
  2269.      * @version 2007/01/10
  2270.      * @author Lorenzo Alberton <l.alberton@quipo.it>
  2271.      * @access private
  2272.      */
  2273.     function _buildGroup()
  2274.     {
  2275.         return $this->_prependTableName($this->getGroup(), $this->table);
  2276.     }
  2277.  
  2278.     // }}}
  2279.     // {{{ _buildHaving()
  2280.  
  2281.     /**
  2282.      * Build the "HAVING" clause, replace 'column' by 'table.column'.
  2283.      *
  2284.      * @return string the rendered "HAVING" clause
  2285.      * @version 2007/01/10
  2286.      * @author Lorenzo Alberton <l.alberton@quipo.it>
  2287.      * @access private
  2288.      */
  2289.     function _buildHaving()
  2290.     {
  2291.         return $this->_prependTableName($this->getHaving(), $this->table);
  2292.     }
  2293.  
  2294.     // }}}
  2295.     // {{{ _buildSelectQuery()
  2296.  
  2297.     /**
  2298.      * Build the "SELECT" query
  2299.      *
  2300.      * @param array   $query               this array contains the elements of the
  2301.      *                                     query, indexed by their key, which are:
  2302.      *                                     'select','from','where', etc.
  2303.      * @param boolean $isCalledViaGetCount whether this method is called via getCount() or not
  2304.      *
  2305.      * @return string $querystring or false on error
  2306.      * @version 2002/07/11
  2307.      * @author Wolfram Kriesing <wk@visionp.de>
  2308.      * @access private
  2309.      */
  2310.     function _buildSelectQuery($query = array(), $isCalledViaGetCount = false)
  2311.     {
  2312.         /*FIXXXME finish this
  2313.         $cacheKey = md5(serialize(????));
  2314.         if (isset($this->_queryCache[$cacheKey])) {
  2315.             $this->_errorLog('using cached query',__LINE__);
  2316.             return $this->_queryCache[$cacheKey];
  2317.         }
  2318.         */
  2319.         $where = isset($query['where']) ? $query['where'] : $this->_buildWhere();
  2320.         if ($where) {
  2321.             $where = 'WHERE '.$where;
  2322.         }
  2323.         $order = isset($query['order']) ? $query['order'] : $this->_buildOrder();
  2324.         if ($order) {
  2325.             $order = 'ORDER BY '.$order;
  2326.         }
  2327.         $group = isset($query['group']) ? $query['group'] : $this->_buildGroup();
  2328.         if ($group) {
  2329.             $group = 'GROUP BY '.$group;
  2330.         }
  2331.         $having = isset($query['having']) ? $query['having'] : $this->_buildHaving();
  2332.         if ($having) {
  2333.             $having = 'HAVING '.$having;
  2334.         }
  2335.         $queryString = sprintf(
  2336.             'SELECT %s FROM %s %s %s %s %s',
  2337.             isset($query['select']) ? $query['select'] : $this->_buildSelect(),
  2338.             isset($query['from']) ? $query['from'] : $this->_buildFrom(),
  2339.             $where,
  2340.             $group,
  2341.             $having,
  2342.             $order
  2343.         );
  2344.         // $query['limit'] has preference!
  2345.         $limit = isset($query['limit']) ? $query['limit'] : $this->_limit;
  2346.         if (!$isCalledViaGetCount && !empty($limit[1])) {
  2347.             // is there a count set?
  2348.             $queryString = $this->db->modifyLimitQuery($queryString, $limit[0], $limit[1]);
  2349.             //echo '<pre>'; var_dump($queryString); echo '</pre>';
  2350.             if (PEAR::isError($queryString)) {
  2351.                 $this->_errorSet('DB_QueryTool::db::modifyLimitQuery failed '.$queryString->getMessage());
  2352.                 $this->_errorLog($queryString->getUserInfo());
  2353.                 return false;
  2354.             }
  2355.         }
  2356.         //        $this->_queryCache[$cacheKey] = $queryString;
  2357.         return $queryString;
  2358.     }
  2359.  
  2360.     // }}}
  2361.     // {{{ _buildUpdateQuery()
  2362.  
  2363.     /**
  2364.      * this simply builds an update query.
  2365.      *
  2366.      * @param array $query the parameter array might contain the following indexes
  2367.      *         'where'     the where clause to be added, i.e.
  2368.      *                     UPDATE table SET x=1 WHERE y=0
  2369.      *                     here the 'where' part simply would be 'y=0'
  2370.      *         'set'       the actual data to be updated
  2371.      *                     in the example above, that would be 'x=1'
  2372.      *
  2373.      * @return string the resulting query
  2374.      * @access private
  2375.      */
  2376.     function _buildUpdateQuery($query = array())
  2377.     {
  2378.         $where = isset($query['where']) ? $query['where'] : $this->_buildWhere();
  2379.         if ($where) {
  2380.             $where = 'WHERE '.$where;
  2381.         }
  2382.  
  2383.         $updateString = sprintf(
  2384.             'UPDATE %s SET %s %s',
  2385.             $this->table,
  2386.             $query['set'],
  2387.             $where
  2388.         );
  2389.         return $updateString;
  2390.     }
  2391.  
  2392.     // }}}
  2393.     // {{{ execute()
  2394.  
  2395.     /**
  2396.      * Execute the query
  2397.      *
  2398.      * @param string $query  query to execute
  2399.      * @param string $method method name
  2400.      *
  2401.      * @return resultSet or false on error
  2402.      * @version 2002/07/11
  2403.      * @author Wolfram Kriesing <wk@visionp.de>
  2404.      * @access public
  2405.      */
  2406.     function execute($query = null, $method = 'getAll')
  2407.     {
  2408.         $this->writeLog();
  2409.         if (is_null($query)) {
  2410.             $query = $this->_buildSelectQuery();
  2411.         }
  2412.         $this->writeLog('query built: '.$query);
  2413.         // FIXXME on ORACLE this doesnt work, since we return joined columns as _TABLE_COLNAME and the _ in front
  2414.         // doesnt work on oracle, add a letter before it!!!
  2415.         $this->_lastQuery = $query;
  2416.  
  2417.         $this->debug($query);
  2418.         $this->writeLog('start query');
  2419.         if (PEAR::isError($res = $this->db->$method($query))) {
  2420.             $this->writeLog('end query (failed)');
  2421.             if ($this->getOption('verbose')) {
  2422.                 $this->_errorSet($res->getMessage());
  2423.             } else {
  2424.                 $this->_errorLog($res->getMessage());
  2425.             }
  2426.             $this->_errorLog($res->getUserInfo(), __LINE__);
  2427.             return false;
  2428.         } else {
  2429.             $this->writeLog('end query');
  2430.         }
  2431.         $res = $this->_makeIndexed($res);
  2432.         return $res;
  2433.     }
  2434.  
  2435.     // }}}
  2436.     // {{{ writeLog()
  2437.  
  2438.     /**
  2439.      * Write events to the logfile.
  2440.      * It does some additional work, like time measuring etc. to
  2441.      * see some additional info
  2442.      *
  2443.      * @param string $text text to log
  2444.      *
  2445.      * @return void
  2446.      * @access public
  2447.      */
  2448.     function writeLog($text = 'START')
  2449.     {
  2450.         //its still really a quicky.... 'refactor' (nice word) that
  2451.         if (!isset($this->options['logfile'])) {
  2452.             return;
  2453.         }
  2454.  
  2455.         include_once 'Log.php';
  2456.         if (!class_exists('Log')) {
  2457.             return;
  2458.         }
  2459.         if (!$this->_logObject) {
  2460.             $this->_logObject =& Log::factory('file', $this->options['logfile']);
  2461.         }
  2462.  
  2463.         if ($text === 'start query' || $text === 'end query') {
  2464.             $bytesSent = $this->db->getAll("SHOW STATUS like 'Bytes_sent'");
  2465.             $bytesSent = $bytesSent[0]['Value'];
  2466.         }
  2467.         if ($text === 'START') {
  2468.             $startTime = split(' ', microtime());
  2469.             $this->_logData['startTime'] = $startTime[1] + $startTime[0];
  2470.         }
  2471.         if ($text === 'start query') {
  2472.             $this->_logData['startBytesSent'] = $bytesSent;
  2473.             $startTime = split(' ', microtime());
  2474.             $this->_logData['startQueryTime'] = $startTime[1] + $startTime[0];
  2475.             return;
  2476.         }
  2477.         if ($text === 'end query') {
  2478.             $text .= ' result size: '.((int)$bytesSent-(int)$this->_logData['startBytesSent']).' bytes';
  2479.             $endTime = split(' ', microtime());
  2480.             $endTime = $endTime[1] + $endTime[0];
  2481.             $text .= ', took: '.(($endTime - $this->_logData['startQueryTime'])).' seconds';
  2482.         }
  2483.         if (strpos($text, 'query built') === 0) {
  2484.             $endTime = split(' ', microtime());
  2485.             $endTime = $endTime[1] + $endTime[0];
  2486.             $this->writeLog('query building took: '.(($endTime - $this->_logData['startTime'])).' seconds');
  2487.         }
  2488.         $this->_logObject->log($text);
  2489.  
  2490.         if (strpos($text, 'end query') === 0) {
  2491.             $endTime = split(' ', microtime());
  2492.             $endTime = $endTime[1] + $endTime[0];
  2493.             $text = 'time over all: '.(($endTime - $this->_logData['startTime'])).' seconds';
  2494.             $this->_logObject->log($text);
  2495.         }
  2496.     }
  2497.  
  2498.     // }}}
  2499.     // {{{ returnResult()
  2500.  
  2501.     /**
  2502.      * Return the chosen result type
  2503.      *
  2504.      * @param object $result object reference
  2505.      *
  2506.      * @return mixed [boolean, array or object]
  2507.      * @version 2004/04/28
  2508.      * @access public
  2509.      */
  2510.     function returnResult($result)
  2511.     {
  2512.         if ($this->_resultType == 'none') {
  2513.             return $result;
  2514.         }
  2515.         if ($result === false) {
  2516.             return false;
  2517.         }
  2518.         //what about allowing other (custom) result types?
  2519.         switch (strtolower($this->_resultType)) {
  2520.         case 'object':
  2521.             return new DB_QueryTool_Result_Object($result);
  2522.         case 'array':
  2523.         default:
  2524.             return new DB_QueryTool_Result($result);
  2525.         }
  2526.     }
  2527.  
  2528.     // }}}
  2529.     // {{{ _makeIndexed()
  2530.  
  2531.     /**
  2532.      * Make the data indexed
  2533.      *
  2534.      * @param mixed &$data data
  2535.      *
  2536.      * @return mixed $data or array $indexedData
  2537.      * @version 2002/07/11
  2538.      * @author Wolfram Kriesing <wk@visionp.de>
  2539.      * @access public
  2540.      */
  2541.     function &_makeIndexed(&$data)
  2542.     {
  2543.         // we can only return an indexed result if the result has a number of columns
  2544.         if (is_array($data) && sizeof($data) && $key = $this->getIndex()) {
  2545.             // build the string to evaluate which might be made up out of multiple indexes of a result-row
  2546.             $evalString = '$val[\''.implode('\'].\',\'.$val[\'', explode(',', $key)).'\']';   //"
  2547.  
  2548.             $indexedData = array();
  2549.             //FIXXME actually we also need to check ONCE if $val is an array,
  2550.             //so to say if $data is 2-dimensional
  2551.             foreach ($data as $val) {
  2552.                 // get the actual real (string-)key (string if multiple cols are used as index)
  2553.                 eval("\$keyValue = $evalString;");  
  2554.                 $indexedData[$keyValue] = $val;
  2555.             }
  2556.             unset($data);
  2557.             return $indexedData;
  2558.         }
  2559.         return $data;
  2560.     }
  2561.  
  2562.     // }}}
  2563.     // {{{ setIndex()
  2564.  
  2565.     /**
  2566.      * format the result to be indexed by $key
  2567.      * NOTE: be careful, when using this you should be aware, that if you
  2568.      * use an index which's value appears multiple times you may loose data
  2569.      * since a key cant exist multiple times!!
  2570.      * the result for a result to be indexed by a key(=columnName)
  2571.      * (i.e. 'relationtoMe') which's values are 'brother' and 'sister'
  2572.      * or alike normally returns this:
  2573.      *     $res['brother'] = array('name'=>'xxx')
  2574.      *     $res['sister'] = array('name'=>'xxx')
  2575.      * but if the column 'relationtoMe' contains multiple entries for 'brother'
  2576.      * then the returned dataset will only contain one brother, since the
  2577.      * value from the column 'relationtoMe' is used
  2578.      * and which 'brother' you get depends on a lot of things, like the sortorder,
  2579.      * how the db saves the data, and whatever else.
  2580.      * You can also set indexes which depend on 2 columns, simply pass the parameters like
  2581.      * 'table1.id,table2.id' it will be used as a string for indexing the result
  2582.      * and the index will be built using the 2 values given, so a possible
  2583.      * index might be '1,2' or '2108,29389' this way you can access data which
  2584.      * have 2 primary keys. Be sure to remember that the index is a string!
  2585.      *
  2586.      * @param string $key index key
  2587.      *
  2588.      * @return void
  2589.      * @version 2002/07/11
  2590.      * @author Wolfram Kriesing <wk@visionp.de>
  2591.      * @access public
  2592.      */
  2593.     function setIndex($key = null)
  2594.     {
  2595.         if ($this->getJoin()) { // is join set?
  2596.             // replace TABLENAME.COLUMNNAME by _TABLENAME_COLUMNNAME
  2597.             // since this is only the result-keys can be used for indexing :-)
  2598.             $regExp = '/('.implode('|', $this->getJoin('tables')).')\.([^\s]+)/';
  2599.             $key = preg_replace($regExp, '_$1_$2', $key);
  2600.  
  2601.             // remove the table name if it is in front of '<$this->table>.columnname'
  2602.             // since the key doesnt contain it neither
  2603.             if ($meta = $this->metadata()) {
  2604.                 foreach ($meta as $aCol => $x) {
  2605.                     $key = preg_replace('/'.$this->table.'\.'.$aCol.'/', $aCol, $key);
  2606.                 }
  2607.             }
  2608.         }
  2609.         $this->_index = $key;
  2610.     }
  2611.  
  2612.     // }}}
  2613.     // {{{ getIndex()
  2614.  
  2615.     /**
  2616.      * Get the index
  2617.      *
  2618.      * @return string index
  2619.      * @version 2002/07/11
  2620.      * @author Wolfram Kriesing <wk@visionp.de>
  2621.      * @access public
  2622.      */
  2623.     function getIndex()
  2624.     {
  2625.         return $this->_index;
  2626.     }
  2627.  
  2628.     // }}}
  2629.     // {{{ useResult()
  2630.  
  2631.     /**
  2632.      * Choose the type of the returned result
  2633.      *
  2634.      * @param string $type ['array' | 'object' | 'none']
  2635.      *                     For BC reasons, $type=true is equal to 'array',
  2636.      *                     $type=false is equal to 'none'
  2637.      *
  2638.      * @return void
  2639.      * @version 2004/04/28
  2640.      * @access public
  2641.      * @access public
  2642.      */
  2643.     function useResult($type = 'array')
  2644.     {
  2645.         if ($type === true) {
  2646.             $type = 'array';
  2647.         } elseif ($type === false) {
  2648.             $type = 'none';
  2649.         }
  2650.         switch (strtolower($type)) {
  2651.         case 'array':
  2652.             $this->_resultType = 'array';
  2653.             include_once 'DB/QueryTool/Result.php';
  2654.             break;
  2655.         case 'object':
  2656.             $this->_resultType = 'object';
  2657.             include_once 'DB/QueryTool/Result/Object.php';
  2658.             break;
  2659.         default:
  2660.             $this->_resultType = 'none';
  2661.         }
  2662.     }
  2663.  
  2664.     // }}}
  2665.     // {{{ setErrorCallback()
  2666.  
  2667.     /**
  2668.      * set both callbacks
  2669.      *
  2670.      * @param string $param callback
  2671.      *
  2672.      * @return void
  2673.      * @access public
  2674.      */
  2675.     function setErrorCallback($param = '')
  2676.     {
  2677.         $this->setErrorLogCallback($param);
  2678.         $this->setErrorSetCallback($param);
  2679.     }
  2680.  
  2681.     // }}}
  2682.     // {{{ setErrorLogCallback()
  2683.  
  2684.     /**
  2685.      * Set the name of the error log callback function
  2686.      *
  2687.      * @param string $param callback
  2688.      *
  2689.      * @return void
  2690.      */
  2691.     function setErrorLogCallback($param = '')
  2692.     {
  2693.         $errorLogCallback = &PEAR::getStaticProperty('DB_QueryTool', '_errorLogCallback');
  2694.         $errorLogCallback = $param;
  2695.     }
  2696.  
  2697.     // }}}
  2698.     // {{{ setErrorSetCallback()
  2699.  
  2700.     /**
  2701.      * Set the name of the error log callback function
  2702.      *
  2703.      * @param string $param callback
  2704.      *
  2705.      * @return void
  2706.      */
  2707.     function setErrorSetCallback($param = '')
  2708.     {
  2709.         $errorSetCallback = &PEAR::getStaticProperty('DB_QueryTool', '_errorSetCallback');
  2710.         $errorSetCallback = $param;
  2711.     }
  2712.  
  2713.     // }}}
  2714.     // {{{ _errorLog()
  2715.  
  2716.     /**
  2717.      * sets error log and adds additional info
  2718.      *
  2719.      * @param string  $msg  the actual message, first word should always be the method name,
  2720.      *                      to build the message like this: className::methodname
  2721.      * @param integer $line the line number
  2722.      *
  2723.      * @return void
  2724.      * @version 2002/04/16
  2725.      * @author Wolfram Kriesing <wk@visionp.de>
  2726.      * @access private
  2727.      */
  2728.     function _errorLog($msg, $line = 'unknown')
  2729.     {
  2730.         $this->_errorHandler('log', $msg, $line);
  2731.         /*
  2732.         if ($this->getOption('verbose') == true) {
  2733.             $this->_errorLog(get_class($this)."::$msg ($line)");
  2734.             return;
  2735.         }
  2736.         if ($this->_errorLogCallback) {
  2737.             call_user_func($this->_errorLogCallback, $msg);
  2738.         }
  2739.         */
  2740.     }
  2741.  
  2742.     // }}}
  2743.     // {{{ _errorSet()
  2744.  
  2745.     /**
  2746.      * Set the error message and line
  2747.      *
  2748.      * @param string $msg  message
  2749.      * @param string $line line number
  2750.      *
  2751.      * @return void
  2752.      */
  2753.     function _errorSet($msg, $line = 'unknown')
  2754.     {
  2755.         $this->_errorHandler('set', $msg, $line);
  2756.     }
  2757.  
  2758.     // }}}
  2759.     // {{{ _errorHandler()
  2760.  
  2761.     /**
  2762.      * Set the error handler
  2763.      *
  2764.      * @param boolean $logOrSet whether to log or set
  2765.      * @param string  $msg      message
  2766.      * @param string  $line     line number
  2767.      *
  2768.      * @return void
  2769.      */
  2770.     function _errorHandler($logOrSet, $msg, $line = 'unknown')
  2771.     {
  2772.         /* what did i do this for?
  2773.         if ($this->getOption('verbose') == true) {
  2774.             $this->_errorHandler($logOrSet, get_class($this)."::$msg ($line)");
  2775.             return;
  2776.         }
  2777.         */
  2778.  
  2779.         $msg = get_class($this)."::$msg ($line)";
  2780.  
  2781.         $logOrSet = ucfirst($logOrSet);
  2782.         $callback = &PEAR::getStaticProperty('DB_QueryTool', '_error'.$logOrSet.'Callback');
  2783.         //var_dump($callback);
  2784.         //if ($callback)
  2785.         //    call_user_func($callback, $msg);
  2786.         //else
  2787.         //    ?????
  2788.     }
  2789.  
  2790.     // }}}
  2791. }
  2792. ?>