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