source: branches/comu-ver2/data/module/DB/common.php @ 17877

Revision 17877, 70.9 KB checked in by Seasoft, 15 years ago (diff)

・PEAR::DB を Ver.1.7.14RC1(2007-11-27)ベースに更改。
・不適切な文字列比較を修正。

 http://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=1808&forum=9

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