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