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

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