Subversion Repositories Applications.bazar

Rev

Rev 468 | Details | Compare with Previous | Last modification | View Log | RSS feed

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