source: temp/trunk/data/module/DB/common.php @ 10229

Revision 10229, 67.9 KB checked in by kaki, 20 years ago (diff)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
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 [email protected] so we can mail you a copy immediately.
15 *
16 * @category   Database
17 * @package    DB
18 * @author     Stig Bakken <[email protected]>
19 * @author     Tomas V.V. Cox <[email protected]>
20 * @author     Daniel Convissor <[email protected]>
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 <[email protected]>
41 * @author     Tomas V.V. Cox <[email protected]>
42 * @author     Daniel Convissor <[email protected]>
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        $realquery = $this->executeEmulateQuery($stmt, $data);
955        if (DB::isError($realquery)) {
956            return $realquery;
957        }
958        $result = $this->simpleQuery($realquery);
959
960        if ($result === DB_OK || DB::isError($result)) {
961            return $result;
962        } else {
963            $tmp =& new DB_result($this, $result);
964            return $tmp;
965        }
966    }
967
968    // }}}
969    // {{{ executeEmulateQuery()
970
971    /**
972     * Emulates executing prepared statements if the DBMS not support them
973     *
974     * @param resource $stmt  a DB statement resource returned from execute()
975     * @param mixed    $data  array, string or numeric data to be used in
976     *                         execution of the statement.  Quantity of items
977     *                         passed must match quantity of placeholders in
978     *                         query:  meaning 1 placeholder for non-array
979     *                         parameters or 1 placeholder per array element.
980     *
981     * @return mixed  a string containing the real query run when emulating
982     *                 prepare/execute.  A DB_Error object on failure.
983     *
984     * @access protected
985     * @see DB_common::execute()
986     */
987    function executeEmulateQuery($stmt, $data = array())
988    {
989        $stmt = (int)$stmt;
990        $data = (array)$data;
991        $this->last_parameters = $data;
992
993        sfprintr($stmt);
994       
995        if (count($this->prepare_types[$stmt]) != count($data)) {
996            $this->last_query = $this->prepared_queries[$stmt];
997            return $this->raiseError(DB_ERROR_MISMATCH);
998        }
999
1000        $realquery = $this->prepare_tokens[$stmt][0];
1001
1002        $i = 0;
1003        foreach ($data as $value) {
1004            if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) {
1005                if ($value != "") {
1006                    $realquery .= $this->quoteSmart($value);
1007                }else{
1008                    $realquery .= 'NULL';
1009                }
1010            } elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) {
1011                $fp = @fopen($value, 'rb');
1012                if (!$fp) {
1013                    return $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
1014                }
1015                $realquery .= $this->quoteSmart(fread($fp, filesize($value)));
1016                fclose($fp);
1017            } else {
1018                $realquery .= $value;
1019            }
1020
1021            $realquery .= $this->prepare_tokens[$stmt][++$i];
1022        }
1023
1024        return $realquery;
1025    }
1026
1027    // }}}
1028    // {{{ executeMultiple()
1029
1030    /**
1031     * Performs several execute() calls on the same statement handle
1032     *
1033     * $data must be an array indexed numerically
1034     * from 0, one execute call is done for every "row" in the array.
1035     *
1036     * If an error occurs during execute(), executeMultiple() does not
1037     * execute the unfinished rows, but rather returns that error.
1038     *
1039     * @param resource $stmt  query handle from prepare()
1040     * @param array    $data  numeric array containing the
1041     *                         data to insert into the query
1042     *
1043     * @return int  DB_OK on success.  A DB_Error object on failure.
1044     *
1045     * @see DB_common::prepare(), DB_common::execute()
1046     */
1047    function executeMultiple($stmt, $data)
1048    {
1049        foreach ($data as $value) {
1050            $res =& $this->execute($stmt, $value);
1051            if (DB::isError($res)) {
1052                return $res;
1053            }
1054        }
1055        return DB_OK;
1056    }
1057
1058    // }}}
1059    // {{{ freePrepared()
1060
1061    /**
1062     * Frees the internal resources associated with a prepared query
1063     *
1064     * @param resource $stmt           the prepared statement's PHP resource
1065     * @param bool     $free_resource  should the PHP resource be freed too?
1066     *                                  Use false if you need to get data
1067     *                                  from the result set later.
1068     *
1069     * @return bool  TRUE on success, FALSE if $result is invalid
1070     *
1071     * @see DB_common::prepare()
1072     */
1073    function freePrepared($stmt, $free_resource = true)
1074    {
1075        $stmt = (int)$stmt;
1076        if (isset($this->prepare_tokens[$stmt])) {
1077            unset($this->prepare_tokens[$stmt]);
1078            unset($this->prepare_types[$stmt]);
1079            unset($this->prepared_queries[$stmt]);
1080            return true;
1081        }
1082        return false;
1083    }
1084
1085    // }}}
1086    // {{{ modifyQuery()
1087
1088    /**
1089     * Changes a query string for various DBMS specific reasons
1090     *
1091     * It is defined here to ensure all drivers have this method available.
1092     *
1093     * @param string $query  the query string to modify
1094     *
1095     * @return string  the modified query string
1096     *
1097     * @access protected
1098     * @see DB_mysql::modifyQuery(), DB_oci8::modifyQuery(),
1099     *      DB_sqlite::modifyQuery()
1100     */
1101    function modifyQuery($query)
1102    {
1103        return $query;
1104    }
1105
1106    // }}}
1107    // {{{ modifyLimitQuery()
1108
1109    /**
1110     * Adds LIMIT clauses to a query string according to current DBMS standards
1111     *
1112     * It is defined here to assure that all implementations
1113     * have this method defined.
1114     *
1115     * @param string $query   the query to modify
1116     * @param int    $from    the row to start to fetching (0 = the first row)
1117     * @param int    $count   the numbers of rows to fetch
1118     * @param mixed  $params  array, string or numeric data to be used in
1119     *                         execution of the statement.  Quantity of items
1120     *                         passed must match quantity of placeholders in
1121     *                         query:  meaning 1 placeholder for non-array
1122     *                         parameters or 1 placeholder per array element.
1123     *
1124     * @return string  the query string with LIMIT clauses added
1125     *
1126     * @access protected
1127     */
1128    function modifyLimitQuery($query, $from, $count, $params = array())
1129    {
1130        return $query;
1131    }
1132
1133    // }}}
1134    // {{{ query()
1135
1136    /**
1137     * Sends a query to the database server
1138     *
1139     * The query string can be either a normal statement to be sent directly
1140     * to the server OR if <var>$params</var> are passed the query can have
1141     * placeholders and it will be passed through prepare() and execute().
1142     *
1143     * @param string $query   the SQL query or the statement to prepare
1144     * @param mixed  $params  array, string or numeric data to be used in
1145     *                         execution of the statement.  Quantity of items
1146     *                         passed must match quantity of placeholders in
1147     *                         query:  meaning 1 placeholder for non-array
1148     *                         parameters or 1 placeholder per array element.
1149     *
1150     * @return mixed  a new DB_result object for successful SELECT queries
1151     *                 or DB_OK for successul data manipulation queries.
1152     *                 A DB_Error object on failure.
1153     *
1154     * @see DB_result, DB_common::prepare(), DB_common::execute()
1155     */
1156    function &query($query, $params = array())
1157    {
1158        if (sizeof($params) > 0) {
1159            $sth = $this->prepare($query);
1160            if (DB::isError($sth)) {
1161                return $sth;
1162            }
1163            $ret =& $this->execute($sth, $params);
1164            $this->freePrepared($sth, false);
1165            return $ret;
1166        } else {
1167            $this->last_parameters = array();
1168            $result = $this->simpleQuery($query);
1169            if ($result === DB_OK || DB::isError($result)) {
1170                return $result;
1171            } else {
1172                $tmp =& new DB_result($this, $result);
1173                return $tmp;
1174            }
1175        }
1176    }
1177
1178    // }}}
1179    // {{{ limitQuery()
1180
1181    /**
1182     * Generates and executes a LIMIT query
1183     *
1184     * @param string $query   the query
1185     * @param intr   $from    the row to start to fetching (0 = the first row)
1186     * @param int    $count   the numbers of rows to fetch
1187     * @param mixed  $params  array, string or numeric data to be used in
1188     *                         execution of the statement.  Quantity of items
1189     *                         passed must match quantity of placeholders in
1190     *                         query:  meaning 1 placeholder for non-array
1191     *                         parameters or 1 placeholder per array element.
1192     *
1193     * @return mixed  a new DB_result object for successful SELECT queries
1194     *                 or DB_OK for successul data manipulation queries.
1195     *                 A DB_Error object on failure.
1196     */
1197    function &limitQuery($query, $from, $count, $params = array())
1198    {
1199        $query = $this->modifyLimitQuery($query, $from, $count, $params);
1200        if (DB::isError($query)){
1201            return $query;
1202        }
1203        $result =& $this->query($query, $params);
1204        if (is_a($result, 'DB_result')) {
1205            $result->setOption('limit_from', $from);
1206            $result->setOption('limit_count', $count);
1207        }
1208        return $result;
1209    }
1210
1211    // }}}
1212    // {{{ getOne()
1213
1214    /**
1215     * Fetches the first column of the first row from a query result
1216     *
1217     * Takes care of doing the query and freeing the results when finished.
1218     *
1219     * @param string $query   the SQL query
1220     * @param mixed  $params  array, string or numeric data to be used in
1221     *                         execution of the statement.  Quantity of items
1222     *                         passed must match quantity of placeholders in
1223     *                         query:  meaning 1 placeholder for non-array
1224     *                         parameters or 1 placeholder per array element.
1225     *
1226     * @return mixed  the returned value of the query.
1227     *                 A DB_Error object on failure.
1228     */
1229    function &getOne($query, $params = array())
1230    {
1231        $params = (array)$params;
1232        // modifyLimitQuery() would be nice here, but it causes BC issues
1233        if (sizeof($params) > 0) {
1234            $sth = $this->prepare($query);
1235            if (DB::isError($sth)) {
1236                return $sth;
1237            }
1238            $res =& $this->execute($sth, $params);
1239            $this->freePrepared($sth);
1240        } else {
1241            $res =& $this->query($query);
1242        }
1243
1244        if (DB::isError($res)) {
1245            return $res;
1246        }
1247
1248        $err = $res->fetchInto($row, DB_FETCHMODE_ORDERED);
1249        $res->free();
1250
1251        if ($err !== DB_OK) {
1252            return $err;
1253        }
1254
1255        return $row[0];
1256    }
1257
1258    // }}}
1259    // {{{ getRow()
1260
1261    /**
1262     * Fetches the first row of data returned from a query result
1263     *
1264     * Takes care of doing the query and freeing the results when finished.
1265     *
1266     * @param string $query   the SQL query
1267     * @param mixed  $params  array, string or numeric data to be used in
1268     *                         execution of the statement.  Quantity of items
1269     *                         passed must match quantity of placeholders in
1270     *                         query:  meaning 1 placeholder for non-array
1271     *                         parameters or 1 placeholder per array element.
1272     * @param int $fetchmode  the fetch mode to use
1273     *
1274     * @return array  the first row of results as an array.
1275     *                 A DB_Error object on failure.
1276     */
1277    function &getRow($query, $params = array(),
1278                     $fetchmode = DB_FETCHMODE_DEFAULT)
1279    {
1280        // compat check, the params and fetchmode parameters used to
1281        // have the opposite order
1282        if (!is_array($params)) {
1283            if (is_array($fetchmode)) {
1284                if ($params === null) {
1285                    $tmp = DB_FETCHMODE_DEFAULT;
1286                } else {
1287                    $tmp = $params;
1288                }
1289                $params = $fetchmode;
1290                $fetchmode = $tmp;
1291            } elseif ($params !== null) {
1292                $fetchmode = $params;
1293                $params = array();
1294            }
1295        }
1296        // modifyLimitQuery() would be nice here, but it causes BC issues
1297        if (sizeof($params) > 0) {
1298            $sth = $this->prepare($query);
1299            if (DB::isError($sth)) {
1300                return $sth;
1301            }
1302            $res =& $this->execute($sth, $params);
1303            $this->freePrepared($sth);
1304        } else {
1305            $res =& $this->query($query);
1306        }
1307
1308        if (DB::isError($res)) {
1309            return $res;
1310        }
1311
1312        $err = $res->fetchInto($row, $fetchmode);
1313
1314        $res->free();
1315
1316        if ($err !== DB_OK) {
1317            return $err;
1318        }
1319
1320        return $row;
1321    }
1322
1323    // }}}
1324    // {{{ getCol()
1325
1326    /**
1327     * Fetches a single column from a query result and returns it as an
1328     * indexed array
1329     *
1330     * @param string $query   the SQL query
1331     * @param mixed  $col     which column to return (integer [column number,
1332     *                         starting at 0] or string [column name])
1333     * @param mixed  $params  array, string or numeric data to be used in
1334     *                         execution of the statement.  Quantity of items
1335     *                         passed must match quantity of placeholders in
1336     *                         query:  meaning 1 placeholder for non-array
1337     *                         parameters or 1 placeholder per array element.
1338     *
1339     * @return array  the results as an array.  A DB_Error object on failure.
1340     *
1341     * @see DB_common::query()
1342     */
1343    function &getCol($query, $col = 0, $params = array())
1344    {
1345        $params = (array)$params;
1346        if (sizeof($params) > 0) {
1347            $sth = $this->prepare($query);
1348
1349            if (DB::isError($sth)) {
1350                return $sth;
1351            }
1352
1353            $res =& $this->execute($sth, $params);
1354            $this->freePrepared($sth);
1355        } else {
1356            $res =& $this->query($query);
1357        }
1358
1359        if (DB::isError($res)) {
1360            return $res;
1361        }
1362
1363        $fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC;
1364
1365        if (!is_array($row = $res->fetchRow($fetchmode))) {
1366            $ret = array();
1367        } else {
1368            if (!array_key_exists($col, $row)) {
1369                $ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD);
1370            } else {
1371                $ret = array($row[$col]);
1372                while (is_array($row = $res->fetchRow($fetchmode))) {
1373                    $ret[] = $row[$col];
1374                }
1375            }
1376        }
1377
1378        $res->free();
1379
1380        if (DB::isError($row)) {
1381            $ret = $row;
1382        }
1383
1384        return $ret;
1385    }
1386
1387    // }}}
1388    // {{{ getAssoc()
1389
1390    /**
1391     * Fetches an entire query result and returns it as an
1392     * associative array using the first column as the key
1393     *
1394     * If the result set contains more than two columns, the value
1395     * will be an array of the values from column 2-n.  If the result
1396     * set contains only two columns, the returned value will be a
1397     * scalar with the value of the second column (unless forced to an
1398     * array with the $force_array parameter).  A DB error code is
1399     * returned on errors.  If the result set contains fewer than two
1400     * columns, a DB_ERROR_TRUNCATED error is returned.
1401     *
1402     * For example, if the table "mytable" contains:
1403     *
1404     * <pre>
1405     *  ID      TEXT       DATE
1406     * --------------------------------
1407     *  1       'one'      944679408
1408     *  2       'two'      944679408
1409     *  3       'three'    944679408
1410     * </pre>
1411     *
1412     * Then the call getAssoc('SELECT id,text FROM mytable') returns:
1413     * <pre>
1414     *   array(
1415     *     '1' => 'one',
1416     *     '2' => 'two',
1417     *     '3' => 'three',
1418     *   )
1419     * </pre>
1420     *
1421     * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
1422     * <pre>
1423     *   array(
1424     *     '1' => array('one', '944679408'),
1425     *     '2' => array('two', '944679408'),
1426     *     '3' => array('three', '944679408')
1427     *   )
1428     * </pre>
1429     *
1430     * If the more than one row occurs with the same value in the
1431     * first column, the last row overwrites all previous ones by
1432     * default.  Use the $group parameter if you don't want to
1433     * overwrite like this.  Example:
1434     *
1435     * <pre>
1436     * getAssoc('SELECT category,id,name FROM mytable', false, null,
1437     *          DB_FETCHMODE_ASSOC, true) returns:
1438     *
1439     *   array(
1440     *     '1' => array(array('id' => '4', 'name' => 'number four'),
1441     *                  array('id' => '6', 'name' => 'number six')
1442     *            ),
1443     *     '9' => array(array('id' => '4', 'name' => 'number four'),
1444     *                  array('id' => '6', 'name' => 'number six')
1445     *            )
1446     *   )
1447     * </pre>
1448     *
1449     * Keep in mind that database functions in PHP usually return string
1450     * values for results regardless of the database's internal type.
1451     *
1452     * @param string $query        the SQL query
1453     * @param bool   $force_array  used only when the query returns
1454     *                              exactly two columns.  If true, the values
1455     *                              of the returned array will be one-element
1456     *                              arrays instead of scalars.
1457     * @param mixed  $params       array, string or numeric data to be used in
1458     *                              execution of the statement.  Quantity of
1459     *                              items passed must match quantity of
1460     *                              placeholders in query:  meaning 1
1461     *                              placeholder for non-array parameters or
1462     *                              1 placeholder per array element.
1463     * @param int   $fetchmode     the fetch mode to use
1464     * @param bool  $group         if true, the values of the returned array
1465     *                              is wrapped in another array.  If the same
1466     *                              key value (in the first column) repeats
1467     *                              itself, the values will be appended to
1468     *                              this array instead of overwriting the
1469     *                              existing values.
1470     *
1471     * @return array  the associative array containing the query results.
1472     *                A DB_Error object on failure.
1473     */
1474    function &getAssoc($query, $force_array = false, $params = array(),
1475                       $fetchmode = DB_FETCHMODE_DEFAULT, $group = false)
1476    {
1477        $params = (array)$params;
1478        if (sizeof($params) > 0) {
1479            $sth = $this->prepare($query);
1480
1481            if (DB::isError($sth)) {
1482                return $sth;
1483            }
1484
1485            $res =& $this->execute($sth, $params);
1486            $this->freePrepared($sth);
1487        } else {
1488            $res =& $this->query($query);
1489        }
1490
1491        if (DB::isError($res)) {
1492            return $res;
1493        }
1494        if ($fetchmode == DB_FETCHMODE_DEFAULT) {
1495            $fetchmode = $this->fetchmode;
1496        }
1497        $cols = $res->numCols();
1498
1499        if ($cols < 2) {
1500            $tmp =& $this->raiseError(DB_ERROR_TRUNCATED);
1501            return $tmp;
1502        }
1503
1504        $results = array();
1505
1506        if ($cols > 2 || $force_array) {
1507            // return array values
1508            // XXX this part can be optimized
1509            if ($fetchmode == DB_FETCHMODE_ASSOC) {
1510                while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) {
1511                    reset($row);
1512                    $key = current($row);
1513                    unset($row[key($row)]);
1514                    if ($group) {
1515                        $results[$key][] = $row;
1516                    } else {
1517                        $results[$key] = $row;
1518                    }
1519                }
1520            } elseif ($fetchmode == DB_FETCHMODE_OBJECT) {
1521                while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) {
1522                    $arr = get_object_vars($row);
1523                    $key = current($arr);
1524                    if ($group) {
1525                        $results[$key][] = $row;
1526                    } else {
1527                        $results[$key] = $row;
1528                    }
1529                }
1530            } else {
1531                while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
1532                    // we shift away the first element to get
1533                    // indices running from 0 again
1534                    $key = array_shift($row);
1535                    if ($group) {
1536                        $results[$key][] = $row;
1537                    } else {
1538                        $results[$key] = $row;
1539                    }
1540                }
1541            }
1542            if (DB::isError($row)) {
1543                $results = $row;
1544            }
1545        } else {
1546            // return scalar values
1547            while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
1548                if ($group) {
1549                    $results[$row[0]][] = $row[1];
1550                } else {
1551                    $results[$row[0]] = $row[1];
1552                }
1553            }
1554            if (DB::isError($row)) {
1555                $results = $row;
1556            }
1557        }
1558
1559        $res->free();
1560
1561        return $results;
1562    }
1563
1564    // }}}
1565    // {{{ getAll()
1566
1567    /**
1568     * Fetches all of the rows from a query result
1569     *
1570     * @param string $query      the SQL query
1571     * @param mixed  $params     array, string or numeric data to be used in
1572     *                            execution of the statement.  Quantity of
1573     *                            items passed must match quantity of
1574     *                            placeholders in query:  meaning 1
1575     *                            placeholder for non-array parameters or
1576     *                            1 placeholder per array element.
1577     * @param int    $fetchmode  the fetch mode to use:
1578     *                            + DB_FETCHMODE_ORDERED
1579     *                            + DB_FETCHMODE_ASSOC
1580     *                            + DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED
1581     *                            + DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED
1582     *
1583     * @return array  the nested array.  A DB_Error object on failure.
1584     */
1585    function &getAll($query, $params = array(),
1586                     $fetchmode = DB_FETCHMODE_DEFAULT)
1587    {
1588        // compat check, the params and fetchmode parameters used to
1589        // have the opposite order
1590        if (!is_array($params)) {
1591            if (is_array($fetchmode)) {
1592                if ($params === null) {
1593                    $tmp = DB_FETCHMODE_DEFAULT;
1594                } else {
1595                    $tmp = $params;
1596                }
1597                $params = $fetchmode;
1598                $fetchmode = $tmp;
1599            } elseif ($params !== null) {
1600                $fetchmode = $params;
1601                $params = array();
1602            }
1603        }
1604
1605        if (sizeof($params) > 0) {
1606            $sth = $this->prepare($query);
1607
1608            if (DB::isError($sth)) {
1609                return $sth;
1610            }
1611
1612            $res =& $this->execute($sth, $params);
1613            $this->freePrepared($sth);
1614        } else {
1615            $res =& $this->query($query);
1616        }
1617
1618        if ($res === DB_OK || DB::isError($res)) {
1619            return $res;
1620        }
1621
1622        $results = array();
1623        while (DB_OK === $res->fetchInto($row, $fetchmode)) {
1624            if ($fetchmode & DB_FETCHMODE_FLIPPED) {
1625                foreach ($row as $key => $val) {
1626                    $results[$key][] = $val;
1627                }
1628            } else {
1629                $results[] = $row;
1630            }
1631        }
1632
1633        $res->free();
1634
1635        if (DB::isError($row)) {
1636            $tmp =& $this->raiseError($row);
1637            return $tmp;
1638        }
1639        return $results;
1640    }
1641
1642    // }}}
1643    // {{{ autoCommit()
1644
1645    /**
1646     * Enables or disables automatic commits
1647     *
1648     * @param bool $onoff  true turns it on, false turns it off
1649     *
1650     * @return int  DB_OK on success.  A DB_Error object if the driver
1651     *               doesn't support auto-committing transactions.
1652     */
1653    function autoCommit($onoff = false)
1654    {
1655        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1656    }
1657
1658    // }}}
1659    // {{{ commit()
1660
1661    /**
1662     * Commits the current transaction
1663     *
1664     * @return int  DB_OK on success.  A DB_Error object on failure.
1665     */
1666    function commit()
1667    {
1668        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1669    }
1670
1671    // }}}
1672    // {{{ rollback()
1673
1674    /**
1675     * Reverts the current transaction
1676     *
1677     * @return int  DB_OK on success.  A DB_Error object on failure.
1678     */
1679    function rollback()
1680    {
1681        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1682    }
1683
1684    // }}}
1685    // {{{ numRows()
1686
1687    /**
1688     * Determines the number of rows in a query result
1689     *
1690     * @param resource $result  the query result idenifier produced by PHP
1691     *
1692     * @return int  the number of rows.  A DB_Error object on failure.
1693     */
1694    function numRows($result)
1695    {
1696        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1697    }
1698
1699    // }}}
1700    // {{{ affectedRows()
1701
1702    /**
1703     * Determines the number of rows affected by a data maniuplation query
1704     *
1705     * 0 is returned for queries that don't manipulate data.
1706     *
1707     * @return int  the number of rows.  A DB_Error object on failure.
1708     */
1709    function affectedRows()
1710    {
1711        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1712    }
1713
1714    // }}}
1715    // {{{ getSequenceName()
1716
1717    /**
1718     * Generates the name used inside the database for a sequence
1719     *
1720     * The createSequence() docblock contains notes about storing sequence
1721     * names.
1722     *
1723     * @param string $sqn  the sequence's public name
1724     *
1725     * @return string  the sequence's name in the backend
1726     *
1727     * @access protected
1728     * @see DB_common::createSequence(), DB_common::dropSequence(),
1729     *      DB_common::nextID(), DB_common::setOption()
1730     */
1731    function getSequenceName($sqn)
1732    {
1733        return sprintf($this->getOption('seqname_format'),
1734                       preg_replace('/[^a-z0-9_.]/i', '_', $sqn));
1735    }
1736
1737    // }}}
1738    // {{{ nextId()
1739
1740    /**
1741     * Returns the next free id in a sequence
1742     *
1743     * @param string  $seq_name  name of the sequence
1744     * @param boolean $ondemand  when true, the seqence is automatically
1745     *                            created if it does not exist
1746     *
1747     * @return int  the next id number in the sequence.
1748     *               A DB_Error object on failure.
1749     *
1750     * @see DB_common::createSequence(), DB_common::dropSequence(),
1751     *      DB_common::getSequenceName()
1752     */
1753    function nextId($seq_name, $ondemand = true)
1754    {
1755        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1756    }
1757
1758    // }}}
1759    // {{{ createSequence()
1760
1761    /**
1762     * Creates a new sequence
1763     *
1764     * The name of a given sequence is determined by passing the string
1765     * provided in the <var>$seq_name</var> argument through PHP's sprintf()
1766     * function using the value from the <var>seqname_format</var> option as
1767     * the sprintf()'s format argument.
1768     *
1769     * <var>seqname_format</var> is set via setOption().
1770     *
1771     * @param string $seq_name  name of the new sequence
1772     *
1773     * @return int  DB_OK on success.  A DB_Error object on failure.
1774     *
1775     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
1776     *      DB_common::nextID()
1777     */
1778    function createSequence($seq_name)
1779    {
1780        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1781    }
1782
1783    // }}}
1784    // {{{ dropSequence()
1785
1786    /**
1787     * Deletes a sequence
1788     *
1789     * @param string $seq_name  name of the sequence to be deleted
1790     *
1791     * @return int  DB_OK on success.  A DB_Error object on failure.
1792     *
1793     * @see DB_common::createSequence(), DB_common::getSequenceName(),
1794     *      DB_common::nextID()
1795     */
1796    function dropSequence($seq_name)
1797    {
1798        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1799    }
1800
1801    // }}}
1802    // {{{ raiseError()
1803
1804    /**
1805     * Communicates an error and invoke error callbacks, etc
1806     *
1807     * Basically a wrapper for PEAR::raiseError without the message string.
1808     *
1809     * @param mixed   integer error code, or a PEAR error object (all
1810     *                 other parameters are ignored if this parameter is
1811     *                 an object
1812     * @param int     error mode, see PEAR_Error docs
1813     * @param mixed   if error mode is PEAR_ERROR_TRIGGER, this is the
1814     *                 error level (E_USER_NOTICE etc).  If error mode is
1815     *                 PEAR_ERROR_CALLBACK, this is the callback function,
1816     *                 either as a function name, or as an array of an
1817     *                 object and method name.  For other error modes this
1818     *                 parameter is ignored.
1819     * @param string  extra debug information.  Defaults to the last
1820     *                 query and native error code.
1821     * @param mixed   native error code, integer or string depending the
1822     *                 backend
1823     *
1824     * @return object  the PEAR_Error object
1825     *
1826     * @see PEAR_Error
1827     */
1828    function &raiseError($code = DB_ERROR, $mode = null, $options = null,
1829                         $userinfo = null, $nativecode = null)
1830    {
1831        // The error is yet a DB error object
1832        if (is_object($code)) {
1833            // because we the static PEAR::raiseError, our global
1834            // handler should be used if it is set
1835            if ($mode === null && !empty($this->_default_error_mode)) {
1836                $mode    = $this->_default_error_mode;
1837                $options = $this->_default_error_options;
1838            }
1839            $tmp = PEAR::raiseError($code, null, $mode, $options,
1840                                    null, null, true);
1841            return $tmp;
1842        }
1843
1844        if ($userinfo === null) {
1845            $userinfo = $this->last_query;
1846        }
1847
1848        if ($nativecode) {
1849            $userinfo .= ' [nativecode=' . trim($nativecode) . ']';
1850        } else {
1851            $userinfo .= ' [DB Error: ' . DB::errorMessage($code) . ']';
1852        }
1853
1854        $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo,
1855                                'DB_Error', true);
1856        return $tmp;
1857    }
1858
1859    // }}}
1860    // {{{ errorNative()
1861
1862    /**
1863     * Gets the DBMS' native error code produced by the last query
1864     *
1865     * @return mixed  the DBMS' error code.  A DB_Error object on failure.
1866     */
1867    function errorNative()
1868    {
1869        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1870    }
1871
1872    // }}}
1873    // {{{ errorCode()
1874
1875    /**
1876     * Maps native error codes to DB's portable ones
1877     *
1878     * Uses the <var>$errorcode_map</var> property defined in each driver.
1879     *
1880     * @param string|int $nativecode  the error code returned by the DBMS
1881     *
1882     * @return int  the portable DB error code.  Return DB_ERROR if the
1883     *               current driver doesn't have a mapping for the
1884     *               $nativecode submitted.
1885     */
1886    function errorCode($nativecode)
1887    {
1888        if (isset($this->errorcode_map[$nativecode])) {
1889            return $this->errorcode_map[$nativecode];
1890        }
1891        // Fall back to DB_ERROR if there was no mapping.
1892        return DB_ERROR;
1893    }
1894
1895    // }}}
1896    // {{{ errorMessage()
1897
1898    /**
1899     * Maps a DB error code to a textual message
1900     *
1901     * @param integer $dbcode  the DB error code
1902     *
1903     * @return string  the error message corresponding to the error code
1904     *                  submitted.  FALSE if the error code is unknown.
1905     *
1906     * @see DB::errorMessage()
1907     */
1908    function errorMessage($dbcode)
1909    {
1910        return DB::errorMessage($this->errorcode_map[$dbcode]);
1911    }
1912
1913    // }}}
1914    // {{{ tableInfo()
1915
1916    /**
1917     * Returns information about a table or a result set
1918     *
1919     * The format of the resulting array depends on which <var>$mode</var>
1920     * you select.  The sample output below is based on this query:
1921     * <pre>
1922     *    SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
1923     *    FROM tblFoo
1924     *    JOIN tblBar ON tblFoo.fldId = tblBar.fldId
1925     * </pre>
1926     *
1927     * <ul>
1928     * <li>
1929     *
1930     * <kbd>null</kbd> (default)
1931     *   <pre>
1932     *   [0] => Array (
1933     *       [table] => tblFoo
1934     *       [name] => fldId
1935     *       [type] => int
1936     *       [len] => 11
1937     *       [flags] => primary_key not_null
1938     *   )
1939     *   [1] => Array (
1940     *       [table] => tblFoo
1941     *       [name] => fldPhone
1942     *       [type] => string
1943     *       [len] => 20
1944     *       [flags] =>
1945     *   )
1946     *   [2] => Array (
1947     *       [table] => tblBar
1948     *       [name] => fldId
1949     *       [type] => int
1950     *       [len] => 11
1951     *       [flags] => primary_key not_null
1952     *   )
1953     *   </pre>
1954     *
1955     * </li><li>
1956     *
1957     * <kbd>DB_TABLEINFO_ORDER</kbd>
1958     *
1959     *   <p>In addition to the information found in the default output,
1960     *   a notation of the number of columns is provided by the
1961     *   <samp>num_fields</samp> element while the <samp>order</samp>
1962     *   element provides an array with the column names as the keys and
1963     *   their location index number (corresponding to the keys in the
1964     *   the default output) as the values.</p>
1965     *
1966     *   <p>If a result set has identical field names, the last one is
1967     *   used.</p>
1968     *
1969     *   <pre>
1970     *   [num_fields] => 3
1971     *   [order] => Array (
1972     *       [fldId] => 2
1973     *       [fldTrans] => 1
1974     *   )
1975     *   </pre>
1976     *
1977     * </li><li>
1978     *
1979     * <kbd>DB_TABLEINFO_ORDERTABLE</kbd>
1980     *
1981     *   <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> but adds more
1982     *   dimensions to the array in which the table names are keys and
1983     *   the field names are sub-keys.  This is helpful for queries that
1984     *   join tables which have identical field names.</p>
1985     *
1986     *   <pre>
1987     *   [num_fields] => 3
1988     *   [ordertable] => Array (
1989     *       [tblFoo] => Array (
1990     *           [fldId] => 0
1991     *           [fldPhone] => 1
1992     *       )
1993     *       [tblBar] => Array (
1994     *           [fldId] => 2
1995     *       )
1996     *   )
1997     *   </pre>
1998     *
1999     * </li>
2000     * </ul>
2001     *
2002     * The <samp>flags</samp> element contains a space separated list
2003     * of extra information about the field.  This data is inconsistent
2004     * between DBMS's due to the way each DBMS works.
2005     *   + <samp>primary_key</samp>
2006     *   + <samp>unique_key</samp>
2007     *   + <samp>multiple_key</samp>
2008     *   + <samp>not_null</samp>
2009     *
2010     * Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
2011     * elements if <var>$result</var> is a table name.  The following DBMS's
2012     * provide full information from queries:
2013     *   + fbsql
2014     *   + mysql
2015     *
2016     * If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp>
2017     * turned on, the names of tables and fields will be lowercased.
2018     *
2019     * @param object|string  $result  DB_result object from a query or a
2020     *                                string containing the name of a table.
2021     *                                While this also accepts a query result
2022     *                                resource identifier, this behavior is
2023     *                                deprecated.
2024     * @param int  $mode   either unused or one of the tableInfo modes:
2025     *                     <kbd>DB_TABLEINFO_ORDERTABLE</kbd>,
2026     *                     <kbd>DB_TABLEINFO_ORDER</kbd> or
2027     *                     <kbd>DB_TABLEINFO_FULL</kbd> (which does both).
2028     *                     These are bitwise, so the first two can be
2029     *                     combined using <kbd>|</kbd>.
2030     *
2031     * @return array  an associative array with the information requested.
2032     *                 A DB_Error object on failure.
2033     *
2034     * @see DB_common::setOption()
2035     */
2036    function tableInfo($result, $mode = null)
2037    {
2038        /*
2039         * If the DB_<driver> class has a tableInfo() method, that one
2040         * overrides this one.  But, if the driver doesn't have one,
2041         * this method runs and tells users about that fact.
2042         */
2043        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
2044    }
2045
2046    // }}}
2047    // {{{ getTables()
2048
2049    /**
2050     * Lists the tables in the current database
2051     *
2052     * @return array  the list of tables.  A DB_Error object on failure.
2053     *
2054     * @deprecated Method deprecated some time before Release 1.2
2055     */
2056    function getTables()
2057    {
2058        return $this->getListOf('tables');
2059    }
2060
2061    // }}}
2062    // {{{ getListOf()
2063
2064    /**
2065     * Lists internal database information
2066     *
2067     * @param string $type  type of information being sought.
2068     *                       Common items being sought are:
2069     *                       tables, databases, users, views, functions
2070     *                       Each DBMS's has its own capabilities.
2071     *
2072     * @return array  an array listing the items sought.
2073     *                 A DB DB_Error object on failure.
2074     */
2075    function getListOf($type)
2076    {
2077        $sql = $this->getSpecialQuery($type);
2078        if ($sql === null) {
2079            $this->last_query = '';
2080            return $this->raiseError(DB_ERROR_UNSUPPORTED);
2081        } elseif (is_int($sql) || DB::isError($sql)) {
2082            // Previous error
2083            return $this->raiseError($sql);
2084        } elseif (is_array($sql)) {
2085            // Already the result
2086            return $sql;
2087        }
2088        // Launch this query
2089        return $this->getCol($sql);
2090    }
2091
2092    // }}}
2093    // {{{ getSpecialQuery()
2094
2095    /**
2096     * Obtains the query string needed for listing a given type of objects
2097     *
2098     * @param string $type  the kind of objects you want to retrieve
2099     *
2100     * @return string  the SQL query string or null if the driver doesn't
2101     *                  support the object type requested
2102     *
2103     * @access protected
2104     * @see DB_common::getListOf()
2105     */
2106    function getSpecialQuery($type)
2107    {
2108        return $this->raiseError(DB_ERROR_UNSUPPORTED);
2109    }
2110
2111    // }}}
2112    // {{{ _rtrimArrayValues()
2113
2114    /**
2115     * Right-trims all strings in an array
2116     *
2117     * @param array $array  the array to be trimmed (passed by reference)
2118     *
2119     * @return void
2120     *
2121     * @access protected
2122     */
2123    function _rtrimArrayValues(&$array)
2124    {
2125        foreach ($array as $key => $value) {
2126            if (is_string($value)) {
2127                $array[$key] = rtrim($value);
2128            }
2129        }
2130    }
2131
2132    // }}}
2133    // {{{ _convertNullArrayValuesToEmpty()
2134
2135    /**
2136     * Converts all null values in an array to empty strings
2137     *
2138     * @param array  $array  the array to be de-nullified (passed by reference)
2139     *
2140     * @return void
2141     *
2142     * @access protected
2143     */
2144    function _convertNullArrayValuesToEmpty(&$array)
2145    {
2146        foreach ($array as $key => $value) {
2147            if (is_null($value)) {
2148                $array[$key] = '';
2149            }
2150        }
2151    }
2152
2153    // }}}
2154}
2155
2156/*
2157 * Local variables:
2158 * tab-width: 4
2159 * c-basic-offset: 4
2160 * End:
2161 */
2162
2163?>
Note: See TracBrowser for help on using the repository browser.