~ubuntu-branches/ubuntu/trusty/fusionforge/trusty

« back to all changes in this revision

Viewing changes to plugins/wiki/www/lib/pear/DB.php

  • Committer: Bazaar Package Importer
  • Author(s): Roland Mas
  • Date: 2011-04-15 14:55:34 UTC
  • mfrom: (4.1.10 sid)
  • Revision ID: james.westby@ubuntu.com-20110415145534-mvn1nochufjmw177
Tags: 5.0.3-1
New upstream bugfix release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
// | Authors: Stig Bakken <ssb@php.net>                                   |
 
17
// |          Tomas V.V.Cox <cox@idecnet.com>                             |
 
18
// | Maintainer: Daniel Convissor <danielc@php.net>                       |
 
19
// +----------------------------------------------------------------------+
 
20
//
 
21
// $Id: DB.php 6184 2008-08-22 10:33:41Z vargenau $
 
22
//
 
23
// Database independent query interface.
 
24
 
 
25
 
 
26
require_once 'PEAR.php';
 
27
 
 
28
// {{{ constants
 
29
// {{{ error codes
 
30
 
 
31
/*
 
32
 * The method mapErrorCode in each DB_dbtype implementation maps
 
33
 * native error codes to one of these.
 
34
 *
 
35
 * If you add an error code here, make sure you also add a textual
 
36
 * version of it in DB::errorMessage().
 
37
 */
 
38
define('DB_OK',                         1);
 
39
define('DB_ERROR',                     -1);
 
40
define('DB_ERROR_SYNTAX',              -2);
 
41
define('DB_ERROR_CONSTRAINT',          -3);
 
42
define('DB_ERROR_NOT_FOUND',           -4);
 
43
define('DB_ERROR_ALREADY_EXISTS',      -5);
 
44
define('DB_ERROR_UNSUPPORTED',         -6);
 
45
define('DB_ERROR_MISMATCH',            -7);
 
46
define('DB_ERROR_INVALID',             -8);
 
47
define('DB_ERROR_NOT_CAPABLE',         -9);
 
48
define('DB_ERROR_TRUNCATED',          -10);
 
49
define('DB_ERROR_INVALID_NUMBER',     -11);
 
50
define('DB_ERROR_INVALID_DATE',       -12);
 
51
define('DB_ERROR_DIVZERO',            -13);
 
52
define('DB_ERROR_NODBSELECTED',       -14);
 
53
define('DB_ERROR_CANNOT_CREATE',      -15);
 
54
define('DB_ERROR_CANNOT_DELETE',      -16);
 
55
define('DB_ERROR_CANNOT_DROP',        -17);
 
56
define('DB_ERROR_NOSUCHTABLE',        -18);
 
57
define('DB_ERROR_NOSUCHFIELD',        -19);
 
58
define('DB_ERROR_NEED_MORE_DATA',     -20);
 
59
define('DB_ERROR_NOT_LOCKED',         -21);
 
60
define('DB_ERROR_VALUE_COUNT_ON_ROW', -22);
 
61
define('DB_ERROR_INVALID_DSN',        -23);
 
62
define('DB_ERROR_CONNECT_FAILED',     -24);
 
63
define('DB_ERROR_EXTENSION_NOT_FOUND',-25);
 
64
define('DB_ERROR_ACCESS_VIOLATION',   -26);
 
65
define('DB_ERROR_NOSUCHDB',           -27);
 
66
define('DB_ERROR_CONSTRAINT_NOT_NULL',-29);
 
67
 
 
68
 
 
69
// }}}
 
70
// {{{ prepared statement-related
 
71
 
 
72
 
 
73
/*
 
74
 * These constants are used when storing information about prepared
 
75
 * statements (using the "prepare" method in DB_dbtype).
 
76
 *
 
77
 * The prepare/execute model in DB is mostly borrowed from the ODBC
 
78
 * extension, in a query the "?" character means a scalar parameter.
 
79
 * There are two extensions though, a "&" character means an opaque
 
80
 * parameter.  An opaque parameter is simply a file name, the real
 
81
 * data are in that file (useful for putting uploaded files into your
 
82
 * database and such). The "!" char means a parameter that must be
 
83
 * left as it is.
 
84
 * They modify the quote behavoir:
 
85
 * DB_PARAM_SCALAR (?) => 'original string quoted'
 
86
 * DB_PARAM_OPAQUE (&) => 'string from file quoted'
 
87
 * DB_PARAM_MISC   (!) => original string
 
88
 */
 
89
define('DB_PARAM_SCALAR', 1);
 
90
define('DB_PARAM_OPAQUE', 2);
 
91
define('DB_PARAM_MISC',   3);
 
92
 
 
93
 
 
94
// }}}
 
95
// {{{ binary data-related
 
96
 
 
97
 
 
98
/*
 
99
 * These constants define different ways of returning binary data
 
100
 * from queries.  Again, this model has been borrowed from the ODBC
 
101
 * extension.
 
102
 *
 
103
 * DB_BINMODE_PASSTHRU sends the data directly through to the browser
 
104
 * when data is fetched from the database.
 
105
 * DB_BINMODE_RETURN lets you return data as usual.
 
106
 * DB_BINMODE_CONVERT returns data as well, only it is converted to
 
107
 * hex format, for example the string "123" would become "313233".
 
108
 */
 
