source: branches/feature-module-update/data/module/DB/common.php @ 15532

Revision 15532, 67.9 KB checked in by nanasess, 15 years ago (diff)

svn:mime-type 修正

  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
1<?php
2
3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5/**
6 * Contains the DB_common base class
7 *
8 * PHP versions 4 and 5
9 *
10 * LICENSE: This source file is subject to version 3.0 of the PHP license
11 * that is available through the world-wide-web at the following URI:
12 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
13 * the PHP License and are unable to obtain it through the web, please
14 * send a note to license@php.net so we can mail you a copy immediately.
15 *
16 * @category   Database
17 * @package    DB
18 * @author     Stig Bakken <ssb@php.net>
19 * @author     Tomas V.V. Cox <cox@idecnet.com>
20 * @author     Daniel Convissor <danielc@php.net>
21 * @copyright  1997-2005 The PHP Group
22 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
23 * @version    CVS: $Id$
24 * @link       http://pear.php.net/package/DB
25 */
26
27/**
28 * Obtain the PEAR class so it can be extended from
29 */
30require_once DB_PHP_DIR . '/PEAR.php';
31
32/**
33 * DB_common is the base class from which each database driver class extends
34 *
35 * All common methods are declared here.  If a given DBMS driver contains
36 * a particular method, that method will overload the one here.
37 *
38 * @category   Database
39 * @package    DB
40 * @author     Stig Bakken <ssb@php.net>
41 * @author     Tomas V.V. Cox <cox@idecnet.com>
42 * @author     Daniel Convissor <danielc@php.net>
43 * @copyright  1997-2005 The PHP Group
44 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
45 * @version    Release: @package_version@
46 * @link       http://pear.php.net/package/DB
47 */
48class DB_common extends PEAR
49{
50    // {{{ properties
51
52    /**
53     * The current default fetch mode
54     * @var integer
55     */
56    var $fetchmode = DB_FETCHMODE_ORDERED;
57
58    /**
59     * The name of the class into which results should be fetched when
60     * DB_FETCHMODE_OBJECT is in effect
61     *
62     * @var string
63     */
64    var $fetchmode_object_class = 'stdClass';
65
66    /**
67     * Was a connection present when the object was serialized()?
68     * @var bool
69     * @see DB_common::__sleep(), DB_common::__wake()
70     */
71    var $was_connected = null;
72
73    /**
74     * The most recently executed query
75     * @var string
76     */
77    var $last_query = '';
78
79    /**
80     * Run-time configuration options
81     *
82     * The 'optimize' option has been deprecated.  Use the 'portability'
83     * option instead.
84     *
85     * @var array
86     * @see DB_common::setOption()
87     */
88    var $options = array(
89        'result_buffering' => 500,
90        'persistent' => false,
91        'ssl' => false,
92        'debug' => 0,
93        'seqname_format' => '%s_seq',
94        'autofree' => false,
95        'portability' => DB_PORTABILITY_NONE,
96        'optimize' => 'performance',  // Deprecated.  Use 'portability'.
97    );
98
99    /**
100     * The parameters from the most recently executed query
101     * @var array
102     * @since Property available since Release 1.7.0
103     */
104    var $last_parameters = array();
105
106    /**
107     * The elements from each prepared statement
108     * @var array
109     */
110    var $prepare_tokens = array();
111
112    /**
113     * The data types of the various elements in each prepared statement
114     * @var array
115     */
116    var $prepare_types = array();
117
118    /**
119     * The prepared queries
120     * @var array
121     */
122    var $prepared_queries = array();
123
124
125    // }}}
126    // {{{ DB_common
127
128    /**
129     * This constructor calls <kbd>$this->PEAR('DB_Error')</kbd>
130     *
131     * @return void
132     */
133    function DB_common()
134    {
135        $this->PEAR('DB_Error');
136    }
137
138    // }}}
139    // {{{ __sleep()
140
141    /**
142     * Automatically indicates which properties should be saved
143     * when PHP's serialize() function is called
144     *
145     * @return array  the array of properties names that should be saved
146     */
147    function __sleep()
148    {
149        if ($this->connection) {
150            // Don't disconnect(), people use serialize() for many reasons
151            $this->was_connected = true;
152        } else {
153            $this->was_connected = false;
154        }
155        if (isset($this->autocommit)) {
156            return array('autocommit',
157                         'dbsyntax',
158                         'dsn',
159                         'features',
160                         'fetchmode',
161                         'fetchmode_object_class',
162                         'options',
163                         'was_connected',
164                   );
165        } else {
166            return array('dbsyntax',
167                         'dsn',
168                         'features',
169                         'fetchmode',
170                         'fetchmode_object_class',
171                         'options',
172                         'was_connected',
173                   );
174        }
175    }
176
177    // }}}
178    // {{{ __wakeup()
179
180    /**
181     * Automatically reconnects to the database when PHP's unserialize()
182     * function is called
183     *
184     * The reconnection attempt is only performed if the object was connected
185     * at the time PHP's serialize() function was run.
186     *
187     * @return void
188     */
189    function __wakeup()
190    {
191        if ($this->was_connected) {
192            $this->connect($this->dsn, $this->options);
193        }
194    }
195
196    // }}}
197    // {{{ __toString()
198
199    /**
200     * Automatic string conversion for PHP 5
201     *
202     * @return string  a string describing the current PEAR DB object
203     *
204     * @since Method available since Release 1.7.0
205     */
206    function __toString()
207    {
208        $info = strtolower(get_class($this));
209        $info .=  ': (phptype=' . $this->phptype .
210                  ', dbsyntax=' . $this->dbsyntax .
211                  ')';
212        if ($this->connection) {
213            $info .= ' [connected]';
214        }
215        return $info;
216    }
217
218    // }}}
219    // {{{ toString()
220
221    /**
222     * DEPRECATED:  String conversion method
223     *
224     * @return string  a string describing the current PEAR DB object
225     *
226     * @deprecated Method deprecated in Release 1.7.0
227     */
228    function toString()
229    {
230        return $this->__toString();
231    }
232
233    // }}}
234    // {{{ quoteString()
235
236    /**
237     * DEPRECATED: Quotes a string so it can be safely used within string
238     * delimiters in a query
239     *
240     * @param string $string  the string to be quoted
241     *
242     * @return string  the quoted string
243     *
244     * @see DB_common::quoteSmart(), DB_common::escapeSimple()
245     * @deprecated Method deprecated some time before Release 1.2
246     */
247    function quoteString($string)
248    {
249        $string = $this->quote($string);
250        if ($string{0} == "'") {
251            return substr($string, 1, -1);
252        }
253        return $string;
254    }
255
256    // }}}
257    // {{{ quote()
258
259    /**
260     * DEPRECATED: Quotes a string so it can be safely used in a query
261     *
262     * @param string $string  the string to quote
263     *
264     * @return string  the quoted string or the string <samp>NULL</samp>
265     *                  if the value submitted is <kbd>null</kbd>.
266     *
267     * @see DB_common::quoteSmart(), DB_common::escapeSimple()
268     * @deprecated Deprecated in release 1.6.0
269     */
270    function quote($string = null)
271    {
272        return ($string === null) ? 'NULL'
273                                  : "'" . str_replace("'", "''", $string) . "'";
274    }
275
276    // }}}
277    // {{{ quoteIdentifier()
278
279    /**
280     * Quotes a string so it can be safely used as a table or column name
281     *
282     * Delimiting style depends on which database driver is being used.
283     *
284     * NOTE: just because you CAN use delimited identifiers doesn't mean
285     * you SHOULD use them.  In general, they end up causing way more
286     * problems than they solve.
287     *
288     * Portability is broken by using the following characters inside
289     * delimited identifiers:
290     *   + backtick (<kbd>`</kbd>) -- due to MySQL
291     *   + double quote (<kbd>"</kbd>) -- due to Oracle
292     *   + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
293     *
294     * Delimited identifiers are known to generally work correctly under
295     * the following drivers:
296     *   + mssql
297     *   + mysql
298     *   + mysqli
299     *   + oci8
300     *   + odbc(access)
301     *   + odbc(db2)
302     *   + pgsql
303     *   + sqlite
304     *   + sybase (must execute <kbd>set quoted_identifier on</kbd> sometime
305     *     prior to use)
306     *
307     * InterBase doesn't seem to be able to use delimited identifiers
308     * via PHP 4.  They work fine under PHP 5.
309     *
310     * @param string $str  the identifier name to be quoted
311     *
312     * @return string  the quoted identifier
313     *
314     * @since Method available since Release 1.6.0
315     */
316    function quoteIdentifier($str)
317    {
318        return '"' . str_replace('"', '""', $str) . '"';
319    }
320
321    // }}}
322    // {{{ quoteSmart()
323
324    /**
325     * Formats input so it can be safely used in a query
326     *
327     * The output depends on the PHP data type of input and the database
328     * type being used.
329     *
330     * @param mixed $in  the data to be formatted
331     *
332     * @return mixed  the formatted data.  The format depends on the input's
333     *                 PHP type:
334     * <ul>
335     *  <li>
336     *    <kbd>input</kbd> -> <samp>returns</samp>
337     *  </li>
338     *  <li>
339     *    <kbd>null</kbd> -> the string <samp>NULL</samp>
340     *  </li>
341     *  <li>
342     *    <kbd>integer</kbd> or <kbd>double</kbd> -> the unquoted number
343     *  </li>
344     *  <li>
345     *    <kbd>bool</kbd> -> output depends on the driver in use
346     *    Most drivers return integers: <samp>1</samp> if
347     *    <kbd>true</kbd> or <samp>0</samp> if
348     *    <kbd>false</kbd>.
349     *    Some return strings: <samp>TRUE</samp> if
350     *    <kbd>true</kbd> or <samp>FALSE</samp> if
351     *    <kbd>false</kbd>.
352     *    Finally one returns strings: <samp>T</samp> if
353     *    <kbd>true</kbd> or <samp>F</samp> if
354     *    <kbd>false</kbd>. Here is a list of each DBMS,
355     *    the values returned and the suggested column type:
356     *    <ul>
357     *      <li>
358     *        <kbd>dbase</kbd> -> <samp>T/F</samp>
359     *        (<kbd>Logical</kbd>)
360     *      </li>
361     *      <li>
362     *        <kbd>fbase</kbd> -> <samp>TRUE/FALSE</samp>
363     *        (<kbd>BOOLEAN</kbd>)
364     *      </li>
365     *      <li>
366     *        <kbd>ibase</kbd> -> <samp>1/0</samp>
367     *        (<kbd>SMALLINT</kbd>) [1]
368     *      </li>
369     *      <li>
370     *        <kbd>ifx</kbd> -> <samp>1/0</samp>
371     *        (<kbd>SMALLINT</kbd>) [1]
372     *      </li>
373     *      <li>
374     *        <kbd>msql</kbd> -> <samp>1/0</samp>
375     *        (<kbd>INTEGER</kbd>)
376     *      </li>
377     *      <li>
378     *        <kbd>mssql</kbd> -> <samp>1/0</samp>
379     *        (<kbd>BIT</kbd>)
380     *      </li>
381     *      <li>
382     *        <kbd>mysql</kbd> -> <samp>1/0</samp>
383     *        (<kbd>TINYINT(1)</kbd>)
384     *      </li>
385     *      <li>
386     *        <kbd>mysqli</kbd> -> <samp>1/0</samp>
387     *        (<kbd>TINYINT(1)</kbd>)
388     *      </li>
389     *      <li>
390     *        <kbd>oci8</kbd> -> <samp>1/0</samp>
391     *        (<kbd>NUMBER(1)</kbd>)
392     *      </li>
393     *      <li>
394     *        <kbd>odbc</kbd> -> <samp>1/0</samp>
395     *        (<kbd>SMALLINT</kbd>) [1]
396     *      </li>
397     *      <li>
398     *        <kbd>pgsql</kbd> -> <samp>TRUE/FALSE</samp>
399     *        (<kbd>BOOLEAN</kbd>)
400     *      </li>
401     *      <li>
402     *        <kbd>sqlite</kbd> -> <samp>1/0</samp>
403     *        (<kbd>INTEGER</kbd>)
404     *      </li>
405     *      <li>
406     *        <kbd>sybase</kbd> -> <samp>1/0</samp>
407     *        (<kbd>TINYINT(1)</kbd>)
408     *      </li>
409     *    </ul>
410     *    [1] Accommodate the lowest common denominator because not all
411     *    versions of have <kbd>BOOLEAN</kbd>.
412     *  </li>
413     *  <li>
414     *    other (including strings and numeric strings) ->
415     *    the data with single quotes escaped by preceeding
416     *    single quotes, backslashes are escaped by preceeding
417     *    backslashes, then the whole string is encapsulated
418     *    between single quotes
419     *  </li>
420     * </ul>
421     *
422     * @see DB_common::escapeSimple()
423     * @since Method available since Release 1.6.0
424     */
425    function quoteSmart($in)
426    {
427        if (is_int($in) || is_double($in)) {
428            return $in;
429        } elseif (is_bool($in)) {
430            return $in ? 1 : 0;
431        } elseif (is_null($in)) {
432            return 'NULL';
433        } else {
434            return "'" . $this->escapeSimple($in) . "'";
435        }
436    }
437
438    // }}}
439    // {{{ escapeSimple()
440
441    /**
442     * Escapes a string according to the current DBMS's standards
443     *
444     * In SQLite, this makes things safe for inserts/updates, but may
445     * cause problems when performing text comparisons against columns
446     * containing binary data. See the
447     * {@link http://php.net/sqlite_escape_string PHP manual} for more info.
448     *
449     * @param string $str  the string to be escaped
450     *
451     * @return string  the escaped string
452     *
453     * @see DB_common::quoteSmart()
454     * @since Method available since Release 1.6.0
455     */
456    function escapeSimple($str)
457    {
458        return str_replace("'", "''", $str);
459    }
460
461    // }}}
462    // {{{ provides()
463
464    /**
465     * Tells whether the present driver supports a given feature
466     *
467     * @param string $feature  the feature you're curious about
468     *
469     * @return bool  whether this driver supports $feature
470     */
471    function provides($feature)
472    {
473        return $this->features[$feature];
474    }
475
476    // }}}
477    // {{{ setFetchMode()
478
479    /**
480     * Sets the fetch mode that should be used by default for query results
481     *
482     * @param integer $fetchmode    DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC
483     *                               or DB_FETCHMODE_OBJECT
484     * @param string $object_class  the class name of the object to be returned
485     *                               by the fetch methods when the
486     *                               DB_FETCHMODE_OBJECT mode is selected.
487     *                               If no class is specified by default a cast
488     *                               to object from the assoc array row will be
489     *                               done.  There is also the posibility to use
490     *                               and extend the 'DB_row' class.
491     *
492     * @see DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT
493     */
494    function setFetchMode($fetchmode, $object_class = 'stdClass')
495    {
496        switch ($fetchmode) {
497            case DB_FETCHMODE_OBJECT:
498                $this->fetchmode_object_class = $object_class;
499            case DB_FETCHMODE_ORDERED:
500            case DB_FETCHMODE_ASSOC:
501                $this->fetchmode = $fetchmode;
502                break;
503            default:
504                return $this->raiseError('invalid fetchmode mode');
505        }
506    }
507
508    // }}}
509    // {{{ setOption()
510
511    /**
512     * Sets run-time configuration options for PEAR DB
513     *
514     * Options, their data types, default values and description:
515     * <ul>
516     * <li>
517     * <var>autofree</var> <kbd>boolean</kbd> = <samp>false</samp>
518     *      <br />should results be freed automatically when there are no
519     *            more rows?
520     * </li><li>
521     * <var>result_buffering</var> <kbd>integer</kbd> = <samp>500</samp>
522     *      <br />how many rows of the result set should be buffered?
523     *      <br />In mysql: mysql_unbuffered_query() is used instead of
524     *            mysql_query() if this value is 0.  (Release 1.7.0)
525     *      <br />In oci8: this value is passed to ocisetprefetch().
526     *            (Release 1.7.0)
527     * </li><li>
528     * <var>debug</var> <kbd>integer</kbd> = <samp>0</samp>
529     *      <br />debug level
530     * </li><li>
531     * <var>persistent</var> <kbd>boolean</kbd> = <samp>false</samp>
532     *      <br />should the connection be persistent?
533     * </li><li>
534     * <var>portability</var> <kbd>integer</kbd> = <samp>DB_PORTABILITY_NONE</samp>
535     *      <br />portability mode constant (see below)
536     * </li><li>
537     * <var>seqname_format</var> <kbd>string</kbd> = <samp>%s_seq</samp>
538     *      <br />the sprintf() format string used on sequence names.  This
539     *            format is applied to sequence names passed to
540     *            createSequence(), nextID() and dropSequence().
541     * </li><li>
542     * <var>ssl</var> <kbd>boolean</kbd> = <samp>false</samp>
543     *      <br />use ssl to connect?
544     * </li>
545     * </ul>
546     *
547     * -----------------------------------------
548     *
549     * PORTABILITY MODES
550     *
551     * These modes are bitwised, so they can be combined using <kbd>|</kbd>
552     * and removed using <kbd>^</kbd>.  See the examples section below on how
553     * to do this.
554     *
555     * <samp>DB_PORTABILITY_NONE</samp>
556     * turn off all portability features
557     *
558     * This mode gets automatically turned on if the deprecated
559     * <var>optimize</var> option gets set to <samp>performance</samp>.
560     *
561     *
562     * <samp>DB_PORTABILITY_LOWERCASE</samp>
563     * convert names of tables and fields to lower case when using
564     * <kbd>get*()</kbd>, <kbd>fetch*()</kbd> and <kbd>tableInfo()</kbd>
565     *
566     * This mode gets automatically turned on in the following databases
567     * if the deprecated option <var>optimize</var> gets set to
568     * <samp>portability</samp>:
569     * + oci8
570     *
571     *
572     * <samp>DB_PORTABILITY_RTRIM</samp>
573     * right trim the data output by <kbd>get*()</kbd> <kbd>fetch*()</kbd>
574     *
575     *
576     * <samp>DB_PORTABILITY_DELETE_COUNT</samp>
577     * force reporting the number of rows deleted
578     *
579     * Some DBMS's don't count the number of rows deleted when performing
580     * simple <kbd>DELETE FROM tablename</kbd> queries.  This portability
581     * mode tricks such DBMS's into telling the count by adding
582     * <samp>WHERE 1=1</samp> to the end of <kbd>DELETE</kbd> queries.
583     *
584     * This mode gets automatically turned on in the following databases
585     * if the deprecated option <var>optimize</var> gets set to
586     * <samp>portability</samp>:
587     * + fbsql
588     * + mysql
589     * + mysqli
590     * + sqlite
591     *
592     *
593     * <samp>DB_PORTABILITY_NUMROWS</samp>
594     * enable hack that makes <kbd>numRows()</kbd> work in Oracle
595     *
596     * This mode gets automatically turned on in the following databases
597     * if the deprecated option <var>optimize</var> gets set to
598     * <samp>portability</samp>:
599     * + oci8
600     *
601     *
602     * <samp>DB_PORTABILITY_ERRORS</samp>
603     * makes certain error messages in certain drivers compatible
604     * with those from other DBMS's
605     *
606     * + mysql, mysqli:  change unique/primary key constraints
607     *   DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
608     *
609     * + odbc(access):  MS's ODBC driver reports 'no such field' as code
610     *   07001, which means 'too few parameters.'  When this option is on
611     *   that code gets mapped to DB_ERROR_NOSUCHFIELD.
612     *   DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD
613     *
614     * <samp>DB_PORTABILITY_NULL_TO_EMPTY</samp>
615     * convert null values to empty strings in data output by get*() and
616     * fetch*().  Needed because Oracle considers empty strings to be null,
617     * while most other DBMS's know the difference between empty and null.
618     *
619     *
620     * <samp>DB_PORTABILITY_ALL</samp>
621     * turn on all portability features
622     *
623     * -----------------------------------------
624     *
625     * Example 1. Simple setOption() example
626     * <code>
627     * $db->setOption('autofree', true);
628     * </code>
629     *
630     * Example 2. Portability for lowercasing and trimming
631     * <code>
632     * $db->setOption('portability',
633     *                 DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM);
634     * </code>
635     *
636     * Example 3. All portability options except trimming
637     * <code>
638     * $db->setOption('portability',
639     *                 DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM);
640     * </code>
641     *
642     * @param string $option option name
643     * @param mixed  $value value for the option
644     *
645     * @return int  DB_OK on success.  A DB_Error object on failure.
646     *
647     * @see DB_common::$options
648     */
649    function setOption($option, $value)
650    {
651        if (isset($this->options[$option])) {
652            $this->options[$option] = $value;
653
654            /*
655             * Backwards compatibility check for the deprecated 'optimize'
656             * option.  Done here in case settings change after connecting.
657             */
658            if ($option == 'optimize') {
659                if ($value == 'portability') {
660                    switch ($this->phptype) {
661                        case 'oci8':
662                            $this->options['portability'] =
663                                    DB_PORTABILITY_LOWERCASE |
664                                    DB_PORTABILITY_NUMROWS;
665                            break;
666                        case 'fbsql':
667                        case 'mysql':
668                        case 'mysqli':
669                        case 'sqlite':
670                            $this->options['portability'] =
671                                    DB_PORTABILITY_DELETE_COUNT;
672                            break;
673                    }
674                } else {
675                    $this->options['portability'] = DB_PORTABILITY_NONE;
676                }
677            }
678
679            return DB_OK;
680        }
681        return $this->raiseError("unknown option $option");
682    }
683
684    // }}}
685    // {{{ getOption()
686
687    /**
688     * Returns the value of an option
689     *
690     * @param string $option  the option name you're curious about
691     *
692     * @return mixed  the option's value
693     */
694    function getOption($option)
695    {
696        if (isset($this->options[$option])) {
697            return $this->options[$option];
698        }
699        return $this->raiseError("unknown option $option");
700    }
701
702    // }}}
703    // {{{ prepare()
704
705    /**
706     * Prepares a query for multiple execution with execute()
707     *
708     * Creates a query that can be run multiple times.  Each time it is run,
709     * the placeholders, if any, will be replaced by the contents of
710     * execute()'s $data argument.
711     *
712     * Three types of placeholders can be used:
713     *   + <kbd>?</kbd>  scalar value (i.e. strings, integers).  The system
714     *                   will automatically quote and escape the data.
715     *   + <kbd>!</kbd>  value is inserted 'as is'
716     *   + <kbd>&</kbd>  requires a file name.  The file's contents get
717     *                   inserted into the query (i.e. saving binary
718     *                   data in a db)
719     *
720     * Example 1.
721     * <code>
722     * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
723     * $data = array(
724     *     "John's text",
725     *     "'it''s good'",
726     *     'filename.txt'
727     * );
728     * $res = $db->execute($sth, $data);
729     * </code>
730     *
731     * Use backslashes to escape placeholder characters if you don't want
732     * them to be interpreted as placeholders:
733     * <pre>
734     *    "UPDATE foo SET col=? WHERE col='over \& under'"
735     * </pre>
736     *
737     * With some database backends, this is emulated.
738     *
739     * {@internal ibase and oci8 have their own prepare() methods.}}
740     *
741     * @param string $query  the query to be prepared
742     *
743     * @return mixed  DB statement resource on success. A DB_Error object
744     *                 on failure.
745     *
746     * @see DB_common::execute()
747     */
748    function prepare($query)
749    {
750        $tokens   = preg_split('/((?<!\\\)[&?!])/', $query, -1,
751                               PREG_SPLIT_DELIM_CAPTURE);
752        $token     = 0;
753        $types     = array();
754        $newtokens = array();
755
756        foreach ($tokens as $val) {
757            switch ($val) {
758                case '?':
759                    $types[$token++] = DB_PARAM_SCALAR;
760                    break;
761                case '&':
762                    $types[$token++] = DB_PARAM_OPAQUE;
763                    break;
764                case '!':
765                    $types[$token++] = DB_PARAM_MISC;
766                    break;
767                default:
768                    $newtokens[] = preg_replace('/\\\([&?!])/', "\\1", $val);
769            }
770        }
771
772        $this->prepare_tokens[] = &$newtokens;
773        end($this->prepare_tokens);
774
775        $k = key($this->prepare_tokens);
776        $this->prepare_types[$k] = $types;
777        $this->prepared_queries[$k] = implode(' ', $newtokens);
778
779        return $k;
780    }
781
782    // }}}
783    // {{{ autoPrepare()
784
785    /**
786     * Automaticaly generates an insert or update query and pass it to prepare()
787     *
788     * @param string $table         the table name
789     * @param array  $table_fields  the array of field names
790     * @param int    $mode          a type of query to make:
791     *                               DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
792     * @param string $where         for update queries: the WHERE clause to
793     *                               append to the SQL statement.  Don't
794     *                               include the "WHERE" keyword.
795     *
796     * @return resource  the query handle
797     *
798     * @uses DB_common::prepare(), DB_common::buildManipSQL()
799     */
800    function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT,
801                         $where = false)
802    {
803        $query = $this->buildManipSQL($table, $table_fields, $mode, $where);
804        if (DB::isError($query)) {
805            return $query;
806        }
807        return $this->prepare($query);
808    }
809
810    // }}}
811    // {{{ autoExecute()
812
813    /**
814     * Automaticaly generates an insert or update query and call prepare()
815     * and execute() with it
816     *
817     * @param string $table         the table name
818     * @param array  $fields_values the associative array where $key is a
819     *                               field name and $value its value
820     * @param int    $mode          a type of query to make:
821     *                               DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
822     * @param string $where         for update queries: the WHERE clause to
823     *                               append to the SQL statement.  Don't
824     *                               include the "WHERE" keyword.
825     *
826     * @return mixed  a new DB_result object for successful SELECT queries
827     *                 or DB_OK for successul data manipulation queries.
828     *                 A DB_Error object on failure.
829     *
830     * @uses DB_common::autoPrepare(), DB_common::execute()
831     */
832    function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT,
833                         $where = false)
834    {
835        $sth = $this->autoPrepare($table, array_keys($fields_values), $mode,
836                                  $where);
837        if (DB::isError($sth)) {
838            return $sth;
839        }
840        $ret =& $this->execute($sth, array_values($fields_values));
841        $this->freePrepared($sth);
842        return $ret;
843
844    }
845
846    // }}}
847    // {{{ buildManipSQL()
848
849    /**
850     * Produces an SQL query string for autoPrepare()
851     *
852     * Example:
853     * <pre>
854     * buildManipSQL('table_sql', array('field1', 'field2', 'field3'),
855     *               DB_AUTOQUERY_INSERT);
856     * </pre>
857     *
858     * That returns
859     * <samp>
860     * INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)
861     * </samp>
862     *
863     * NOTES:
864     *   - This belongs more to a SQL Builder class, but this is a simple
865     *     facility.
866     *   - Be carefull! If you don't give a $where param with an UPDATE
867     *     query, all the records of the table will be updated!
868     *
869     * @param string $table         the table name
870     * @param array  $table_fields  the array of field names
871     * @param int    $mode          a type of query to make:
872     *                               DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
873     * @param string $where         for update queries: the WHERE clause to
874     *                               append to the SQL statement.  Don't
875     *                               include the "WHERE" keyword.
876     *
877     * @return string  the sql query for autoPrepare()
878     */
879    function buildManipSQL($table, $table_fields, $mode, $where = false)
880    {
881        if (count($table_fields) == 0) {
882            return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
883        }
884        $first = true;
885        switch ($mode) {
886            case DB_AUTOQUERY_INSERT:
887                $values = '';
888                $names = '';
889                foreach ($table_fields as $value) {
890                    if ($first) {
891                        $first = false;
892                    } else {
893                        $names .= ',';
894                        $values .= ',';
895                    }
896                    $names .= $value;
897                    $values .= '?';
898                }
899                return "INSERT INTO $table ($names) VALUES ($values)";
900            case DB_AUTOQUERY_UPDATE:
901                $set = '';
902                foreach ($table_fields as $value) {
903                    if ($first) {
904                        $first = false;
905                    } else {
906                        $set .= ',';
907                    }
908                    $set .= "$value = ?";
909                }
910                $sql = "UPDATE $table SET $set";
911                if ($where) {
912                    $sql .= " WHERE $where";
913                }
914                return $sql;
915            default:
916                return $this->raiseError(DB_ERROR_SYNTAX);
917        }
918    }
919
920    // }}}
921    // {{{ execute()
922
923    /**
924     * Executes a DB statement prepared with prepare()
925     *
926     * Example 1.
927     * <code>
928     * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
929     * $data = array(
930     *     "John's text",
931     *     "'it''s good'",
932     *     'filename.txt'
933     * );
934     * $res =& $db->execute($sth, $data);
935     * </code>
936     *
937     * @param resource $stmt  a DB statement resource returned from prepare()
938     * @param mixed    $data  array, string or numeric data to be used in
939     *                         execution of the statement.  Quantity of items
940     *                         passed must match quantity of placeholders in
941     *                         query:  meaning 1 placeholder for non-array
942     *                         parameters or 1 placeholder per array element.
943     *
944     * @return mixed  a new DB_result object for successful SELECT queries
945     *                 or DB_OK for successul data manipulation queries.
946     *                 A DB_Error object on failure.
947     *
948     * {@internal ibase and oci8 have their own execute() methods.}}
949     *
950     * @see DB_common::prepare()
951     */
952    function &execute($stmt, $data = array())
953    {
954
955        $realquery = $this->executeEmulateQuery($stmt, $data);
956        if (DB::isError($realquery)) {
957            return $realquery;
958        }
959        $result = $this->simpleQuery($realquery);
960
961        if ($result === DB_OK || DB::isError($result)) {
962            return $result;
963        } else {
964            $tmp =& new DB_result($this, $result);
965            return $tmp;
966        }
967    }
968
969    // }}}
970    // {{{ executeEmulateQuery()
971
972    /**
973     * Emulates executing prepared statements if the DBMS not support them
974     *
975     * @param resource $stmt  a DB statement resource returned from execute()
976     * @param mixed    $data  array, string or numeric data to be used in
977     *                         execution of the statement.  Quantity of items
978     *                         passed must match quantity of placeholders in
979     *                         query:  meaning 1 placeholder for non-array
980     *                         parameters or 1 placeholder per array element.
981     *
982     * @return mixed  a string containing the real query run when emulating
983     *                 prepare/execute.  A DB_Error object on failure.
984     *
985     * @access protected
986     * @see DB_common::execute()
987     */
988    function executeEmulateQuery($stmt, $data = array())
989    {
990        $stmt = (int)$stmt;
991        $data = (array)$data;
992        $this->last_parameters = $data;
993   
994        if (count($this->prepare_types[$stmt]) != count($data)) {
995            $this->last_query = $this->prepared_queries[$stmt];
996            return $this->raiseError(DB_ERROR_MISMATCH);
997        }
998
999        $realquery = $this->prepare_tokens[$stmt][0];
1000
1001        $i = 0;
1002        foreach ($data as $value) {
1003            if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) {
1004                if ($value != "") {
1005                    $realquery .= $this->quoteSmart($value);
1006                }else{
1007                    $realquery .= 'NULL';
1008                }
1009            } elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) {
1010                $fp = @fopen($value, 'rb');
1011                if (!$fp) {
1012                    return $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
1013                }
1014                $realquery .= $this->quoteSmart(fread($fp, filesize($value)));
1015                fclose($fp);
1016            } else {
1017                $realquery .= $value;
1018            }
1019
1020            $realquery .= $this->prepare_tokens[$stmt][++$i];
1021        }
1022
1023        return $realquery;
1024    }
1025
1026    // }}}
1027    // {{{ executeMultiple()
1028
1029    /**
1030     * Performs several execute() calls on the same statement handle
1031     *
1032     * $data must be an array indexed numerically
1033     * from 0, one execute call is done for every "row" in the array.
1034     *
1035     * If an error occurs during execute(), executeMultiple() does not
1036     * execute the unfinished rows, but rather returns that error.
1037     *
1038     * @param resource $stmt  query handle from prepare()
1039     * @param array    $data  numeric array containing the
1040     *                         data to insert into the query
1041     *
1042     * @return int  DB_OK on success.  A DB_Error object on failure.
1043     *
1044     * @see DB_common::prepare(), DB_common::execute()
1045     */
1046    function executeMultiple($stmt, $data)
1047    {
1048        foreach ($data as $value) {
1049            $res =& $this->execute($stmt, $value);
1050            if (DB::isError($res)) {
1051                return $res;
1052            }
1053        }
1054        return DB_OK;
1055    }
1056
1057    // }}}
1058    // {{{ freePrepared()
1059
1060    /**
1061     * Frees the internal resources associated with a prepared query
1062     *
1063     * @param resource $stmt           the prepared statement's PHP resource
1064     * @param bool     $free_resource  should the PHP resource be freed too?
1065     *                                  Use false if you need to get data
1066     *                                  from the result set later.
1067     *
1068     * @return bool  TRUE on success, FALSE if $result is invalid
1069     *
1070     * @see DB_common::prepare()
1071     */
1072    function freePrepared($stmt, $free_resource = true)
1073    {
1074        $stmt = (int)$stmt;
1075        if (isset($this->prepare_tokens[$stmt])) {
1076            unset($this->prepare_tokens[$stmt]);
1077            unset($this->prepare_types[$stmt]);
1078            unset($this->prepared_queries[$stmt]);
1079            return true;
1080        }
1081        return false;
1082    }
1083
1084    // }}}
1085    // {{{ modifyQuery()
1086
1087    /**
1088     * Changes a query string for various DBMS specific reasons
1089     *
1090     * It is defined here to ensure all drivers have this method available.
1091     *
1092     * @param string $query  the query string to modify
1093     *
1094     * @return string  the modified query string
1095     *
1096     * @access protected
1097     * @see DB_mysql::modifyQuery(), DB_oci8::modifyQuery(),
1098     *      DB_sqlite::modifyQuery()
1099     */
1100    function modifyQuery($query)
1101    {
1102        return $query;
1103    }
1104
1105    // }}}
1106    // {{{ modifyLimitQuery()
1107
1108    /**
1109     * Adds LIMIT clauses to a query string according to current DBMS standards
1110     *
1111     * It is defined here to assure that all implementations
1112     * have this method defined.
1113     *
1114     * @param string $query   the query to modify
1115     * @param int    $from    the row to start to fetching (0 = the first row)
1116     * @param int    $count   the numbers of rows to fetch
1117     * @param mixed  $params  array, string or numeric data to be used in
1118     *                         execution of the statement.  Quantity of items
1119     *                         passed must match quantity of placeholders in
1120     *                         query:  meaning 1 placeholder for non-array
1121     *                         parameters or 1 placeholder per array element.
1122     *
1123     * @return string  the query string with LIMIT clauses added
1124     *
1125     * @access protected
1126     */
1127    function modifyLimitQuery($query, $from, $count, $params = array())
1128    {
1129        return $query;
1130    }
1131
1132    // }}}
1133    // {{{ query()
1134
1135    /**
1136     * Sends a query to the database server
1137     *
1138     * The query string can be either a normal statement to be sent directly
1139     * to the server OR if <var>$params</var> are passed the query can have
1140     * placeholders and it will be passed through prepare() and execute().
1141     *
1142     * @param string $query   the SQL query or the statement to prepare
1143     * @param mixed  $params  array, string or numeric data to be used in
1144     *                         execution of the statement.  Quantity of items
1145     *                         passed must match quantity of placeholders in
1146     *                         query:  meaning 1 placeholder for non-array
1147     *                         parameters or 1 placeholder per array element.
1148     *
1149     * @return mixed  a new DB_result object for successful SELECT queries
1150     *                 or DB_OK for successul data manipulation queries.
1151     *                 A DB_Error object on failure.
1152     *
1153     * @see DB_result, DB_common::prepare(), DB_common::execute()
1154     */
1155    function &query($query, $params = array())
1156    {
1157        if (sizeof($params) > 0) {
1158            $sth = $this->prepare($query);
1159            if (DB::isError($sth)) {
1160                return $sth;
1161            }
1162            $ret =& $this->execute($sth, $params);
1163            $this->freePrepared($sth, false);
1164            return $ret;
1165        } else {
1166            $this->last_parameters = array();
1167            $result = $this->simpleQuery($query);
1168            if ($result === DB_OK || DB::isError($result)) {
1169                return $result;
1170            } else {
1171                $tmp =& new DB_result($this, $result);
1172                return $tmp;
1173            }
1174        }
1175    }
1176
1177    // }}}
1178    // {{{ limitQuery()
1179
1180    /**
1181     * Generates and executes a LIMIT query
1182     *
1183     * @param string $query   the query
1184     * @param intr   $from    the row to start to fetching (0 = the first row)
1185     * @param int    $count   the numbers of rows to fetch
1186     * @param mixed  $params  array, string or numeric data to be used in
1187     *                         execution of the statement.  Quantity of items
1188     *                         passed must match quantity of placeholders in
1189     *                         query:  meaning 1 placeholder for non-array
1190     *                         parameters or 1 placeholder per array element.
1191     *
1192     * @return mixed  a new DB_result object for successful SELECT queries
1193     *                 or DB_OK for successul data manipulation queries.
1194     *                 A DB_Error object on failure.
1195     */
1196    function &limitQuery($query, $from, $count, $params = array())
1197    {
1198        $query = $this->modifyLimitQuery($query, $from, $count, $params);
1199        if (DB::isError($query)){
1200            return $query;
1201        }
1202        $result =& $this->query($query, $params);
1203        if (is_a($result, 'DB_result')) {
1204            $result->setOption('limit_from', $from);
1205            $result->setOption('limit_count', $count);
1206        }
1207        return $result;
1208    }
1209
1210    // }}}
1211    // {{{ getOne()
1212
1213    /**
1214     * Fetches the first column of the first row from a query result
1215     *
1216     * Takes care of doing the query and freeing the results when finished.
1217     *
1218     * @param string $query   the SQL query
1219     * @param mixed  $params  array, string or numeric data to be used in
1220     *                         execution of the statement.  Quantity of items
1221     *                         passed must match quantity of placeholders in
1222     *                         query:  meaning 1 placeholder for non-array
1223     *                         parameters or 1 placeholder per array element.
1224     *
1225     * @return mixed  the returned value of the query.
1226     *                 A DB_Error object on failure.
1227     */
1228    function &getOne($query, $params = array())
1229    {
1230        $params = (array)$params;
1231        // modifyLimitQuery() would be nice here, but it causes BC issues
1232        if (sizeof($params) > 0) {
1233            $sth = $this->prepare($query);
1234            if (DB::isError($sth)) {
1235                return $sth;
1236            }
1237            $res =& $this->execute($sth, $params);
1238            $this->freePrepared($sth);
1239        } else {
1240            $res =& $this->query($query);
1241        }
1242
1243        if (DB::isError($res)) {
1244            return $res;
1245        }
1246
1247        $err = $res->fetchInto($row, DB_FETCHMODE_ORDERED);
1248        $res->free();
1249
1250        if ($err !== DB_OK) {
1251            return $err;
1252        }
1253
1254        return $row[0];
1255    }
1256
1257    // }}}
1258    // {{{ getRow()
1259
1260    /**
1261     * Fetches the first row of data returned from a query result
1262     *
1263     * Takes care of doing the query and freeing the results when finished.
1264     *
1265     * @param string $query   the SQL query
1266     * @param mixed  $params  array, string or numeric data to be used in
1267     *                         execution of the statement.  Quantity of items
1268     *                         passed must match quantity of placeholders in
1269     *                         query:  meaning 1 placeholder for non-array
1270     *                         parameters or 1 placeholder per array element.
1271     * @param int $fetchmode  the fetch mode to use
1272     *
1273     * @return array  the first row of results as an array.
1274     *                 A DB_Error object on failure.
1275     */
1276    function &getRow($query, $params = array(),
1277                     $fetchmode = DB_FETCHMODE_DEFAULT)
1278    {
1279        // compat check, the params and fetchmode parameters used to
1280        // have the opposite order
1281        if (!is_array($params)) {
1282            if (is_array($fetchmode)) {
1283                if ($params === null) {
1284                    $tmp = DB_FETCHMODE_DEFAULT;
1285                } else {
1286                    $tmp = $params;
1287                }
1288                $params = $fetchmode;
1289                $fetchmode = $tmp;
1290            } elseif ($params !== null) {
1291                $fetchmode = $params;
1292                $params = array();
1293            }
1294        }
1295        // modifyLimitQuery() would be nice here, but it causes BC issues
1296        if (sizeof($params) > 0) {
1297            $sth = $this->prepare($query);
1298            if (DB::isError($sth)) {
1299                return $sth;
1300            }
1301            $res =& $this->execute($sth, $params);
1302            $this->freePrepared($sth);
1303        } else {
1304            $res =& $this->query($query);
1305        }
1306
1307        if (DB::isError($res)) {
1308            return $res;
1309        }
1310
1311        $err = $res->fetchInto($row, $fetchmode);
1312
1313        $res->free();
1314
1315        if ($err !== DB_OK) {
1316            return $err;
1317        }
1318
1319        return $row;
1320    }
1321
1322    // }}}
1323    // {{{ getCol()
1324
1325    /**
1326     * Fetches a single column from a query result and returns it as an
1327     * indexed array
1328     *
1329     * @param string $query   the SQL query
1330     * @param mixed  $col     which column to return (integer [column number,
1331     *                         starting at 0] or string [column name])
1332     * @param mixed  $params  array, string or numeric data to be used in
1333     *                         execution of the statement.  Quantity of items
1334     *                         passed must match quantity of placeholders in
1335     *                         query:  meaning 1 placeholder for non-array
1336     *                         parameters or 1 placeholder per array element.
1337     *
1338     * @return array  the results as an array.  A DB_Error object on failure.
1339     *
1340     * @see DB_common::query()
1341     */
1342    function &getCol($query, $col = 0, $params = array())
1343    {
1344        $params = (array)$params;
1345        if (sizeof($params) > 0) {
1346            $sth = $this->prepare($query);
1347
1348            if (DB::isError($sth)) {
1349                return $sth;
1350            }
1351
1352            $res =& $this->execute($sth, $params);
1353            $this->freePrepared($sth);
1354        } else {
1355            $res =& $this->query($query);
1356        }
1357
1358        if (DB::isError($res)) {
1359            return $res;
1360        }
1361
1362        $fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC;
1363
1364        if (!is_array($row = $res->fetchRow($fetchmode))) {
1365            $ret = array();
1366        } else {
1367            if (!array_key_exists($col, $row)) {
1368                $ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD);
1369            } else {
1370                $ret = array($row[$col]);
1371                while (is_array($row = $res->fetchRow($fetchmode))) {
1372                    $ret[] = $row[$col];
1373                }
1374            }
1375        }
1376
1377        $res->free();
1378
1379        if (DB::isError($row)) {
1380            $ret = $row;
1381        }
1382
1383        return $ret;
1384    }
1385
1386    // }}}
1387    // {{{ getAssoc()
1388
1389    /**
1390     * Fetches an entire query result and returns it as an
1391     * associative array using the first column as the key
1392     *
1393     * If the result set contains more than two columns, the value
1394     * will be an array of the values from column 2-n.  If the result
1395     * set contains only two columns, the returned value will be a
1396     * scalar with the value of the second column (unless forced to an
1397     * array with the $force_array parameter).  A DB error code is
1398     * returned on errors.  If the result set contains fewer than two
1399     * columns, a DB_ERROR_TRUNCATED error is returned.
1400     *
1401     * For example, if the table "mytable" contains:
1402     *
1403     * <pre>
1404     *  ID      TEXT       DATE
1405     * --------------------------------
1406     *  1       'one'      944679408
1407     *  2       'two'      944679408
1408     *  3       'three'    944679408
1409     * </pre>
1410     *
1411     * Then the call getAssoc('SELECT id,text FROM mytable') returns:
1412     * <pre>
1413     *   array(
1414     *     '1' => 'one',
1415     *     '2' => 'two',
1416     *     '3' => 'three',
1417     *   )
1418     * </pre>
1419     *
1420     * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
1421     * <pre>
1422     *   array(
1423     *     '1' => array('one', '944679408'),
1424     *     '2' => array('two', '944679408'),
1425     *     '3' => array('three', '944679408')
1426     *   )
1427     * </pre>
1428     *
1429     * If the more than one row occurs with the same value in the
1430     * first column, the last row overwrites all previous ones by
1431     * default.  Use the $group parameter if you don't want to
1432     * overwrite like this.  Example:
1433     *
1434     * <pre>
1435     * getAssoc('SELECT category,id,name FROM mytable', false, null,
1436     *          DB_FETCHMODE_ASSOC, true) returns:
1437     *
1438     *   array(
1439     *     '1' => array(array('id' => '4', 'name' => 'number four'),
1440     *                  array('id' => '6', 'name' => 'number six')
1441     *            ),
1442     *     '9' => array(array('id' => '4', 'name' => 'number four'),
1443     *                  array('id' => '6', 'name' => 'number six')
1444     *            )
1445     *   )
1446     * </pre>
1447     *
1448     * Keep in mind that database functions in PHP usually return string
1449     * values for results regardless of the database's internal type.
1450     *
1451     * @param string $query        the SQL query
1452     * @param bool   $force_array  used only when the query returns
1453     *                              exactly two columns.  If true, the values
1454     *                              of the returned array will be one-element
1455     *                              arrays instead of scalars.
1456     * @param mixed  $params       array, string or numeric data to be used in
1457     *                              execution of the statement.  Quantity of
1458     *                              items passed must match quantity of
1459     *                              placeholders in query:  meaning 1
1460     *                              placeholder for non-array parameters or
1461     *                              1 placeholder per array element.
1462     * @param int   $fetchmode     the fetch mode to use
1463     * @param bool  $group         if true, the values of the returned array
1464     *                              is wrapped in another array.  If the same
1465     *                              key value (in the first column) repeats
1466     *                              itself, the values will be appended to
1467     *                              this array instead of overwriting the
1468     *                              existing values.
1469     *
1470     * @return array  the associative array containing the query results.
1471     *                A DB_Error object on failure.
1472     */
1473    function &getAssoc($query, $force_array = false, $params = array(),
1474                       $fetchmode = DB_FETCHMODE_DEFAULT, $group = false)
1475    {
1476        $params = (array)$params;
1477        if (sizeof($params) > 0) {
1478            $sth = $this->prepare($query);
1479
1480            if (DB::isError($sth)) {
1481                return $sth;
1482            }
1483
1484            $res =& $this->execute($sth, $params);
1485            $this->freePrepared($sth);
1486        } else {
1487            $res =& $this->query($query);
1488        }
1489
1490        if (DB::isError($res)) {
1491            return $res;
1492        }
1493        if ($fetchmode == DB_FETCHMODE_DEFAULT) {
1494            $fetchmode = $this->fetchmode;
1495        }
1496        $cols = $res->numCols();
1497
1498        if ($cols < 2) {
1499            $tmp =& $this->raiseError(DB_ERROR_TRUNCATED);
1500            return $tmp;
1501        }
1502
1503        $results = array();
1504
1505        if ($cols > 2 || $force_array) {
1506            // return array values
1507            // XXX this part can be optimized
1508            if ($fetchmode == DB_FETCHMODE_ASSOC) {
1509                while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) {
1510                    reset($row);
1511                    $key = current($row);
1512                    unset($row[key($row)]);
1513                    if ($group) {
1514                        $results[$key][] = $row;
1515                    } else {
1516                        $results[$key] = $row;
1517                    }
1518                }
1519            } elseif ($fetchmode == DB_FETCHMODE_OBJECT) {
1520                while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) {
1521                    $arr = get_object_vars($row);
1522                    $key = current($arr);
1523                    if ($group) {
1524                        $results[$key][] = $row;
1525                    } else {
1526                        $results[$key] = $row;
1527                    }
1528                }
1529            } else {
1530                while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
1531                    // we shift away the first element to get
1532                    // indices running from 0 again
1533                    $key = array_shift($row);
1534                    if ($group) {
1535                        $results[$key][] = $row;
1536                    } else {
1537                        $results[$key] = $row;
1538                    }
1539                }
1540            }
1541            if (DB::isError($row)) {
1542                $results = $row;
1543            }
1544        } else {
1545            // return scalar values
1546            while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
1547                if ($group) {
1548                    $results[$row[0]][] = $row[1];
1549                } else {
1550                    $results[$row[0]] = $row[1];
1551                }
1552            }
1553            if (DB::isError($row)) {
1554                $results = $row;
1555            }
1556        }
1557
1558        $res->free();
1559
1560        return $results;
1561    }
1562
1563    // }}}
1564    // {{{ getAll()
1565
1566    /**
1567     * Fetches all of the rows from a query result
1568     *
1569     * @param string $query      the SQL query
1570     * @param mixed  $params     array, string or numeric data to be used in
1571     *                            execution of the statement.  Quantity of
1572     *                            items passed must match quantity of
1573     *                            placeholders in query:  meaning 1
1574     *                            placeholder for non-array parameters or
1575     *                            1 placeholder per array element.
1576     * @param int    $fetchmode  the fetch mode to use:
1577     *                            + DB_FETCHMODE_ORDERED
1578     *                            + DB_FETCHMODE_ASSOC
1579     *                            + DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED
1580     *                            + DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED
1581     *
1582     * @return array  the nested array.  A DB_Error object on failure.
1583     */
1584    function &getAll($query, $params = array(),
1585                     $fetchmode = DB_FETCHMODE_DEFAULT)
1586    {
1587        // compat check, the params and fetchmode parameters used to
1588        // have the opposite order
1589        if (!is_array($params)) {
1590            if (is_array($fetchmode)) {
1591                if ($params === null) {
1592                    $tmp = DB_FETCHMODE_DEFAULT;
1593                } else {
1594                    $tmp = $params;
1595                }
1596                $params = $fetchmode;
1597                $fetchmode = $tmp;
1598            } elseif ($params !== null) {
1599                $fetchmode = $params;
1600                $params = array();
1601            }
1602        }
1603        if (sizeof($params) > 0) {
1604            $sth = $this->prepare($query);
1605
1606            if (DB::isError($sth)) {
1607                return $sth;
1608            }
1609
1610            $res =& $this->execute($sth, $params);
1611            $this->freePrepared($sth);
1612        } else {
1613            $res =& $this->query($query);
1614        }
1615
1616        if ($res === DB_OK || DB::isError($res)) {
1617            return $res;
1618        }
1619
1620        $results = array();
1621        while (DB_OK === $res->fetchInto($row, $fetchmode)) {
1622            if ($fetchmode & DB_FETCHMODE_FLIPPED) {
1623                foreach ($row as $key => $val) {
1624                    $results[$key][] = $val;
1625                }
1626            } else {
1627                $results[] = $row;
1628            }
1629        }
1630
1631        $res->free();
1632
1633        if (DB::isError($row)) {
1634            $tmp =& $this->raiseError($row);
1635            return $tmp;
1636        }
1637        return $results;
1638    }
1639
1640    // }}}
1641    // {{{ autoCommit()
1642
1643    /**
1644     * Enables or disables automatic commits
1645     *
1646     * @param bool $onoff  true turns it on, false turns it off
1647     *
1648     * @return int  DB_OK on success.  A DB_Error object if the driver
1649     *               doesn't support auto-committing transactions.
1650     */
1651    function autoCommit($onoff = false)
1652    {
1653        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1654    }
1655
1656    // }}}
1657    // {{{ commit()
1658
1659    /**
1660     * Commits the current transaction
1661     *
1662     * @return int  DB_OK on success.  A DB_Error object on failure.
1663     */
1664    function commit()
1665    {
1666        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1667    }
1668
1669    // }}}
1670    // {{{ rollback()
1671
1672    /**
1673     * Reverts the current transaction
1674     *
1675     * @return int  DB_OK on success.  A DB_Error object on failure.
1676     */
1677    function rollback()
1678    {
1679        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1680    }
1681
1682    // }}}
1683    // {{{ numRows()
1684
1685    /**
1686     * Determines the number of rows in a query result
1687     *
1688     * @param resource $result  the query result idenifier produced by PHP
1689     *
1690     * @return int  the number of rows.  A DB_Error object on failure.
1691     */
1692    function numRows($result)
1693    {
1694        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1695    }
1696
1697    // }}}
1698    // {{{ affectedRows()
1699
1700    /**
1701     * Determines the number of rows affected by a data maniuplation query
1702     *
1703     * 0 is returned for queries that don't manipulate data.
1704     *
1705     * @return int  the number of rows.  A DB_Error object on failure.
1706     */
1707    function affectedRows()
1708    {
1709        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1710    }
1711
1712    // }}}
1713    // {{{ getSequenceName()
1714
1715    /**
1716     * Generates the name used inside the database for a sequence
1717     *
1718     * The createSequence() docblock contains notes about storing sequence
1719     * names.
1720     *
1721     * @param string $sqn  the sequence's public name
1722     *
1723     * @return string  the sequence's name in the backend
1724     *
1725     * @access protected
1726     * @see DB_common::createSequence(), DB_common::dropSequence(),
1727     *      DB_common::nextID(), DB_common::setOption()
1728     */
1729    function getSequenceName($sqn)
1730    {
1731        return sprintf($this->getOption('seqname_format'),
1732                       preg_replace('/[^a-z0-9_.]/i', '_', $sqn));
1733    }
1734
1735    // }}}
1736    // {{{ nextId()
1737
1738    /**
1739     * Returns the next free id in a sequence
1740     *
1741     * @param string  $seq_name  name of the sequence
1742     * @param boolean $ondemand  when true, the seqence is automatically
1743     *                            created if it does not exist
1744     *
1745     * @return int  the next id number in the sequence.
1746     *               A DB_Error object on failure.
1747     *
1748     * @see DB_common::createSequence(), DB_common::dropSequence(),
1749     *      DB_common::getSequenceName()
1750     */
1751    function nextId($seq_name, $ondemand = true)
1752    {
1753        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1754    }
1755
1756    // }}}
1757    // {{{ createSequence()
1758
1759    /**
1760     * Creates a new sequence
1761     *
1762     * The name of a given sequence is determined by passing the string
1763     * provided in the <var>$seq_name</var> argument through PHP's sprintf()
1764     * function using the value from the <var>seqname_format</var> option as
1765     * the sprintf()'s format argument.
1766     *
1767     * <var>seqname_format</var> is set via setOption().
1768     *
1769     * @param string $seq_name  name of the new sequence
1770     *
1771     * @return int  DB_OK on success.  A DB_Error object on failure.
1772     *
1773     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
1774     *      DB_common::nextID()
1775     */
1776    function createSequence($seq_name)
1777    {
1778        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1779    }
1780
1781    // }}}
1782    // {{{ dropSequence()
1783
1784    /**
1785     * Deletes a sequence
1786     *
1787     * @param string $seq_name  name of the sequence to be deleted
1788     *
1789     * @return int  DB_OK on success.  A DB_Error object on failure.
1790     *
1791     * @see DB_common::createSequence(), DB_common::getSequenceName(),
1792     *      DB_common::nextID()
1793     */
1794    function dropSequence($seq_name)
1795    {
1796        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1797    }
1798
1799    // }}}
1800    // {{{ raiseError()
1801
1802    /**
1803     * Communicates an error and invoke error callbacks, etc
1804     *
1805     * Basically a wrapper for PEAR::raiseError without the message string.
1806     *
1807     * @param mixed   integer error code, or a PEAR error object (all
1808     *                 other parameters are ignored if this parameter is
1809     *                 an object
1810     * @param int     error mode, see PEAR_Error docs
1811     * @param mixed   if error mode is PEAR_ERROR_TRIGGER, this is the
1812     *                 error level (E_USER_NOTICE etc).  If error mode is
1813     *                 PEAR_ERROR_CALLBACK, this is the callback function,
1814     *                 either as a function name, or as an array of an
1815     *                 object and method name.  For other error modes this
1816     *                 parameter is ignored.
1817     * @param string  extra debug information.  Defaults to the last
1818     *                 query and native error code.
1819     * @param mixed   native error code, integer or string depending the
1820     *                 backend
1821     *
1822     * @return object  the PEAR_Error object
1823     *
1824     * @see PEAR_Error
1825     */
1826    function &raiseError($code = DB_ERROR, $mode = null, $options = null,
1827                         $userinfo = null, $nativecode = null)
1828    {
1829        // The error is yet a DB error object
1830        if (is_object($code)) {
1831            // because we the static PEAR::raiseError, our global
1832            // handler should be used if it is set
1833            if ($mode === null && !empty($this->_default_error_mode)) {
1834                $mode    = $this->_default_error_mode;
1835                $options = $this->_default_error_options;
1836            }
1837            $tmp = PEAR::raiseError($code, null, $mode, $options,
1838                                    null, null, true);
1839            return $tmp;
1840        }
1841
1842        if ($userinfo === null) {
1843            $userinfo = $this->last_query;
1844        }
1845
1846        if ($nativecode) {
1847            $userinfo .= ' [nativecode=' . trim($nativecode) . ']';
1848        } else {
1849            $userinfo .= ' [DB Error: ' . DB::errorMessage($code) . ']';
1850        }
1851
1852        $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo,
1853                                'DB_Error', true);
1854        return $tmp;
1855    }
1856
1857    // }}}
1858    // {{{ errorNative()
1859
1860    /**
1861     * Gets the DBMS' native error code produced by the last query
1862     *
1863     * @return mixed  the DBMS' error code.  A DB_Error object on failure.
1864     */
1865    function errorNative()
1866    {
1867        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1868    }
1869
1870    // }}}
1871    // {{{ errorCode()
1872
1873    /**
1874     * Maps native error codes to DB's portable ones
1875     *
1876     * Uses the <var>$errorcode_map</var> property defined in each driver.
1877     *
1878     * @param string|int $nativecode  the error code returned by the DBMS
1879     *
1880     * @return int  the portable DB error code.  Return DB_ERROR if the
1881     *               current driver doesn't have a mapping for the
1882     *               $nativecode submitted.
1883     */
1884    function errorCode($nativecode)
1885    {
1886        if (isset($this->errorcode_map[$nativecode])) {
1887            return $this->errorcode_map[$nativecode];
1888        }
1889        // Fall back to DB_ERROR if there was no mapping.
1890        return DB_ERROR;
1891    }
1892
1893    // }}}
1894    // {{{ errorMessage()
1895
1896    /**
1897     * Maps a DB error code to a textual message
1898     *
1899     * @param integer $dbcode  the DB error code
1900     *
1901     * @return string  the error message corresponding to the error code
1902     *                  submitted.  FALSE if the error code is unknown.
1903     *
1904     * @see DB::errorMessage()
1905     */
1906    function errorMessage($dbcode)
1907    {
1908        return DB::errorMessage($this->errorcode_map[$dbcode]);
1909    }
1910
1911    // }}}
1912    // {{{ tableInfo()
1913
1914    /**
1915     * Returns information about a table or a result set
1916     *
1917     * The format of the resulting array depends on which <var>$mode</var>
1918     * you select.  The sample output below is based on this query:
1919     * <pre>
1920     *    SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
1921     *    FROM tblFoo
1922     *    JOIN tblBar ON tblFoo.fldId = tblBar.fldId
1923     * </pre>
1924     *
1925     * <ul>
1926     * <li>
1927     *
1928     * <kbd>null</kbd> (default)
1929     *   <pre>
1930     *   [0] => Array (
1931     *       [table] => tblFoo
1932     *       [name] => fldId
1933     *       [type] => int
1934     *       [len] => 11
1935     *       [flags] => primary_key not_null
1936     *   )
1937     *   [1] => Array (
1938     *       [table] => tblFoo
1939     *       [name] => fldPhone
1940     *       [type] => string
1941     *       [len] => 20
1942     *       [flags] =>
1943     *   )
1944     *   [2] => Array (
1945     *       [table] => tblBar
1946     *       [name] => fldId
1947     *       [type] => int
1948     *       [len] => 11
1949     *       [flags] => primary_key not_null
1950     *   )
1951     *   </pre>
1952     *
1953     * </li><li>
1954     *
1955     * <kbd>DB_TABLEINFO_ORDER</kbd>
1956     *
1957     *   <p>In addition to the information found in the default output,
1958     *   a notation of the number of columns is provided by the
1959     *   <samp>num_fields</samp> element while the <samp>order</samp>
1960     *   element provides an array with the column names as the keys and
1961     *   their location index number (corresponding to the keys in the
1962     *   the default output) as the values.</p>
1963     *
1964     *   <p>If a result set has identical field names, the last one is
1965     *   used.</p>
1966     *
1967     *   <pre>
1968     *   [num_fields] => 3
1969     *   [order] => Array (
1970     *       [fldId] => 2
1971     *       [fldTrans] => 1
1972     *   )
1973     *   </pre>
1974     *
1975     * </li><li>
1976     *
1977     * <kbd>DB_TABLEINFO_ORDERTABLE</kbd>
1978     *
1979     *   <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> but adds more
1980     *   dimensions to the array in which the table names are keys and
1981     *   the field names are sub-keys.  This is helpful for queries that
1982     *   join tables which have identical field names.</p>
1983     *
1984     *   <pre>
1985     *   [num_fields] => 3
1986     *   [ordertable] => Array (
1987     *       [tblFoo] => Array (
1988     *           [fldId] => 0
1989     *           [fldPhone] => 1
1990     *       )
1991     *       [tblBar] => Array (
1992     *           [fldId] => 2
1993     *       )
1994     *   )
1995     *   </pre>
1996     *
1997     * </li>
1998     * </ul>
1999     *
2000     * The <samp>flags</samp> element contains a space separated list
2001     * of extra information about the field.  This data is inconsistent
2002     * between DBMS's due to the way each DBMS works.
2003     *   + <samp>primary_key</samp>
2004     *   + <samp>unique_key</samp>
2005     *   + <samp>multiple_key</samp>
2006     *   + <samp>not_null</samp>
2007     *
2008     * Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
2009     * elements if <var>$result</var> is a table name.  The following DBMS's
2010     * provide full information from queries:
2011     *   + fbsql
2012     *   + mysql
2013     *
2014     * If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp>
2015     * turned on, the names of tables and fields will be lowercased.
2016     *
2017     * @param object|string  $result  DB_result object from a query or a
2018     *                                string containing the name of a table.
2019     *                                While this also accepts a query result
2020     *                                resource identifier, this behavior is
2021     *                                deprecated.
2022     * @param int  $mode   either unused or one of the tableInfo modes:
2023     *                     <kbd>DB_TABLEINFO_ORDERTABLE</kbd>,
2024     *                     <kbd>DB_TABLEINFO_ORDER</kbd> or
2025     *                     <kbd>DB_TABLEINFO_FULL</kbd> (which does both).
2026     *                     These are bitwise, so the first two can be
2027     *                     combined using <kbd>|</kbd>.
2028     *
2029     * @return array  an associative array with the information requested.
2030     *                 A DB_Error object on failure.
2031     *
2032     * @see DB_common::setOption()
2033     */
2034    function tableInfo($result, $mode = null)
2035    {
2036        /*
2037         * If the DB_<driver> class has a tableInfo() method, that one
2038         * overrides this one.  But, if the driver doesn't have one,
2039         * this method runs and tells users about that fact.
2040         */
2041        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
2042    }
2043
2044    // }}}
2045    // {{{ getTables()
2046
2047    /**
2048     * Lists the tables in the current database
2049     *
2050     * @return array  the list of tables.  A DB_Error object on failure.
2051     *
2052     * @deprecated Method deprecated some time before Release 1.2
2053     */
2054    function getTables()
2055    {
2056        return $this->getListOf('tables');
2057    }
2058
2059    // }}}
2060    // {{{ getListOf()
2061
2062    /**
2063     * Lists internal database information
2064     *
2065     * @param string $type  type of information being sought.
2066     *                       Common items being sought are:
2067     *                       tables, databases, users, views, functions
2068     *                       Each DBMS's has its own capabilities.
2069     *
2070     * @return array  an array listing the items sought.
2071     *                 A DB DB_Error object on failure.
2072     */
2073    function getListOf($type)
2074    {
2075        $sql = $this->getSpecialQuery($type);
2076        if ($sql === null) {
2077            $this->last_query = '';
2078            return $this->raiseError(DB_ERROR_UNSUPPORTED);
2079        } elseif (is_int($sql) || DB::isError($sql)) {
2080            // Previous error
2081            return $this->raiseError($sql);
2082        } elseif (is_array($sql)) {
2083            // Already the result
2084            return $sql;
2085        }
2086        // Launch this query
2087        return $this->getCol($sql);
2088    }
2089
2090    // }}}
2091    // {{{ getSpecialQuery()
2092
2093    /**
2094     * Obtains the query string needed for listing a given type of objects
2095     *
2096     * @param string $type  the kind of objects you want to retrieve
2097     *
2098     * @return string  the SQL query string or null if the driver doesn't
2099     *                  support the object type requested
2100     *
2101     * @access protected
2102     * @see DB_common::getListOf()
2103     */
2104    function getSpecialQuery($type)
2105    {
2106        return $this->raiseError(DB_ERROR_UNSUPPORTED);
2107    }
2108
2109    // }}}
2110    // {{{ _rtrimArrayValues()
2111
2112    /**
2113     * Right-trims all strings in an array
2114     *
2115     * @param array $array  the array to be trimmed (passed by reference)
2116     *
2117     * @return void
2118     *
2119     * @access protected
2120     */
2121    function _rtrimArrayValues(&$array)
2122    {
2123        foreach ($array as $key => $value) {
2124            if (is_string($value)) {
2125                $array[$key] = rtrim($value);
2126            }
2127        }
2128    }
2129
2130    // }}}
2131    // {{{ _convertNullArrayValuesToEmpty()
2132
2133    /**
2134     * Converts all null values in an array to empty strings
2135     *
2136     * @param array  $array  the array to be de-nullified (passed by reference)
2137     *
2138     * @return void
2139     *
2140     * @access protected
2141     */
2142    function _convertNullArrayValuesToEmpty(&$array)
2143    {
2144        foreach ($array as $key => $value) {
2145            if (is_null($value)) {
2146                $array[$key] = '';
2147            }
2148        }
2149    }
2150
2151    // }}}
2152}
2153
2154/*
2155 * Local variables:
2156 * tab-width: 4
2157 * c-basic-offset: 4
2158 * End:
2159 */
2160
2161?>
Note: See TracBrowser for help on using the repository browser.