Редакция 459 | Содержимое файла | Сравнить с предыдущей | Последнее изменение | Открыть журнал | RSS
Редакция | Автор | № строки | Строка |
---|---|---|---|
304 | alex-w | 1 | <?php |
2 | |||
3 | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
||
4 | |||
5 | /** |
||
6 | * Database independent query interface |
||
7 | * |
||
8 | * PHP versions 4 and 5 |
||
9 | * |
||
10 | * LICENSE: This source file is subject to version 3.0 of the PHP license |
||
11 | * that is available through the world-wide-web at the following URI: |
||
12 | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
||
13 | * the PHP License and are unable to obtain it through the web, please |
||
14 | * send a note to license@php.net so we can mail you a copy immediately. |
||
15 | * |
||
16 | * @category Database |
||
17 | * @package DB |
||
18 | * @author Stig Bakken <ssb@php.net> |
||
19 | * @author Tomas V.V.Cox <cox@idecnet.com> |
||
20 | * @author Daniel Convissor <danielc@php.net> |
||
21 | * @copyright 1997-2007 The PHP Group |
||
22 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
23 | * @version CVS: $Id: DB.php,v 1.88 2007/08/12 05:27:25 aharvey Exp $ |
||
24 | * @link http://pear.php.net/package/DB |
||
25 | */ |
||
26 | |||
27 | /** |
||
28 | * Obtain the PEAR class so it can be extended from |
||
29 | */ |
||
305 | alex-w | 30 | require_once dirname(__FILE__).'/PEAR.php'; |
304 | alex-w | 31 | |
32 | |||
33 | // {{{ constants |
||
34 | // {{{ error codes |
||
35 | |||
36 | /**#@+ |
||
37 | * One of PEAR DB's portable error codes. |
||
38 | * @see DB_common::errorCode(), DB::errorMessage() |
||
39 | * |
||
40 | * {@internal If you add an error code here, make sure you also add a textual |
||
41 | * version of it in DB::errorMessage().}} |
||
42 | */ |
||
43 | |||
44 | /** |
||
45 | * The code returned by many methods upon success |
||
46 | */ |
||
47 | define('DB_OK', 1); |
||
48 | |||
49 | /** |
||
50 | * Unkown error |
||
51 | */ |
||
52 | define('DB_ERROR', -1); |
||
53 | |||
54 | /** |
||
55 | * Syntax error |
||
56 | */ |
||
57 | define('DB_ERROR_SYNTAX', -2); |
||
58 | |||
59 | /** |
||
60 | * Tried to insert a duplicate value into a primary or unique index |
||
61 | */ |
||
62 | define('DB_ERROR_CONSTRAINT', -3); |
||
63 | |||
64 | /** |
||
65 | * An identifier in the query refers to a non-existant object |
||
66 | */ |
||
67 | define('DB_ERROR_NOT_FOUND', -4); |
||
68 | |||
69 | /** |
||
70 | * Tried to create a duplicate object |
||
71 | */ |
||
72 | define('DB_ERROR_ALREADY_EXISTS', -5); |
||
73 | |||
74 | /** |
||
75 | * The current driver does not support the action you attempted |
||
76 | */ |
||
77 | define('DB_ERROR_UNSUPPORTED', -6); |
||
78 | |||
79 | /** |
||
80 | * The number of parameters does not match the number of placeholders |
||
81 | */ |
||
82 | define('DB_ERROR_MISMATCH', -7); |
||
83 | |||
84 | /** |
||
85 | * A literal submitted did not match the data type expected |
||
86 | */ |
||
87 | define('DB_ERROR_INVALID', -8); |
||
88 | |||
89 | /** |
||
90 | * The current DBMS does not support the action you attempted |
||
91 | */ |
||
92 | define('DB_ERROR_NOT_CAPABLE', -9); |
||
93 | |||
94 | /** |
||
95 | * A literal submitted was too long so the end of it was removed |
||
96 | */ |
||
97 | define('DB_ERROR_TRUNCATED', -10); |
||
98 | |||
99 | /** |
||
100 | * A literal number submitted did not match the data type expected |
||
101 | */ |
||
102 | define('DB_ERROR_INVALID_NUMBER', -11); |
||
103 | |||
104 | /** |
||
105 | * A literal date submitted did not match the data type expected |
||
106 | */ |
||
107 | define('DB_ERROR_INVALID_DATE', -12); |
||
108 | |||
109 | /** |
||
110 | * Attempt to divide something by zero |
||
111 | */ |
||
112 | define('DB_ERROR_DIVZERO', -13); |
||
113 | |||
114 | /** |
||
115 | * A database needs to be selected |
||
116 | */ |
||
117 | define('DB_ERROR_NODBSELECTED', -14); |
||
118 | |||
119 | /** |
||
120 | * Could not create the object requested |
||
121 | */ |
||
122 | define('DB_ERROR_CANNOT_CREATE', -15); |
||
123 | |||
124 | /** |
||
125 | * Could not drop the database requested because it does not exist |
||
126 | */ |
||
127 | define('DB_ERROR_CANNOT_DROP', -17); |
||
128 | |||
129 | /** |
||
130 | * An identifier in the query refers to a non-existant table |
||
131 | */ |
||
132 | define('DB_ERROR_NOSUCHTABLE', -18); |
||
133 | |||
134 | /** |
||
135 | * An identifier in the query refers to a non-existant column |
||
136 | */ |
||
137 | define('DB_ERROR_NOSUCHFIELD', -19); |
||
138 | |||
139 | /** |
||
140 | * The data submitted to the method was inappropriate |
||
141 | */ |
||
142 | define('DB_ERROR_NEED_MORE_DATA', -20); |
||
143 | |||
144 | /** |
||
145 | * The attempt to lock the table failed |
||
146 | */ |
||
147 | define('DB_ERROR_NOT_LOCKED', -21); |
||
148 | |||
149 | /** |
||
150 | * The number of columns doesn't match the number of values |
||
151 | */ |
||
152 | define('DB_ERROR_VALUE_COUNT_ON_ROW', -22); |
||
153 | |||
154 | /** |
||
155 | * The DSN submitted has problems |
||
156 | */ |
||
157 | define('DB_ERROR_INVALID_DSN', -23); |
||
158 | |||
159 | /** |
||
160 | * Could not connect to the database |
||
161 | */ |
||
162 | define('DB_ERROR_CONNECT_FAILED', -24); |
||
163 | |||
164 | /** |
||
165 | * The PHP extension needed for this DBMS could not be found |
||
166 | */ |
||
167 | define('DB_ERROR_EXTENSION_NOT_FOUND',-25); |
||
168 | |||
169 | /** |
||
170 | * The present user has inadequate permissions to perform the task requestd |
||
171 | */ |
||
172 | define('DB_ERROR_ACCESS_VIOLATION', -26); |
||
173 | |||
174 | /** |
||
175 | * The database requested does not exist |
||
176 | */ |
||
177 | define('DB_ERROR_NOSUCHDB', -27); |
||
178 | |||
179 | /** |
||
180 | * Tried to insert a null value into a column that doesn't allow nulls |
||
181 | */ |
||
182 | define('DB_ERROR_CONSTRAINT_NOT_NULL',-29); |
||
183 | /**#@-*/ |
||
184 | |||
185 | |||
186 | // }}} |
||
187 | // {{{ prepared statement-related |
||
188 | |||
189 | |||
190 | /**#@+ |
||
191 | * Identifiers for the placeholders used in prepared statements. |
||
192 | * @see DB_common::prepare() |
||
193 | */ |
||
194 | |||
195 | /** |
||
196 | * Indicates a scalar (<kbd>?</kbd>) placeholder was used |
||
197 | * |
||
198 | * Quote and escape the value as necessary. |
||
199 | */ |
||
200 | define('DB_PARAM_SCALAR', 1); |
||
201 | |||
202 | /** |
||
203 | * Indicates an opaque (<kbd>&</kbd>) placeholder was used |
||
204 | * |
||
205 | * The value presented is a file name. Extract the contents of that file |
||
206 | * and place them in this column. |
||
207 | */ |
||
208 | define('DB_PARAM_OPAQUE', 2); |
||
209 | |||
210 | /** |
||
211 | * Indicates a misc (<kbd>!</kbd>) placeholder was used |
||
212 | * |
||
213 | * The value should not be quoted or escaped. |
||
214 | */ |
||
215 | define('DB_PARAM_MISC', 3); |
||
216 | /**#@-*/ |
||
217 | |||
218 | |||
219 | // }}} |
||
220 | // {{{ binary data-related |
||
221 | |||
222 | |||
223 | /**#@+ |
||
224 | * The different ways of returning binary data from queries. |
||
225 | */ |
||
226 | |||
227 | /** |
||
228 | * Sends the fetched data straight through to output |
||
229 | */ |
||
230 | define('DB_BINMODE_PASSTHRU', 1); |
||
231 | |||
232 | /** |
||
233 | * Lets you return data as usual |
||
234 | */ |
||
235 | define('DB_BINMODE_RETURN', 2); |
||
236 | |||
237 | /** |
||
238 | * Converts the data to hex format before returning it |
||
239 | * |
||
240 | * For example the string "123" would become "313233". |
||
241 | */ |
||
242 | define('DB_BINMODE_CONVERT', 3); |
||
243 | /**#@-*/ |
||
244 | |||
245 | |||
246 | // }}} |
||
247 | // {{{ fetch modes |
||
248 | |||
249 | |||
250 | /**#@+ |
||
251 | * Fetch Modes. |
||
252 | * @see DB_common::setFetchMode() |
||
253 | */ |
||
254 | |||
255 | /** |
||
256 | * Indicates the current default fetch mode should be used |
||
257 | * @see DB_common::$fetchmode |
||
258 | */ |
||
259 | define('DB_FETCHMODE_DEFAULT', 0); |
||
260 | |||
261 | /** |
||
262 | * Column data indexed by numbers, ordered from 0 and up |
||
263 | */ |
||
264 | define('DB_FETCHMODE_ORDERED', 1); |
||
265 | |||
266 | /** |
||
267 | * Column data indexed by column names |
||
268 | */ |
||
269 | define('DB_FETCHMODE_ASSOC', 2); |
||
270 | |||
271 | /** |
||
272 | * Column data as object properties |
||
273 | */ |
||
274 | define('DB_FETCHMODE_OBJECT', 3); |
||
275 | |||
276 | /** |
||
277 | * For multi-dimensional results, make the column name the first level |
||
278 | * of the array and put the row number in the second level of the array |
||
279 | * |
||
280 | * This is flipped from the normal behavior, which puts the row numbers |
||
281 | * in the first level of the array and the column names in the second level. |
||
282 | */ |
||
283 | define('DB_FETCHMODE_FLIPPED', 4); |
||
284 | /**#@-*/ |
||
285 | |||
286 | /**#@+ |
||
287 | * Old fetch modes. Left here for compatibility. |
||
288 | */ |
||
289 | define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED); |
||
290 | define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC); |
||
291 | define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED); |
||
292 | /**#@-*/ |
||
293 | |||
294 | |||
295 | // }}} |
||
296 | // {{{ tableInfo() && autoPrepare()-related |
||
297 | |||
298 | |||
299 | /**#@+ |
||
300 | * The type of information to return from the tableInfo() method. |
||
301 | * |
||
302 | * Bitwised constants, so they can be combined using <kbd>|</kbd> |
||
303 | * and removed using <kbd>^</kbd>. |
||
304 | * |
||
305 | * @see DB_common::tableInfo() |
||
306 | * |
||
307 | * {@internal Since the TABLEINFO constants are bitwised, if more of them are |
||
308 | * added in the future, make sure to adjust DB_TABLEINFO_FULL accordingly.}} |
||
309 | */ |
||
310 | define('DB_TABLEINFO_ORDER', 1); |
||
311 | define('DB_TABLEINFO_ORDERTABLE', 2); |
||
312 | define('DB_TABLEINFO_FULL', 3); |
||
313 | /**#@-*/ |
||
314 | |||
315 | |||
316 | /**#@+ |
||
317 | * The type of query to create with the automatic query building methods. |
||
318 | * @see DB_common::autoPrepare(), DB_common::autoExecute() |
||
319 | */ |
||
320 | define('DB_AUTOQUERY_INSERT', 1); |
||
321 | define('DB_AUTOQUERY_UPDATE', 2); |
||
322 | /**#@-*/ |
||
323 | |||
324 | |||
325 | // }}} |
||
326 | // {{{ portability modes |
||
327 | |||
328 | |||
329 | /**#@+ |
||
330 | * Portability Modes. |
||
331 | * |
||
332 | * Bitwised constants, so they can be combined using <kbd>|</kbd> |
||
333 | * and removed using <kbd>^</kbd>. |
||
334 | * |
||
335 | * @see DB_common::setOption() |
||
336 | * |
||
337 | * {@internal Since the PORTABILITY constants are bitwised, if more of them are |
||
338 | * added in the future, make sure to adjust DB_PORTABILITY_ALL accordingly.}} |
||
339 | */ |
||
340 | |||
341 | /** |
||
342 | * Turn off all portability features |
||
343 | */ |
||
344 | define('DB_PORTABILITY_NONE', 0); |
||
345 | |||
346 | /** |
||
347 | * Convert names of tables and fields to lower case |
||
348 | * when using the get*(), fetch*() and tableInfo() methods |
||
349 | */ |
||
350 | define('DB_PORTABILITY_LOWERCASE', 1); |
||
351 | |||
352 | /** |
||
353 | * Right trim the data output by get*() and fetch*() |
||
354 | */ |
||
355 | define('DB_PORTABILITY_RTRIM', 2); |
||
356 | |||
357 | /** |
||
358 | * Force reporting the number of rows deleted |
||
359 | */ |
||
360 | define('DB_PORTABILITY_DELETE_COUNT', 4); |
||
361 | |||
362 | /** |
||
363 | * Enable hack that makes numRows() work in Oracle |
||
364 | */ |
||
365 | define('DB_PORTABILITY_NUMROWS', 8); |
||
366 | |||
367 | /** |
||
368 | * Makes certain error messages in certain drivers compatible |
||
369 | * with those from other DBMS's |
||
370 | * |
||
371 | * + mysql, mysqli: change unique/primary key constraints |
||
372 | * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT |
||
373 | * |
||
374 | * + odbc(access): MS's ODBC driver reports 'no such field' as code |
||
375 | * 07001, which means 'too few parameters.' When this option is on |
||
376 | * that code gets mapped to DB_ERROR_NOSUCHFIELD. |
||
377 | */ |
||
378 | define('DB_PORTABILITY_ERRORS', 16); |
||
379 | |||
380 | /** |
||
381 | * Convert null values to empty strings in data output by |
||
382 | * get*() and fetch*() |
||
383 | */ |
||
384 | define('DB_PORTABILITY_NULL_TO_EMPTY', 32); |
||
385 | |||
386 | /** |
||
387 | * Turn on all portability features |
||
388 | */ |
||
389 | define('DB_PORTABILITY_ALL', 63); |
||
390 | /**#@-*/ |
||
391 | |||
392 | // }}} |
||
393 | |||
394 | |||
395 | // }}} |
||
396 | // {{{ class DB |
||
397 | |||
398 | /** |
||
399 | * Database independent query interface |
||
400 | * |
||
401 | * The main "DB" class is simply a container class with some static |
||
402 | * methods for creating DB objects as well as some utility functions |
||
403 | * common to all parts of DB. |
||
404 | * |
||
405 | * The object model of DB is as follows (indentation means inheritance): |
||
406 | * <pre> |
||
407 | * DB The main DB class. This is simply a utility class |
||
408 | * with some "static" methods for creating DB objects as |
||
409 | * well as common utility functions for other DB classes. |
||
410 | * |
||
411 | * DB_common The base for each DB implementation. Provides default |
||
412 | * | implementations (in OO lingo virtual methods) for |
||
413 | * | the actual DB implementations as well as a bunch of |
||
414 | * | query utility functions. |
||
415 | * | |
||
416 | * +-DB_mysql The DB implementation for MySQL. Inherits DB_common. |
||
417 | * When calling DB::factory or DB::connect for MySQL |
||
418 | * connections, the object returned is an instance of this |
||
419 | * class. |
||
420 | * </pre> |
||
421 | * |
||
422 | * @category Database |
||
423 | * @package DB |
||
424 | * @author Stig Bakken <ssb@php.net> |
||
425 | * @author Tomas V.V.Cox <cox@idecnet.com> |
||
426 | * @author Daniel Convissor <danielc@php.net> |
||
427 | * @copyright 1997-2007 The PHP Group |
||
428 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
429 | * @version Release: 1.7.13 |
||
430 | * @link http://pear.php.net/package/DB |
||
431 | */ |
||
432 | class DB |
||
433 | { |
||
434 | // {{{ &factory() |
||
435 | |||
436 | /** |
||
437 | * Create a new DB object for the specified database type but don't |
||
438 | * connect to the database |
||
439 | * |
||
440 | * @param string $type the database type (eg "mysql") |
||
441 | * @param array $options an associative array of option names and values |
||
442 | * |
||
443 | * @return object a new DB object. A DB_Error object on failure. |
||
444 | * |
||
445 | * @see DB_common::setOption() |
||
446 | */ |
||
447 | function &factory($type, $options = false) |
||
448 | { |
||
449 | if (!is_array($options)) { |
||
450 | $options = array('persistent' => $options); |
||
451 | } |
||
452 | |||
453 | if (isset($options['debug']) && $options['debug'] >= 2) { |
||
454 | // expose php errors with sufficient debug level |
||
455 | include_once "DB/{$type}.php"; |
||
456 | } else { |
||
457 | @include_once "DB/{$type}.php"; |
||
458 | } |
||
459 | |||
460 | $classname = "DB_${type}"; |
||
461 | |||
462 | if (!class_exists($classname)) { |
||
463 | $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, |
||
464 | "Unable to include the DB/{$type}.php" |
||
465 | . " file for '$dsn'", |
||
466 | 'DB_Error', true); |
||
467 | return $tmp; |
||
468 | } |
||
469 | |||
470 | @$obj = new $classname; |
||
471 | |||
472 | foreach ($options as $option => $value) { |
||
473 | $test = $obj->setOption($option, $value); |
||
474 | if (DB::isError($test)) { |
||
475 | return $test; |
||
476 | } |
||
477 | } |
||
478 | |||
479 | return $obj; |
||
480 | } |
||
481 | |||
482 | // }}} |
||
483 | // {{{ &connect() |
||
484 | |||
485 | /** |
||
486 | * Create a new DB object including a connection to the specified database |
||
487 | * |
||
488 | * Example 1. |
||
489 | * <code> |
||
490 | * require_once 'DB.php'; |
||
491 | * |
||
492 | * $dsn = 'pgsql://user:password@host/database'; |
||
493 | * $options = array( |
||
494 | * 'debug' => 2, |
||
495 | * 'portability' => DB_PORTABILITY_ALL, |
||
496 | * ); |
||
497 | * |
||
498 | * $db =& DB::connect($dsn, $options); |
||
499 | * if (PEAR::isError($db)) { |
||
500 | * die($db->getMessage()); |
||
501 | * } |
||
502 | * </code> |
||
503 | * |
||
504 | * @param mixed $dsn the string "data source name" or array in the |
||
505 | * format returned by DB::parseDSN() |
||
506 | * @param array $options an associative array of option names and values |
||
507 | * |
||
508 | * @return object a new DB object. A DB_Error object on failure. |
||
509 | * |
||
510 | * @uses DB_dbase::connect(), DB_fbsql::connect(), DB_ibase::connect(), |
||
511 | * DB_ifx::connect(), DB_msql::connect(), DB_mssql::connect(), |
||
512 | * DB_mysql::connect(), DB_mysqli::connect(), DB_oci8::connect(), |
||
513 | * DB_odbc::connect(), DB_pgsql::connect(), DB_sqlite::connect(), |
||
514 | * DB_sybase::connect() |
||
515 | * |
||
516 | * @uses DB::parseDSN(), DB_common::setOption(), PEAR::isError() |
||
517 | */ |
||
518 | function &connect($dsn, $options = array()) |
||
519 | { |
||
520 | $dsninfo = DB::parseDSN($dsn); |
||
521 | $type = $dsninfo['phptype']; |
||
522 | |||
523 | if (!is_array($options)) { |
||
524 | /* |
||
525 | * For backwards compatibility. $options used to be boolean, |
||
526 | * indicating whether the connection should be persistent. |
||
527 | */ |
||
528 | $options = array('persistent' => $options); |
||
529 | } |
||
530 | |||
531 | if (isset($options['debug']) && $options['debug'] >= 2) { |
||
532 | // expose php errors with sufficient debug level |
||
533 | include_once "DB/${type}.php"; |
||
534 | } else { |
||
535 | @include_once "DB/${type}.php"; |
||
536 | } |
||
537 | |||
538 | $classname = "DB_${type}"; |
||
539 | if (!class_exists($classname)) { |
||
540 | $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, |
||
541 | "Unable to include the DB/{$type}.php" |
||
542 | . " file for '$dsn'", |
||
543 | 'DB_Error', true); |
||
544 | return $tmp; |
||
545 | } |
||
546 | |||
547 | @$obj = new $classname; |
||
548 | |||
549 | foreach ($options as $option => $value) { |
||
550 | $test = $obj->setOption($option, $value); |
||
551 | if (DB::isError($test)) { |
||
552 | return $test; |
||
553 | } |
||
554 | } |
||
555 | |||
556 | $err = $obj->connect($dsninfo, $obj->getOption('persistent')); |
||
557 | if (DB::isError($err)) { |
||
558 | if (is_array($dsn)) { |
||
559 | $err->addUserInfo(DB::getDSNString($dsn, true)); |
||
560 | } else { |
||
561 | $err->addUserInfo($dsn); |
||
562 | } |
||
563 | return $err; |
||
564 | } |
||
565 | |||
566 | return $obj; |
||
567 | } |
||
568 | |||
569 | // }}} |
||
570 | // {{{ apiVersion() |
||
571 | |||
572 | /** |
||
573 | * Return the DB API version |
||
574 | * |
||
575 | * @return string the DB API version number |
||
576 | */ |
||
577 | function apiVersion() |
||
578 | { |
||
579 | return '1.7.13'; |
||
580 | } |
||
581 | |||
582 | // }}} |
||
583 | // {{{ isError() |
||
584 | |||
585 | /** |
||
586 | * Determines if a variable is a DB_Error object |
||
587 | * |
||
588 | * @param mixed $value the variable to check |
||
589 | * |
||
590 | * @return bool whether $value is DB_Error object |
||
591 | */ |
||
592 | function isError($value) |
||
593 | { |
||
594 | return is_a($value, 'DB_Error'); |
||
595 | } |
||
596 | |||
597 | // }}} |
||
598 | // {{{ isConnection() |
||
599 | |||
600 | /** |
||
601 | * Determines if a value is a DB_<driver> object |
||
602 | * |
||
603 | * @param mixed $value the value to test |
||
604 | * |
||
605 | * @return bool whether $value is a DB_<driver> object |
||
606 | */ |
||
607 | function isConnection($value) |
||
608 | { |
||
609 | return (is_object($value) && |
||
610 | is_subclass_of($value, 'db_common') && |
||
611 | method_exists($value, 'simpleQuery')); |
||
612 | } |
||
613 | |||
614 | // }}} |
||
615 | // {{{ isManip() |
||
616 | |||
617 | /** |
||
618 | * Tell whether a query is a data manipulation or data definition query |
||
619 | * |
||
620 | * Examples of data manipulation queries are INSERT, UPDATE and DELETE. |
||
621 | * Examples of data definition queries are CREATE, DROP, ALTER, GRANT, |
||
622 | * REVOKE. |
||
623 | * |
||
624 | * @param string $query the query |
||
625 | * |
||
626 | * @return boolean whether $query is a data manipulation query |
||
627 | */ |
||
628 | function isManip($query) |
||
629 | { |
||
630 | $manips = 'INSERT|UPDATE|DELETE|REPLACE|' |
||
631 | . 'CREATE|DROP|' |
||
632 | . 'LOAD DATA|SELECT .* INTO .* FROM|COPY|' |
||
633 | . 'ALTER|GRANT|REVOKE|' |
||
634 | . 'LOCK|UNLOCK'; |
||
635 | if (preg_match('/^\s*"?(' . $manips . ')\s+/i', $query)) { |
||
636 | return true; |
||
637 | } |
||
638 | return false; |
||
639 | } |
||
640 | |||
641 | // }}} |
||
642 | // {{{ errorMessage() |
||
643 | |||
644 | /** |
||
645 | * Return a textual error message for a DB error code |
||
646 | * |
||
647 | * @param integer $value the DB error code |
||
648 | * |
||
649 | * @return string the error message or false if the error code was |
||
650 | * not recognized |
||
651 | */ |
||
652 | function errorMessage($value) |
||
653 | { |
||
654 | static $errorMessages; |
||
655 | if (!isset($errorMessages)) { |
||
656 | $errorMessages = array( |
||
657 | DB_ERROR => 'unknown error', |
||
658 | DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions', |
||
659 | DB_ERROR_ALREADY_EXISTS => 'already exists', |
||
660 | DB_ERROR_CANNOT_CREATE => 'can not create', |
||
661 | DB_ERROR_CANNOT_DROP => 'can not drop', |
||
662 | DB_ERROR_CONNECT_FAILED => 'connect failed', |
||
663 | DB_ERROR_CONSTRAINT => 'constraint violation', |
||
664 | DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint', |
||
665 | DB_ERROR_DIVZERO => 'division by zero', |
||
666 | DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', |
||
667 | DB_ERROR_INVALID => 'invalid', |
||
668 | DB_ERROR_INVALID_DATE => 'invalid date or time', |
||
669 | DB_ERROR_INVALID_DSN => 'invalid DSN', |
||
670 | DB_ERROR_INVALID_NUMBER => 'invalid number', |
||
671 | DB_ERROR_MISMATCH => 'mismatch', |
||
672 | DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied', |
||
673 | DB_ERROR_NODBSELECTED => 'no database selected', |
||
674 | DB_ERROR_NOSUCHDB => 'no such database', |
||
675 | DB_ERROR_NOSUCHFIELD => 'no such field', |
||
676 | DB_ERROR_NOSUCHTABLE => 'no such table', |
||
677 | DB_ERROR_NOT_CAPABLE => 'DB backend not capable', |
||
678 | DB_ERROR_NOT_FOUND => 'not found', |
||
679 | DB_ERROR_NOT_LOCKED => 'not locked', |
||
680 | DB_ERROR_SYNTAX => 'syntax error', |
||
681 | DB_ERROR_UNSUPPORTED => 'not supported', |
||
682 | DB_ERROR_TRUNCATED => 'truncated', |
||
683 | DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', |
||
684 | DB_OK => 'no error', |
||
685 | ); |
||
686 | } |
||
687 | |||
688 | if (DB::isError($value)) { |
||
689 | $value = $value->getCode(); |
||
690 | } |
||
691 | |||
692 | return isset($errorMessages[$value]) ? $errorMessages[$value] |
||
693 | : $errorMessages[DB_ERROR]; |
||
694 | } |
||
695 | |||
696 | // }}} |
||
697 | // {{{ parseDSN() |
||
698 | |||
699 | /** |
||
700 | * Parse a data source name |
||
701 | * |
||
702 | * Additional keys can be added by appending a URI query string to the |
||
703 | * end of the DSN. |
||
704 | * |
||
705 | * The format of the supplied DSN is in its fullest form: |
||
706 | * <code> |
||
707 | * phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true |
||
708 | * </code> |
||
709 | * |
||
710 | * Most variations are allowed: |
||
711 | * <code> |
||
712 | * phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644 |
||
713 | * phptype://username:password@hostspec/database_name |
||
714 | * phptype://username:password@hostspec |
||
715 | * phptype://username@hostspec |
||
716 | * phptype://hostspec/database |
||
717 | * phptype://hostspec |
||
718 | * phptype(dbsyntax) |
||
719 | * phptype |
||
720 | * </code> |
||
721 | * |
||
722 | * @param string $dsn Data Source Name to be parsed |
||
723 | * |
||
724 | * @return array an associative array with the following keys: |
||
725 | * + phptype: Database backend used in PHP (mysql, odbc etc.) |
||
726 | * + dbsyntax: Database used with regards to SQL syntax etc. |
||
727 | * + protocol: Communication protocol to use (tcp, unix etc.) |
||
728 | * + hostspec: Host specification (hostname[:port]) |
||
729 | * + database: Database to use on the DBMS server |
||
730 | * + username: User name for login |
||
731 | * + password: Password for login |
||
732 | */ |
||
733 | function parseDSN($dsn) |
||
734 | { |
||
735 | $parsed = array( |
||
736 | 'phptype' => false, |
||
737 | 'dbsyntax' => false, |
||
738 | 'username' => false, |
||
739 | 'password' => false, |
||
740 | 'protocol' => false, |
||
741 | 'hostspec' => false, |
||
742 | 'port' => false, |
||
743 | 'socket' => false, |
||
744 | 'database' => false, |
||
745 | ); |
||
746 | |||
747 | if (is_array($dsn)) { |
||
748 | $dsn = array_merge($parsed, $dsn); |
||
749 | if (!$dsn['dbsyntax']) { |
||
750 | $dsn['dbsyntax'] = $dsn['phptype']; |
||
751 | } |
||
752 | return $dsn; |
||
753 | } |
||
754 | |||
755 | // Find phptype and dbsyntax |
||
756 | if (($pos = strpos($dsn, '://')) !== false) { |
||
757 | $str = substr($dsn, 0, $pos); |
||
758 | $dsn = substr($dsn, $pos + 3); |
||
759 | } else { |
||
760 | $str = $dsn; |
||
761 | $dsn = null; |
||
762 | } |
||
763 | |||
764 | // Get phptype and dbsyntax |
||
765 | // $str => phptype(dbsyntax) |
||
766 | if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { |
||
767 | $parsed['phptype'] = $arr[1]; |
||
768 | $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2]; |
||
769 | } else { |
||
770 | $parsed['phptype'] = $str; |
||
771 | $parsed['dbsyntax'] = $str; |
||
772 | } |
||
773 | |||
774 | if (!count($dsn)) { |
||
775 | return $parsed; |
||
776 | } |
||
777 | |||
778 | // Get (if found): username and password |
||
779 | // $dsn => username:password@protocol+hostspec/database |
||
780 | if (($at = strrpos($dsn,'@')) !== false) { |
||
781 | $str = substr($dsn, 0, $at); |
||
782 | $dsn = substr($dsn, $at + 1); |
||
783 | if (($pos = strpos($str, ':')) !== false) { |
||
784 | $parsed['username'] = rawurldecode(substr($str, 0, $pos)); |
||
785 | $parsed['password'] = rawurldecode(substr($str, $pos + 1)); |
||
786 | } else { |
||
787 | $parsed['username'] = rawurldecode($str); |
||
788 | } |
||
789 | } |
||
790 | |||
791 | // Find protocol and hostspec |
||
792 | |||
793 | if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { |
||
794 | // $dsn => proto(proto_opts)/database |
||
795 | $proto = $match[1]; |
||
796 | $proto_opts = $match[2] ? $match[2] : false; |
||
797 | $dsn = $match[3]; |
||
798 | |||
799 | } else { |
||
800 | // $dsn => protocol+hostspec/database (old format) |
||
801 | if (strpos($dsn, '+') !== false) { |
||
802 | list($proto, $dsn) = explode('+', $dsn, 2); |
||
803 | } |
||
804 | if (strpos($dsn, '/') !== false) { |
||
805 | list($proto_opts, $dsn) = explode('/', $dsn, 2); |
||
806 | } else { |
||
807 | $proto_opts = $dsn; |
||
808 | $dsn = null; |
||
809 | } |
||
810 | } |
||
811 | |||
812 | // process the different protocol options |
||
813 | $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp'; |
||
814 | $proto_opts = rawurldecode($proto_opts); |
||
815 | if (strpos($proto_opts, ':') !== false) { |
||
816 | list($proto_opts, $parsed['port']) = explode(':', $proto_opts); |
||
817 | } |
||
818 | if ($parsed['protocol'] == 'tcp') { |
||
819 | $parsed['hostspec'] = $proto_opts; |
||
820 | } elseif ($parsed['protocol'] == 'unix') { |
||
821 | $parsed['socket'] = $proto_opts; |
||
822 | } |
||
823 | |||
824 | // Get dabase if any |
||
825 | // $dsn => database |
||
826 | if ($dsn) { |
||
827 | if (($pos = strpos($dsn, '?')) === false) { |
||
828 | // /database |
||
829 | $parsed['database'] = rawurldecode($dsn); |
||
830 | } else { |
||
831 | // /database?param1=value1¶m2=value2 |
||
832 | $parsed['database'] = rawurldecode(substr($dsn, 0, $pos)); |
||
833 | $dsn = substr($dsn, $pos + 1); |
||
834 | if (strpos($dsn, '&') !== false) { |
||
835 | $opts = explode('&', $dsn); |
||
836 | } else { // database?param1=value1 |
||
837 | $opts = array($dsn); |
||
838 | } |
||
839 | foreach ($opts as $opt) { |
||
840 | list($key, $value) = explode('=', $opt); |
||
841 | if (!isset($parsed[$key])) { |
||
842 | // don't allow params overwrite |
||
843 | $parsed[$key] = rawurldecode($value); |
||
844 | } |
||
845 | } |
||
846 | } |
||
847 | } |
||
848 | |||
849 | return $parsed; |
||
850 | } |
||
851 | |||
852 | // }}} |
||
853 | // {{{ getDSNString() |
||
854 | |||
855 | /** |
||
856 | * Returns the given DSN in a string format suitable for output. |
||
857 | * |
||
858 | * @param array|string the DSN to parse and format |
||
859 | * @param boolean true to hide the password, false to include it |
||
860 | * @return string |
||
861 | */ |
||
862 | function getDSNString($dsn, $hidePassword) { |
||
863 | /* Calling parseDSN will ensure that we have all the array elements |
||
864 | * defined, and means that we deal with strings and array in the same |
||
865 | * manner. */ |
||
866 | $dsnArray = DB::parseDSN($dsn); |
||
867 | |||
868 | if ($hidePassword) { |
||
869 | $dsnArray['password'] = 'PASSWORD'; |
||
870 | } |
||
871 | |||
872 | /* Protocol is special-cased, as using the default "tcp" along with an |
||
873 | * Oracle TNS connection string fails. */ |
||
874 | if (is_string($dsn) && strpos($dsn, 'tcp') === false && $dsnArray['protocol'] == 'tcp') { |
||
875 | $dsnArray['protocol'] = false; |
||
876 | } |
||
877 | |||
878 | // Now we just have to construct the actual string. This is ugly. |
||
879 | $dsnString = $dsnArray['phptype']; |
||
880 | if ($dsnArray['dbsyntax']) { |
||
881 | $dsnString .= '('.$dsnArray['dbsyntax'].')'; |
||
882 | } |
||
883 | $dsnString .= '://' |
||
884 | .$dsnArray['username'] |
||
885 | .':' |
||
886 | .$dsnArray['password'] |
||
887 | .'@' |
||
888 | .$dsnArray['protocol']; |
||
889 | if ($dsnArray['socket']) { |
||
890 | $dsnString .= '('.$dsnArray['socket'].')'; |
||
891 | } |
||
892 | if ($dsnArray['protocol'] && $dsnArray['hostspec']) { |
||
893 | $dsnString .= '+'; |
||
894 | } |
||
895 | $dsnString .= $dsnArray['hostspec']; |
||
896 | if ($dsnArray['port']) { |
||
897 | $dsnString .= ':'.$dsnArray['port']; |
||
898 | } |
||
899 | $dsnString .= '/'.$dsnArray['database']; |
||
900 | |||
901 | /* Option handling. Unfortunately, parseDSN simply places options into |
||
902 | * the top-level array, so we'll first get rid of the fields defined by |
||
903 | * DB and see what's left. */ |
||
904 | unset($dsnArray['phptype'], |
||
905 | $dsnArray['dbsyntax'], |
||
906 | $dsnArray['username'], |
||
907 | $dsnArray['password'], |
||
908 | $dsnArray['protocol'], |
||
909 | $dsnArray['socket'], |
||
910 | $dsnArray['hostspec'], |
||
911 | $dsnArray['port'], |
||
912 | $dsnArray['database'] |
||
913 | ); |
||
914 | if (count($dsnArray) > 0) { |
||
915 | $dsnString .= '?'; |
||
916 | $i = 0; |
||
917 | foreach ($dsnArray as $key => $value) { |
||
918 | if (++$i > 1) { |
||
919 | $dsnString .= '&'; |
||
920 | } |
||
921 | $dsnString .= $key.'='.$value; |
||
922 | } |
||
923 | } |
||
924 | |||
925 | return $dsnString; |
||
926 | } |
||
927 | |||
928 | // }}} |
||
929 | } |
||
930 | |||
931 | // }}} |
||
932 | // {{{ class DB_Error |
||
933 | |||
934 | /** |
||
935 | * DB_Error implements a class for reporting portable database error |
||
936 | * messages |
||
937 | * |
||
938 | * @category Database |
||
939 | * @package DB |
||
940 | * @author Stig Bakken <ssb@php.net> |
||
941 | * @copyright 1997-2007 The PHP Group |
||
942 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
943 | * @version Release: 1.7.13 |
||
944 | * @link http://pear.php.net/package/DB |
||
945 | */ |
||
946 | class DB_Error extends PEAR_Error |
||
947 | { |
||
948 | // {{{ constructor |
||
949 | |||
950 | /** |
||
951 | * DB_Error constructor |
||
952 | * |
||
953 | * @param mixed $code DB error code, or string with error message |
||
954 | * @param int $mode what "error mode" to operate in |
||
955 | * @param int $level what error level to use for $mode & |
||
956 | * PEAR_ERROR_TRIGGER |
||
957 | * @param mixed $debuginfo additional debug info, such as the last query |
||
958 | * |
||
959 | * @see PEAR_Error |
||
960 | */ |
||
961 | function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN, |
||
962 | $level = E_USER_NOTICE, $debuginfo = null) |
||
963 | { |
||
964 | if (is_int($code)) { |
||
965 | $this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, |
||
966 | $mode, $level, $debuginfo); |
||
967 | } else { |
||
968 | $this->PEAR_Error("DB Error: $code", DB_ERROR, |
||
969 | $mode, $level, $debuginfo); |
||
970 | } |
||
971 | } |
||
972 | |||
973 | // }}} |
||
974 | } |
||
975 | |||
976 | // }}} |
||
977 | // {{{ class DB_result |
||
978 | |||
979 | /** |
||
980 | * This class implements a wrapper for a DB result set |
||
981 | * |
||
982 | * A new instance of this class will be returned by the DB implementation |
||
983 | * after processing a query that returns data. |
||
984 | * |
||
985 | * @category Database |
||
986 | * @package DB |
||
987 | * @author Stig Bakken <ssb@php.net> |
||
988 | * @copyright 1997-2007 The PHP Group |
||
989 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
990 | * @version Release: 1.7.13 |
||
991 | * @link http://pear.php.net/package/DB |
||
992 | */ |
||
993 | class DB_result |
||
994 | { |
||
995 | // {{{ properties |
||
996 | |||
997 | /** |
||
998 | * Should results be freed automatically when there are no more rows? |
||
999 | * @var boolean |
||
1000 | * @see DB_common::$options |
||
1001 | */ |
||
1002 | var $autofree; |
||
1003 | |||
1004 | /** |
||
1005 | * A reference to the DB_<driver> object |
||
1006 | * @var object |
||
1007 | */ |
||
1008 | var $dbh; |
||
1009 | |||
1010 | /** |
||
1011 | * The current default fetch mode |
||
1012 | * @var integer |
||
1013 | * @see DB_common::$fetchmode |
||
1014 | */ |
||
1015 | var $fetchmode; |
||
1016 | |||
1017 | /** |
||
1018 | * The name of the class into which results should be fetched when |
||
1019 | * DB_FETCHMODE_OBJECT is in effect |
||
1020 | * |
||
1021 | * @var string |
||
1022 | * @see DB_common::$fetchmode_object_class |
||
1023 | */ |
||
1024 | var $fetchmode_object_class; |
||
1025 | |||
1026 | /** |
||
1027 | * The number of rows to fetch from a limit query |
||
1028 | * @var integer |
||
1029 | */ |
||
1030 | var $limit_count = null; |
||
1031 | |||
1032 | /** |
||
1033 | * The row to start fetching from in limit queries |
||
1034 | * @var integer |
||
1035 | */ |
||
1036 | var $limit_from = null; |
||
1037 | |||
1038 | /** |
||
1039 | * The execute parameters that created this result |
||
1040 | * @var array |
||
1041 | * @since Property available since Release 1.7.0 |
||
1042 | */ |
||
1043 | var $parameters; |
||
1044 | |||
1045 | /** |
||
1046 | * The query string that created this result |
||
1047 | * |
||
1048 | * Copied here incase it changes in $dbh, which is referenced |
||
1049 | * |
||
1050 | * @var string |
||
1051 | * @since Property available since Release 1.7.0 |
||
1052 | */ |
||
1053 | var $query; |
||
1054 | |||
1055 | /** |
||
1056 | * The query result resource id created by PHP |
||
1057 | * @var resource |
||
1058 | */ |
||
1059 | var $result; |
||
1060 | |||
1061 | /** |
||
1062 | * The present row being dealt with |
||
1063 | * @var integer |
||
1064 | */ |
||
1065 | var $row_counter = null; |
||
1066 | |||
1067 | /** |
||
1068 | * The prepared statement resource id created by PHP in $dbh |
||
1069 | * |
||
1070 | * This resource is only available when the result set was created using |
||
1071 | * a driver's native execute() method, not PEAR DB's emulated one. |
||
1072 | * |
||
1073 | * Copied here incase it changes in $dbh, which is referenced |
||
1074 | * |
||
1075 | * {@internal Mainly here because the InterBase/Firebird API is only |
||
1076 | * able to retrieve data from result sets if the statemnt handle is |
||
1077 | * still in scope.}} |
||
1078 | * |
||
1079 | * @var resource |
||
1080 | * @since Property available since Release 1.7.0 |
||
1081 | */ |
||
1082 | var $statement; |
||
1083 | |||
1084 | |||
1085 | // }}} |
||
1086 | // {{{ constructor |
||
1087 | |||
1088 | /** |
||
1089 | * This constructor sets the object's properties |
||
1090 | * |
||
1091 | * @param object &$dbh the DB object reference |
||
1092 | * @param resource $result the result resource id |
||
1093 | * @param array $options an associative array with result options |
||
1094 | * |
||
1095 | * @return void |
||
1096 | */ |
||
1097 | function DB_result(&$dbh, $result, $options = array()) |
||
1098 | { |
||
1099 | $this->autofree = $dbh->options['autofree']; |
||
1100 | $this->dbh = &$dbh; |
||
1101 | $this->fetchmode = $dbh->fetchmode; |
||
1102 | $this->fetchmode_object_class = $dbh->fetchmode_object_class; |
||
1103 | $this->parameters = $dbh->last_parameters; |
||
1104 | $this->query = $dbh->last_query; |
||
1105 | $this->result = $result; |
||
1106 | $this->statement = empty($dbh->last_stmt) ? null : $dbh->last_stmt; |
||
1107 | foreach ($options as $key => $value) { |
||
1108 | $this->setOption($key, $value); |
||
1109 | } |
||
1110 | } |
||
1111 | |||
1112 | /** |
||
1113 | * Set options for the DB_result object |
||
1114 | * |
||
1115 | * @param string $key the option to set |
||
1116 | * @param mixed $value the value to set the option to |
||
1117 | * |
||
1118 | * @return void |
||
1119 | */ |
||
1120 | function setOption($key, $value = null) |
||
1121 | { |
||
1122 | switch ($key) { |
||
1123 | case 'limit_from': |
||
1124 | $this->limit_from = $value; |
||
1125 | break; |
||
1126 | case 'limit_count': |
||
1127 | $this->limit_count = $value; |
||
1128 | } |
||
1129 | } |
||
1130 | |||
1131 | // }}} |
||
1132 | // {{{ fetchRow() |
||
1133 | |||
1134 | /** |
||
1135 | * Fetch a row of data and return it by reference into an array |
||
1136 | * |
||
1137 | * The type of array returned can be controlled either by setting this |
||
1138 | * method's <var>$fetchmode</var> parameter or by changing the default |
||
1139 | * fetch mode setFetchMode() before calling this method. |
||
1140 | * |
||
1141 | * There are two options for standardizing the information returned |
||
1142 | * from databases, ensuring their values are consistent when changing |
||
1143 | * DBMS's. These portability options can be turned on when creating a |
||
1144 | * new DB object or by using setOption(). |
||
1145 | * |
||
1146 | * + <var>DB_PORTABILITY_LOWERCASE</var> |
||
1147 | * convert names of fields to lower case |
||
1148 | * |
||
1149 | * + <var>DB_PORTABILITY_RTRIM</var> |
||
1150 | * right trim the data |
||
1151 | * |
||
1152 | * @param int $fetchmode the constant indicating how to format the data |
||
1153 | * @param int $rownum the row number to fetch (index starts at 0) |
||
1154 | * |
||
1155 | * @return mixed an array or object containing the row's data, |
||
1156 | * NULL when the end of the result set is reached |
||
1157 | * or a DB_Error object on failure. |
||
1158 | * |
||
1159 | * @see DB_common::setOption(), DB_common::setFetchMode() |
||
1160 | */ |
||
1161 | function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) |
||
1162 | { |
||
1163 | if ($fetchmode === DB_FETCHMODE_DEFAULT) { |
||
1164 | $fetchmode = $this->fetchmode; |
||
1165 | } |
||
1166 | if ($fetchmode === DB_FETCHMODE_OBJECT) { |
||
1167 | $fetchmode = DB_FETCHMODE_ASSOC; |
||
1168 | $object_class = $this->fetchmode_object_class; |
||
1169 | } |
||
1170 | if (is_null($rownum) && $this->limit_from !== null) { |
||
1171 | if ($this->row_counter === null) { |
||
1172 | $this->row_counter = $this->limit_from; |
||
1173 | // Skip rows |
||
1174 | if ($this->dbh->features['limit'] === false) { |
||
1175 | $i = 0; |
||
1176 | while ($i++ < $this->limit_from) { |
||
1177 | $this->dbh->fetchInto($this->result, $arr, $fetchmode); |
||
1178 | } |
||
1179 | } |
||
1180 | } |
||
1181 | if ($this->row_counter >= ($this->limit_from + $this->limit_count)) |
||
1182 | { |
||
1183 | if ($this->autofree) { |
||
1184 | $this->free(); |
||
1185 | } |
||
1186 | $tmp = null; |
||
1187 | return $tmp; |
||
1188 | } |
||
1189 | if ($this->dbh->features['limit'] === 'emulate') { |
||
1190 | $rownum = $this->row_counter; |
||
1191 | } |
||
1192 | $this->row_counter++; |
||
1193 | } |
||
1194 | $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); |
||
1195 | if ($res === DB_OK) { |
||
1196 | if (isset($object_class)) { |
||
1197 | // The default mode is specified in the |
||
1198 | // DB_common::fetchmode_object_class property |
||
1199 | if ($object_class == 'stdClass') { |
||
1200 | $arr = (object) $arr; |
||
1201 | } else { |
||
1202 | $arr = new $object_class($arr); |
||
1203 | } |
||
1204 | } |
||
1205 | return $arr; |
||
1206 | } |
||
1207 | if ($res == null && $this->autofree) { |
||
1208 | $this->free(); |
||
1209 | } |
||
1210 | return $res; |
||
1211 | } |
||
1212 | |||
1213 | // }}} |
||
1214 | // {{{ fetchInto() |
||
1215 | |||
1216 | /** |
||
1217 | * Fetch a row of data into an array which is passed by reference |
||
1218 | * |
||
1219 | * The type of array returned can be controlled either by setting this |
||
1220 | * method's <var>$fetchmode</var> parameter or by changing the default |
||
1221 | * fetch mode setFetchMode() before calling this method. |
||
1222 | * |
||
1223 | * There are two options for standardizing the information returned |
||
1224 | * from databases, ensuring their values are consistent when changing |
||
1225 | * DBMS's. These portability options can be turned on when creating a |
||
1226 | * new DB object or by using setOption(). |
||
1227 | * |
||
1228 | * + <var>DB_PORTABILITY_LOWERCASE</var> |
||
1229 | * convert names of fields to lower case |
||
1230 | * |
||
1231 | * + <var>DB_PORTABILITY_RTRIM</var> |
||
1232 | * right trim the data |
||
1233 | * |
||
1234 | * @param array &$arr the variable where the data should be placed |
||
1235 | * @param int $fetchmode the constant indicating how to format the data |
||
1236 | * @param int $rownum the row number to fetch (index starts at 0) |
||
1237 | * |
||
1238 | * @return mixed DB_OK if a row is processed, NULL when the end of the |
||
1239 | * result set is reached or a DB_Error object on failure |
||
1240 | * |
||
1241 | * @see DB_common::setOption(), DB_common::setFetchMode() |
||
1242 | */ |
||
1243 | function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) |
||
1244 | { |
||
1245 | if ($fetchmode === DB_FETCHMODE_DEFAULT) { |
||
1246 | $fetchmode = $this->fetchmode; |
||
1247 | } |
||
1248 | if ($fetchmode === DB_FETCHMODE_OBJECT) { |
||
1249 | $fetchmode = DB_FETCHMODE_ASSOC; |
||
1250 | $object_class = $this->fetchmode_object_class; |
||
1251 | } |
||
1252 | if (is_null($rownum) && $this->limit_from !== null) { |
||
1253 | if ($this->row_counter === null) { |
||
1254 | $this->row_counter = $this->limit_from; |
||
1255 | // Skip rows |
||
1256 | if ($this->dbh->features['limit'] === false) { |
||
1257 | $i = 0; |
||
1258 | while ($i++ < $this->limit_from) { |
||
1259 | $this->dbh->fetchInto($this->result, $arr, $fetchmode); |
||
1260 | } |
||
1261 | } |
||
1262 | } |
||
1263 | if ($this->row_counter >= ( |
||
1264 | $this->limit_from + $this->limit_count)) |
||
1265 | { |
||
1266 | if ($this->autofree) { |
||
1267 | $this->free(); |
||
1268 | } |
||
1269 | return null; |
||
1270 | } |
||
1271 | if ($this->dbh->features['limit'] === 'emulate') { |
||
1272 | $rownum = $this->row_counter; |
||
1273 | } |
||
1274 | |||
1275 | $this->row_counter++; |
||
1276 | } |
||
1277 | $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); |
||
1278 | if ($res === DB_OK) { |
||
1279 | if (isset($object_class)) { |
||
1280 | // default mode specified in the |
||
1281 | // DB_common::fetchmode_object_class property |
||
1282 | if ($object_class == 'stdClass') { |
||
1283 | $arr = (object) $arr; |
||
1284 | } else { |
||
1285 | $arr = new $object_class($arr); |
||
1286 | } |
||
1287 | } |
||
1288 | return DB_OK; |
||
1289 | } |
||
1290 | if ($res == null && $this->autofree) { |
||
1291 | $this->free(); |
||
1292 | } |
||
1293 | return $res; |
||
1294 | } |
||
1295 | |||
1296 | // }}} |
||
1297 | // {{{ numCols() |
||
1298 | |||
1299 | /** |
||
1300 | * Get the the number of columns in a result set |
||
1301 | * |
||
1302 | * @return int the number of columns. A DB_Error object on failure. |
||
1303 | */ |
||
1304 | function numCols() |
||
1305 | { |
||
1306 | return $this->dbh->numCols($this->result); |
||
1307 | } |
||
1308 | |||
1309 | // }}} |
||
1310 | // {{{ numRows() |
||
1311 | |||
1312 | /** |
||
1313 | * Get the number of rows in a result set |
||
1314 | * |
||
1315 | * @return int the number of rows. A DB_Error object on failure. |
||
1316 | */ |
||
1317 | function numRows() |
||
1318 | { |
||
1319 | if ($this->dbh->features['numrows'] === 'emulate' |
||
1320 | && $this->dbh->options['portability'] & DB_PORTABILITY_NUMROWS) |
||
1321 | { |
||
1322 | if ($this->dbh->features['prepare']) { |
||
1323 | $res = $this->dbh->query($this->query, $this->parameters); |
||
1324 | } else { |
||
1325 | $res = $this->dbh->query($this->query); |
||
1326 | } |
||
1327 | if (DB::isError($res)) { |
||
1328 | return $res; |
||
1329 | } |
||
1330 | $i = 0; |
||
1331 | while ($res->fetchInto($tmp, DB_FETCHMODE_ORDERED)) { |
||
1332 | $i++; |
||
1333 | } |
||
1334 | $count = $i; |
||
1335 | } else { |
||
1336 | $count = $this->dbh->numRows($this->result); |
||
1337 | } |
||
1338 | |||
1339 | /* fbsql is checked for here because limit queries are implemented |
||
1340 | * using a TOP() function, which results in fbsql_num_rows still |
||
1341 | * returning the total number of rows that would have been returned, |
||
1342 | * rather than the real number. As a result, we'll just do the limit |
||
1343 | * calculations for fbsql in the same way as a database with emulated |
||
1344 | * limits. Unfortunately, we can't just do this in DB_fbsql::numRows() |
||
1345 | * because that only gets the result resource, rather than the full |
||
1346 | * DB_Result object. */ |
||
1347 | if (($this->dbh->features['limit'] === 'emulate' |
||
1348 | && $this->limit_from !== null) |
||
1349 | || $this->dbh->phptype == 'fbsql') { |
||
1350 | $limit_count = is_null($this->limit_count) ? $count : $this->limit_count; |
||
1351 | if ($count < $this->limit_from) { |
||
1352 | $count = 0; |
||
1353 | } elseif ($count < ($this->limit_from + $limit_count)) { |
||
1354 | $count -= $this->limit_from; |
||
1355 | } else { |
||
1356 | $count = $limit_count; |
||
1357 | } |
||
1358 | } |
||
1359 | |||
1360 | return $count; |
||
1361 | } |
||
1362 | |||
1363 | // }}} |
||
1364 | // {{{ nextResult() |
||
1365 | |||
1366 | /** |
||
1367 | * Get the next result if a batch of queries was executed |
||
1368 | * |
||
1369 | * @return bool true if a new result is available or false if not |
||
1370 | */ |
||
1371 | function nextResult() |
||
1372 | { |
||
1373 | return $this->dbh->nextResult($this->result); |
||
1374 | } |
||
1375 | |||
1376 | // }}} |
||
1377 | // {{{ free() |
||
1378 | |||
1379 | /** |
||
1380 | * Frees the resources allocated for this result set |
||
1381 | * |
||
1382 | * @return bool true on success. A DB_Error object on failure. |
||
1383 | */ |
||
1384 | function free() |
||
1385 | { |
||
1386 | $err = $this->dbh->freeResult($this->result); |
||
1387 | if (DB::isError($err)) { |
||
1388 | return $err; |
||
1389 | } |
||
1390 | $this->result = false; |
||
1391 | $this->statement = false; |
||
1392 | return true; |
||
1393 | } |
||
1394 | |||
1395 | // }}} |
||
1396 | // {{{ tableInfo() |
||
1397 | |||
1398 | /** |
||
1399 | * @see DB_common::tableInfo() |
||
1400 | * @deprecated Method deprecated some time before Release 1.2 |
||
1401 | */ |
||
1402 | function tableInfo($mode = null) |
||
1403 | { |
||
1404 | if (is_string($mode)) { |
||
1405 | return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA); |
||
1406 | } |
||
1407 | return $this->dbh->tableInfo($this, $mode); |
||
1408 | } |
||
1409 | |||
1410 | // }}} |
||
1411 | // {{{ getQuery() |
||
1412 | |||
1413 | /** |
||
1414 | * Determine the query string that created this result |
||
1415 | * |
||
1416 | * @return string the query string |
||
1417 | * |
||
1418 | * @since Method available since Release 1.7.0 |
||
1419 | */ |
||
1420 | function getQuery() |
||
1421 | { |
||
1422 | return $this->query; |
||
1423 | } |
||
1424 | |||
1425 | // }}} |
||
1426 | // {{{ getRowCounter() |
||
1427 | |||
1428 | /** |
||
1429 | * Tells which row number is currently being processed |
||
1430 | * |
||
1431 | * @return integer the current row being looked at. Starts at 1. |
||
1432 | */ |
||
1433 | function getRowCounter() |
||
1434 | { |
||
1435 | return $this->row_counter; |
||
1436 | } |
||
1437 | |||
1438 | // }}} |
||
1439 | } |
||
1440 | |||
1441 | // }}} |
||
1442 | // {{{ class DB_row |
||
1443 | |||
1444 | /** |
||
1445 | * PEAR DB Row Object |
||
1446 | * |
||
1447 | * The object contains a row of data from a result set. Each column's data |
||
1448 | * is placed in a property named for the column. |
||
1449 | * |
||
1450 | * @category Database |
||
1451 | * @package DB |
||
1452 | * @author Stig Bakken <ssb@php.net> |
||
1453 | * @copyright 1997-2007 The PHP Group |
||
1454 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
1455 | * @version Release: 1.7.13 |
||
1456 | * @link http://pear.php.net/package/DB |
||
1457 | * @see DB_common::setFetchMode() |
||
1458 | */ |
||
1459 | class DB_row |
||
1460 | { |
||
1461 | // {{{ constructor |
||
1462 | |||
1463 | /** |
||
1464 | * The constructor places a row's data into properties of this object |
||
1465 | * |
||
1466 | * @param array the array containing the row's data |
||
1467 | * |
||
1468 | * @return void |
||
1469 | */ |
||
1470 | function DB_row(&$arr) |
||
1471 | { |
||
1472 | foreach ($arr as $key => $value) { |
||
1473 | $this->$key = &$arr[$key]; |
||
1474 | } |
||
1475 | } |
||
1476 | |||
1477 | // }}} |
||
1478 | } |
||
1479 | |||
1480 | // }}} |
||
1481 | |||
1482 | /* |
||
1483 | * Local variables: |
||
1484 | * tab-width: 4 |
||
1485 | * c-basic-offset: 4 |
||
1486 | * End: |
||
1487 | */ |
||
1488 | |||
1489 | ?> |