1<?php 2// vim: set et ts=4 sw=4 fdm=marker: 3// +----------------------------------------------------------------------+ 4// | PHP versions 4 and 5 | 5// +----------------------------------------------------------------------+ 6// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, | 7// | Stig. S. Bakken, Lukas Smith | 8// | All rights reserved. | 9// +----------------------------------------------------------------------+ 10// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB | 11// | API as well as database abstraction for PHP applications. | 12// | This LICENSE is in the BSD license style. | 13// | | 14// | Redistribution and use in source and binary forms, with or without | 15// | modification, are permitted provided that the following conditions | 16// | are met: | 17// | | 18// | Redistributions of source code must retain the above copyright | 19// | notice, this list of conditions and the following disclaimer. | 20// | | 21// | Redistributions in binary form must reproduce the above copyright | 22// | notice, this list of conditions and the following disclaimer in the | 23// | documentation and/or other materials provided with the distribution. | 24// | | 25// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, | 26// | Lukas Smith nor the names of his contributors may be used to endorse | 27// | or promote products derived from this software without specific prior| 28// | written permission. | 29// | | 30// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 31// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 32// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 33// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | 34// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | 35// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | 36// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS| 37// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | 38// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 39// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY| 40// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 41// | POSSIBILITY OF SUCH DAMAGE. | 42// +----------------------------------------------------------------------+ 43// | Author: Lukas Smith <smith@pooteeweet.org> | 44// +----------------------------------------------------------------------+ 45// 46// $Id: MDB2.php 328183 2012-10-29 15:10:42Z danielc $ 47// 48 49/** 50 * @package MDB2 51 * @category Database 52 * @author Lukas Smith <smith@pooteeweet.org> 53 */ 54 55require_once 'PEAR.php'; 56 57// {{{ Error constants 58 59/** 60 * The method mapErrorCode in each MDB2_dbtype implementation maps 61 * native error codes to one of these. 62 * 63 * If you add an error code here, make sure you also add a textual 64 * version of it in MDB2::errorMessage(). 65 */ 66 67define('MDB2_OK', true); 68define('MDB2_ERROR', -1); 69define('MDB2_ERROR_SYNTAX', -2); 70define('MDB2_ERROR_CONSTRAINT', -3); 71define('MDB2_ERROR_NOT_FOUND', -4); 72define('MDB2_ERROR_ALREADY_EXISTS', -5); 73define('MDB2_ERROR_UNSUPPORTED', -6); 74define('MDB2_ERROR_MISMATCH', -7); 75define('MDB2_ERROR_INVALID', -8); 76define('MDB2_ERROR_NOT_CAPABLE', -9); 77define('MDB2_ERROR_TRUNCATED', -10); 78define('MDB2_ERROR_INVALID_NUMBER', -11); 79define('MDB2_ERROR_INVALID_DATE', -12); 80define('MDB2_ERROR_DIVZERO', -13); 81define('MDB2_ERROR_NODBSELECTED', -14); 82define('MDB2_ERROR_CANNOT_CREATE', -15); 83define('MDB2_ERROR_CANNOT_DELETE', -16); 84define('MDB2_ERROR_CANNOT_DROP', -17); 85define('MDB2_ERROR_NOSUCHTABLE', -18); 86define('MDB2_ERROR_NOSUCHFIELD', -19); 87define('MDB2_ERROR_NEED_MORE_DATA', -20); 88define('MDB2_ERROR_NOT_LOCKED', -21); 89define('MDB2_ERROR_VALUE_COUNT_ON_ROW', -22); 90define('MDB2_ERROR_INVALID_DSN', -23); 91define('MDB2_ERROR_CONNECT_FAILED', -24); 92define('MDB2_ERROR_EXTENSION_NOT_FOUND',-25); 93define('MDB2_ERROR_NOSUCHDB', -26); 94define('MDB2_ERROR_ACCESS_VIOLATION', -27); 95define('MDB2_ERROR_CANNOT_REPLACE', -28); 96define('MDB2_ERROR_CONSTRAINT_NOT_NULL',-29); 97define('MDB2_ERROR_DEADLOCK', -30); 98define('MDB2_ERROR_CANNOT_ALTER', -31); 99define('MDB2_ERROR_MANAGER', -32); 100define('MDB2_ERROR_MANAGER_PARSE', -33); 101define('MDB2_ERROR_LOADMODULE', -34); 102define('MDB2_ERROR_INSUFFICIENT_DATA', -35); 103define('MDB2_ERROR_NO_PERMISSION', -36); 104define('MDB2_ERROR_DISCONNECT_FAILED', -37); 105 106// }}} 107// {{{ Verbose constants 108/** 109 * These are just helper constants to more verbosely express parameters to prepare() 110 */ 111 112define('MDB2_PREPARE_MANIP', false); 113define('MDB2_PREPARE_RESULT', null); 114 115// }}} 116// {{{ Fetchmode constants 117 118/** 119 * This is a special constant that tells MDB2 the user hasn't specified 120 * any particular get mode, so the default should be used. 121 */ 122define('MDB2_FETCHMODE_DEFAULT', 0); 123 124/** 125 * Column data indexed by numbers, ordered from 0 and up 126 */ 127define('MDB2_FETCHMODE_ORDERED', 1); 128 129/** 130 * Column data indexed by column names 131 */ 132define('MDB2_FETCHMODE_ASSOC', 2); 133 134/** 135 * Column data as object properties 136 */ 137define('MDB2_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 * MDB2_FETCHMODE_FLIPPED switches this order, so the first level of arrays 143 * is the column name, and the second level the row number. 144 */ 145define('MDB2_FETCHMODE_FLIPPED', 4); 146 147// }}} 148// {{{ Portability mode constants 149 150/** 151 * Portability: turn off all portability features. 152 * @see MDB2_Driver_Common::setOption() 153 */ 154define('MDB2_PORTABILITY_NONE', 0); 155 156/** 157 * Portability: convert names of tables and fields to case defined in the 158 * "field_case" option when using the query*(), fetch*() and tableInfo() methods. 159 * @see MDB2_Driver_Common::setOption() 160 */ 161define('MDB2_PORTABILITY_FIX_CASE', 1); 162 163/** 164 * Portability: right trim the data output by query*() and fetch*(). 165 * @see MDB2_Driver_Common::setOption() 166 */ 167define('MDB2_PORTABILITY_RTRIM', 2); 168 169/** 170 * Portability: force reporting the number of rows deleted. 171 * @see MDB2_Driver_Common::setOption() 172 */ 173define('MDB2_PORTABILITY_DELETE_COUNT', 4); 174 175/** 176 * Portability: not needed in MDB2 (just left here for compatibility to DB) 177 * @see MDB2_Driver_Common::setOption() 178 */ 179define('MDB2_PORTABILITY_NUMROWS', 8); 180 181/** 182 * Portability: makes certain error messages in certain drivers compatible 183 * with those from other DBMS's. 184 * 185 * + mysql, mysqli: change unique/primary key constraints 186 * MDB2_ERROR_ALREADY_EXISTS -> MDB2_ERROR_CONSTRAINT 187 * 188 * + odbc(access): MS's ODBC driver reports 'no such field' as code 189 * 07001, which means 'too few parameters.' When this option is on 190 * that code gets mapped to MDB2_ERROR_NOSUCHFIELD. 191 * 192 * @see MDB2_Driver_Common::setOption() 193 */ 194define('MDB2_PORTABILITY_ERRORS', 16); 195 196/** 197 * Portability: convert empty values to null strings in data output by 198 * query*() and fetch*(). 199 * @see MDB2_Driver_Common::setOption() 200 */ 201define('MDB2_PORTABILITY_EMPTY_TO_NULL', 32); 202 203/** 204 * Portability: removes database/table qualifiers from associative indexes 205 * @see MDB2_Driver_Common::setOption() 206 */ 207define('MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES', 64); 208 209/** 210 * Portability: turn on all portability features. 211 * @see MDB2_Driver_Common::setOption() 212 */ 213define('MDB2_PORTABILITY_ALL', 127); 214 215// }}} 216// {{{ Globals for class instance tracking 217 218/** 219 * These are global variables that are used to track the various class instances 220 */ 221 222$GLOBALS['_MDB2_databases'] = array(); 223$GLOBALS['_MDB2_dsninfo_default'] = array( 224 'phptype' => false, 225 'dbsyntax' => false, 226 'username' => false, 227 'password' => false, 228 'protocol' => false, 229 'hostspec' => false, 230 'port' => false, 231 'socket' => false, 232 'database' => false, 233 'mode' => false, 234); 235 236// }}} 237// {{{ class MDB2 238 239/** 240 * The main 'MDB2' class is simply a container class with some static 241 * methods for creating DB objects as well as some utility functions 242 * common to all parts of DB. 243 * 244 * The object model of MDB2 is as follows (indentation means inheritance): 245 * 246 * MDB2 The main MDB2 class. This is simply a utility class 247 * with some 'static' methods for creating MDB2 objects as 248 * well as common utility functions for other MDB2 classes. 249 * 250 * MDB2_Driver_Common The base for each MDB2 implementation. Provides default 251 * | implementations (in OO lingo virtual methods) for 252 * | the actual DB implementations as well as a bunch of 253 * | query utility functions. 254 * | 255 * +-MDB2_Driver_mysql The MDB2 implementation for MySQL. Inherits MDB2_Driver_Common. 256 * When calling MDB2::factory or MDB2::connect for MySQL 257 * connections, the object returned is an instance of this 258 * class. 259 * +-MDB2_Driver_pgsql The MDB2 implementation for PostGreSQL. Inherits MDB2_Driver_Common. 260 * When calling MDB2::factory or MDB2::connect for PostGreSQL 261 * connections, the object returned is an instance of this 262 * class. 263 * 264 * @package MDB2 265 * @category Database 266 * @author Lukas Smith <smith@pooteeweet.org> 267 */ 268class MDB2 269{ 270 // {{{ function setOptions($db, $options) 271 272 /** 273 * set option array in an exiting database object 274 * 275 * @param MDB2_Driver_Common MDB2 object 276 * @param array An associative array of option names and their values. 277 * 278 * @return mixed MDB2_OK or a PEAR Error object 279 * 280 * @access public 281 */ 282 static function setOptions($db, $options) 283 { 284 if (is_array($options)) { 285 foreach ($options as $option => $value) { 286 $test = $db->setOption($option, $value); 287 if (MDB2::isError($test)) { 288 return $test; 289 } 290 } 291 } 292 return MDB2_OK; 293 } 294 295 // }}} 296 // {{{ function classExists($classname) 297 298 /** 299 * Checks if a class exists without triggering __autoload 300 * 301 * @param string classname 302 * 303 * @return bool true success and false on error 304 * @static 305 * @access public 306 */ 307 static function classExists($classname) 308 { 309 return class_exists($classname, false); 310 } 311 312 // }}} 313 // {{{ function loadClass($class_name, $debug) 314 315 /** 316 * Loads a PEAR class. 317 * 318 * @param string classname to load 319 * @param bool if errors should be suppressed 320 * 321 * @return mixed true success or PEAR_Error on failure 322 * 323 * @access public 324 */ 325 static function loadClass($class_name, $debug) 326 { 327 if (!MDB2::classExists($class_name)) { 328 $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php'; 329 if ($debug) { 330 $include = include_once($file_name); 331 } else { 332 $include = @include_once($file_name); 333 } 334 if (!$include) { 335 if (!MDB2::fileExists($file_name)) { 336 $msg = "unable to find package '$class_name' file '$file_name'"; 337 } else { 338 $msg = "unable to load class '$class_name' from file '$file_name'"; 339 } 340 $err = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg); 341 return $err; 342 } 343 if (!MDB2::classExists($class_name)) { 344 $msg = "unable to load class '$class_name' from file '$file_name'"; 345 $err = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg); 346 return $err; 347 } 348 } 349 return MDB2_OK; 350 } 351 352 // }}} 353 // {{{ function factory($dsn, $options = false) 354 355 /** 356 * Create a new MDB2 object for the specified database type 357 * 358 * @param mixed 'data source name', see the MDB2::parseDSN 359 * method for a description of the dsn format. 360 * Can also be specified as an array of the 361 * format returned by MDB2::parseDSN. 362 * @param array An associative array of option names and 363 * their values. 364 * 365 * @return mixed a newly created MDB2 object, or false on error 366 * 367 * @access public 368 */ 369 static function factory($dsn, $options = false) 370 { 371 $dsninfo = MDB2::parseDSN($dsn); 372 if (empty($dsninfo['phptype'])) { 373 $err = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, 374 null, null, 'no RDBMS driver specified'); 375 return $err; 376 } 377 $class_name = 'MDB2_Driver_'.$dsninfo['phptype']; 378 379 $debug = (!empty($options['debug'])); 380 $err = MDB2::loadClass($class_name, $debug); 381 if (MDB2::isError($err)) { 382 return $err; 383 } 384 385 $db = new $class_name(); 386 $db->setDSN($dsninfo); 387 $err = MDB2::setOptions($db, $options); 388 if (MDB2::isError($err)) { 389 return $err; 390 } 391 392 return $db; 393 } 394 395 // }}} 396 // {{{ function connect($dsn, $options = false) 397 398 /** 399 * Create a new MDB2_Driver_* connection object and connect to the specified 400 * database 401 * 402 * @param mixed $dsn 'data source name', see the MDB2::parseDSN 403 * method for a description of the dsn format. 404 * Can also be specified as an array of the 405 * format returned by MDB2::parseDSN. 406 * @param array $options An associative array of option names and 407 * their values. 408 * 409 * @return mixed a newly created MDB2 connection object, or a MDB2 410 * error object on error 411 * 412 * @access public 413 * @see MDB2::parseDSN 414 */ 415 static function connect($dsn, $options = false) 416 { 417 $db = MDB2::factory($dsn, $options); 418 if (MDB2::isError($db)) { 419 return $db; 420 } 421 422 $err = $db->connect(); 423 if (MDB2::isError($err)) { 424 $dsn = $db->getDSN('string', 'xxx'); 425 $db->disconnect(); 426 $err->addUserInfo($dsn); 427 return $err; 428 } 429 430 return $db; 431 } 432 433 // }}} 434 // {{{ function singleton($dsn = null, $options = false) 435 436 /** 437 * Returns a MDB2 connection with the requested DSN. 438 * A new MDB2 connection object is only created if no object with the 439 * requested DSN exists yet. 440 * 441 * @param mixed 'data source name', see the MDB2::parseDSN 442 * method for a description of the dsn format. 443 * Can also be specified as an array of the 444 * format returned by MDB2::parseDSN. 445 * @param array An associative array of option names and 446 * their values. 447 * 448 * @return mixed a newly created MDB2 connection object, or a MDB2 449 * error object on error 450 * 451 * @access public 452 * @see MDB2::parseDSN 453 */ 454 static function singleton($dsn = null, $options = false) 455 { 456 if ($dsn) { 457 $dsninfo = MDB2::parseDSN($dsn); 458 $dsninfo = array_merge($GLOBALS['_MDB2_dsninfo_default'], $dsninfo); 459 $keys = array_keys($GLOBALS['_MDB2_databases']); 460 for ($i=0, $j=count($keys); $i<$j; ++$i) { 461 if (isset($GLOBALS['_MDB2_databases'][$keys[$i]])) { 462 $tmp_dsn = $GLOBALS['_MDB2_databases'][$keys[$i]]->getDSN('array'); 463 if (count(array_diff_assoc($tmp_dsn, $dsninfo)) == 0) { 464 MDB2::setOptions($GLOBALS['_MDB2_databases'][$keys[$i]], $options); 465 return $GLOBALS['_MDB2_databases'][$keys[$i]]; 466 } 467 } 468 } 469 } elseif (is_array($GLOBALS['_MDB2_databases']) && reset($GLOBALS['_MDB2_databases'])) { 470 return $GLOBALS['_MDB2_databases'][key($GLOBALS['_MDB2_databases'])]; 471 } 472 $db = MDB2::factory($dsn, $options); 473 return $db; 474 } 475 476 // }}} 477 // {{{ function areEquals() 478 479 /** 480 * It looks like there's a memory leak in array_diff() in PHP 5.1.x, 481 * so use this method instead. 482 * @see http://pear.php.net/bugs/bug.php?id=11790 483 * 484 * @param array $arr1 485 * @param array $arr2 486 * @return boolean 487 */ 488 static function areEquals($arr1, $arr2) 489 { 490 if (count($arr1) != count($arr2)) { 491 return false; 492 } 493 foreach (array_keys($arr1) as $k) { 494 if (!array_key_exists($k, $arr2) || $arr1[$k] != $arr2[$k]) { 495 return false; 496 } 497 } 498 return true; 499 } 500 501 // }}} 502 // {{{ function loadFile($file) 503 504 /** 505 * load a file (like 'Date') 506 * 507 * @param string $file name of the file in the MDB2 directory (without '.php') 508 * 509 * @return string name of the file that was included 510 * 511 * @access public 512 */ 513 static function loadFile($file) 514 { 515 $file_name = 'MDB2'.DIRECTORY_SEPARATOR.$file.'.php'; 516 if (!MDB2::fileExists($file_name)) { 517 return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 518 'unable to find: '.$file_name); 519 } 520 if (!include_once($file_name)) { 521 return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 522 'unable to load driver class: '.$file_name); 523 } 524 return $file_name; 525 } 526 527 // }}} 528 // {{{ function apiVersion() 529 530 /** 531 * Return the MDB2 API version 532 * 533 * @return string the MDB2 API version number 534 * 535 * @access public 536 */ 537 static function apiVersion() 538 { 539 return '2.5.0b5'; 540 } 541 542 // }}} 543 // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null) 544 545 /** 546 * This method is used to communicate an error and invoke error 547 * callbacks etc. Basically a wrapper for PEAR::raiseError 548 * without the message string. 549 * 550 * @param mixed int error code 551 * 552 * @param int error mode, see PEAR_Error docs 553 * 554 * @param mixed If error mode is PEAR_ERROR_TRIGGER, this is the 555 * error level (E_USER_NOTICE etc). If error mode is 556 * PEAR_ERROR_CALLBACK, this is the callback function, 557 * either as a function name, or as an array of an 558 * object and method name. For other error modes this 559 * parameter is ignored. 560 * 561 * @param string Extra debug information. Defaults to the last 562 * query and native error code. 563 * 564 * @return PEAR_Error instance of a PEAR Error object 565 * 566 * @access private 567 * @see PEAR_Error 568 */ 569 public static function &raiseError($code = null, 570 $mode = null, 571 $options = null, 572 $userinfo = null, 573 $dummy1 = null, 574 $dummy2 = null, 575 $dummy3 = false) 576 { 577 $pear = new PEAR; 578 $err =& $pear->raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true); 579 return $err; 580 } 581 582 // }}} 583 // {{{ function isError($data, $code = null) 584 585 /** 586 * Tell whether a value is a MDB2 error. 587 * 588 * @param mixed the value to test 589 * @param int if is an error object, return true 590 * only if $code is a string and 591 * $db->getMessage() == $code or 592 * $code is an integer and $db->getCode() == $code 593 * 594 * @return bool true if parameter is an error 595 * 596 * @access public 597 */ 598 static function isError($data, $code = null) 599 { 600 if ($data instanceof MDB2_Error) { 601 if (null === $code) { 602 return true; 603 } 604 if (is_string($code)) { 605 return $data->getMessage() === $code; 606 } 607 return in_array($data->getCode(), (array)$code); 608 } 609 return false; 610 } 611 612 // }}} 613 // {{{ function isConnection($value) 614 615 /** 616 * Tell whether a value is a MDB2 connection 617 * 618 * @param mixed value to test 619 * 620 * @return bool whether $value is a MDB2 connection 621 * @access public 622 */ 623 static function isConnection($value) 624 { 625 return ($value instanceof MDB2_Driver_Common); 626 } 627 628 // }}} 629 // {{{ function isResult($value) 630 631 /** 632 * Tell whether a value is a MDB2 result 633 * 634 * @param mixed $value value to test 635 * 636 * @return bool whether $value is a MDB2 result 637 * 638 * @access public 639 */ 640 static function isResult($value) 641 { 642 return ($value instanceof MDB2_Result); 643 } 644 645 // }}} 646 // {{{ function isResultCommon($value) 647 648 /** 649 * Tell whether a value is a MDB2 result implementing the common interface 650 * 651 * @param mixed $value value to test 652 * 653 * @return bool whether $value is a MDB2 result implementing the common interface 654 * 655 * @access public 656 */ 657 static function isResultCommon($value) 658 { 659 return ($value instanceof MDB2_Result_Common); 660 } 661 662 // }}} 663 // {{{ function isStatement($value) 664 665 /** 666 * Tell whether a value is a MDB2 statement interface 667 * 668 * @param mixed value to test 669 * 670 * @return bool whether $value is a MDB2 statement interface 671 * 672 * @access public 673 */ 674 static function isStatement($value) 675 { 676 return ($value instanceof MDB2_Statement_Common); 677 } 678 679 // }}} 680 // {{{ function errorMessage($value = null) 681 682 /** 683 * Return a textual error message for a MDB2 error code 684 * 685 * @param int|array integer error code, 686 null to get the current error code-message map, 687 or an array with a new error code-message map 688 * 689 * @return string error message, or false if the error code was 690 * not recognized 691 * 692 * @access public 693 */ 694 static function errorMessage($value = null) 695 { 696 static $errorMessages; 697 698 if (is_array($value)) { 699 $errorMessages = $value; 700 return MDB2_OK; 701 } 702 703 if (!isset($errorMessages)) { 704 $errorMessages = array( 705 MDB2_OK => 'no error', 706 MDB2_ERROR => 'unknown error', 707 MDB2_ERROR_ALREADY_EXISTS => 'already exists', 708 MDB2_ERROR_CANNOT_CREATE => 'can not create', 709 MDB2_ERROR_CANNOT_ALTER => 'can not alter', 710 MDB2_ERROR_CANNOT_REPLACE => 'can not replace', 711 MDB2_ERROR_CANNOT_DELETE => 'can not delete', 712 MDB2_ERROR_CANNOT_DROP => 'can not drop', 713 MDB2_ERROR_CONSTRAINT => 'constraint violation', 714 MDB2_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint', 715 MDB2_ERROR_DIVZERO => 'division by zero', 716 MDB2_ERROR_INVALID => 'invalid', 717 MDB2_ERROR_INVALID_DATE => 'invalid date or time', 718 MDB2_ERROR_INVALID_NUMBER => 'invalid number', 719 MDB2_ERROR_MISMATCH => 'mismatch', 720 MDB2_ERROR_NODBSELECTED => 'no database selected', 721 MDB2_ERROR_NOSUCHFIELD => 'no such field', 722 MDB2_ERROR_NOSUCHTABLE => 'no such table', 723 MDB2_ERROR_NOT_CAPABLE => 'MDB2 backend not capable', 724 MDB2_ERROR_NOT_FOUND => 'not found', 725 MDB2_ERROR_NOT_LOCKED => 'not locked', 726 MDB2_ERROR_SYNTAX => 'syntax error', 727 MDB2_ERROR_UNSUPPORTED => 'not supported', 728 MDB2_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', 729 MDB2_ERROR_INVALID_DSN => 'invalid DSN', 730 MDB2_ERROR_CONNECT_FAILED => 'connect failed', 731 MDB2_ERROR_NEED_MORE_DATA => 'insufficient data supplied', 732 MDB2_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', 733 MDB2_ERROR_NOSUCHDB => 'no such database', 734 MDB2_ERROR_ACCESS_VIOLATION => 'insufficient permissions', 735 MDB2_ERROR_LOADMODULE => 'error while including on demand module', 736 MDB2_ERROR_TRUNCATED => 'truncated', 737 MDB2_ERROR_DEADLOCK => 'deadlock detected', 738 MDB2_ERROR_NO_PERMISSION => 'no permission', 739 MDB2_ERROR_DISCONNECT_FAILED => 'disconnect failed', 740 ); 741 } 742 743 if (null === $value) { 744 return $errorMessages; 745 } 746 747 if (MDB2::isError($value)) { 748 $value = $value->getCode(); 749 } 750 751 return isset($errorMessages[$value]) ? 752 $errorMessages[$value] : $errorMessages[MDB2_ERROR]; 753 } 754 755 // }}} 756 // {{{ function parseDSN($dsn) 757 758 /** 759 * Parse a data source name. 760 * 761 * Additional keys can be added by appending a URI query string to the 762 * end of the DSN. 763 * 764 * The format of the supplied DSN is in its fullest form: 765 * <code> 766 * phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true 767 * </code> 768 * 769 * Most variations are allowed: 770 * <code> 771 * phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644 772 * phptype://username:password@hostspec/database_name 773 * phptype://username:password@hostspec 774 * phptype://username@hostspec 775 * phptype://hostspec/database 776 * phptype://hostspec 777 * phptype(dbsyntax) 778 * phptype 779 * </code> 780 * 781 * @param string Data Source Name to be parsed 782 * 783 * @return array an associative array with the following keys: 784 * + phptype: Database backend used in PHP (mysql, odbc etc.) 785 * + dbsyntax: Database used with regards to SQL syntax etc. 786 * + protocol: Communication protocol to use (tcp, unix etc.) 787 * + hostspec: Host specification (hostname[:port]) 788 * + database: Database to use on the DBMS server 789 * + username: User name for login 790 * + password: Password for login 791 * 792 * @access public 793 * @author Tomas V.V.Cox <cox@idecnet.com> 794 */ 795 static function parseDSN($dsn) 796 { 797 $parsed = $GLOBALS['_MDB2_dsninfo_default']; 798 799 if (is_array($dsn)) { 800 $dsn = array_merge($parsed, $dsn); 801 if (!$dsn['dbsyntax']) { 802 $dsn['dbsyntax'] = $dsn['phptype']; 803 } 804 return $dsn; 805 } 806 807 // Find phptype and dbsyntax 808 if (($pos = strpos($dsn, '://')) !== false) { 809 $str = substr($dsn, 0, $pos); 810 $dsn = substr($dsn, $pos + 3); 811 } else { 812 $str = $dsn; 813 $dsn = null; 814 } 815 816 // Get phptype and dbsyntax 817 // $str => phptype(dbsyntax) 818 if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { 819 $parsed['phptype'] = $arr[1]; 820 $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2]; 821 } else { 822 $parsed['phptype'] = $str; 823 $parsed['dbsyntax'] = $str; 824 } 825 826 if (!count($dsn)) { 827 return $parsed; 828 } 829 830 // Get (if found): username and password 831 // $dsn => username:password@protocol+hostspec/database 832 if (($at = strrpos($dsn,'@')) !== false) { 833 $str = substr($dsn, 0, $at); 834 $dsn = substr($dsn, $at + 1); 835 if (($pos = strpos($str, ':')) !== false) { 836 $parsed['username'] = rawurldecode(substr($str, 0, $pos)); 837 $parsed['password'] = rawurldecode(substr($str, $pos + 1)); 838 } else { 839 $parsed['username'] = rawurldecode($str); 840 } 841 } 842 843 // Find protocol and hostspec 844 845 // $dsn => proto(proto_opts)/database 846 if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { 847 $proto = $match[1]; 848 $proto_opts = $match[2] ? $match[2] : false; 849 $dsn = $match[3]; 850 851 // $dsn => protocol+hostspec/database (old format) 852 } else { 853 if (strpos($dsn, '+') !== false) { 854 list($proto, $dsn) = explode('+', $dsn, 2); 855 } 856 if ( strpos($dsn, '//') === 0 857 && strpos($dsn, '/', 2) !== false 858 && $parsed['phptype'] == 'oci8' 859 ) { 860 //oracle's "Easy Connect" syntax: 861 //"username/password@[//]host[:port][/service_name]" 862 //e.g. "scott/tiger@//mymachine:1521/oracle" 863 $proto_opts = $dsn; 864 $pos = strrpos($proto_opts, '/'); 865 $dsn = substr($proto_opts, $pos + 1); 866 $proto_opts = substr($proto_opts, 0, $pos); 867 } elseif (strpos($dsn, '/') !== false) { 868 list($proto_opts, $dsn) = explode('/', $dsn, 2); 869 } else { 870 $proto_opts = $dsn; 871 $dsn = null; 872 } 873 } 874 875 // process the different protocol options 876 $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp'; 877 $proto_opts = rawurldecode($proto_opts); 878 if (strpos($proto_opts, ':') !== false) { 879 list($proto_opts, $parsed['port']) = explode(':', $proto_opts); 880 } 881 if ($parsed['protocol'] == 'tcp') { 882 $parsed['hostspec'] = $proto_opts; 883 } elseif ($parsed['protocol'] == 'unix') { 884 $parsed['socket'] = $proto_opts; 885 } 886 887 // Get dabase if any 888 // $dsn => database 889 if ($dsn) { 890 // /database 891 if (($pos = strpos($dsn, '?')) === false) { 892 $parsed['database'] = rawurldecode($dsn); 893 // /database?param1=value1¶m2=value2 894 } else { 895 $parsed['database'] = rawurldecode(substr($dsn, 0, $pos)); 896 $dsn = substr($dsn, $pos + 1); 897 if (strpos($dsn, '&') !== false) { 898 $opts = explode('&', $dsn); 899 } else { // database?param1=value1 900 $opts = array($dsn); 901 } 902 foreach ($opts as $opt) { 903 list($key, $value) = explode('=', $opt); 904 if (!array_key_exists($key, $parsed) || false === $parsed[$key]) { 905 // don't allow params overwrite 906 $parsed[$key] = rawurldecode($value); 907 } 908 } 909 } 910 } 911 912 return $parsed; 913 } 914 915 // }}} 916 // {{{ function fileExists($file) 917 918 /** 919 * Checks if a file exists in the include path 920 * 921 * @param string filename 922 * 923 * @return bool true success and false on error 924 * 925 * @access public 926 */ 927 static function fileExists($file) 928 { 929 // safe_mode does notwork with is_readable() 930 if (!@ini_get('safe_mode')) { 931 $dirs = explode(PATH_SEPARATOR, ini_get('include_path')); 932 foreach ($dirs as $dir) { 933 if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) { 934 return true; 935 } 936 } 937 } else { 938 $fp = @fopen($file, 'r', true); 939 if (is_resource($fp)) { 940 @fclose($fp); 941 return true; 942 } 943 } 944 return false; 945 } 946 // }}} 947} 948 949// }}} 950// {{{ class MDB2_Error extends PEAR_Error 951 952/** 953 * MDB2_Error implements a class for reporting portable database error 954 * messages. 955 * 956 * @package MDB2 957 * @category Database 958 * @author Stig Bakken <ssb@fast.no> 959 */ 960class MDB2_Error extends PEAR_Error 961{ 962 // {{{ constructor: function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, $debuginfo = null) 963 964 /** 965 * MDB2_Error constructor. 966 * 967 * @param mixed MDB2 error code, or string with error message. 968 * @param int what 'error mode' to operate in 969 * @param int what error level to use for $mode & PEAR_ERROR_TRIGGER 970 * @param mixed additional debug info, such as the last query 971 */ 972 function __construct($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, 973 $level = E_USER_NOTICE, $debuginfo = null, $dummy = null) 974 { 975 if (null === $code) { 976 $code = MDB2_ERROR; 977 } 978 $this->PEAR_Error('MDB2 Error: '.MDB2::errorMessage($code), $code, 979 $mode, $level, $debuginfo); 980 } 981 982 // }}} 983} 984 985// }}} 986// {{{ class MDB2_Driver_Common extends PEAR 987 988/** 989 * MDB2_Driver_Common: Base class that is extended by each MDB2 driver 990 * 991 * @package MDB2 992 * @category Database 993 * @author Lukas Smith <smith@pooteeweet.org> 994 */ 995class MDB2_Driver_Common 996{ 997 // {{{ Variables (Properties) 998 999 /** 1000 * @var MDB2_Driver_Datatype_Common 1001 */ 1002 public $datatype; 1003 1004 /** 1005 * @var MDB2_Extended 1006 */ 1007 public $extended; 1008 1009 /** 1010 * @var MDB2_Driver_Function_Common 1011 */ 1012 public $function; 1013 1014 /** 1015 * @var MDB2_Driver_Manager_Common 1016 */ 1017 public $manager; 1018 1019 /** 1020 * @var MDB2_Driver_Native_Commonn 1021 */ 1022 public $native; 1023 1024 /** 1025 * @var MDB2_Driver_Reverse_Common 1026 */ 1027 public $reverse; 1028 1029 /** 1030 * index of the MDB2 object within the $GLOBALS['_MDB2_databases'] array 1031 * @var int 1032 * @access public 1033 */ 1034 public $db_index = 0; 1035 1036 /** 1037 * DSN used for the next query 1038 * @var array 1039 * @access protected 1040 */ 1041 public $dsn = array(); 1042 1043 /** 1044 * DSN that was used to create the current connection 1045 * @var array 1046 * @access protected 1047 */ 1048 public $connected_dsn = array(); 1049 1050 /** 1051 * connection resource 1052 * @var mixed 1053 * @access protected 1054 */ 1055 public $connection = 0; 1056 1057 /** 1058 * if the current opened connection is a persistent connection 1059 * @var bool 1060 * @access protected 1061 */ 1062 public $opened_persistent; 1063 1064 /** 1065 * the name of the database for the next query 1066 * @var string 1067 * @access public 1068 */ 1069 public $database_name = ''; 1070 1071 /** 1072 * the name of the database currently selected 1073 * @var string 1074 * @access protected 1075 */ 1076 public $connected_database_name = ''; 1077 1078 /** 1079 * server version information 1080 * @var string 1081 * @access protected 1082 */ 1083 public $connected_server_info = ''; 1084 1085 /** 1086 * list of all supported features of the given driver 1087 * @var array 1088 * @access public 1089 */ 1090 public $supported = array( 1091 'sequences' => false, 1092 'indexes' => false, 1093 'affected_rows' => false, 1094 'summary_functions' => false, 1095 'order_by_text' => false, 1096 'transactions' => false, 1097 'savepoints' => false, 1098 'current_id' => false, 1099 'limit_queries' => false, 1100 'LOBs' => false, 1101 'replace' => false, 1102 'sub_selects' => false, 1103 'triggers' => false, 1104 'auto_increment' => false, 1105 'primary_key' => false, 1106 'result_introspection' => false, 1107 'prepared_statements' => false, 1108 'identifier_quoting' => false, 1109 'pattern_escaping' => false, 1110 'new_link' => false, 1111 ); 1112 1113 /** 1114 * Array of supported options that can be passed to the MDB2 instance. 1115 * 1116 * The options can be set during object creation, using 1117 * MDB2::connect(), MDB2::factory() or MDB2::singleton(). The options can 1118 * also be set after the object is created, using MDB2::setOptions() or 1119 * MDB2_Driver_Common::setOption(). 1120 * The list of available option includes: 1121 * <ul> 1122 * <li>$options['ssl'] -> boolean: determines if ssl should be used for connections</li> 1123 * <li>$options['field_case'] -> CASE_LOWER|CASE_UPPER: determines what case to force on field/table names</li> 1124 * <li>$options['disable_query'] -> boolean: determines if queries should be executed</li> 1125 * <li>$options['result_class'] -> string: class used for result sets</li> 1126 * <li>$options['buffered_result_class'] -> string: class used for buffered result sets</li> 1127 * <li>$options['result_wrap_class'] -> string: class used to wrap result sets into</li> 1128 * <li>$options['result_buffering'] -> boolean should results be buffered or not?</li> 1129 * <li>$options['fetch_class'] -> string: class to use when fetch mode object is used</li> 1130 * <li>$options['persistent'] -> boolean: persistent connection?</li> 1131 * <li>$options['debug'] -> integer: numeric debug level</li> 1132 * <li>$options['debug_handler'] -> string: function/method that captures debug messages</li> 1133 * <li>$options['debug_expanded_output'] -> bool: BC option to determine if more context information should be send to the debug handler</li> 1134 * <li>$options['default_text_field_length'] -> integer: default text field length to use</li> 1135 * <li>$options['lob_buffer_length'] -> integer: LOB buffer length</li> 1136 * <li>$options['log_line_break'] -> string: line-break format</li> 1137 * <li>$options['idxname_format'] -> string: pattern for index name</li> 1138 * <li>$options['seqname_format'] -> string: pattern for sequence name</li> 1139 * <li>$options['savepoint_format'] -> string: pattern for auto generated savepoint names</li> 1140 * <li>$options['statement_format'] -> string: pattern for prepared statement names</li> 1141 * <li>$options['seqcol_name'] -> string: sequence column name</li> 1142 * <li>$options['quote_identifier'] -> boolean: if identifier quoting should be done when check_option is used</li> 1143 * <li>$options['use_transactions'] -> boolean: if transaction use should be enabled</li> 1144 * <li>$options['decimal_places'] -> integer: number of decimal places to handle</li> 1145 * <li>$options['portability'] -> integer: portability constant</li> 1146 * <li>$options['modules'] -> array: short to long module name mapping for __call()</li> 1147 * <li>$options['emulate_prepared'] -> boolean: force prepared statements to be emulated</li> 1148 * <li>$options['datatype_map'] -> array: map user defined datatypes to other primitive datatypes</li> 1149 * <li>$options['datatype_map_callback'] -> array: callback function/method that should be called</li> 1150 * <li>$options['bindname_format'] -> string: regular expression pattern for named parameters</li> 1151 * <li>$options['multi_query'] -> boolean: determines if queries returning multiple result sets should be executed</li> 1152 * <li>$options['max_identifiers_length'] -> integer: max identifier length</li> 1153 * <li>$options['default_fk_action_onupdate'] -> string: default FOREIGN KEY ON UPDATE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']</li> 1154 * <li>$options['default_fk_action_ondelete'] -> string: default FOREIGN KEY ON DELETE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']</li> 1155 * </ul> 1156 * 1157 * @var array 1158 * @access public 1159 * @see MDB2::connect() 1160 * @see MDB2::factory() 1161 * @see MDB2::singleton() 1162 * @see MDB2_Driver_Common::setOption() 1163 */ 1164 public $options = array( 1165 'ssl' => false, 1166 'field_case' => CASE_LOWER, 1167 'disable_query' => false, 1168 'result_class' => 'MDB2_Result_%s', 1169 'buffered_result_class' => 'MDB2_BufferedResult_%s', 1170 'result_wrap_class' => false, 1171 'result_buffering' => true, 1172 'fetch_class' => 'stdClass', 1173 'persistent' => false, 1174 'debug' => 0, 1175 'debug_handler' => 'MDB2_defaultDebugOutput', 1176 'debug_expanded_output' => false, 1177 'default_text_field_length' => 4096, 1178 'lob_buffer_length' => 8192, 1179 'log_line_break' => "\n", 1180 'idxname_format' => '%s_idx', 1181 'seqname_format' => '%s_seq', 1182 'savepoint_format' => 'MDB2_SAVEPOINT_%s', 1183 'statement_format' => 'MDB2_STATEMENT_%1$s_%2$s', 1184 'seqcol_name' => 'sequence', 1185 'quote_identifier' => false, 1186 'use_transactions' => true, 1187 'decimal_places' => 2, 1188 'portability' => MDB2_PORTABILITY_ALL, 1189 'modules' => array( 1190 'ex' => 'Extended', 1191 'dt' => 'Datatype', 1192 'mg' => 'Manager', 1193 'rv' => 'Reverse', 1194 'na' => 'Native', 1195 'fc' => 'Function', 1196 ), 1197 'emulate_prepared' => false, 1198 'datatype_map' => array(), 1199 'datatype_map_callback' => array(), 1200 'nativetype_map_callback' => array(), 1201 'lob_allow_url_include' => false, 1202 'bindname_format' => '(?:\d+)|(?:[a-zA-Z][a-zA-Z0-9_]*)', 1203 'max_identifiers_length' => 30, 1204 'default_fk_action_onupdate' => 'RESTRICT', 1205 'default_fk_action_ondelete' => 'RESTRICT', 1206 ); 1207 1208 /** 1209 * string array 1210 * @var string 1211 * @access public 1212 */ 1213 public $string_quoting = array( 1214 'start' => "'", 1215 'end' => "'", 1216 'escape' => false, 1217 'escape_pattern' => false, 1218 ); 1219 1220 /** 1221 * identifier quoting 1222 * @var array 1223 * @access public 1224 */ 1225 public $identifier_quoting = array( 1226 'start' => '"', 1227 'end' => '"', 1228 'escape' => '"', 1229 ); 1230 1231 /** 1232 * sql comments 1233 * @var array 1234 * @access protected 1235 */ 1236 public $sql_comments = array( 1237 array('start' => '--', 'end' => "\n", 'escape' => false), 1238 array('start' => '/*', 'end' => '*/', 'escape' => false), 1239 ); 1240 1241 /** 1242 * comparision wildcards 1243 * @var array 1244 * @access protected 1245 */ 1246 protected $wildcards = array('%', '_'); 1247 1248 /** 1249 * column alias keyword 1250 * @var string 1251 * @access protected 1252 */ 1253 public $as_keyword = ' AS '; 1254 1255 /** 1256 * warnings 1257 * @var array 1258 * @access protected 1259 */ 1260 public $warnings = array(); 1261 1262 /** 1263 * string with the debugging information 1264 * @var string 1265 * @access public 1266 */ 1267 public $debug_output = ''; 1268 1269 /** 1270 * determine if there is an open transaction 1271 * @var bool 1272 * @access protected 1273 */ 1274 public $in_transaction = false; 1275 1276 /** 1277 * the smart transaction nesting depth 1278 * @var int 1279 * @access protected 1280 */ 1281 public $nested_transaction_counter = null; 1282 1283 /** 1284 * the first error that occured inside a nested transaction 1285 * @var MDB2_Error|bool 1286 * @access protected 1287 */ 1288 protected $has_transaction_error = false; 1289 1290 /** 1291 * result offset used in the next query 1292 * @var int 1293 * @access public 1294 */ 1295 public $offset = 0; 1296 1297 /** 1298 * result limit used in the next query 1299 * @var int 1300 * @access public 1301 */ 1302 public $limit = 0; 1303 1304 /** 1305 * Database backend used in PHP (mysql, odbc etc.) 1306 * @var string 1307 * @access public 1308 */ 1309 public $phptype; 1310 1311 /** 1312 * Database used with regards to SQL syntax etc. 1313 * @var string 1314 * @access public 1315 */ 1316 public $dbsyntax; 1317 1318 /** 1319 * the last query sent to the driver 1320 * @var string 1321 * @access public 1322 */ 1323 public $last_query; 1324 1325 /** 1326 * the default fetchmode used 1327 * @var int 1328 * @access public 1329 */ 1330 public $fetchmode = MDB2_FETCHMODE_ORDERED; 1331 1332 /** 1333 * array of module instances 1334 * @var array 1335 * @access protected 1336 */ 1337 protected $modules = array(); 1338 1339 /** 1340 * determines of the PHP4 destructor emulation has been enabled yet 1341 * @var array 1342 * @access protected 1343 */ 1344 protected $destructor_registered = true; 1345 1346 /** 1347 * @var PEAR 1348 */ 1349 protected $pear; 1350 1351 // }}} 1352 // {{{ constructor: function __construct() 1353 1354 /** 1355 * Constructor 1356 */ 1357 function __construct() 1358 { 1359 end($GLOBALS['_MDB2_databases']); 1360 $db_index = key($GLOBALS['_MDB2_databases']) + 1; 1361 $GLOBALS['_MDB2_databases'][$db_index] = &$this; 1362 $this->db_index = $db_index; 1363 $this->pear = new PEAR; 1364 } 1365 1366 // }}} 1367 // {{{ destructor: function __destruct() 1368 1369 /** 1370 * Destructor 1371 */ 1372 function __destruct() 1373 { 1374 $this->disconnect(false); 1375 } 1376 1377 // }}} 1378 // {{{ function free() 1379 1380 /** 1381 * Free the internal references so that the instance can be destroyed 1382 * 1383 * @return bool true on success, false if result is invalid 1384 * 1385 * @access public 1386 */ 1387 function free() 1388 { 1389 unset($GLOBALS['_MDB2_databases'][$this->db_index]); 1390 unset($this->db_index); 1391 return MDB2_OK; 1392 } 1393 1394 // }}} 1395 // {{{ function __toString() 1396 1397 /** 1398 * String conversation 1399 * 1400 * @return string representation of the object 1401 * 1402 * @access public 1403 */ 1404 function __toString() 1405 { 1406 $info = get_class($this); 1407 $info.= ': (phptype = '.$this->phptype.', dbsyntax = '.$this->dbsyntax.')'; 1408 if ($this->connection) { 1409 $info.= ' [connected]'; 1410 } 1411 return $info; 1412 } 1413 1414 // }}} 1415 // {{{ function errorInfo($error = null) 1416 1417 /** 1418 * This method is used to collect information about an error 1419 * 1420 * @param mixed error code or resource 1421 * 1422 * @return array with MDB2 errorcode, native error code, native message 1423 * 1424 * @access public 1425 */ 1426 function errorInfo($error = null) 1427 { 1428 return array($error, null, null); 1429 } 1430 1431 // }}} 1432 // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null) 1433 1434 /** 1435 * This method is used to communicate an error and invoke error 1436 * callbacks etc. Basically a wrapper for PEAR::raiseError 1437 * without the message string. 1438 * 1439 * @param mixed $code integer error code, or a PEAR error object (all 1440 * other parameters are ignored if this parameter is 1441 * an object 1442 * @param int $mode error mode, see PEAR_Error docs 1443 * @param mixed $options If error mode is PEAR_ERROR_TRIGGER, this is the 1444 * error level (E_USER_NOTICE etc). If error mode is 1445 * PEAR_ERROR_CALLBACK, this is the callback function, 1446 * either as a function name, or as an array of an 1447 * object and method name. For other error modes this 1448 * parameter is ignored. 1449 * @param string $userinfo Extra debug information. Defaults to the last 1450 * query and native error code. 1451 * @param string $method name of the method that triggered the error 1452 * @param string $dummy1 not used 1453 * @param bool $dummy2 not used 1454 * 1455 * @return PEAR_Error instance of a PEAR Error object 1456 * @access public 1457 * @see PEAR_Error 1458 */ 1459 function &raiseError($code = null, 1460 $mode = null, 1461 $options = null, 1462 $userinfo = null, 1463 $method = null, 1464 $dummy1 = null, 1465 $dummy2 = false 1466 ) { 1467 $userinfo = "[Error message: $userinfo]\n"; 1468 // The error is yet a MDB2 error object 1469 if (MDB2::isError($code)) { 1470 // because we use the static PEAR::raiseError, our global 1471 // handler should be used if it is set 1472 if ((null === $mode) && !empty($this->_default_error_mode)) { 1473 $mode = $this->_default_error_mode; 1474 $options = $this->_default_error_options; 1475 } 1476 if (null === $userinfo) { 1477 $userinfo = $code->getUserinfo(); 1478 } 1479 $code = $code->getCode(); 1480 } elseif ($code == MDB2_ERROR_NOT_FOUND) { 1481 // extension not loaded: don't call $this->errorInfo() or the script 1482 // will die 1483 } elseif (isset($this->connection)) { 1484 if (!empty($this->last_query)) { 1485 $userinfo.= "[Last executed query: {$this->last_query}]\n"; 1486 } 1487 $native_errno = $native_msg = null; 1488 list($code, $native_errno, $native_msg) = $this->errorInfo($code); 1489 if ((null !== $native_errno) && $native_errno !== '') { 1490 $userinfo.= "[Native code: $native_errno]\n"; 1491 } 1492 if ((null !== $native_msg) && $native_msg !== '') { 1493 $userinfo.= "[Native message: ". strip_tags($native_msg) ."]\n"; 1494 } 1495 if (null !== $method) { 1496 $userinfo = $method.': '.$userinfo; 1497 } 1498 } 1499 1500 $err = $this->pear->raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true); 1501 if ($err->getMode() !== PEAR_ERROR_RETURN 1502 && isset($this->nested_transaction_counter) && !$this->has_transaction_error) { 1503 $this->has_transaction_error = $err; 1504 } 1505 return $err; 1506 } 1507 1508 // }}} 1509 // {{{ function resetWarnings() 1510 1511 /** 1512 * reset the warning array 1513 * 1514 * @return void 1515 * 1516 * @access public 1517 */ 1518 function resetWarnings() 1519 { 1520 $this->warnings = array(); 1521 } 1522 1523 // }}} 1524 // {{{ function getWarnings() 1525 1526 /** 1527 * Get all warnings in reverse order. 1528 * This means that the last warning is the first element in the array 1529 * 1530 * @return array with warnings 1531 * 1532 * @access public 1533 * @see resetWarnings() 1534 */ 1535 function getWarnings() 1536 { 1537 return array_reverse($this->warnings); 1538 } 1539 1540 // }}} 1541 // {{{ function setFetchMode($fetchmode, $object_class = 'stdClass') 1542 1543 /** 1544 * Sets which fetch mode should be used by default on queries 1545 * on this connection 1546 * 1547 * @param int MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC 1548 * or MDB2_FETCHMODE_OBJECT 1549 * @param string the class name of the object to be returned 1550 * by the fetch methods when the 1551 * MDB2_FETCHMODE_OBJECT mode is selected. 1552 * If no class is specified by default a cast 1553 * to object from the assoc array row will be 1554 * done. There is also the possibility to use 1555 * and extend the 'MDB2_row' class. 1556 * 1557 * @return mixed MDB2_OK or MDB2 Error Object 1558 * 1559 * @access public 1560 * @see MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC, MDB2_FETCHMODE_OBJECT 1561 */ 1562 function setFetchMode($fetchmode, $object_class = 'stdClass') 1563 { 1564 switch ($fetchmode) { 1565 case MDB2_FETCHMODE_OBJECT: 1566 $this->options['fetch_class'] = $object_class; 1567 case MDB2_FETCHMODE_ORDERED: 1568 case MDB2_FETCHMODE_ASSOC: 1569 $this->fetchmode = $fetchmode; 1570 break; 1571 default: 1572 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 1573 'invalid fetchmode mode', __FUNCTION__); 1574 } 1575 1576 return MDB2_OK; 1577 } 1578 1579 // }}} 1580 // {{{ function setOption($option, $value) 1581 1582 /** 1583 * set the option for the db class 1584 * 1585 * @param string option name 1586 * @param mixed value for the option 1587 * 1588 * @return mixed MDB2_OK or MDB2 Error Object 1589 * 1590 * @access public 1591 */ 1592 function setOption($option, $value) 1593 { 1594 if (array_key_exists($option, $this->options)) { 1595 $this->options[$option] = $value; 1596 return MDB2_OK; 1597 } 1598 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 1599 "unknown option $option", __FUNCTION__); 1600 } 1601 1602 // }}} 1603 // {{{ function getOption($option) 1604 1605 /** 1606 * Returns the value of an option 1607 * 1608 * @param string option name 1609 * 1610 * @return mixed the option value or error object 1611 * 1612 * @access public 1613 */ 1614 function getOption($option) 1615 { 1616 if (array_key_exists($option, $this->options)) { 1617 return $this->options[$option]; 1618 } 1619 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 1620 "unknown option $option", __FUNCTION__); 1621 } 1622 1623 // }}} 1624 // {{{ function debug($message, $scope = '', $is_manip = null) 1625 1626 /** 1627 * set a debug message 1628 * 1629 * @param string message that should be appended to the debug variable 1630 * @param string usually the method name that triggered the debug call: 1631 * for example 'query', 'prepare', 'execute', 'parameters', 1632 * 'beginTransaction', 'commit', 'rollback' 1633 * @param array contains context information about the debug() call 1634 * common keys are: is_manip, time, result etc. 1635 * 1636 * @return void 1637 * 1638 * @access public 1639 */ 1640 function debug($message, $scope = '', $context = array()) 1641 { 1642 if ($this->options['debug'] && $this->options['debug_handler']) { 1643 if (!$this->options['debug_expanded_output']) { 1644 if (!empty($context['when']) && $context['when'] !== 'pre') { 1645 return null; 1646 } 1647 $context = empty($context['is_manip']) ? false : $context['is_manip']; 1648 } 1649 return call_user_func_array($this->options['debug_handler'], array(&$this, $scope, $message, $context)); 1650 } 1651 return null; 1652 } 1653 1654 // }}} 1655 // {{{ function getDebugOutput() 1656 1657 /** 1658 * output debug info 1659 * 1660 * @return string content of the debug_output class variable 1661 * 1662 * @access public 1663 */ 1664 function getDebugOutput() 1665 { 1666 return $this->debug_output; 1667 } 1668 1669 // }}} 1670 // {{{ function escape($text) 1671 1672 /** 1673 * Quotes a string so it can be safely used in a query. It will quote 1674 * the text so it can safely be used within a query. 1675 * 1676 * @param string the input string to quote 1677 * @param bool escape wildcards 1678 * 1679 * @return string quoted string 1680 * 1681 * @access public 1682 */ 1683 function escape($text, $escape_wildcards = false) 1684 { 1685 if ($escape_wildcards) { 1686 $text = $this->escapePattern($text); 1687 } 1688 1689 $text = str_replace($this->string_quoting['end'], $this->string_quoting['escape'] . $this->string_quoting['end'], $text); 1690 return $text; 1691 } 1692 1693 // }}} 1694 // {{{ function escapePattern($text) 1695 1696 /** 1697 * Quotes pattern (% and _) characters in a string) 1698 * 1699 * @param string the input string to quote 1700 * 1701 * @return string quoted string 1702 * 1703 * @access public 1704 */ 1705 function escapePattern($text) 1706 { 1707 if ($this->string_quoting['escape_pattern']) { 1708 $text = str_replace($this->string_quoting['escape_pattern'], $this->string_quoting['escape_pattern'] . $this->string_quoting['escape_pattern'], $text); 1709 foreach ($this->wildcards as $wildcard) { 1710 $text = str_replace($wildcard, $this->string_quoting['escape_pattern'] . $wildcard, $text); 1711 } 1712 } 1713 return $text; 1714 } 1715 1716 // }}} 1717 // {{{ function quoteIdentifier($str, $check_option = false) 1718 1719 /** 1720 * Quote a string so it can be safely used as a table or column name 1721 * 1722 * Delimiting style depends on which database driver is being used. 1723 * 1724 * NOTE: just because you CAN use delimited identifiers doesn't mean 1725 * you SHOULD use them. In general, they end up causing way more 1726 * problems than they solve. 1727 * 1728 * NOTE: if you have table names containing periods, don't use this method 1729 * (@see bug #11906) 1730 * 1731 * Portability is broken by using the following characters inside 1732 * delimited identifiers: 1733 * + backtick (<kbd>`</kbd>) -- due to MySQL 1734 * + double quote (<kbd>"</kbd>) -- due to Oracle 1735 * + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access 1736 * 1737 * Delimited identifiers are known to generally work correctly under 1738 * the following drivers: 1739 * + mssql 1740 * + mysql 1741 * + mysqli 1742 * + oci8 1743 * + pgsql 1744 * + sqlite 1745 * 1746 * InterBase doesn't seem to be able to use delimited identifiers 1747 * via PHP 4. They work fine under PHP 5. 1748 * 1749 * @param string identifier name to be quoted 1750 * @param bool check the 'quote_identifier' option 1751 * 1752 * @return string quoted identifier string 1753 * 1754 * @access public 1755 */ 1756 function quoteIdentifier($str, $check_option = false) 1757 { 1758 if ($check_option && !$this->options['quote_identifier']) { 1759 return $str; 1760 } 1761 $str = str_replace($this->identifier_quoting['end'], $this->identifier_quoting['escape'] . $this->identifier_quoting['end'], $str); 1762 $parts = explode('.', $str); 1763 foreach (array_keys($parts) as $k) { 1764 $parts[$k] = $this->identifier_quoting['start'] . $parts[$k] . $this->identifier_quoting['end']; 1765 } 1766 return implode('.', $parts); 1767 } 1768 1769 // }}} 1770 // {{{ function getAsKeyword() 1771 1772 /** 1773 * Gets the string to alias column 1774 * 1775 * @return string to use when aliasing a column 1776 */ 1777 function getAsKeyword() 1778 { 1779 return $this->as_keyword; 1780 } 1781 1782 // }}} 1783 // {{{ function getConnection() 1784 1785 /** 1786 * Returns a native connection 1787 * 1788 * @return mixed a valid MDB2 connection object, 1789 * or a MDB2 error object on error 1790 * 1791 * @access public 1792 */ 1793 function getConnection() 1794 { 1795 $result = $this->connect(); 1796 if (MDB2::isError($result)) { 1797 return $result; 1798 } 1799 return $this->connection; 1800 } 1801 1802 // }}} 1803 // {{{ function _fixResultArrayValues(&$row, $mode) 1804 1805 /** 1806 * Do all necessary conversions on result arrays to fix DBMS quirks 1807 * 1808 * @param array the array to be fixed (passed by reference) 1809 * @param array bit-wise addition of the required portability modes 1810 * 1811 * @return void 1812 * 1813 * @access protected 1814 */ 1815 function _fixResultArrayValues(&$row, $mode) 1816 { 1817 switch ($mode) { 1818 case MDB2_PORTABILITY_EMPTY_TO_NULL: 1819 foreach ($row as $key => $value) { 1820 if ($value === '') { 1821 $row[$key] = null; 1822 } 1823 } 1824 break; 1825 case MDB2_PORTABILITY_RTRIM: 1826 foreach ($row as $key => $value) { 1827 if (is_string($value)) { 1828 $row[$key] = rtrim($value); 1829 } 1830 } 1831 break; 1832 case MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES: 1833 $tmp_row = array(); 1834 foreach ($row as $key => $value) { 1835 $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value; 1836 } 1837 $row = $tmp_row; 1838 break; 1839 case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL): 1840 foreach ($row as $key => $value) { 1841 if ($value === '') { 1842 $row[$key] = null; 1843 } elseif (is_string($value)) { 1844 $row[$key] = rtrim($value); 1845 } 1846 } 1847 break; 1848 case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES): 1849 $tmp_row = array(); 1850 foreach ($row as $key => $value) { 1851 if (is_string($value)) { 1852 $value = rtrim($value); 1853 } 1854 $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value; 1855 } 1856 $row = $tmp_row; 1857 break; 1858 case (MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES): 1859 $tmp_row = array(); 1860 foreach ($row as $key => $value) { 1861 if ($value === '') { 1862 $value = null; 1863 } 1864 $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value; 1865 } 1866 $row = $tmp_row; 1867 break; 1868 case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES): 1869 $tmp_row = array(); 1870 foreach ($row as $key => $value) { 1871 if ($value === '') { 1872 $value = null; 1873 } elseif (is_string($value)) { 1874 $value = rtrim($value); 1875 } 1876 $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value; 1877 } 1878 $row = $tmp_row; 1879 break; 1880 } 1881 } 1882 1883 // }}} 1884 // {{{ function loadModule($module, $property = null, $phptype_specific = null) 1885 1886 /** 1887 * loads a module 1888 * 1889 * @param string name of the module that should be loaded 1890 * (only used for error messages) 1891 * @param string name of the property into which the class will be loaded 1892 * @param bool if the class to load for the module is specific to the 1893 * phptype 1894 * 1895 * @return object on success a reference to the given module is returned 1896 * and on failure a PEAR error 1897 * 1898 * @access public 1899 */ 1900 function loadModule($module, $property = null, $phptype_specific = null) 1901 { 1902 if (!$property) { 1903 $property = strtolower($module); 1904 } 1905 1906 if (!isset($this->{$property})) { 1907 $version = $phptype_specific; 1908 if ($phptype_specific !== false) { 1909 $version = true; 1910 $class_name = 'MDB2_Driver_'.$module.'_'.$this->phptype; 1911 $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php'; 1912 } 1913 if ($phptype_specific === false 1914 || (!MDB2::classExists($class_name) && !MDB2::fileExists($file_name)) 1915 ) { 1916 $version = false; 1917 $class_name = 'MDB2_'.$module; 1918 $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php'; 1919 } 1920 1921 $err = MDB2::loadClass($class_name, $this->getOption('debug')); 1922 if (MDB2::isError($err)) { 1923 return $err; 1924 } 1925 1926 // load module in a specific version 1927 if ($version) { 1928 if (method_exists($class_name, 'getClassName')) { 1929 $class_name_new = call_user_func(array($class_name, 'getClassName'), $this->db_index); 1930 if ($class_name != $class_name_new) { 1931 $class_name = $class_name_new; 1932 $err = MDB2::loadClass($class_name, $this->getOption('debug')); 1933 if (MDB2::isError($err)) { 1934 return $err; 1935 } 1936 } 1937 } 1938 } 1939 1940 if (!MDB2::classExists($class_name)) { 1941 $err = $this->raiseError(MDB2_ERROR_LOADMODULE, null, null, 1942 "unable to load module '$module' into property '$property'", __FUNCTION__); 1943 return $err; 1944 } 1945 $this->{$property} = new $class_name($this->db_index); 1946 $this->modules[$module] = $this->{$property}; 1947 if ($version) { 1948 // this will be used in the connect method to determine if the module 1949 // needs to be loaded with a different version if the server 1950 // version changed in between connects 1951 $this->loaded_version_modules[] = $property; 1952 } 1953 } 1954 1955 return $this->{$property}; 1956 } 1957 1958 // }}} 1959 // {{{ function __call($method, $params) 1960 1961 /** 1962 * Calls a module method using the __call magic method 1963 * 1964 * @param string Method name. 1965 * @param array Arguments. 1966 * 1967 * @return mixed Returned value. 1968 */ 1969 function __call($method, $params) 1970 { 1971 $module = null; 1972 if (preg_match('/^([a-z]+)([A-Z])(.*)$/', $method, $match) 1973 && isset($this->options['modules'][$match[1]]) 1974 ) { 1975 $module = $this->options['modules'][$match[1]]; 1976 $method = strtolower($match[2]).$match[3]; 1977 if (!isset($this->modules[$module]) || !is_object($this->modules[$module])) { 1978 $result = $this->loadModule($module); 1979 if (MDB2::isError($result)) { 1980 return $result; 1981 } 1982 } 1983 } else { 1984 foreach ($this->modules as $key => $foo) { 1985 if (is_object($this->modules[$key]) 1986 && method_exists($this->modules[$key], $method) 1987 ) { 1988 $module = $key; 1989 break; 1990 } 1991 } 1992 } 1993 if (null !== $module) { 1994 return call_user_func_array(array(&$this->modules[$module], $method), $params); 1995 } 1996 1997 $class = get_class($this); 1998 $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); 1999 $loc = 'in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line']; 2000 if ($method == 'isError') { 2001 trigger_error("Deprecated: $class::isError() is deprecated, use MDB2::isError() $loc", E_USER_DEPRECATED); 2002 if (!array_key_exists(0, $params)) { 2003 trigger_error("Missing argument 1 for $class::$method, called $loc", E_USER_ERROR); 2004 } 2005 return MDB2::isError($params[0]); 2006 } 2007 trigger_error("Call to undefined function: $class::$method() $loc.", E_USER_ERROR); 2008 } 2009 2010 // }}} 2011 // {{{ function __callStatic($method, $params) 2012 2013 /** 2014 * Calls a module method using the __callStatic magic method 2015 * 2016 * @param string Method name. 2017 * @param array Arguments. 2018 * 2019 * @return mixed Returned value. 2020 */ 2021 public static function __callStatic($method, $params) 2022 { 2023 $class = get_called_class(); 2024 $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); 2025 $loc = 'in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line']; 2026 if ($method == 'isError') { 2027 trigger_error("Deprecated: $class::isError() is deprecated, use MDB2::isError() $loc", E_USER_DEPRECATED); 2028 if (!array_key_exists(0, $params)) { 2029 trigger_error("Missing argument 1 for $class::$method, called $loc", E_USER_ERROR); 2030 } 2031 return MDB2::isError($params[0]); 2032 } 2033 trigger_error("Call to undefined function: $class::$method() $loc.", E_USER_ERROR); 2034 } 2035 2036 // }}} 2037 // {{{ function beginTransaction($savepoint = null) 2038 2039 /** 2040 * Start a transaction or set a savepoint. 2041 * 2042 * @param string name of a savepoint to set 2043 * @return mixed MDB2_OK on success, a MDB2 error on failure 2044 * 2045 * @access public 2046 */ 2047 function beginTransaction($savepoint = null) 2048 { 2049 $this->debug('Starting transaction', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); 2050 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2051 'transactions are not supported', __FUNCTION__); 2052 } 2053 2054 // }}} 2055 // {{{ function commit($savepoint = null) 2056 2057 /** 2058 * Commit the database changes done during a transaction that is in 2059 * progress or release a savepoint. This function may only be called when 2060 * auto-committing is disabled, otherwise it will fail. Therefore, a new 2061 * transaction is implicitly started after committing the pending changes. 2062 * 2063 * @param string name of a savepoint to release 2064 * @return mixed MDB2_OK on success, a MDB2 error on failure 2065 * 2066 * @access public 2067 */ 2068 function commit($savepoint = null) 2069 { 2070 $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); 2071 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2072 'commiting transactions is not supported', __FUNCTION__); 2073 } 2074 2075 // }}} 2076 // {{{ function rollback($savepoint = null) 2077 2078 /** 2079 * Cancel any database changes done during a transaction or since a specific 2080 * savepoint that is in progress. This function may only be called when 2081 * auto-committing is disabled, otherwise it will fail. Therefore, a new 2082 * transaction is implicitly started after canceling the pending changes. 2083 * 2084 * @param string name of a savepoint to rollback to 2085 * @return mixed MDB2_OK on success, a MDB2 error on failure 2086 * 2087 * @access public 2088 */ 2089 function rollback($savepoint = null) 2090 { 2091 $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); 2092 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2093 'rolling back transactions is not supported', __FUNCTION__); 2094 } 2095 2096 // }}} 2097 // {{{ function inTransaction($ignore_nested = false) 2098 2099 /** 2100 * If a transaction is currently open. 2101 * 2102 * @param bool if the nested transaction count should be ignored 2103 * @return int|bool - an integer with the nesting depth is returned if a 2104 * nested transaction is open 2105 * - true is returned for a normal open transaction 2106 * - false is returned if no transaction is open 2107 * 2108 * @access public 2109 */ 2110 function inTransaction($ignore_nested = false) 2111 { 2112 if (!$ignore_nested && isset($this->nested_transaction_counter)) { 2113 return $this->nested_transaction_counter; 2114 } 2115 return $this->in_transaction; 2116 } 2117 2118 // }}} 2119 // {{{ function setTransactionIsolation($isolation) 2120 2121 /** 2122 * Set the transacton isolation level. 2123 * 2124 * @param string standard isolation level 2125 * READ UNCOMMITTED (allows dirty reads) 2126 * READ COMMITTED (prevents dirty reads) 2127 * REPEATABLE READ (prevents nonrepeatable reads) 2128 * SERIALIZABLE (prevents phantom reads) 2129 * @param array some transaction options: 2130 * 'wait' => 'WAIT' | 'NO WAIT' 2131 * 'rw' => 'READ WRITE' | 'READ ONLY' 2132 * @return mixed MDB2_OK on success, a MDB2 error on failure 2133 * 2134 * @access public 2135 * @since 2.1.1 2136 */ 2137 function setTransactionIsolation($isolation, $options = array()) 2138 { 2139 $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true)); 2140 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2141 'isolation level setting is not supported', __FUNCTION__); 2142 } 2143 2144 // }}} 2145 // {{{ function beginNestedTransaction($savepoint = false) 2146 2147 /** 2148 * Start a nested transaction. 2149 * 2150 * @return mixed MDB2_OK on success/savepoint name, a MDB2 error on failure 2151 * 2152 * @access public 2153 * @since 2.1.1 2154 */ 2155 function beginNestedTransaction() 2156 { 2157 if ($this->in_transaction) { 2158 ++$this->nested_transaction_counter; 2159 $savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter); 2160 if ($this->supports('savepoints') && $savepoint) { 2161 return $this->beginTransaction($savepoint); 2162 } 2163 return MDB2_OK; 2164 } 2165 $this->has_transaction_error = false; 2166 $result = $this->beginTransaction(); 2167 $this->nested_transaction_counter = 1; 2168 return $result; 2169 } 2170 2171 // }}} 2172 // {{{ function completeNestedTransaction($force_rollback = false, $release = false) 2173 2174 /** 2175 * Finish a nested transaction by rolling back if an error occured or 2176 * committing otherwise. 2177 * 2178 * @param bool if the transaction should be rolled back regardless 2179 * even if no error was set within the nested transaction 2180 * @return mixed MDB_OK on commit/counter decrementing, false on rollback 2181 * and a MDB2 error on failure 2182 * 2183 * @access public 2184 * @since 2.1.1 2185 */ 2186 function completeNestedTransaction($force_rollback = false) 2187 { 2188 if ($this->nested_transaction_counter > 1) { 2189 $savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter); 2190 if ($this->supports('savepoints') && $savepoint) { 2191 if ($force_rollback || $this->has_transaction_error) { 2192 $result = $this->rollback($savepoint); 2193 if (!MDB2::isError($result)) { 2194 $result = false; 2195 $this->has_transaction_error = false; 2196 } 2197 } else { 2198 $result = $this->commit($savepoint); 2199 } 2200 } else { 2201 $result = MDB2_OK; 2202 } 2203 --$this->nested_transaction_counter; 2204 return $result; 2205 } 2206 2207 $this->nested_transaction_counter = null; 2208 $result = MDB2_OK; 2209 2210 // transaction has not yet been rolled back 2211 if ($this->in_transaction) { 2212 if ($force_rollback || $this->has_transaction_error) { 2213 $result = $this->rollback(); 2214 if (!MDB2::isError($result)) { 2215 $result = false; 2216 } 2217 } else { 2218 $result = $this->commit(); 2219 } 2220 } 2221 $this->has_transaction_error = false; 2222 return $result; 2223 } 2224 2225 // }}} 2226 // {{{ function failNestedTransaction($error = null, $immediately = false) 2227 2228 /** 2229 * Force setting nested transaction to failed. 2230 * 2231 * @param mixed value to return in getNestededTransactionError() 2232 * @param bool if the transaction should be rolled back immediately 2233 * @return bool MDB2_OK 2234 * 2235 * @access public 2236 * @since 2.1.1 2237 */ 2238 function failNestedTransaction($error = null, $immediately = false) 2239 { 2240 if (null !== $error) { 2241 $error = $this->has_transaction_error ? $this->has_transaction_error : true; 2242 } elseif (!$error) { 2243 $error = true; 2244 } 2245 $this->has_transaction_error = $error; 2246 if (!$immediately) { 2247 return MDB2_OK; 2248 } 2249 return $this->rollback(); 2250 } 2251 2252 // }}} 2253 // {{{ function getNestedTransactionError() 2254 2255 /** 2256 * The first error that occured since the transaction start. 2257 * 2258 * @return MDB2_Error|bool MDB2 error object if an error occured or false. 2259 * 2260 * @access public 2261 * @since 2.1.1 2262 */ 2263 function getNestedTransactionError() 2264 { 2265 return $this->has_transaction_error; 2266 } 2267 2268 // }}} 2269 // {{{ connect() 2270 2271 /** 2272 * Connect to the database 2273 * 2274 * @return true on success, MDB2 Error Object on failure 2275 */ 2276 function connect() 2277 { 2278 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2279 'method not implemented', __FUNCTION__); 2280 } 2281 2282 // }}} 2283 // {{{ databaseExists() 2284 2285 /** 2286 * check if given database name is exists? 2287 * 2288 * @param string $name name of the database that should be checked 2289 * 2290 * @return mixed true/false on success, a MDB2 error on failure 2291 * @access public 2292 */ 2293 function databaseExists($name) 2294 { 2295 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2296 'method not implemented', __FUNCTION__); 2297 } 2298 2299 // }}} 2300 // {{{ setCharset($charset, $connection = null) 2301 2302 /** 2303 * Set the charset on the current connection 2304 * 2305 * @param string charset 2306 * @param resource connection handle 2307 * 2308 * @return true on success, MDB2 Error Object on failure 2309 */ 2310 function setCharset($charset, $connection = null) 2311 { 2312 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2313 'method not implemented', __FUNCTION__); 2314 } 2315 2316 // }}} 2317 // {{{ function disconnect($force = true) 2318 2319 /** 2320 * Log out and disconnect from the database. 2321 * 2322 * @param boolean $force whether the disconnect should be forced even if the 2323 * connection is opened persistently 2324 * 2325 * @return mixed true on success, false if not connected and error object on error 2326 * 2327 * @access public 2328 */ 2329 function disconnect($force = true) 2330 { 2331 $this->connection = 0; 2332 $this->connected_dsn = array(); 2333 $this->connected_database_name = ''; 2334 $this->opened_persistent = null; 2335 $this->connected_server_info = ''; 2336 $this->in_transaction = null; 2337 $this->nested_transaction_counter = null; 2338 return MDB2_OK; 2339 } 2340 2341 // }}} 2342 // {{{ function setDatabase($name) 2343 2344 /** 2345 * Select a different database 2346 * 2347 * @param string name of the database that should be selected 2348 * 2349 * @return string name of the database previously connected to 2350 * 2351 * @access public 2352 */ 2353 function setDatabase($name) 2354 { 2355 $previous_database_name = (isset($this->database_name)) ? $this->database_name : ''; 2356 $this->database_name = $name; 2357 if (!empty($this->connected_database_name) && ($this->connected_database_name != $this->database_name)) { 2358 $this->disconnect(false); 2359 } 2360 return $previous_database_name; 2361 } 2362 2363 // }}} 2364 // {{{ function getDatabase() 2365 2366 /** 2367 * Get the current database 2368 * 2369 * @return string name of the database 2370 * 2371 * @access public 2372 */ 2373 function getDatabase() 2374 { 2375 return $this->database_name; 2376 } 2377 2378 // }}} 2379 // {{{ function setDSN($dsn) 2380 2381 /** 2382 * set the DSN 2383 * 2384 * @param mixed DSN string or array 2385 * 2386 * @return MDB2_OK 2387 * 2388 * @access public 2389 */ 2390 function setDSN($dsn) 2391 { 2392 $dsn_default = $GLOBALS['_MDB2_dsninfo_default']; 2393 $dsn = MDB2::parseDSN($dsn); 2394 if (array_key_exists('database', $dsn)) { 2395 $this->database_name = $dsn['database']; 2396 unset($dsn['database']); 2397 } 2398 $this->dsn = array_merge($dsn_default, $dsn); 2399 return $this->disconnect(false); 2400 } 2401 2402 // }}} 2403 // {{{ function getDSN($type = 'string', $hidepw = false) 2404 2405 /** 2406 * return the DSN as a string 2407 * 2408 * @param string format to return ("array", "string") 2409 * @param string string to hide the password with 2410 * 2411 * @return mixed DSN in the chosen type 2412 * 2413 * @access public 2414 */ 2415 function getDSN($type = 'string', $hidepw = false) 2416 { 2417 $dsn = array_merge($GLOBALS['_MDB2_dsninfo_default'], $this->dsn); 2418 $dsn['phptype'] = $this->phptype; 2419 $dsn['database'] = $this->database_name; 2420 if ($hidepw) { 2421 $dsn['password'] = $hidepw; 2422 } 2423 switch ($type) { 2424 // expand to include all possible options 2425 case 'string': 2426 $dsn = $dsn['phptype']. 2427 ($dsn['dbsyntax'] ? ('('.$dsn['dbsyntax'].')') : ''). 2428 '://'.$dsn['username'].':'. 2429 $dsn['password'].'@'.$dsn['hostspec']. 2430 ($dsn['port'] ? (':'.$dsn['port']) : ''). 2431 '/'.$dsn['database']; 2432 break; 2433 case 'array': 2434 default: 2435 break; 2436 } 2437 return $dsn; 2438 } 2439 2440 // }}} 2441 // {{{ _isNewLinkSet() 2442 2443 /** 2444 * Check if the 'new_link' option is set 2445 * 2446 * @return boolean 2447 * 2448 * @access protected 2449 */ 2450 function _isNewLinkSet() 2451 { 2452 return (isset($this->dsn['new_link']) 2453 && ($this->dsn['new_link'] === true 2454 || (is_string($this->dsn['new_link']) && preg_match('/^true$/i', $this->dsn['new_link'])) 2455 || (is_numeric($this->dsn['new_link']) && 0 != (int)$this->dsn['new_link']) 2456 ) 2457 ); 2458 } 2459 2460 // }}} 2461 // {{{ function &standaloneQuery($query, $types = null, $is_manip = false) 2462 2463 /** 2464 * execute a query as database administrator 2465 * 2466 * @param string the SQL query 2467 * @param mixed array that contains the types of the columns in 2468 * the result set 2469 * @param bool if the query is a manipulation query 2470 * 2471 * @return mixed MDB2_OK on success, a MDB2 error on failure 2472 * 2473 * @access public 2474 */ 2475 function standaloneQuery($query, $types = null, $is_manip = false) 2476 { 2477 $offset = $this->offset; 2478 $limit = $this->limit; 2479 $this->offset = $this->limit = 0; 2480 $query = $this->_modifyQuery($query, $is_manip, $limit, $offset); 2481 2482 $connection = $this->getConnection(); 2483 if (MDB2::isError($connection)) { 2484 return $connection; 2485 } 2486 2487 $result = $this->_doQuery($query, $is_manip, $connection, false); 2488 if (MDB2::isError($result)) { 2489 return $result; 2490 } 2491 2492 if ($is_manip) { 2493 $affected_rows = $this->_affectedRows($connection, $result); 2494 return $affected_rows; 2495 } 2496 $result = $this->_wrapResult($result, $types, true, true, $limit, $offset); 2497 return $result; 2498 } 2499 2500 // }}} 2501 // {{{ function _modifyQuery($query, $is_manip, $limit, $offset) 2502 2503 /** 2504 * Changes a query string for various DBMS specific reasons 2505 * 2506 * @param string query to modify 2507 * @param bool if it is a DML query 2508 * @param int limit the number of rows 2509 * @param int start reading from given offset 2510 * 2511 * @return string modified query 2512 * 2513 * @access protected 2514 */ 2515 function _modifyQuery($query, $is_manip, $limit, $offset) 2516 { 2517 return $query; 2518 } 2519 2520 // }}} 2521 // {{{ function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null) 2522 2523 /** 2524 * Execute a query 2525 * @param string query 2526 * @param bool if the query is a manipulation query 2527 * @param resource connection handle 2528 * @param string database name 2529 * 2530 * @return result or error object 2531 * 2532 * @access protected 2533 */ 2534 function _doQuery($query, $is_manip = false, $connection = null, $database_name = null) 2535 { 2536 $this->last_query = $query; 2537 $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre')); 2538 if ($result) { 2539 if (MDB2::isError($result)) { 2540 return $result; 2541 } 2542 $query = $result; 2543 } 2544 $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2545 'method not implemented', __FUNCTION__); 2546 return $err; 2547 } 2548 2549 // }}} 2550 // {{{ function _affectedRows($connection, $result = null) 2551 2552 /** 2553 * Returns the number of rows affected 2554 * 2555 * @param resource result handle 2556 * @param resource connection handle 2557 * 2558 * @return mixed MDB2 Error Object or the number of rows affected 2559 * 2560 * @access private 2561 */ 2562 function _affectedRows($connection, $result = null) 2563 { 2564 return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2565 'method not implemented', __FUNCTION__); 2566 } 2567 2568 // }}} 2569 // {{{ function &exec($query) 2570 2571 /** 2572 * Execute a manipulation query to the database and return the number of affected rows 2573 * 2574 * @param string the SQL query 2575 * 2576 * @return mixed number of affected rows on success, a MDB2 error on failure 2577 * 2578 * @access public 2579 */ 2580 function exec($query) 2581 { 2582 $offset = $this->offset; 2583 $limit = $this->limit; 2584 $this->offset = $this->limit = 0; 2585 $query = $this->_modifyQuery($query, true, $limit, $offset); 2586 2587 $connection = $this->getConnection(); 2588 if (MDB2::isError($connection)) { 2589 return $connection; 2590 } 2591 2592 $result = $this->_doQuery($query, true, $connection, $this->database_name); 2593 if (MDB2::isError($result)) { 2594 return $result; 2595 } 2596 2597 $affectedRows = $this->_affectedRows($connection, $result); 2598 return $affectedRows; 2599 } 2600 2601 // }}} 2602 // {{{ function &query($query, $types = null, $result_class = true, $result_wrap_class = false) 2603 2604 /** 2605 * Send a query to the database and return any results 2606 * 2607 * @param string the SQL query 2608 * @param mixed array that contains the types of the columns in 2609 * the result set 2610 * @param mixed string which specifies which result class to use 2611 * @param mixed string which specifies which class to wrap results in 2612 * 2613 * @return mixed an MDB2_Result handle on success, a MDB2 error on failure 2614 * 2615 * @access public 2616 */ 2617 function query($query, $types = null, $result_class = true, $result_wrap_class = true) 2618 { 2619 $offset = $this->offset; 2620 $limit = $this->limit; 2621 $this->offset = $this->limit = 0; 2622 $query = $this->_modifyQuery($query, false, $limit, $offset); 2623 2624 $connection = $this->getConnection(); 2625 if (MDB2::isError($connection)) { 2626 return $connection; 2627 } 2628 2629 $result = $this->_doQuery($query, false, $connection, $this->database_name); 2630 if (MDB2::isError($result)) { 2631 return $result; 2632 } 2633 2634 $result = $this->_wrapResult($result, $types, $result_class, $result_wrap_class, $limit, $offset); 2635 return $result; 2636 } 2637 2638 // }}} 2639 // {{{ function _wrapResult($result_resource, $types = array(), $result_class = true, $result_wrap_class = false, $limit = null, $offset = null) 2640 2641 /** 2642 * wrap a result set into the correct class 2643 * 2644 * @param resource result handle 2645 * @param mixed array that contains the types of the columns in 2646 * the result set 2647 * @param mixed string which specifies which result class to use 2648 * @param mixed string which specifies which class to wrap results in 2649 * @param string number of rows to select 2650 * @param string first row to select 2651 * 2652 * @return mixed an MDB2_Result, a MDB2 error on failure 2653 * 2654 * @access protected 2655 */ 2656 function _wrapResult($result_resource, $types = array(), $result_class = true, 2657 $result_wrap_class = true, $limit = null, $offset = null) 2658 { 2659 if ($types === true) { 2660 if ($this->supports('result_introspection')) { 2661 $this->loadModule('Reverse', null, true); 2662 $tableInfo = $this->reverse->tableInfo($result_resource); 2663 if (MDB2::isError($tableInfo)) { 2664 return $tableInfo; 2665 } 2666 $types = array(); 2667 $types_assoc = array(); 2668 foreach ($tableInfo as $field) { 2669 $types[] = $field['mdb2type']; 2670 $types_assoc[$field['name']] = $field['mdb2type']; 2671 } 2672 } else { 2673 $types = null; 2674 } 2675 } 2676 2677 if ($result_class === true) { 2678 $result_class = $this->options['result_buffering'] 2679 ? $this->options['buffered_result_class'] : $this->options['result_class']; 2680 } 2681 2682 if ($result_class) { 2683 $class_name = sprintf($result_class, $this->phptype); 2684 if (!MDB2::classExists($class_name)) { 2685 $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 2686 'result class does not exist '.$class_name, __FUNCTION__); 2687 return $err; 2688 } 2689 $result = new $class_name($this, $result_resource, $limit, $offset); 2690 if (!MDB2::isResultCommon($result)) { 2691 $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 2692 'result class is not extended from MDB2_Result_Common', __FUNCTION__); 2693 return $err; 2694 } 2695 2696 if (!empty($types)) { 2697 $err = $result->setResultTypes($types); 2698 if (MDB2::isError($err)) { 2699 $result->free(); 2700 return $err; 2701 } 2702 } 2703 if (!empty($types_assoc)) { 2704 $err = $result->setResultTypes($types_assoc); 2705 if (MDB2::isError($err)) { 2706 $result->free(); 2707 return $err; 2708 } 2709 } 2710 2711 if ($result_wrap_class === true) { 2712 $result_wrap_class = $this->options['result_wrap_class']; 2713 } 2714 if ($result_wrap_class) { 2715 if (!MDB2::classExists($result_wrap_class)) { 2716 $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 2717 'result wrap class does not exist '.$result_wrap_class, __FUNCTION__); 2718 return $err; 2719 } 2720 $result = new $result_wrap_class($result, $this->fetchmode); 2721 } 2722 2723 return $result; 2724 } 2725 2726 return $result_resource; 2727 } 2728 2729 // }}} 2730 // {{{ function getServerVersion($native = false) 2731 2732 /** 2733 * return version information about the server 2734 * 2735 * @param bool determines if the raw version string should be returned 2736 * 2737 * @return mixed array with version information or row string 2738 * 2739 * @access public 2740 */ 2741 function getServerVersion($native = false) 2742 { 2743 return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2744 'method not implemented', __FUNCTION__); 2745 } 2746 2747 // }}} 2748 // {{{ function setLimit($limit, $offset = null) 2749 2750 /** 2751 * set the range of the next query 2752 * 2753 * @param string number of rows to select 2754 * @param string first row to select 2755 * 2756 * @return mixed MDB2_OK on success, a MDB2 error on failure 2757 * 2758 * @access public 2759 */ 2760 function setLimit($limit, $offset = null) 2761 { 2762 if (!$this->supports('limit_queries')) { 2763 return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2764 'limit is not supported by this driver', __FUNCTION__); 2765 } 2766 $limit = (int)$limit; 2767 if ($limit < 0) { 2768 return MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, 2769 'it was not specified a valid selected range row limit', __FUNCTION__); 2770 } 2771 $this->limit = $limit; 2772 if (null !== $offset) { 2773 $offset = (int)$offset; 2774 if ($offset < 0) { 2775 return MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, 2776 'it was not specified a valid first selected range row', __FUNCTION__); 2777 } 2778 $this->offset = $offset; 2779 } 2780 return MDB2_OK; 2781 } 2782 2783 // }}} 2784 // {{{ function subSelect($query, $type = false) 2785 2786 /** 2787 * simple subselect emulation: leaves the query untouched for all RDBMS 2788 * that support subselects 2789 * 2790 * @param string the SQL query for the subselect that may only 2791 * return a column 2792 * @param string determines type of the field 2793 * 2794 * @return string the query 2795 * 2796 * @access public 2797 */ 2798 function subSelect($query, $type = false) 2799 { 2800 if ($this->supports('sub_selects') === true) { 2801 return $query; 2802 } 2803 2804 if (!$this->supports('sub_selects')) { 2805 return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2806 'method not implemented', __FUNCTION__); 2807 } 2808 2809 $col = $this->queryCol($query, $type); 2810 if (MDB2::isError($col)) { 2811 return $col; 2812 } 2813 if (!is_array($col) || count($col) == 0) { 2814 return 'NULL'; 2815 } 2816 if ($type) { 2817 $this->loadModule('Datatype', null, true); 2818 return $this->datatype->implodeArray($col, $type); 2819 } 2820 return implode(', ', $col); 2821 } 2822 2823 // }}} 2824 // {{{ function replace($table, $fields) 2825 2826 /** 2827 * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT 2828 * query, except that if there is already a row in the table with the same 2829 * key field values, the old row is deleted before the new row is inserted. 2830 * 2831 * The REPLACE type of query does not make part of the SQL standards. Since 2832 * practically only MySQL and SQLite implement it natively, this type of 2833 * query isemulated through this method for other DBMS using standard types 2834 * of queries inside a transaction to assure the atomicity of the operation. 2835 * 2836 * @param string name of the table on which the REPLACE query will 2837 * be executed. 2838 * @param array associative array that describes the fields and the 2839 * values that will be inserted or updated in the specified table. The 2840 * indexes of the array are the names of all the fields of the table. 2841 * The values of the array are also associative arrays that describe 2842 * the values and other properties of the table fields. 2843 * 2844 * Here follows a list of field properties that need to be specified: 2845 * 2846 * value 2847 * Value to be assigned to the specified field. This value may be 2848 * of specified in database independent type format as this 2849 * function can perform the necessary datatype conversions. 2850 * 2851 * Default: this property is required unless the Null property is 2852 * set to 1. 2853 * 2854 * type 2855 * Name of the type of the field. Currently, all types MDB2 2856 * are supported except for clob and blob. 2857 * 2858 * Default: no type conversion 2859 * 2860 * null 2861 * bool property that indicates that the value for this field 2862 * should be set to null. 2863 * 2864 * The default value for fields missing in INSERT queries may be 2865 * specified the definition of a table. Often, the default value 2866 * is already null, but since the REPLACE may be emulated using 2867 * an UPDATE query, make sure that all fields of the table are 2868 * listed in this function argument array. 2869 * 2870 * Default: 0 2871 * 2872 * key 2873 * bool property that indicates that this field should be 2874 * handled as a primary key or at least as part of the compound 2875 * unique index of the table that will determine the row that will 2876 * updated if it exists or inserted a new row otherwise. 2877 * 2878 * This function will fail if no key field is specified or if the 2879 * value of a key field is set to null because fields that are 2880 * part of unique index they may not be null. 2881 * 2882 * Default: 0 2883 * 2884 * @return mixed MDB2_OK on success, a MDB2 error on failure 2885 * 2886 * @access public 2887 */ 2888 function replace($table, $fields) 2889 { 2890 if (!$this->supports('replace')) { 2891 return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 2892 'replace query is not supported', __FUNCTION__); 2893 } 2894 $count = count($fields); 2895 $condition = $values = array(); 2896 for ($colnum = 0, reset($fields); $colnum < $count; next($fields), $colnum++) { 2897 $name = key($fields); 2898 if (isset($fields[$name]['null']) && $fields[$name]['null']) { 2899 $value = 'NULL'; 2900 } else { 2901 $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null; 2902 $value = $this->quote($fields[$name]['value'], $type); 2903 } 2904 $values[$name] = $value; 2905 if (isset($fields[$name]['key']) && $fields[$name]['key']) { 2906 if ($value === 'NULL') { 2907 return MDB2_Driver_Common::raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, 2908 'key value '.$name.' may not be NULL', __FUNCTION__); 2909 } 2910 $condition[] = $this->quoteIdentifier($name, true) . '=' . $value; 2911 } 2912 } 2913 if (empty($condition)) { 2914 return MDB2_Driver_Common::raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, 2915 'not specified which fields are keys', __FUNCTION__); 2916 } 2917 2918 $result = null; 2919 $in_transaction = $this->in_transaction; 2920 if (!$in_transaction && MDB2::isError($result = $this->beginTransaction())) { 2921 return $result; 2922 } 2923 2924 $connection = $this->getConnection(); 2925 if (MDB2::isError($connection)) { 2926 return $connection; 2927 } 2928 2929 $condition = ' WHERE '.implode(' AND ', $condition); 2930 $query = 'DELETE FROM ' . $this->quoteIdentifier($table, true) . $condition; 2931 $result = $this->_doQuery($query, true, $connection); 2932 if (!MDB2::isError($result)) { 2933 $affected_rows = $this->_affectedRows($connection, $result); 2934 $insert = ''; 2935 foreach ($values as $key => $value) { 2936 $insert .= ($insert?', ':'') . $this->quoteIdentifier($key, true); 2937 } 2938 $values = implode(', ', $values); 2939 $query = 'INSERT INTO '. $this->quoteIdentifier($table, true) . "($insert) VALUES ($values)"; 2940 $result = $this->_doQuery($query, true, $connection); 2941 if (!MDB2::isError($result)) { 2942 $affected_rows += $this->_affectedRows($connection, $result);; 2943 } 2944 } 2945 2946 if (!$in_transaction) { 2947 if (MDB2::isError($result)) { 2948 $this->rollback(); 2949 } else { 2950 $result = $this->commit(); 2951 } 2952 } 2953 2954 if (MDB2::isError($result)) { 2955 return $result; 2956 } 2957 2958 return $affected_rows; 2959 } 2960 2961 // }}} 2962 // {{{ function &prepare($query, $types = null, $result_types = null, $lobs = array()) 2963 2964 /** 2965 * Prepares a query for multiple execution with execute(). 2966 * With some database backends, this is emulated. 2967 * prepare() requires a generic query as string like 2968 * 'INSERT INTO numbers VALUES(?,?)' or 2969 * 'INSERT INTO numbers VALUES(:foo,:bar)'. 2970 * The ? and :name and are placeholders which can be set using 2971 * bindParam() and the query can be sent off using the execute() method. 2972 * The allowed format for :name can be set with the 'bindname_format' option. 2973 * 2974 * @param string the query to prepare 2975 * @param mixed array that contains the types of the placeholders 2976 * @param mixed array that contains the types of the columns in 2977 * the result set or MDB2_PREPARE_RESULT, if set to 2978 * MDB2_PREPARE_MANIP the query is handled as a manipulation query 2979 * @param mixed key (field) value (parameter) pair for all lob placeholders 2980 * 2981 * @return mixed resource handle for the prepared query on success, 2982 * a MDB2 error on failure 2983 * 2984 * @access public 2985 * @see bindParam, execute 2986 */ 2987 function prepare($query, $types = null, $result_types = null, $lobs = array()) 2988 { 2989 $is_manip = ($result_types === MDB2_PREPARE_MANIP); 2990 $offset = $this->offset; 2991 $limit = $this->limit; 2992 $this->offset = $this->limit = 0; 2993 $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre')); 2994 if ($result) { 2995 if (MDB2::isError($result)) { 2996 return $result; 2997 } 2998 $query = $result; 2999 } 3000 $placeholder_type_guess = $placeholder_type = null; 3001 $question = '?'; 3002 $colon = ':'; 3003 $positions = array(); 3004 $position = 0; 3005 while ($position < strlen($query)) { 3006 $q_position = strpos($query, $question, $position); 3007 $c_position = strpos($query, $colon, $position); 3008 if ($q_position && $c_position) { 3009 $p_position = min($q_position, $c_position); 3010 } elseif ($q_position) { 3011 $p_position = $q_position; 3012 } elseif ($c_position) { 3013 $p_position = $c_position; 3014 } else { 3015 break; 3016 } 3017 if (null === $placeholder_type) { 3018 $placeholder_type_guess = $query[$p_position]; 3019 } 3020 3021 $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position); 3022 if (MDB2::isError($new_pos)) { 3023 return $new_pos; 3024 } 3025 if ($new_pos != $position) { 3026 $position = $new_pos; 3027 continue; //evaluate again starting from the new position 3028 } 3029 3030 if ($query[$position] == $placeholder_type_guess) { 3031 if (null === $placeholder_type) { 3032 $placeholder_type = $query[$p_position]; 3033 $question = $colon = $placeholder_type; 3034 if (!empty($types) && is_array($types)) { 3035 if ($placeholder_type == ':') { 3036 if (is_int(key($types))) { 3037 $types_tmp = $types; 3038 $types = array(); 3039 $count = -1; 3040 } 3041 } else { 3042 $types = array_values($types); 3043 } 3044 } 3045 } 3046 if ($placeholder_type == ':') { 3047 $regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s'; 3048 $parameter = preg_replace($regexp, '\\1', $query); 3049 if ($parameter === '') { 3050 $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, 3051 'named parameter name must match "bindname_format" option', __FUNCTION__); 3052 return $err; 3053 } 3054 $positions[$p_position] = $parameter; 3055 $query = substr_replace($query, '?', $position, strlen($parameter)+1); 3056 // use parameter name in type array 3057 if (isset($count) && isset($types_tmp[++$count])) { 3058 $types[$parameter] = $types_tmp[$count]; 3059 } 3060 } else { 3061 $positions[$p_position] = count($positions); 3062 } 3063 $position = $p_position + 1; 3064 } else { 3065 $position = $p_position; 3066 } 3067 } 3068 $class_name = 'MDB2_Statement_'.$this->phptype; 3069 $statement = null; 3070 $obj = new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset); 3071 $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj)); 3072 return $obj; 3073 } 3074 3075 // }}} 3076 // {{{ function _skipDelimitedStrings($query, $position, $p_position) 3077 3078 /** 3079 * Utility method, used by prepare() to avoid replacing placeholders within delimited strings. 3080 * Check if the placeholder is contained within a delimited string. 3081 * If so, skip it and advance the position, otherwise return the current position, 3082 * which is valid 3083 * 3084 * @param string $query 3085 * @param integer $position current string cursor position 3086 * @param integer $p_position placeholder position 3087 * 3088 * @return mixed integer $new_position on success 3089 * MDB2_Error on failure 3090 * 3091 * @access protected 3092 */ 3093 function _skipDelimitedStrings($query, $position, $p_position) 3094 { 3095 $ignores = array(); 3096 $ignores[] = $this->string_quoting; 3097 $ignores[] = $this->identifier_quoting; 3098 $ignores = array_merge($ignores, $this->sql_comments); 3099 3100 foreach ($ignores as $ignore) { 3101 if (!empty($ignore['start'])) { 3102 if (is_int($start_quote = strpos($query, $ignore['start'], $position)) && $start_quote < $p_position) { 3103 $end_quote = $start_quote; 3104 do { 3105 if (!is_int($end_quote = strpos($query, $ignore['end'], $end_quote + 1))) { 3106 if ($ignore['end'] === "\n") { 3107 $end_quote = strlen($query) - 1; 3108 } else { 3109 $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, 3110 'query with an unterminated text string specified', __FUNCTION__); 3111 return $err; 3112 } 3113 } 3114 } while ($ignore['escape'] 3115 && $end_quote-1 != $start_quote 3116 && $query[($end_quote - 1)] == $ignore['escape'] 3117 && ( $ignore['escape_pattern'] !== $ignore['escape'] 3118 || $query[($end_quote - 2)] != $ignore['escape']) 3119 ); 3120 3121 $position = $end_quote + 1; 3122 return $position; 3123 } 3124 } 3125 } 3126 return $position; 3127 } 3128 3129 // }}} 3130 // {{{ function quote($value, $type = null, $quote = true) 3131 3132 /** 3133 * Convert a text value into a DBMS specific format that is suitable to 3134 * compose query statements. 3135 * 3136 * @param string text string value that is intended to be converted. 3137 * @param string type to which the value should be converted to 3138 * @param bool quote 3139 * @param bool escape wildcards 3140 * 3141 * @return string text string that represents the given argument value in 3142 * a DBMS specific format. 3143 * 3144 * @access public 3145 */ 3146 function quote($value, $type = null, $quote = true, $escape_wildcards = false) 3147 { 3148 $result = $this->loadModule('Datatype', null, true); 3149 if (MDB2::isError($result)) { 3150 return $result; 3151 } 3152 3153 return $this->datatype->quote($value, $type, $quote, $escape_wildcards); 3154 } 3155 3156 // }}} 3157 // {{{ function getDeclaration($type, $name, $field) 3158 3159 /** 3160 * Obtain DBMS specific SQL code portion needed to declare 3161 * of the given type 3162 * 3163 * @param string type to which the value should be converted to 3164 * @param string name the field to be declared. 3165 * @param string definition of the field 3166 * 3167 * @return string DBMS specific SQL code portion that should be used to 3168 * declare the specified field. 3169 * 3170 * @access public 3171 */ 3172 function getDeclaration($type, $name, $field) 3173 { 3174 $result = $this->loadModule('Datatype', null, true); 3175 if (MDB2::isError($result)) { 3176 return $result; 3177 } 3178 return $this->datatype->getDeclaration($type, $name, $field); 3179 } 3180 3181 // }}} 3182 // {{{ function compareDefinition($current, $previous) 3183 3184 /** 3185 * Obtain an array of changes that may need to applied 3186 * 3187 * @param array new definition 3188 * @param array old definition 3189 * 3190 * @return array containing all changes that will need to be applied 3191 * 3192 * @access public 3193 */ 3194 function compareDefinition($current, $previous) 3195 { 3196 $result = $this->loadModule('Datatype', null, true); 3197 if (MDB2::isError($result)) { 3198 return $result; 3199 } 3200 return $this->datatype->compareDefinition($current, $previous); 3201 } 3202 3203 // }}} 3204 // {{{ function supports($feature) 3205 3206 /** 3207 * Tell whether a DB implementation or its backend extension 3208 * supports a given feature. 3209 * 3210 * @param string name of the feature (see the MDB2 class doc) 3211 * 3212 * @return bool|string if this DB implementation supports a given feature 3213 * false means no, true means native, 3214 * 'emulated' means emulated 3215 * 3216 * @access public 3217 */ 3218 function supports($feature) 3219 { 3220 if (array_key_exists($feature, $this->supported)) { 3221 return $this->supported[$feature]; 3222 } 3223 return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 3224 "unknown support feature $feature", __FUNCTION__); 3225 } 3226 3227 // }}} 3228 // {{{ function getSequenceName($sqn) 3229 3230 /** 3231 * adds sequence name formatting to a sequence name 3232 * 3233 * @param string name of the sequence 3234 * 3235 * @return string formatted sequence name 3236 * 3237 * @access public 3238 */ 3239 function getSequenceName($sqn) 3240 { 3241 return sprintf($this->options['seqname_format'], 3242 preg_replace('/[^a-z0-9_\-\$.]/i', '_', $sqn)); 3243 } 3244 3245 // }}} 3246 // {{{ function getIndexName($idx) 3247 3248 /** 3249 * adds index name formatting to a index name 3250 * 3251 * @param string name of the index 3252 * 3253 * @return string formatted index name 3254 * 3255 * @access public 3256 */ 3257 function getIndexName($idx) 3258 { 3259 return sprintf($this->options['idxname_format'], 3260 preg_replace('/[^a-z0-9_\-\$.]/i', '_', $idx)); 3261 } 3262 3263 // }}} 3264 // {{{ function nextID($seq_name, $ondemand = true) 3265 3266 /** 3267 * Returns the next free id of a sequence 3268 * 3269 * @param string name of the sequence 3270 * @param bool when true missing sequences are automatic created 3271 * 3272 * @return mixed MDB2 Error Object or id 3273 * 3274 * @access public 3275 */ 3276 function nextID($seq_name, $ondemand = true) 3277 { 3278 return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 3279 'method not implemented', __FUNCTION__); 3280 } 3281 3282 // }}} 3283 // {{{ function lastInsertID($table = null, $field = null) 3284 3285 /** 3286 * Returns the autoincrement ID if supported or $id or fetches the current 3287 * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field) 3288 * 3289 * @param string name of the table into which a new row was inserted 3290 * @param string name of the field into which a new row was inserted 3291 * 3292 * @return mixed MDB2 Error Object or id 3293 * 3294 * @access public 3295 */ 3296 function lastInsertID($table = null, $field = null) 3297 { 3298 return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 3299 'method not implemented', __FUNCTION__); 3300 } 3301 3302 // }}} 3303 // {{{ function currID($seq_name) 3304 3305 /** 3306 * Returns the current id of a sequence 3307 * 3308 * @param string name of the sequence 3309 * 3310 * @return mixed MDB2 Error Object or id 3311 * 3312 * @access public 3313 */ 3314 function currID($seq_name) 3315 { 3316 $this->warnings[] = 'database does not support getting current 3317 sequence value, the sequence value was incremented'; 3318 return $this->nextID($seq_name); 3319 } 3320 3321 // }}} 3322 // {{{ function queryOne($query, $type = null, $colnum = 0) 3323 3324 /** 3325 * Execute the specified query, fetch the value from the first column of 3326 * the first row of the result set and then frees 3327 * the result set. 3328 * 3329 * @param string $query the SELECT query statement to be executed. 3330 * @param string $type optional argument that specifies the expected 3331 * datatype of the result set field, so that an eventual 3332 * conversion may be performed. The default datatype is 3333 * text, meaning that no conversion is performed 3334 * @param mixed $colnum the column number (or name) to fetch 3335 * 3336 * @return mixed MDB2_OK or field value on success, a MDB2 error on failure 3337 * 3338 * @access public 3339 */ 3340 function queryOne($query, $type = null, $colnum = 0) 3341 { 3342 $result = $this->query($query, $type); 3343 if (!MDB2::isResultCommon($result)) { 3344 return $result; 3345 } 3346 3347 $one = $result->fetchOne($colnum); 3348 $result->free(); 3349 return $one; 3350 } 3351 3352 // }}} 3353 // {{{ function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT) 3354 3355 /** 3356 * Execute the specified query, fetch the values from the first 3357 * row of the result set into an array and then frees 3358 * the result set. 3359 * 3360 * @param string the SELECT query statement to be executed. 3361 * @param array optional array argument that specifies a list of 3362 * expected datatypes of the result set columns, so that the eventual 3363 * conversions may be performed. The default list of datatypes is 3364 * empty, meaning that no conversion is performed. 3365 * @param int how the array data should be indexed 3366 * 3367 * @return mixed MDB2_OK or data array on success, a MDB2 error on failure 3368 * 3369 * @access public 3370 */ 3371 function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT) 3372 { 3373 $result = $this->query($query, $types); 3374 if (!MDB2::isResultCommon($result)) { 3375 return $result; 3376 } 3377 3378 $row = $result->fetchRow($fetchmode); 3379 $result->free(); 3380 return $row; 3381 } 3382 3383 // }}} 3384 // {{{ function queryCol($query, $type = null, $colnum = 0) 3385 3386 /** 3387 * Execute the specified query, fetch the value from the first column of 3388 * each row of the result set into an array and then frees the result set. 3389 * 3390 * @param string $query the SELECT query statement to be executed. 3391 * @param string $type optional argument that specifies the expected 3392 * datatype of the result set field, so that an eventual 3393 * conversion may be performed. The default datatype is text, 3394 * meaning that no conversion is performed 3395 * @param mixed $colnum the column number (or name) to fetch 3396 * 3397 * @return mixed MDB2_OK or data array on success, a MDB2 error on failure 3398 * @access public 3399 */ 3400 function queryCol($query, $type = null, $colnum = 0) 3401 { 3402 $result = $this->query($query, $type); 3403 if (!MDB2::isResultCommon($result)) { 3404 return $result; 3405 } 3406 3407 $col = $result->fetchCol($colnum); 3408 $result->free(); 3409 return $col; 3410 } 3411 3412 // }}} 3413 // {{{ function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false) 3414 3415 /** 3416 * Execute the specified query, fetch all the rows of the result set into 3417 * a two dimensional array and then frees the result set. 3418 * 3419 * @param string the SELECT query statement to be executed. 3420 * @param array optional array argument that specifies a list of 3421 * expected datatypes of the result set columns, so that the eventual 3422 * conversions may be performed. The default list of datatypes is 3423 * empty, meaning that no conversion is performed. 3424 * @param int how the array data should be indexed 3425 * @param bool if set to true, the $all will have the first 3426 * column as its first dimension 3427 * @param bool used only when the query returns exactly 3428 * two columns. If true, the values of the returned array will be 3429 * one-element arrays instead of scalars. 3430 * @param bool if true, the values of the returned array is 3431 * wrapped in another array. If the same key value (in the first 3432 * column) repeats itself, the values will be appended to this array 3433 * instead of overwriting the existing values. 3434 * 3435 * @return mixed MDB2_OK or data array on success, a MDB2 error on failure 3436 * 3437 * @access public 3438 */ 3439 function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, 3440 $rekey = false, $force_array = false, $group = false) 3441 { 3442 $result = $this->query($query, $types); 3443 if (!MDB2::isResultCommon($result)) { 3444 return $result; 3445 } 3446 3447 $all = $result->fetchAll($fetchmode, $rekey, $force_array, $group); 3448 $result->free(); 3449 return $all; 3450 } 3451 3452 // }}} 3453 // {{{ function delExpect($error_code) 3454 3455 /** 3456 * This method deletes all occurences of the specified element from 3457 * the expected error codes stack. 3458 * 3459 * @param mixed $error_code error code that should be deleted 3460 * @return mixed list of error codes that were deleted or error 3461 * 3462 * @uses PEAR::delExpect() 3463 */ 3464 public function delExpect($error_code) 3465 { 3466 return $this->pear->delExpect($error_code); 3467 } 3468 3469 // }}} 3470 // {{{ function expectError($code) 3471 3472 /** 3473 * This method is used to tell which errors you expect to get. 3474 * Expected errors are always returned with error mode 3475 * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, 3476 * and this method pushes a new element onto it. The list of 3477 * expected errors are in effect until they are popped off the 3478 * stack with the popExpect() method. 3479 * 3480 * Note that this method can not be called statically 3481 * 3482 * @param mixed $code a single error code or an array of error codes to expect 3483 * 3484 * @return int the new depth of the "expected errors" stack 3485 * 3486 * @uses PEAR::expectError() 3487 */ 3488 public function expectError($code = '*') 3489 { 3490 return $this->pear->expectError($code); 3491 } 3492 3493 // }}} 3494 // {{{ function getStaticProperty($class, $var) 3495 3496 /** 3497 * If you have a class that's mostly/entirely static, and you need static 3498 * properties, you can use this method to simulate them. Eg. in your method(s) 3499 * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar'); 3500 * You MUST use a reference, or they will not persist! 3501 * 3502 * @param string $class The calling classname, to prevent clashes 3503 * @param string $var The variable to retrieve. 3504 * @return mixed A reference to the variable. If not set it will be 3505 * auto initialised to NULL. 3506 * 3507 * @uses PEAR::getStaticProperty() 3508 */ 3509 public function &getStaticProperty($class, $var) 3510 { 3511 $tmp =& $this->pear->getStaticProperty($class, $var); 3512 return $tmp; 3513 } 3514 3515 // }}} 3516 // {{{ function popErrorHandling() 3517 3518 /** 3519 * Pop the last error handler used 3520 * 3521 * @return bool Always true 3522 * 3523 * @see PEAR::pushErrorHandling 3524 * @uses PEAR::popErrorHandling() 3525 */ 3526 public function popErrorHandling() 3527 { 3528 return $this->pear->popErrorHandling(); 3529 } 3530 3531 // }}} 3532 // {{{ function popExpect() 3533 3534 /** 3535 * This method pops one element off the expected error codes 3536 * stack. 3537 * 3538 * @return array the list of error codes that were popped 3539 * 3540 * @uses PEAR::popExpect() 3541 */ 3542 public function popExpect() 3543 { 3544 return $this->pear->popExpect(); 3545 } 3546 3547 // }}} 3548 // {{{ function pushErrorHandling($mode, $options = null) 3549 3550 /** 3551 * Push a new error handler on top of the error handler options stack. With this 3552 * you can easily override the actual error handler for some code and restore 3553 * it later with popErrorHandling. 3554 * 3555 * @param mixed $mode (same as setErrorHandling) 3556 * @param mixed $options (same as setErrorHandling) 3557 * 3558 * @return bool Always true 3559 * 3560 * @see PEAR::setErrorHandling 3561 * @uses PEAR::pushErrorHandling() 3562 */ 3563 public function pushErrorHandling($mode, $options = null) 3564 { 3565 return $this->pear->pushErrorHandling($mode, $options); 3566 } 3567 3568 // }}} 3569 // {{{ function registerShutdownFunc($func, $args = array()) 3570 3571 /** 3572 * Use this function to register a shutdown method for static 3573 * classes. 3574 * 3575 * @param mixed $func The function name (or array of class/method) to call 3576 * @param mixed $args The arguments to pass to the function 3577 * @return void 3578 * 3579 * @uses PEAR::registerShutdownFunc() 3580 */ 3581 public function registerShutdownFunc($func, $args = array()) 3582 { 3583 return $this->pear->registerShutdownFunc($func, $args); 3584 } 3585 3586 // }}} 3587 // {{{ function setErrorHandling($mode = null, $options = null) 3588 3589 /** 3590 * Sets how errors generated by this object should be handled. 3591 * Can be invoked both in objects and statically. If called 3592 * statically, setErrorHandling sets the default behaviour for all 3593 * PEAR objects. If called in an object, setErrorHandling sets 3594 * the default behaviour for that object. 3595 * 3596 * @param int $mode 3597 * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, 3598 * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, 3599 * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. 3600 * 3601 * @param mixed $options 3602 * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one 3603 * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). 3604 * 3605 * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected 3606 * to be the callback function or method. A callback 3607 * function is a string with the name of the function, a 3608 * callback method is an array of two elements: the element 3609 * at index 0 is the object, and the element at index 1 is 3610 * the name of the method to call in the object. 3611 * 3612 * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is 3613 * a printf format string used when printing the error 3614 * message. 3615 * 3616 * @access public 3617 * @return void 3618 * @see PEAR_ERROR_RETURN 3619 * @see PEAR_ERROR_PRINT 3620 * @see PEAR_ERROR_TRIGGER 3621 * @see PEAR_ERROR_DIE 3622 * @see PEAR_ERROR_CALLBACK 3623 * @see PEAR_ERROR_EXCEPTION 3624 * 3625 * @since PHP 4.0.5 3626 * @uses PEAR::setErrorHandling($mode, $options) 3627 */ 3628 public function setErrorHandling($mode = null, $options = null) 3629 { 3630 return $this->pear->setErrorHandling($mode, $options); 3631 } 3632 3633 /** 3634 * @uses PEAR::staticPopErrorHandling() 3635 */ 3636 public function staticPopErrorHandling() 3637 { 3638 return $this->pear->staticPopErrorHandling(); 3639 } 3640 3641 // }}} 3642 // {{{ function staticPushErrorHandling($mode, $options = null) 3643 3644 /** 3645 * @uses PEAR::staticPushErrorHandling($mode, $options) 3646 */ 3647 public function staticPushErrorHandling($mode, $options = null) 3648 { 3649 return $this->pear->staticPushErrorHandling($mode, $options); 3650 } 3651 3652 // }}} 3653 // {{{ function &throwError($message = null, $code = null, $userinfo = null) 3654 3655 /** 3656 * Simpler form of raiseError with fewer options. In most cases 3657 * message, code and userinfo are enough. 3658 * 3659 * @param mixed $message a text error message or a PEAR error object 3660 * 3661 * @param int $code a numeric error code (it is up to your class 3662 * to define these if you want to use codes) 3663 * 3664 * @param string $userinfo If you need to pass along for example debug 3665 * information, this parameter is meant for that. 3666 * 3667 * @return object a PEAR error object 3668 * @see PEAR::raiseError 3669 * @uses PEAR::&throwError() 3670 */ 3671 public function &throwError($message = null, $code = null, $userinfo = null) 3672 { 3673 $tmp =& $this->pear->throwError($message, $code, $userinfo); 3674 return $tmp; 3675 } 3676 3677 // }}} 3678} 3679 3680// }}} 3681// {{{ class MDB2_Result 3682 3683/** 3684 * The dummy class that all user space result classes should extend from 3685 * 3686 * @package MDB2 3687 * @category Database 3688 * @author Lukas Smith <smith@pooteeweet.org> 3689 */ 3690class MDB2_Result 3691{ 3692} 3693 3694// }}} 3695// {{{ class MDB2_Result_Common extends MDB2_Result 3696 3697/** 3698 * The common result class for MDB2 result objects 3699 * 3700 * @package MDB2 3701 * @category Database 3702 * @author Lukas Smith <smith@pooteeweet.org> 3703 */ 3704class MDB2_Result_Common extends MDB2_Result 3705{ 3706 // {{{ Variables (Properties) 3707 3708 public $db; 3709 public $result; 3710 public $rownum = -1; 3711 public $types = array(); 3712 public $types_assoc = array(); 3713 public $values = array(); 3714 public $offset; 3715 public $offset_count = 0; 3716 public $limit; 3717 public $column_names; 3718 3719 // }}} 3720 // {{{ constructor: function __construct($db, &$result, $limit = 0, $offset = 0) 3721 3722 /** 3723 * Constructor 3724 */ 3725 function __construct($db, &$result, $limit = 0, $offset = 0) 3726 { 3727 $this->db = $db; 3728 $this->result = $result; 3729 $this->offset = $offset; 3730 $this->limit = max(0, $limit - 1); 3731 } 3732 3733 // }}} 3734 // {{{ function setResultTypes($types) 3735 3736 /** 3737 * Define the list of types to be associated with the columns of a given 3738 * result set. 3739 * 3740 * This function may be called before invoking fetchRow(), fetchOne(), 3741 * fetchCol() and fetchAll() so that the necessary data type 3742 * conversions are performed on the data to be retrieved by them. If this 3743 * function is not called, the type of all result set columns is assumed 3744 * to be text, thus leading to not perform any conversions. 3745 * 3746 * @param array variable that lists the 3747 * data types to be expected in the result set columns. If this array 3748 * contains less types than the number of columns that are returned 3749 * in the result set, the remaining columns are assumed to be of the 3750 * type text. Currently, the types clob and blob are not fully 3751 * supported. 3752 * 3753 * @return mixed MDB2_OK on success, a MDB2 error on failure 3754 * 3755 * @access public 3756 */ 3757 function setResultTypes($types) 3758 { 3759 $load = $this->db->loadModule('Datatype', null, true); 3760 if (MDB2::isError($load)) { 3761 return $load; 3762 } 3763 $types = $this->db->datatype->checkResultTypes($types); 3764 if (MDB2::isError($types)) { 3765 return $types; 3766 } 3767 foreach ($types as $key => $value) { 3768 if (is_numeric($key)) { 3769 $this->types[$key] = $value; 3770 } else { 3771 $this->types_assoc[$key] = $value; 3772 } 3773 } 3774 return MDB2_OK; 3775 } 3776 3777 // }}} 3778 // {{{ function seek($rownum = 0) 3779 3780 /** 3781 * Seek to a specific row in a result set 3782 * 3783 * @param int number of the row where the data can be found 3784 * 3785 * @return mixed MDB2_OK on success, a MDB2 error on failure 3786 * 3787 * @access public 3788 */ 3789 function seek($rownum = 0) 3790 { 3791 $target_rownum = $rownum - 1; 3792 if ($this->rownum > $target_rownum) { 3793 return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 3794 'seeking to previous rows not implemented', __FUNCTION__); 3795 } 3796 while ($this->rownum < $target_rownum) { 3797 $this->fetchRow(); 3798 } 3799 return MDB2_OK; 3800 } 3801 3802 // }}} 3803 // {{{ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) 3804 3805 /** 3806 * Fetch and return a row of data 3807 * 3808 * @param int how the array data should be indexed 3809 * @param int number of the row where the data can be found 3810 * 3811 * @return int data array on success, a MDB2 error on failure 3812 * 3813 * @access public 3814 */ 3815 function fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) 3816 { 3817 $err = MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 3818 'method not implemented', __FUNCTION__); 3819 return $err; 3820 } 3821 3822 // }}} 3823 // {{{ function fetchOne($colnum = 0) 3824 3825 /** 3826 * fetch single column from the next row from a result set 3827 * 3828 * @param int|string the column number (or name) to fetch 3829 * @param int number of the row where the data can be found 3830 * 3831 * @return string data on success, a MDB2 error on failure 3832 * @access public 3833 */ 3834 function fetchOne($colnum = 0, $rownum = null) 3835 { 3836 $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC; 3837 $row = $this->fetchRow($fetchmode, $rownum); 3838 if (!is_array($row) || MDB2::isError($row)) { 3839 return $row; 3840 } 3841 if (!array_key_exists($colnum, $row)) { 3842 return MDB2::raiseError(MDB2_ERROR_TRUNCATED, null, null, 3843 'column is not defined in the result set: '.$colnum, __FUNCTION__); 3844 } 3845 return $row[$colnum]; 3846 } 3847 3848 // }}} 3849 // {{{ function fetchCol($colnum = 0) 3850 3851 /** 3852 * Fetch and return a column from the current row pointer position 3853 * 3854 * @param int|string the column number (or name) to fetch 3855 * 3856 * @return mixed data array on success, a MDB2 error on failure 3857 * @access public 3858 */ 3859 function fetchCol($colnum = 0) 3860 { 3861 $column = array(); 3862 $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC; 3863 $row = $this->fetchRow($fetchmode); 3864 if (is_array($row)) { 3865 if (!array_key_exists($colnum, $row)) { 3866 return MDB2::raiseError(MDB2_ERROR_TRUNCATED, null, null, 3867 'column is not defined in the result set: '.$colnum, __FUNCTION__); 3868 } 3869 do { 3870 $column[] = $row[$colnum]; 3871 } while (is_array($row = $this->fetchRow($fetchmode))); 3872 } 3873 if (MDB2::isError($row)) { 3874 return $row; 3875 } 3876 return $column; 3877 } 3878 3879 // }}} 3880 // {{{ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false) 3881 3882 /** 3883 * Fetch and return all rows from the current row pointer position 3884 * 3885 * @param int $fetchmode the fetch mode to use: 3886 * + MDB2_FETCHMODE_ORDERED 3887 * + MDB2_FETCHMODE_ASSOC 3888 * + MDB2_FETCHMODE_ORDERED | MDB2_FETCHMODE_FLIPPED 3889 * + MDB2_FETCHMODE_ASSOC | MDB2_FETCHMODE_FLIPPED 3890 * @param bool if set to true, the $all will have the first 3891 * column as its first dimension 3892 * @param bool used only when the query returns exactly 3893 * two columns. If true, the values of the returned array will be 3894 * one-element arrays instead of scalars. 3895 * @param bool if true, the values of the returned array is 3896 * wrapped in another array. If the same key value (in the first 3897 * column) repeats itself, the values will be appended to this array 3898 * instead of overwriting the existing values. 3899 * 3900 * @return mixed data array on success, a MDB2 error on failure 3901 * 3902 * @access public 3903 * @see getAssoc() 3904 */ 3905 function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, 3906 $force_array = false, $group = false) 3907 { 3908 $all = array(); 3909 $row = $this->fetchRow($fetchmode); 3910 if (MDB2::isError($row)) { 3911 return $row; 3912 } elseif (!$row) { 3913 return $all; 3914 } 3915 3916 $shift_array = $rekey ? false : null; 3917 if (null !== $shift_array) { 3918 if (is_object($row)) { 3919 $colnum = count(get_object_vars($row)); 3920 } else { 3921 $colnum = count($row); 3922 } 3923 if ($colnum < 2) { 3924 return MDB2::raiseError(MDB2_ERROR_TRUNCATED, null, null, 3925 'rekey feature requires atleast 2 column', __FUNCTION__); 3926 } 3927 $shift_array = (!$force_array && $colnum == 2); 3928 } 3929 3930 if ($rekey) { 3931 do { 3932 if (is_object($row)) { 3933 $arr = get_object_vars($row); 3934 $key = reset($arr); 3935 unset($row->{$key}); 3936 } else { 3937 if ( $fetchmode == MDB2_FETCHMODE_ASSOC 3938 || $fetchmode == MDB2_FETCHMODE_OBJECT 3939 ) { 3940 $key = reset($row); 3941 unset($row[key($row)]); 3942 } else { 3943 $key = array_shift($row); 3944 } 3945 if ($shift_array) { 3946 $row = array_shift($row); 3947 } 3948 } 3949 if ($group) { 3950 $all[$key][] = $row; 3951 } else { 3952 $all[$key] = $row; 3953 } 3954 } while (($row = $this->fetchRow($fetchmode))); 3955 } elseif ($fetchmode == MDB2_FETCHMODE_FLIPPED) { 3956 do { 3957 foreach ($row as $key => $val) { 3958 $all[$key][] = $val; 3959 } 3960 } while (($row = $this->fetchRow($fetchmode))); 3961 } else { 3962 do { 3963 $all[] = $row; 3964 } while (($row = $this->fetchRow($fetchmode))); 3965 } 3966 3967 return $all; 3968 } 3969 3970 // }}} 3971 // {{{ function rowCount() 3972 /** 3973 * Returns the actual row number that was last fetched (count from 0) 3974 * @return int 3975 * 3976 * @access public 3977 */ 3978 function rowCount() 3979 { 3980 return $this->rownum + 1; 3981 } 3982 3983 // }}} 3984 // {{{ function numRows() 3985 3986 /** 3987 * Returns the number of rows in a result object 3988 * 3989 * @return mixed MDB2 Error Object or the number of rows 3990 * 3991 * @access public 3992 */ 3993 function numRows() 3994 { 3995 return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 3996 'method not implemented', __FUNCTION__); 3997 } 3998 3999 // }}} 4000 // {{{ function nextResult() 4001 4002 /** 4003 * Move the internal result pointer to the next available result 4004 * 4005 * @return true on success, false if there is no more result set or an error object on failure 4006 * 4007 * @access public 4008 */ 4009 function nextResult() 4010 { 4011 return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 4012 'method not implemented', __FUNCTION__); 4013 } 4014 4015 // }}} 4016 // {{{ function getColumnNames() 4017 4018 /** 4019 * Retrieve the names of columns returned by the DBMS in a query result or 4020 * from the cache. 4021 * 4022 * @param bool If set to true the values are the column names, 4023 * otherwise the names of the columns are the keys. 4024 * @return mixed Array variable that holds the names of columns or an 4025 * MDB2 error on failure. 4026 * Some DBMS may not return any columns when the result set 4027 * does not contain any rows. 4028 * 4029 * @access public 4030 */ 4031 function getColumnNames($flip = false) 4032 { 4033 if (!isset($this->column_names)) { 4034 $result = $this->_getColumnNames(); 4035 if (MDB2::isError($result)) { 4036 return $result; 4037 } 4038 $this->column_names = $result; 4039 } 4040 if ($flip) { 4041 return array_flip($this->column_names); 4042 } 4043 return $this->column_names; 4044 } 4045 4046 // }}} 4047 // {{{ function _getColumnNames() 4048 4049 /** 4050 * Retrieve the names of columns returned by the DBMS in a query result. 4051 * 4052 * @return mixed Array variable that holds the names of columns as keys 4053 * or an MDB2 error on failure. 4054 * Some DBMS may not return any columns when the result set 4055 * does not contain any rows. 4056 * 4057 * @access private 4058 */ 4059 function _getColumnNames() 4060 { 4061 return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 4062 'method not implemented', __FUNCTION__); 4063 } 4064 4065 // }}} 4066 // {{{ function numCols() 4067 4068 /** 4069 * Count the number of columns returned by the DBMS in a query result. 4070 * 4071 * @return mixed integer value with the number of columns, a MDB2 error 4072 * on failure 4073 * 4074 * @access public 4075 */ 4076 function numCols() 4077 { 4078 return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 4079 'method not implemented', __FUNCTION__); 4080 } 4081 4082 // }}} 4083 // {{{ function getResource() 4084 4085 /** 4086 * return the resource associated with the result object 4087 * 4088 * @return resource 4089 * 4090 * @access public 4091 */ 4092 function getResource() 4093 { 4094 return $this->result; 4095 } 4096 4097 // }}} 4098 // {{{ function bindColumn($column, &$value, $type = null) 4099 4100 /** 4101 * Set bind variable to a column. 4102 * 4103 * @param int column number or name 4104 * @param mixed variable reference 4105 * @param string specifies the type of the field 4106 * 4107 * @return mixed MDB2_OK on success, a MDB2 error on failure 4108 * 4109 * @access public 4110 */ 4111 function bindColumn($column, &$value, $type = null) 4112 { 4113 if (!is_numeric($column)) { 4114 $column_names = $this->getColumnNames(); 4115 if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { 4116 if ($this->db->options['field_case'] == CASE_LOWER) { 4117 $column = strtolower($column); 4118 } else { 4119 $column = strtoupper($column); 4120 } 4121 } 4122 $column = $column_names[$column]; 4123 } 4124 $this->values[$column] =& $value; 4125 if (null !== $type) { 4126 $this->types[$column] = $type; 4127 } 4128 return MDB2_OK; 4129 } 4130 4131 // }}} 4132 // {{{ function _assignBindColumns($row) 4133 4134 /** 4135 * Bind a variable to a value in the result row. 4136 * 4137 * @param array row data 4138 * 4139 * @return mixed MDB2_OK on success, a MDB2 error on failure 4140 * 4141 * @access private 4142 */ 4143 function _assignBindColumns($row) 4144 { 4145 $row = array_values($row); 4146 foreach ($row as $column => $value) { 4147 if (array_key_exists($column, $this->values)) { 4148 $this->values[$column] = $value; 4149 } 4150 } 4151 return MDB2_OK; 4152 } 4153 4154 // }}} 4155 // {{{ function free() 4156 4157 /** 4158 * Free the internal resources associated with result. 4159 * 4160 * @return bool true on success, false if result is invalid 4161 * 4162 * @access public 4163 */ 4164 function free() 4165 { 4166 $this->result = false; 4167 return MDB2_OK; 4168 } 4169 4170 // }}} 4171} 4172 4173// }}} 4174// {{{ class MDB2_Row 4175 4176/** 4177 * The simple class that accepts row data as an array 4178 * 4179 * @package MDB2 4180 * @category Database 4181 * @author Lukas Smith <smith@pooteeweet.org> 4182 */ 4183class MDB2_Row 4184{ 4185 // {{{ constructor: function __construct(&$row) 4186 4187 /** 4188 * constructor 4189 * 4190 * @param resource row data as array 4191 */ 4192 function __construct(&$row) 4193 { 4194 foreach ($row as $key => $value) { 4195 $this->$key = &$row[$key]; 4196 } 4197 } 4198 4199 // }}} 4200} 4201 4202// }}} 4203// {{{ class MDB2_Statement_Common 4204 4205/** 4206 * The common statement class for MDB2 statement objects 4207 * 4208 * @package MDB2 4209 * @category Database 4210 * @author Lukas Smith <smith@pooteeweet.org> 4211 */ 4212class MDB2_Statement_Common 4213{ 4214 // {{{ Variables (Properties) 4215 4216 var $db; 4217 var $statement; 4218 var $query; 4219 var $result_types; 4220 var $types; 4221 var $values = array(); 4222 var $limit; 4223 var $offset; 4224 var $is_manip; 4225 4226 // }}} 4227 // {{{ constructor: function __construct($db, $statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null) 4228 4229 /** 4230 * Constructor 4231 */ 4232 function __construct($db, $statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null) 4233 { 4234 $this->db = $db; 4235 $this->statement = $statement; 4236 $this->positions = $positions; 4237 $this->query = $query; 4238 $this->types = (array)$types; 4239 $this->result_types = (array)$result_types; 4240 $this->limit = $limit; 4241 $this->is_manip = $is_manip; 4242 $this->offset = $offset; 4243 } 4244 4245 // }}} 4246 // {{{ function bindValue($parameter, &$value, $type = null) 4247 4248 /** 4249 * Set the value of a parameter of a prepared query. 4250 * 4251 * @param int the order number of the parameter in the query 4252 * statement. The order number of the first parameter is 1. 4253 * @param mixed value that is meant to be assigned to specified 4254 * parameter. The type of the value depends on the $type argument. 4255 * @param string specifies the type of the field 4256 * 4257 * @return mixed MDB2_OK on success, a MDB2 error on failure 4258 * 4259 * @access public 4260 */ 4261 function bindValue($parameter, $value, $type = null) 4262 { 4263 if (!is_numeric($parameter)) { 4264 if (strpos($parameter, ':') === 0) { 4265 $parameter = substr($parameter, 1); 4266 } 4267 } 4268 if (!in_array($parameter, $this->positions)) { 4269 return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 4270 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__); 4271 } 4272 $this->values[$parameter] = $value; 4273 if (null !== $type) { 4274 $this->types[$parameter] = $type; 4275 } 4276 return MDB2_OK; 4277 } 4278 4279 // }}} 4280 // {{{ function bindValueArray($values, $types = null) 4281 4282 /** 4283 * Set the values of multiple a parameter of a prepared query in bulk. 4284 * 4285 * @param array specifies all necessary information 4286 * for bindValue() the array elements must use keys corresponding to 4287 * the number of the position of the parameter. 4288 * @param array specifies the types of the fields 4289 * 4290 * @return mixed MDB2_OK on success, a MDB2 error on failure 4291 * 4292 * @access public 4293 * @see bindParam() 4294 */ 4295 function bindValueArray($values, $types = null) 4296 { 4297 $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null); 4298 $parameters = array_keys($values); 4299 $this->db->pushErrorHandling(PEAR_ERROR_RETURN); 4300 $this->db->expectError(MDB2_ERROR_NOT_FOUND); 4301 foreach ($parameters as $key => $parameter) { 4302 $err = $this->bindValue($parameter, $values[$parameter], $types[$key]); 4303 if (MDB2::isError($err)) { 4304 if ($err->getCode() == MDB2_ERROR_NOT_FOUND) { 4305 //ignore (extra value for missing placeholder) 4306 continue; 4307 } 4308 $this->db->popExpect(); 4309 $this->db->popErrorHandling(); 4310 return $err; 4311 } 4312 } 4313 $this->db->popExpect(); 4314 $this->db->popErrorHandling(); 4315 return MDB2_OK; 4316 } 4317 4318 // }}} 4319 // {{{ function bindParam($parameter, &$value, $type = null) 4320 4321 /** 4322 * Bind a variable to a parameter of a prepared query. 4323 * 4324 * @param int the order number of the parameter in the query 4325 * statement. The order number of the first parameter is 1. 4326 * @param mixed variable that is meant to be bound to specified 4327 * parameter. The type of the value depends on the $type argument. 4328 * @param string specifies the type of the field 4329 * 4330 * @return mixed MDB2_OK on success, a MDB2 error on failure 4331 * 4332 * @access public 4333 */ 4334 function bindParam($parameter, &$value, $type = null) 4335 { 4336 if (!is_numeric($parameter)) { 4337 if (strpos($parameter, ':') === 0) { 4338 $parameter = substr($parameter, 1); 4339 } 4340 } 4341 if (!in_array($parameter, $this->positions)) { 4342 return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 4343 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__); 4344 } 4345 $this->values[$parameter] =& $value; 4346 if (null !== $type) { 4347 $this->types[$parameter] = $type; 4348 } 4349 return MDB2_OK; 4350 } 4351 4352 // }}} 4353 // {{{ function bindParamArray(&$values, $types = null) 4354 4355 /** 4356 * Bind the variables of multiple a parameter of a prepared query in bulk. 4357 * 4358 * @param array specifies all necessary information 4359 * for bindParam() the array elements must use keys corresponding to 4360 * the number of the position of the parameter. 4361 * @param array specifies the types of the fields 4362 * 4363 * @return mixed MDB2_OK on success, a MDB2 error on failure 4364 * 4365 * @access public 4366 * @see bindParam() 4367 */ 4368 function bindParamArray(&$values, $types = null) 4369 { 4370 $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null); 4371 $parameters = array_keys($values); 4372 foreach ($parameters as $key => $parameter) { 4373 $err = $this->bindParam($parameter, $values[$parameter], $types[$key]); 4374 if (MDB2::isError($err)) { 4375 return $err; 4376 } 4377 } 4378 return MDB2_OK; 4379 } 4380 4381 // }}} 4382 // {{{ function &execute($values = null, $result_class = true, $result_wrap_class = false) 4383 4384 /** 4385 * Execute a prepared query statement. 4386 * 4387 * @param array specifies all necessary information 4388 * for bindParam() the array elements must use keys corresponding 4389 * to the number of the position of the parameter. 4390 * @param mixed specifies which result class to use 4391 * @param mixed specifies which class to wrap results in 4392 * 4393 * @return mixed MDB2_Result or integer (affected rows) on success, 4394 * a MDB2 error on failure 4395 * @access public 4396 */ 4397 function execute($values = null, $result_class = true, $result_wrap_class = false) 4398 { 4399 if (null === $this->positions) { 4400 return MDB2::raiseError(MDB2_ERROR, null, null, 4401 'Prepared statement has already been freed', __FUNCTION__); 4402 } 4403 4404 $values = (array)$values; 4405 if (!empty($values)) { 4406 $err = $this->bindValueArray($values); 4407 if (MDB2::isError($err)) { 4408 return MDB2::raiseError(MDB2_ERROR, null, null, 4409 'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__); 4410 } 4411 } 4412 $result = $this->_execute($result_class, $result_wrap_class); 4413 return $result; 4414 } 4415 4416 // }}} 4417 // {{{ function _execute($result_class = true, $result_wrap_class = false) 4418 4419 /** 4420 * Execute a prepared query statement helper method. 4421 * 4422 * @param mixed specifies which result class to use 4423 * @param mixed specifies which class to wrap results in 4424 * 4425 * @return mixed MDB2_Result or integer (affected rows) on success, 4426 * a MDB2 error on failure 4427 * @access private 4428 */ 4429 function _execute($result_class = true, $result_wrap_class = false) 4430 { 4431 $this->last_query = $this->query; 4432 $query = ''; 4433 $last_position = 0; 4434 foreach ($this->positions as $current_position => $parameter) { 4435 if (!array_key_exists($parameter, $this->values)) { 4436 return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 4437 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__); 4438 } 4439 $value = $this->values[$parameter]; 4440 $query.= substr($this->query, $last_position, $current_position - $last_position); 4441 if (!isset($value)) { 4442 $value_quoted = 'NULL'; 4443 } else { 4444 $type = !empty($this->types[$parameter]) ? $this->types[$parameter] : null; 4445 $value_quoted = $this->db->quote($value, $type); 4446 if (MDB2::isError($value_quoted)) { 4447 return $value_quoted; 4448 } 4449 } 4450 $query.= $value_quoted; 4451 $last_position = $current_position + 1; 4452 } 4453 $query.= substr($this->query, $last_position); 4454 4455 $this->db->offset = $this->offset; 4456 $this->db->limit = $this->limit; 4457 if ($this->is_manip) { 4458 $result = $this->db->exec($query); 4459 } else { 4460 $result = $this->db->query($query, $this->result_types, $result_class, $result_wrap_class); 4461 } 4462 return $result; 4463 } 4464 4465 // }}} 4466 // {{{ function free() 4467 4468 /** 4469 * Release resources allocated for the specified prepared query. 4470 * 4471 * @return mixed MDB2_OK on success, a MDB2 error on failure 4472 * 4473 * @access public 4474 */ 4475 function free() 4476 { 4477 if (null === $this->positions) { 4478 return MDB2::raiseError(MDB2_ERROR, null, null, 4479 'Prepared statement has already been freed', __FUNCTION__); 4480 } 4481 4482 $this->statement = null; 4483 $this->positions = null; 4484 $this->query = null; 4485 $this->types = null; 4486 $this->result_types = null; 4487 $this->limit = null; 4488 $this->is_manip = null; 4489 $this->offset = null; 4490 $this->values = null; 4491 4492 return MDB2_OK; 4493 } 4494 4495 // }}} 4496} 4497 4498// }}} 4499// {{{ class MDB2_Module_Common 4500 4501/** 4502 * The common modules class for MDB2 module objects 4503 * 4504 * @package MDB2 4505 * @category Database 4506 * @author Lukas Smith <smith@pooteeweet.org> 4507 */ 4508class MDB2_Module_Common 4509{ 4510 // {{{ Variables (Properties) 4511 4512 /** 4513 * contains the key to the global MDB2 instance array of the associated 4514 * MDB2 instance 4515 * 4516 * @var int 4517 * @access protected 4518 */ 4519 protected $db_index; 4520 4521 // }}} 4522 // {{{ constructor: function __construct($db_index) 4523 4524 /** 4525 * Constructor 4526 */ 4527 function __construct($db_index) 4528 { 4529 $this->db_index = $db_index; 4530 } 4531 4532 // }}} 4533 // {{{ function getDBInstance() 4534 4535 /** 4536 * Get the instance of MDB2 associated with the module instance 4537 * 4538 * @return object MDB2 instance or a MDB2 error on failure 4539 * 4540 * @access public 4541 */ 4542 function getDBInstance() 4543 { 4544 if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) { 4545 $result = $GLOBALS['_MDB2_databases'][$this->db_index]; 4546 } else { 4547 $result = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 4548 'could not find MDB2 instance'); 4549 } 4550 return $result; 4551 } 4552 4553 // }}} 4554} 4555 4556// }}} 4557// {{{ function MDB2_closeOpenTransactions() 4558 4559/** 4560 * Close any open transactions form persistent connections 4561 * 4562 * @return void 4563 * 4564 * @access public 4565 */ 4566 4567function MDB2_closeOpenTransactions() 4568{ 4569 reset($GLOBALS['_MDB2_databases']); 4570 while (next($GLOBALS['_MDB2_databases'])) { 4571 $key = key($GLOBALS['_MDB2_databases']); 4572 if ($GLOBALS['_MDB2_databases'][$key]->opened_persistent 4573 && $GLOBALS['_MDB2_databases'][$key]->in_transaction 4574 ) { 4575 $GLOBALS['_MDB2_databases'][$key]->rollback(); 4576 } 4577 } 4578} 4579 4580// }}} 4581// {{{ function MDB2_defaultDebugOutput(&$db, $scope, $message, $is_manip = null) 4582 4583/** 4584 * default debug output handler 4585 * 4586 * @param object reference to an MDB2 database object 4587 * @param string usually the method name that triggered the debug call: 4588 * for example 'query', 'prepare', 'execute', 'parameters', 4589 * 'beginTransaction', 'commit', 'rollback' 4590 * @param string message that should be appended to the debug variable 4591 * @param array contains context information about the debug() call 4592 * common keys are: is_manip, time, result etc. 4593 * 4594 * @return void|string optionally return a modified message, this allows 4595 * rewriting a query before being issued or prepared 4596 * 4597 * @access public 4598 */ 4599function MDB2_defaultDebugOutput(&$db, $scope, $message, $context = array()) 4600{ 4601 $db->debug_output.= $scope.'('.$db->db_index.'): '; 4602 $db->debug_output.= $message.$db->getOption('log_line_break'); 4603 return $message; 4604} 4605 4606// }}} 4607?> 4608