]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/pear/DB.php
phpdoc_params
[SourceForge/phpwiki.git] / lib / pear / DB.php
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$
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$
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: 8
1110  * c-basic-offset: 4
1111  * End:
1112  */
1113
1114 ?>