109
define('DB_BINMODE_PASSTHRU', 1);
 
110
define('DB_BINMODE_RETURN',   2);
 
111
define('DB_BINMODE_CONVERT',  3);
 
112
 
 
113
 
 
114
// }}}
 
115
// {{{ fetch modes
 
116
 
 
117
 
 
118
/**
 
119
 * This is a special constant that tells DB the user hasn't specified
 
120
 * any particular get mode, so the default should be used.
 
121
 */
 
122
define('DB_FETCHMODE_DEFAULT', 0);
 
123
 
 
124
/**
 
125
 * Column data indexed by numbers, ordered from 0 and up
 
126
 */
 
127
define('DB_FETCHMODE_ORDERED', 1);
 
128
 
 
129
/**
 
130
 * Column data indexed by column names
 
131
 */
 
132
define('DB_FETCHMODE_ASSOC', 2);
 
133
 
 
134
/**
 
135
 * Column data as object properties
 
136
 */
 
137
define('DB_FETCHMODE_OBJECT', 3);
 
138
 
 
139
/**
 
140
 * For multi-dimensional results: normally the first level of arrays
 
141
 * is the row number, and the second level indexed by column number or name.
 
142
 * DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays
 
143
 * is the column name, and the second level the row number.
 
144
 */
 
145
define('DB_FETCHMODE_FLIPPED', 4);
 
146
 
 
147
/* for compatibility */
 
148
define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED);
 
149
define('DB_GETMODE_ASSOC',   DB_FETCHMODE_ASSOC);
 
150
define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED);
 
151
 
 
152
 
 
153
// }}}
 
154
// {{{ tableInfo() && autoPrepare()-related
 
155
 
 
156
 
 
157
/**
 
158
 * these are constants for the tableInfo-function
 
159
 * they are bitwised or'ed. so if there are more constants to be defined
 
160
 * in the future, adjust DB_TABLEINFO_FULL accordingly
 
161
 */
 
162
define('DB_TABLEINFO_ORDER', 1);
 
163
define('DB_TABLEINFO_ORDERTABLE', 2);
 
164
define('DB_TABLEINFO_FULL', 3);
 
165
 
 
166
/*
 
167
 * Used by autoPrepare()
 
168
 */
 
169
define('DB_AUTOQUERY_INSERT', 1);
 
170
define('DB_AUTOQUERY_UPDATE', 2);
 
171
 
 
172
 
 
173
// }}}
 
174
// {{{ portability modes
 
175
 
 
176
 
 
177
/**
 
178
 * Portability: turn off all portability features.
 
179
 * @see DB_common::setOption()
 
180
 */
 
181
define('DB_PORTABILITY_NONE', 0);
 
182
 
 
183
/**
 
184
 * Portability: convert names of tables and fields to lower case
 
185
 * when using the get*(), fetch*() and tableInfo() methods.
 
186
 * @see DB_common::setOption()
 
187
 */
 
188
define('DB_PORTABILITY_LOWERCASE', 1);
 
189
 
 
190
/**
 
191
 * Portability: right trim the data output by get*() and fetch*().
 
192
 * @see DB_common::setOption()
 
193
 */
 
194
define('DB_PORTABILITY_RTRIM', 2);
 
195
 
 
196
/**
 
197
 * Portability: force reporting the number of rows deleted.
 
198
 * @see DB_common::setOption()
 
199
 */
 
200
define('DB_PORTABILITY_DELETE_COUNT', 4);
 
201
 
 
202
/**
 
203
 * Portability: enable hack that makes numRows() work in Oracle.
 
204
 * @see DB_common::setOption()
 
205
 */
 
206
define('DB_PORTABILITY_NUMROWS', 8);
 
207
 
 
208
/**
 
209
 * Portability: makes certain error messages in certain drivers compatible
 
210
 * with those from other DBMS's.
 
211
 *
 
212
 * + mysql, mysqli:  change unique/primary key constraints
 
213
 *   DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
 
214
 *
 
215
 * + odbc(access):  MS's ODBC driver reports 'no such field' as code
 
216
 *   07001, which means 'too few parameters.'  When this option is on
 
217
 *   that code gets mapped to DB_ERROR_NOSUCHFIELD.
 
218
 *
 
219
 * @see DB_common::setOption()
 
220
 */
 
221
define('DB_PORTABILITY_ERRORS', 16);
 
222
 
 
223
/**
 
224
 * Portability: convert null values to empty strings in data output by
 
225
 * get*() and fetch*().
 
226
 * @see DB_common::setOption()
 
227
 */
 
228
define('DB_PORTABILITY_NULL_TO_EMPTY', 32);
 
229
 
 
230
/**
 
231
 * Portability: turn on all portability features.
 
232
 * @see DB_common::setOption()
 
233
 */
 
234
define('DB_PORTABILITY_ALL', 63);
 
235
 
 
236
// }}}
 
237
 
 
238
 
 
239
// }}}
 
240
// {{{ class DB
 
