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