Subversion Repositories eFlore/Applications.cel

Rev

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

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