241
 
 
242
/**
 
243
 * The main "DB" class is simply a container class with some static
 
244
 * methods for creating DB objects as well as some utility functions
 
245
 * common to all parts of DB.
 
246
 *
 
247
 * The object model of DB is as follows (indentation means inheritance):
 
248
 *
 
249
 * DB           The main DB class.  This is simply a utility class
 
250
 *              with some "static" methods for creating DB objects as
 
251
 *              well as common utility functions for other DB classes.
 
252
 *
 
253
 * DB_common    The base for each DB implementation.  Provides default
 
254
 * |            implementations (in OO lingo virtual methods) for
 
255
 * |            the actual DB implementations as well as a bunch of
 
256
 * |            query utility functions.
 
257
 * |
 
258
 * +-DB_mysql   The DB implementation for MySQL.  Inherits DB_common.
 
259
 *              When calling DB::factory or DB::connect for MySQL
 
260
 *              connections, the object returned is an instance of this
 
261
 *              class.
 
262
 *
 
263
 * @package  DB
 
264
 * @author   Stig Bakken <ssb@php.net>
 
265
 * @author   Tomas V.V.Cox <cox@idecnet.com>
 
266
 * @since    PHP 4.0
 
267
 * @version  $Id: DB.php 6184 2008-08-22 10:33:41Z vargenau $
 
268
 * @category Database
 
269
 */
 
270
class DB
 
271
{
 
272
    // {{{ &factory()
 
273
 
 
274
    /**
 
275
     * Create a new DB object for the specified database type.
 
276
     *
 
277
     * Allows creation of a DB_<driver> object from which the object's
 
278
     * methods can be utilized without actually connecting to a database.
 
279
     *
 
280
     * @param string $type    database type, for example "mysql"
 
281
     * @param array  $options associative array of option names and values
 
282
     *
 
283
     * @return object  a new DB object.  On error, an error object.
 
284
     *
 
285
     * @see DB_common::setOption()
 
286
     * @access public
 
287
     */
 
288
    function &factory($type, $options = false)
 
289
    {
 
290
        if (!is_array($options)) {
 
291
            $options = array('persistent' => $options);
 
292
        }
 
293
 
 
294
        if (isset($options['debug']) && $options['debug'] >= 2) {
 
295
            // expose php errors with sufficient debug level
 
296
            include_once "DB/{$type}.php";
 
297
        } else {
 
298
            @include_once "DB/{$type}.php";
 
299
        }
 
300
 
 
301
        $classname = "DB_${type}";
 
302
 
 
303
        if (!class_exists($classname)) {
 
304
            $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
 
305
                                    "Unable to include the DB/{$type}.php file",
 
306
                                    'DB_Error', true);
 
307
            return $tmp;
 
308
        }
 
309
 
 
310
        @$obj =& new $classname;
 
311
 
 
312
        foreach ($options as $option => $value) {
 
313
            $test = $obj->setOption($option, $value);
 
314
            if (DB::isError($test)) {
 
315
                return $test;
 
316
            }
 
317
        }
 
318
 
 
319
        return $obj;
 
320
    }
 
321
 
 
322
    // }}}
 
323
    // {{{ &connect()
 
324
 
 
325
    /**
 
326
     * Create a new DB object and connect to the specified database.
 
327
     *
 
328
     * Example 1.
 
329
     * <code> <?php
 
330
     * require_once 'DB.php';
 
331
     *
 
332
     * $dsn = 'mysql://user:password@host/database'
 
333
     * $options = array(
 
334
     *     'debug'       => 2,
 
335
     *     'portability' => DB_PORTABILITY_ALL,
 
336
     * );
 
337
     *
 
338
     * $dbh =& DB::connect($dsn, $options);
 
339
     * if (DB::isError($dbh)) {
 
340
     *     die($dbh->getMessage());
 
341
     * }
 
342
     * ?></code>
 
343
     *
 
344
     * @param mixed $dsn      string "data source name" or an array in the
 
345
     *                        format returned by DB::parseDSN()
 
346
     *
 
347
     * @param array $options  an associative array of option names and
 
348
     *                        their values
 
349
     *
 
350
     * @return object  a newly created DB connection object, or a DB
 
351
     *                 error object on error
 
352
     *
 
353
     * @see DB::parseDSN(), DB_common::setOption(), DB::isError()
 
354
     * @access public
 
355
     */
 
356
    function &connect($dsn, $options = array())
 
357
    {
 
358
        $dsninfo = DB::parseDSN($dsn);
 
359
        $type = $dsninfo['phptype'];
 
360
 
 
361
        if (!is_array($options)) {
 
362
            /*
 
363
             * For backwards compatibility.  $options used to be boolean,
 
364
             * indicating whether the connection should be persistent.
 
365
             */
 
366
            $options = array('persistent' => $options);
 
367
        }
 
368
 
 
369
        if (isset($options['debug']) && $options['debug'] >= 2) {
 
370
            // expose php errors with sufficient debug level
 
371
            include_once "DB/${type}.php";
 
372
        } else {
 
373
            @include_once "DB/${type}.php";
 
374
        }
 
375
 
 
376
        $classname = "DB_${type}";
 
377
        if (!class_exists($classname)) {
 
378
            $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
 
379
                                    "Unable to include the DB/{$type}.php file for `$dsn'",
 
380
                                    'DB_Error', true);
 
381
            return $tmp;
 
382
        }
 
383
 
 
384
        @$obj =& new $classname;
 
385
 
 
386
        foreach ($options as $option => $value) {
 
387
            $test = $obj->setOption($option, $value);
 
388
            if (DB::isError($test)) {
 
389
                return $test;
 
390
            }
 
391
        }
 
392
 
 
393
        $err = $obj->connect($dsninfo, $obj->getOption('persistent'));
 
394
        if (DB::isError($err)) {
 
395
            $err->addUserInfo($dsn);
 
396
            return $err;
 
397
        }
 
398
 
 
399
        return $obj;
 
400
    }
 
401
 
 
402
    // }}}
 
403
    // {{{ apiVersion()
 
404
 
 
405
    /**
 
406
     * Return the DB API version
 
407
     *
 
408
     * @return int the DB API version number
 
409
     *
 
410
     * @access public
 
411
     */
 
412
    function apiVersion()
 
413
    {
 
414
        return 2;
 
415
    }
 
416
 
 
417
    // }}}
 
418
    // {{{ isError()
 
419
 
 
420
    /**
 
421
     * Tell whether a result code from a DB method is an error
 
422
     *
 
423
     * @param int $value result code
 
424
     *
 
425
     * @return bool whether $value is an error
 
426
     *
 
427
     * @access public
 
428
     */
 
429
    function isError($value)
 
430
    {
 
431
        return is_a($value, 'DB_Error');
 
432
    }
 
433
 
 
434
    // }}}
 
435
    // {{{ isConnection()
 
436
 
 
437
    /**
 
438
     * Tell whether a value is a DB connection
 
439
     *
 
440
     * @param mixed $value value to test
 
441
     *
 
442
     * @return bool whether $value is a DB connection
 
443
     *
 
444
     * @access public
 
445
     */
 
446
    function isConnection($value)
 
447
    {
 
448
        return (is_object($value) &&
 
449
                is_subclass_of($value, 'db_common') &&
 
450
                method_exists($value, 'simpleQuery'));
 
451
    }
 
452
 
 
453
    // }}}
 
454
    // {{{ isManip()
 
455
 
 
456
    /**
 
457
     * Tell whether a query is a data manipulation query (insert,
 
458
     * update or delete) or a data definition query (create, drop,
 
459
     * alter, grant, revoke).
 
460
     *
 
461
     * @access public
 
462
     *
 
463
     * @param string $query the query
 
464
     *
 
465
     * @return boolean whether $query is a data manipulation query
 
466
     */
 
467
    function isManip($query)
 
468
    {
 
469
        $manips = 'INSERT|UPDATE|DELETE|LOAD DATA|'.'REPLACE|CREATE|DROP|'.
 
470
                  'ALTER|GRANT|REVOKE|'.'LOCK|UNLOCK';
 
471
        if (preg_match('/^\s*"?('.$manips.')\s+/i', $query)) {
 
472
            return true;
 
473
        }
 
474
        return false;
 
475
    }
 
476
 
 
477
    // }}}
 
478
    // {{{ errorMessage()
 
479
 
 
480
    /**
 
481
     * Return a textual error message for a DB error code
 
482
     *
 
483
     * @param integer $value error code
 
484
     *
 
485
     * @return string error message, or false if the error code was
 
486
     * not recognized
 
487
     */
 
488
    function errorMessage($value)
 
489
    {
 
490
        static $errorMessages;
 
491
        if (!isset($errorMessages)) {
 
492
            $errorMessages = array(
 
493
                DB_ERROR                    => 'unknown error',
 
494
                DB_ERROR_ALREADY_EXISTS     => 'already exists',
 
495
                DB_ERROR_CANNOT_CREATE      => 'can not create',
 
496
                DB_ERROR_CANNOT_DELETE      => 'can not delete',
 
497
                DB_ERROR_CANNOT_DROP        => 'can not drop',
 
498
                DB_ERROR_CONSTRAINT         => 'constraint violation',
 
499
                DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
 
500
                DB_ERROR_DIVZERO            => 'division by zero',
 
501
                DB_ERROR_INVALID            => 'invalid',
 
502
                DB_ERROR_INVALID_DATE       => 'invalid date or time',
 
503
                DB_ERROR_INVALID_NUMBER     => 'invalid number',
 
504
                DB_ERROR_MISMATCH           => 'mismatch',
 
505
                DB_ERROR_NODBSELECTED       => 'no database selected',
 
506
                DB_ERROR_NOSUCHFIELD        => 'no such field',
 
507
                DB_ERROR_NOSUCHTABLE        => 'no such table',
 
508
                DB_ERROR_NOT_CAPABLE        => 'DB backend not capable',
 
509
                DB_ERROR_NOT_FOUND          => 'not found',
 
510
                DB_ERROR_NOT_LOCKED         => 'not locked',
 
511
                DB_ERROR_SYNTAX             => 'syntax error',
 
512
                DB_ERROR_UNSUPPORTED        => 'not supported',
 
513
                DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
 
514
                DB_ERROR_INVALID_DSN        => 'invalid DSN',
 
515
                DB_ERROR_CONNECT_FAILED     => 'connect failed',
 
516
                DB_OK                       => 'no error',
 
517
                DB_ERROR_NEED_MORE_DATA     => 'insufficient data supplied',
 
518
                DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
 
519
                DB_ERROR_NOSUCHDB           => 'no such database',
 
520
                DB_ERROR_ACCESS_VIOLATION   => 'insufficient permissions',
 
521
                DB_ERROR_TRUNCATED          => 'truncated'
 
522
            );
 
523
        }
 
524
 
 
525
        if (DB::isError($value)) {
 
526
            $value = $value->getCode();
 
527
        }
 
528
 
 
529
        return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[DB_ERROR];
 
530
    }
 
531
 
 
532
    // }}}
 
533
    // {{{ parseDSN()
 
534
 
 
535
    /**
 
536
     * Parse a data source name.
 
537
     *
 
538
     * Additional keys can be added by appending a URI query string to the
 
539
     * end of the DSN.
 
540
     *
 
541
     * The format of the supplied DSN is in its fullest form:
 
542
     * <code>
 
543
     *  phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true
 
544
     * </code>
 
545
     *
 
546
     * Most variations are allowed:
 
547
     * <code>
 
548
     *  phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644
 
549
     *  phptype://username:password@hostspec/database_name
 
550
     *  phptype://username:password@hostspec
 
551
     *  phptype://username@hostspec
 
552
     *  phptype://hostspec/database
 
553
     *  phptype://hostspec
 
554
     *  phptype(dbsyntax)
 
555
     *  phptype
 
556
     * </code>
 
557
     *
 
558
     * @param string $dsn Data Source Name to be parsed
 
559
     *
 
560
     * @return array an associative array with the following keys:
 
561
     *  + phptype:  Database backend used in PHP (mysql, odbc etc.)
 
562
     *  + dbsyntax: Database used with regards to SQL syntax etc.
 
563
     *  + protocol: Communication protocol to use (tcp, unix etc.)
 
564
     *  + hostspec: Host specification (hostname[:port])
 
565
     *  + database: Database to use on the DBMS server
 
566
     *  + username: User name for login
 
567
     *  + password: Password for login
 
568
     *
 
569
     * @author Tomas V.V.Cox <cox@idecnet.com>
 
570
     */
 
571
    function parseDSN($dsn)
 
572
    {
 
573
        $parsed = array(
 
574
            'phptype'  => false,
 
575
            'dbsyntax' => false,
 
576
            'username' => false,
 
577
            'password' => false,
 
578
            'protocol' => false,
 
579
            'hostspec' => false,
 
580
            'port'     => false,
 
581
            'socket'   => false,
 
582
            'database' => false,
 
583
        );
 
584
 
 
585
        if (is_array($dsn)) {
 
586
            $dsn = array_merge($parsed, $dsn);
 
587
            if (!$dsn['dbsyntax']) {
 
588
                $dsn['dbsyntax'] = $dsn['phptype'];
 
589
            }
 
590
            return $dsn;
 
591
        }
 
592
 
 
593
        // Find phptype and dbsyntax
 
594
        if (($pos = strpos($dsn, '://')) !== false) {
 
595
            $str = substr($dsn, 0, $pos);
 
596
            $dsn = substr($dsn, $pos + 3);
 
597
        } else {
 
598
            $str = $dsn;
 
599
            $dsn = null;
 
600
        }
 
601
 
 
602
        // Get phptype and dbsyntax
 
603
        // $str => phptype(dbsyntax)
 
604
        if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
 
605
            $parsed['phptype']  = $arr[1];
 
606
            $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2];
 
607
        } else {
 
608
            $parsed['phptype']  = $str;
 
609
            $parsed['dbsyntax'] = $str;
 
610
        }
 
611
 
 
612
        if (!count($dsn)) {
 
613
            return $parsed;
 
614
        }
 
615
 
 
616
        // Get (if found): username and password
 
617
        // $dsn => username:password@protocol+hostspec/database
 
618
        if (($at = strrpos($dsn,'@')) !== false) {
 
619
            $str = substr($dsn, 0, $at);
 
620
            $dsn = substr($dsn, $at + 1);
 
621
            if (($pos = strpos($str, ':')) !== false) {
 
622
                $parsed['username'] = rawurldecode(substr($str, 0, $pos));
 
623
                $parsed['password'] = rawurldecode(substr($str, $pos + 1));
 
624
            } else {
 
625
                $parsed['username'] = rawurldecode($str);
 
626
            }
 
627
        }
 
628
 
 
629
        // Find protocol and hostspec
 
630
 
 
631
        // $dsn => proto(proto_opts)/database
 
632
        if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
 
633
            $proto       = $match[1];
 
634
            $proto_opts  = $match[2] ? $match[2] : false;
 
635
            $dsn         = $match[3];
 
636
 
 
637
        // $dsn => protocol+hostspec/database (old format)
 
638
        } else {
 
639
            if (strpos($dsn, '+') !== false) {
 
640
                list($proto, $dsn) = explode('+', $dsn, 2);
 
641
            }
 
642
            if (strpos($dsn, '/') !== false) {
 
643
                list($proto_opts, $dsn) = explode('/', $dsn, 2);
 
644
            } else {
 
645
                $proto_opts = $dsn;
 
646
                $dsn = null;
 
647
            }
 
648
        }
 
649
 
 
650
        // process the different protocol options
 
651
        $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
 
652
        $proto_opts = rawurldecode($proto_opts);
 
653
        if ($parsed['protocol'] == 'tcp') {
 
654
            if (strpos($proto_opts, ':') !== false) {
 
655
                list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts);
 
656
            } else {
 
657
                $parsed['hostspec'] = $proto_opts;
 
658
            }
 
659
        } elseif ($parsed['protocol'] == 'unix') {
 
660
            $parsed['socket'] = $proto_opts;
 
661
        }
 
662
 
 
663
        // Get dabase if any
 
664
        // $dsn => database
 
665
        if ($dsn) {
 
666
            // /database
 
667
            if (($pos = strpos($dsn, '?')) === false) {
 
668
                $parsed['database'] = $dsn;
 
669
            // /database?param1=value1&param2=value2
 
670
            } else {
 
671
                $parsed['database'] = substr($dsn, 0, $pos);
 
672
                $dsn = substr($dsn, $pos + 1);
 
673
                if (strpos($dsn, '&') !== false) {
 
674
                    $opts = explode('&', $dsn);
 
675
                } else { // database?param1=value1
 
676
                    $opts = array($dsn);
 
677
                }
 
678
                foreach ($opts as $opt) {
 
679
                    list($key, $value) = explode('=', $opt);
 
680
                    if (!isset($parsed[$key])) {
 
681
                        // don't allow params overwrite
 
682
                        $parsed[$key] = rawurldecode($value);
 
683
                    }
 
684
                }
 
685
            }
 
686
        }
 
687
 
 
688
        return $parsed;
 
689
    }
 
690
 
 
691
    // }}}
 
692
    // {{{ assertExtension()
 
693
 
 
694
    /**
 
695
     * Load a PHP database extension if it is not loaded already.
 
696
     *
 
697
     * @access public
 
698
     *
 
699
     * @param string $name the base name of the extension (without the .so or
 
700
     *                     .dll suffix)
 
701
     *
 
702
     * @return boolean true if the extension was already or successfully
 
703
     *                 loaded, false if it could not be loaded
 
704
     */
 
705
    function assertExtension($name)
 
706
    {
 
707
        if (!extension_loaded($name)) {
 
708
            $dlext = OS_WINDOWS ? '.dll' : '.so';
 
709
            $dlprefix = OS_WINDOWS ? 'php_' : '';
 
710
            @dl($dlprefix . $name . $dlext);
 
711
            return extension_loaded($name);
 
712
        }
 
713
        return true;
 
714
    }
 
715
    // }}}
 
716
}
 
717
 
 
718
// }}}
 
719
// {{{ class DB_Error
 
720
 
 
721
/**
 
722
 * DB_Error implements a class for reporting portable database error
 
723
 * messages.
 
724
 *
 
725
 * @package  DB
 
726
 * @author Stig Bakken <ssb@php.net>
 
727
 */
 
728
class DB_Error extends PEAR_Error
 
729
{
 
730
    // {{{ constructor
 
731
 
 
732
    /**
 
733
     * DB_Error constructor.
 
734
     *
 
735
     * @param mixed   $code   DB error code, or string with error message.
 
736
     * @param integer $mode   what "error mode" to operate in
 
737
     * @param integer $level  what error level to use for $mode & PEAR_ERROR_TRIGGER
 
738
     * @param mixed   $debuginfo  additional debug info, such as the last query
 
739
     *
 
740
     * @access public
 
741
     *
 
742
     * @see PEAR_Error
 
743
     */
 
744
    function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
 
745
              $level = E_USER_NOTICE, $debuginfo = null)
 
746
    {
 
747
        if (is_int($code)) {
 
748
            $this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, $mode, $level, $debuginfo);
 
749
        } else {
 
750
            $this->PEAR_Error("DB Error: $code", DB_ERROR, $mode, $level, $debuginfo);
 
751
        }
 
752
    }
 
753
    // }}}
 
754
}
 
755
 
 
756
// }}}
 
757
// {{{ class DB_result
 
758
 
 
759
/**
 
760
 * This class implements a wrapper for a DB result set.
 
761
 * A new instance of this class will be returned by the DB implementation
 
762
 * after processing a query that returns data.
 
763
 *
 
764
 * @package  DB
 
765
 * @author Stig Bakken <ssb@php.net>
 
766
 */
 
767
class DB_result
 
768
{
 
769
    // {{{ properties
 
770
 
 
771
    var $dbh;
 
772
    var $result;
 
773
    var $row_counter = null;
 
774
 
 
775
    /**
 
776
     * for limit queries, the row to start fetching
 
777
     * @var integer
 
778
     */
 
779
    var $limit_from  = null;
 
780
 
 
781
    /**
 
782
     * for limit queries, the number of rows to fetch
 
783
     * @var integer
 
784
     */
 
785
    var $limit_count = null;
 
786
 
 
787
    // }}}
 
788
    // {{{ constructor
 
789
 
 
790
    /**
 
791
     * DB_result constructor.
 
792
     * @param resource &$dbh   DB object reference
 
793
     * @param resource $result  result resource id
 
794
     * @param array    $options assoc array with optional result options
 
795
     */
 
796
    function DB_result(&$dbh, $result, $options = array())
 
797
    {
 
798
        $this->dbh = &$dbh;
 
799
        $this->result = $result;
 
800
        foreach ($options as $key => $value) {
 
801
            $this->setOption($key, $value);
 
802
        }
 
803
        $this->limit_type  = $dbh->features['limit'];
 
804
        $this->autofree    = $dbh->options['autofree'];
 
805
        $this->fetchmode   = $dbh->fetchmode;
 
806
        $this->fetchmode_object_class = $dbh->fetchmode_object_class;
 
807
    }
 
808
 
 
809
    function setOption($key, $value = null)
 
810
    {
 
811
        switch ($key) {
 
812
            case 'limit_from':
 
813
                $this->limit_from = $value; break;
 
814
            case 'limit_count':
 
815
                $this->limit_count = $value; break;
 
816
        }
 
817
    }
 
818
 
 
819
    // }}}
 
820
    // {{{ fetchRow()
 
821
 
 
822
    /**
 
823
     * Fetch a row of data and return it by reference into an array.
 
824
     *
 
825
     * The type of array returned can be controlled either by setting this
 
826
     * method's <var>$fetchmode</var> parameter or by changing the default
 
827
     * fetch mode setFetchMode() before calling this method.
 
828
     *
 
829
     * There are two options for standardizing the information returned
 
830
     * from databases, ensuring their values are consistent when changing
 
831
     * DBMS's.  These portability options can be turned on when creating a
 
832
     * new DB object or by using setOption().
 
833
     *
 
834
     *   + <samp>DB_PORTABILITY_LOWERCASE</samp>
 
835
     *     convert names of fields to lower case
 
836
     *
 
837
     *   + <samp>DB_PORTABILITY_RTRIM</samp>
 
838
     *     right trim the data
 
839
     *
 
840
     * @param int $fetchmode  how the resulting array should be indexed
 
841
     * @param int $rownum     the row number to fetch
 
842
     *
 
843
     * @return array  a row of data, null on no more rows or PEAR_Error
 
844
     *                object on error
 
845
     *
 
846
     * @see DB_common::setOption(), DB_common::setFetchMode()
 
847
     * @access public
 
848
     */
 
849
    function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
 
850
    {
 
851
        if ($fetchmode === DB_FETCHMODE_DEFAULT) {
 
852
            $fetchmode = $this->fetchmode;
 
853
        }
 
854
        if ($fetchmode === DB_FETCHMODE_OBJECT) {
 
855
            $fetchmode = DB_FETCHMODE_ASSOC;
 
856
            $object_class = $this->fetchmode_object_class;
 
857
        }
 
858
        if ($this->limit_from !== null) {
 
859
            if ($this->row_counter === null) {
 
860
                $this->row_counter = $this->limit_from;
 
861
                // Skip rows
 
862
                if ($this->limit_type == false) {
 
863
                    $i = 0;
 
864
                    while ($i++ < $this->limit_from) {
 
865
                        $this->dbh->fetchInto($this->result, $arr, $fetchmode);
 
866
                    }
 
867
                }
 
868
            }
 
869
            if ($this->row_counter >= (
 
870
                    $this->limit_from + $this->limit_count))
 
871
            {
 
872
                if ($this->autofree) {
 
873
                    $this->free();
 
874
                }
 
875
                $tmp = null;
 
876
                return $tmp;
 
877
            }
 
878
            if ($this->limit_type == 'emulate') {
 
879
                $rownum = $this->row_counter;
 
880
            }
 
881
            $this->row_counter++;
 
882
        }
 
883
        $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
 
884
        if ($res === DB_OK) {
 
885
            if (isset($object_class)) {
 
886
                // default mode specified in DB_common::fetchmode_object_class property
 
887
                if ($object_class == 'stdClass') {
 
888
                    $arr = (object) $arr;
 
889
                } else {
 
890
                    $arr = &new $object_class($arr);
 
891
                }
 
892
            }
 
893
            return $arr;
 
894
        }
 
895
        if ($res == null && $this->autofree) {
 
896
            $this->free();
 
897
        }
 
898
        return $res;
 
899
    }
 
900
 
 
901
    // }}}
 
902
    // {{{ fetchInto()
 
903
 
 
904
    /**
 
905
     * Fetch a row of data into an array which is passed by reference.
 
906
     *
 
907
     * The type of array returned can be controlled either by setting this
 
908
     * method's <var>$fetchmode</var> parameter or by changing the default
 
909
     * fetch mode setFetchMode() before calling this method.
 
910
     *
 
911
     * There are two options for standardizing the information returned
 
912
     * from databases, ensuring their values are consistent when changing
 
913
     * DBMS's.  These portability options can be turned on when creating a
 
914
     * new DB object or by using setOption().
 
915
     *
 
916
     *   + <samp>DB_PORTABILITY_LOWERCASE</samp>
 
917
     *     convert names of fields to lower case
 
918
     *
 
919
     *   + <samp>DB_PORTABILITY_RTRIM</samp>
 
920
     *     right trim the data
 
921
     *
 
922
     * @param array &$arr       (reference) array where data from the row
 
923
     *                          should be placed
 
924
     * @param int   $fetchmode  how the resulting array should be indexed
 
925
     * @param int   $rownum     the row number to fetch
 
926
     *
 
927
     * @return mixed  DB_OK on success, null on no more rows or
 
928
     *                a DB_Error object on error
 
929
     *
 
930
     * @see DB_common::setOption(), DB_common::setFetchMode()
 
931
     * @access public
 
932
     */
 
933
    function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
 
934
    {
 
935
        if ($fetchmode === DB_FETCHMODE_DEFAULT) {
 
936
            $fetchmode = $this->fetchmode;
 
937
        }
 
938
        if ($fetchmode === DB_FETCHMODE_OBJECT) {
 
939
            $fetchmode = DB_FETCHMODE_ASSOC;
 
940
            $object_class = $this->fetchmode_object_class;
 
941
        }
 
942
        if ($this->limit_from !== null) {
 
943
            if ($this->row_counter === null) {
 
944
                $this->row_counter = $this->limit_from;
 
945
                // Skip rows
 
946
                if ($this->limit_type == false) {
 
947
                    $i = 0;
 
948
                    while ($i++ < $this->limit_from) {
 
949
                        $this->dbh->fetchInto($this->result, $arr, $fetchmode);
 
950
                    }
 
951
                }
 
952
            }
 
953
            if ($this->row_counter >= (
 
954
                    $this->limit_from + $this->limit_count))
 
955
            {
 
956
                if ($this->autofree) {
 
957
                    $this->free();
 
958
                }
 
959
                return null;
 
960
            }
 
961
            if ($this->limit_type == 'emulate') {
 
962
                $rownum = $this->row_counter;
 
963
            }
 
964
 
 
965
            $this->row_counter++;
 
966
        }
 
967
        $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
 
968
        if ($res === DB_OK) {
 
969
            if (isset($object_class)) {
 
970
                // default mode specified in DB_common::fetchmode_object_class property
 
971
                if ($object_class == 'stdClass') {
 
972
                    $arr = (object) $arr;
 
973
                } else {
 
974
                    $arr = new $object_class($arr);
 
975
                }
 
976
            }
 
977
            return DB_OK;
 
978
        }
 
979
        if ($res == null && $this->autofree) {
 
980
            $this->free();
 
981
        }
 
982
        return $res;
 
983
    }
 
984
 
 
985
    // }}}
 
986
    // {{{ numCols()
 
987
 
 
988
    /**
 
989
     * Get the the number of columns in a result set.
 
990
     *
 
991
     * @return int the number of columns, or a DB error
 
992
     *
 
993
     * @access public
 
994
     */
 
995
    function numCols()
 
996
    {
 
997
        return $this->dbh->numCols($this->result);
 
998
    }
 
999
 
 
1000
    // }}}
 
1001
    // {{{ numRows()
 
1002
 
 
1003
    /**
 
1004
     * Get the number of rows in a result set.
 
1005
     *
 
1006
     * @return int the number of rows, or a DB error
 
1007
     *
 
1008
     * @access public
 
1009
     */
 
1010
    function numRows()
 
1011
    {
 
1012
        return $this->dbh->numRows($this->result);
 
1013
    }
 
1014
 
 
1015
    // }}}
 
1016
    // {{{ nextResult()
 
1017
 
 
1018
    /**
 
1019
     * Get the next result if a batch of queries was executed.
 
1020
     *
 
1021
     * @return bool true if a new result is available or false if not.
 
1022
     *
 
1023
     * @access public
 
1024
     */
 
1025
    function nextResult()
 
1026
    {
 
1027
        return $this->dbh->nextResult($this->result);
 
1028
    }
 
1029
 
 
1030
    // }}}
 
1031
    // {{{ free()
 
1032
 
 
1033
    /**
 
1034
     * Frees the resources allocated for this result set.
 
1035
     * @return  int error code
 
1036
     *
 
1037
     * @access public
 
1038
     */
 
1039
    function free()
 
1040
    {
 
1041
        $err = $this->dbh->freeResult($this->result);
 
1042
        if (DB::isError($err)) {
 
1043
            return $err;
 
1044
        }
 
1045
        $this->result = false;
 
1046
        return true;
 
1047
    }
 
1048
 
 
1049
    // }}}
 
1050
    // {{{ tableInfo()
 
1051
 
 
1052
    /**
 
1053
     * @deprecated
 
1054
     * @internal
 
1055
     * @see DB_common::tableInfo()
 
1056
     */
 
1057
    function tableInfo($mode = null)
 
1058
    {
 
1059
        if (is_string($mode)) {
 
1060
            return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA);
 
1061
        }
 
1062
        return $this->dbh->tableInfo($this, $mode);
 
1063
    }
 
1064
 
 
1065
    // }}}
 
1066
    // {{{ getRowCounter()
 
1067
 
 
1068
    /**
 
1069
     * returns the actual row number
 
1070
     * @return integer
 
1071
     */
 
1072
    function getRowCounter()
 
1073
    {
 
1074
        return $this->row_counter;
 
1075
    }
 
1076
    // }}}
 
1077
}
 
1078
 
 
1079
// }}}
 
1080
// {{{ class DB_row
 
1081
 
 
1082
/**
 
1083
 * Pear DB Row Object
 
1084
 * @see DB_common::setFetchMode()
 
1085
 */
 
1086
class DB_row
 
1087
{
 
1088
    // {{{ constructor
 
1089
 
 
1090
    /**
 
1091
     * constructor
 
1092
     *
 
1093
     * @param resource row data as array
 
1094
     */
 
1095
    function DB_row(&$arr)
 
1096
    {
 
1097
        foreach ($arr as $key => $value) {
 
1098
            $this->$key = &$arr[$key];
 
1099
        }
 
1100
    }
 
1101
 
 
1102
    // }}}
 
1103
}
 
1104
 
 
1105
// }}}
 
1106
 
 
1107
/*
 
1108
 * Local variables:
 
1109
 * tab-width: 4
 
1110
 * c-basic-offset: 4
 
1111
 * End:
 
1112
 */
 
1113
 
 
1114
?>