You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

628 lines
18 KiB

26 years ago
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4.0 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2001 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Stig Bakken <ssb@fast.no> |
  17. // | Tomas V.V.Cox <cox@idecnet.com> |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id$
  21. //
  22. // Database independent query interface.
  23. //
  24. require_once "PEAR.php";
  25. /*
  26. * The method mapErrorCode in each DB_dbtype implementation maps
  27. * native error codes to one of these.
  28. *
  29. * If you add an error code here, make sure you also add a textual
  30. * version of it in DB::errorMessage().
  31. */
  32. define("DB_OK", 0);
  33. define("DB_ERROR", -1);
  34. define("DB_ERROR_SYNTAX", -2);
  35. define("DB_ERROR_CONSTRAINT", -3);
  36. define("DB_ERROR_NOT_FOUND", -4);
  37. define("DB_ERROR_ALREADY_EXISTS", -5);
  38. define("DB_ERROR_UNSUPPORTED", -6);
  39. define("DB_ERROR_MISMATCH", -7);
  40. define("DB_ERROR_INVALID", -8);
  41. define("DB_ERROR_NOT_CAPABLE", -9);
  42. define("DB_ERROR_TRUNCATED", -10);
  43. define("DB_ERROR_INVALID_NUMBER", -11);
  44. define("DB_ERROR_INVALID_DATE", -12);
  45. define("DB_ERROR_DIVZERO", -13);
  46. define("DB_ERROR_NODBSELECTED", -14);
  47. define("DB_ERROR_CANNOT_CREATE", -15);
  48. define("DB_ERROR_CANNOT_DELETE", -16);
  49. define("DB_ERROR_CANNOT_DROP", -17);
  50. define("DB_ERROR_NOSUCHTABLE", -18);
  51. define("DB_ERROR_NOSUCHFIELD", -19);
  52. define("DB_ERROR_NEED_MORE_DATA", -20);
  53. define("DB_ERROR_NOT_LOCKED", -21);
  54. define("DB_ERROR_VALUE_COUNT_ON_ROW", -22);
  55. define("DB_ERROR_INVALID_DSN", -23);
  56. /*
  57. * Warnings are not detected as errors by DB::isError(), and are not
  58. * fatal. You can detect whether an error is in fact a warning with
  59. * DB::isWarning().
  60. */
  61. define("DB_WARNING", -1000);
  62. define("DB_WARNING_READ_ONLY", -1001);
  63. /*
  64. * These constants are used when storing information about prepared
  65. * statements (using the "prepare" method in DB_dbtype).
  66. *
  67. * The prepare/execute model in DB is mostly borrowed from the ODBC
  68. * extension, in a query the "?" character means a scalar parameter.
  69. * There is one extension though, a "*" character means an opaque
  70. * parameter. An opaque parameter is simply a file name, the real
  71. * data are in that file (useful for stuff like putting uploaded files
  72. * into your database).
  73. */
  74. define("DB_PARAM_SCALAR", 1);
  75. define("DB_PARAM_OPAQUE", 2);
  76. /*
  77. * These constants define different ways of returning binary data
  78. * from queries. Again, this model has been borrowed from the ODBC
  79. * extension.
  80. *
  81. * DB_BINMODE_PASSTHRU sends the data directly through to the browser
  82. * when data is fetched from the database.
  83. * DB_BINMODE_RETURN lets you return data as usual.
  84. * DB_BINMODE_CONVERT returns data as well, only it is converted to
  85. * hex format, for example the string "123" would become "313233".
  86. */
  87. define("DB_BINMODE_PASSTHRU", 1);
  88. define("DB_BINMODE_RETURN", 2);
  89. define("DB_BINMODE_CONVERT", 3);
  90. /**
  91. * This is a special constant that tells DB the user hasn't specified
  92. * any particular get mode, so the default should be used.
  93. */
  94. define("DB_FETCHMODE_DEFAULT", 0);
  95. /**
  96. * Column data indexed by numbers, ordered from 0 and up
  97. */
  98. define("DB_FETCHMODE_ORDERED", 1);
  99. /**
  100. * Column data indexed by column names
  101. */
  102. define("DB_FETCHMODE_ASSOC", 2);
  103. /**
  104. * For multi-dimensional results: normally the first level of arrays
  105. * is the row number, and the second level indexed by column number or name.
  106. * DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays
  107. * is the column name, and the second level the row number.
  108. */
  109. define("DB_FETCHMODE_FLIPPED", 4);
  110. /* for compatibility */
  111. define("DB_GETMODE_ORDERED", DB_FETCHMODE_ORDERED);
  112. define("DB_GETMODE_ASSOC", DB_FETCHMODE_ASSOC);
  113. define("DB_GETMODE_FLIPPED", DB_FETCHMODE_FLIPPED);
  114. /**
  115. * The main "DB" class is simply a container class with some static
  116. * methods for creating DB objects as well as some utility functions
  117. * common to all parts of DB.
  118. *
  119. * The object model of DB is as follows (indentation means inheritance):
  120. *
  121. * DB The main DB class. This is simply a utility class
  122. * with some "static" methods for creating DB objects as
  123. * well as common utility functions for other DB classes.
  124. *
  125. * DB_common The base for each DB implementation. Provides default
  126. * | implementations (in OO lingo virtual methods) for
  127. * | the actual DB implementations as well as a bunch of
  128. * | query utility functions.
  129. * |
  130. * +-DB_mysql The DB implementation for MySQL. Inherits DB_common.
  131. * When calling DB::factory or DB::connect for MySQL
  132. * connections, the object returned is an instance of this
  133. * class.
  134. *
  135. * @version 2
  136. * @author Stig Bakken <ssb@fast.no>
  137. * @since PHP 4.0
  138. */
  139. class DB
  140. {
  141. /**
  142. * Create a new DB object for the specified database type
  143. *
  144. * @param $type string database type, for example "mysql"
  145. *
  146. * @return object a newly created DB object, or a DB error code on
  147. * error
  148. */
  149. function &factory($type)
  150. {
  151. @include_once("DB/${type}.php");
  152. $classname = "DB_${type}";
  153. @$obj =& new $classname;
  154. if (!$obj) {
  155. return new DB_Error(DB_ERROR_NOT_FOUND);
  156. }
  157. return $obj;
  158. }
  159. /**
  160. * Create a new DB object and connect to the specified database
  161. *
  162. * @param $dsn mixed "data source name", see the DB::parseDSN
  163. * method for a description of the dsn format. Can also be
  164. * specified as an array of the format returned by DB::parseDSN.
  165. *
  166. * @param $options mixed if boolean (or scalar), tells whether
  167. * this connection should be persistent (for backends that support
  168. * this). This parameter can also be an array of options, see
  169. * DB_common::setOption for more information on connection
  170. * options.
  171. *
  172. * @return object a newly created DB object, or a DB error code on
  173. * error
  174. *
  175. * @see DB::parseDSN
  176. */
  177. function &connect($dsn, $options = false)
  178. {
  179. if (is_array($dsn)) {
  180. $dsninfo = $dsn;
  181. } else {
  182. $dsninfo = DB::parseDSN($dsn);
  183. }
  184. $type = $dsninfo["phptype"];
  185. @include_once "DB/${type}.php";
  186. $classname = "DB_${type}";
  187. @$obj =& new $classname;
  188. if (!$obj) {
  189. return new DB_Error(DB_ERROR_NOT_FOUND);
  190. }
  191. if (is_array($options)) {
  192. foreach ($options as $option => $value) {
  193. $test = $obj->setOption($option, $value);
  194. if (DB::isError($test)) {
  195. return $test;
  196. }
  197. }
  198. } else {
  199. $obj->setOption('persistent', $options);
  200. }
  201. $err = $obj->connect($dsninfo, $obj->getOption('persistent'));
  202. if (DB::isError($err)) {
  203. return $err;
  204. }
  205. return $obj;
  206. }
  207. /**
  208. * Return the DB API version
  209. *
  210. * @return int the DB API version number
  211. */
  212. function apiVersion()
  213. {
  214. return 2;
  215. }
  216. /**
  217. * Tell whether a result code from a DB method is an error
  218. *
  219. * @param $value int result code
  220. *
  221. * @return bool whether $value is an error
  222. */
  223. function isError($value)
  224. {
  225. return (is_object($value) &&
  226. (get_class($value) == 'db_error' ||
  227. is_subclass_of($value, 'db_error')));
  228. }
  229. /**
  230. * Tell whether a query is a data manipulation query (insert, update
  231. * or delete).
  232. *
  233. * @access public
  234. *
  235. * @param string the query
  236. *
  237. * @return bool whether $query is a data manipulation query
  238. */
  239. function isManip($query)
  240. {
  241. if (preg_match('/^\s*(INSERT|UPDATE|DELETE|REPLACE)\s+/i', $query)) {
  242. return true;
  243. }
  244. return false;
  245. }
  246. /**
  247. * Tell whether a result code from a DB method is a warning.
  248. * Warnings differ from errors in that they are generated by DB,
  249. * and are not fatal.
  250. *
  251. * @param $value mixed result value
  252. *
  253. * @return bool whether $value is a warning
  254. */
  255. function isWarning($value)
  256. {
  257. return is_object($value) &&
  258. (get_class( $value ) == "db_warning" ||
  259. is_subclass_of($value, "db_warning"));
  260. }
  261. /**
  262. * Return a textual error message for a DB error code
  263. *
  264. * @param $value int error code
  265. *
  266. * @return string error message, or false if the error code was
  267. * not recognized
  268. */
  269. function errorMessage($value)
  270. {
  271. if (!isset($errorMessages)) {
  272. $errorMessages = array(
  273. DB_ERROR => "unknown error",
  274. DB_ERROR_ALREADY_EXISTS => "already exists",
  275. DB_ERROR_CANNOT_CREATE => "can not create",
  276. DB_ERROR_CANNOT_DELETE => "can not delete",
  277. DB_ERROR_CANNOT_DROP => "can not drop",
  278. DB_ERROR_CONSTRAINT => "constraint violation",
  279. DB_ERROR_DIVZERO => "division by zero",
  280. DB_ERROR_INVALID => "invalid",
  281. DB_ERROR_INVALID_DATE => "invalid date or time",
  282. DB_ERROR_INVALID_NUMBER => "invalid number",
  283. DB_ERROR_MISMATCH => "mismatch",
  284. DB_ERROR_NODBSELECTED => "no database selected",
  285. DB_ERROR_NOSUCHFIELD => "no such field",
  286. DB_ERROR_NOSUCHTABLE => "no such table",
  287. DB_ERROR_NOT_CAPABLE => "DB backend not capable",
  288. DB_ERROR_NOT_FOUND => "not found",
  289. DB_ERROR_NOT_LOCKED => "not locked",
  290. DB_ERROR_SYNTAX => "syntax error",
  291. DB_ERROR_UNSUPPORTED => "not supported",
  292. DB_ERROR_VALUE_COUNT_ON_ROW => "value count on row",
  293. DB_OK => "no error",
  294. DB_WARNING => "unknown warning",
  295. DB_WARNING_READ_ONLY => "read only"
  296. );
  297. }
  298. if (DB::isError($value)) {
  299. $value = $value->code;
  300. }
  301. return $errorMessages[$value];
  302. }
  303. /**
  304. * Parse a data source name
  305. *
  306. * @param $dsn string Data Source Name to be parsed
  307. *
  308. * @return array an associative array with the following keys:
  309. *
  310. * phptype: Database backend used in PHP (mysql, odbc etc.)
  311. * dbsyntax: Database used with regards to SQL syntax etc.
  312. * protocol: Communication protocol to use (tcp, unix etc.)
  313. * hostspec: Host specification (hostname[:port])
  314. * database: Database to use on the DBMS server
  315. * username: User name for login
  316. * password: Password for login
  317. *
  318. * The format of the supplied DSN is in its fullest form:
  319. *
  320. * phptype(dbsyntax)://username:password@protocol+hostspec/database
  321. *
  322. * Most variations are allowed:
  323. *
  324. * phptype://username:password@protocol+hostspec:110//usr/db_file.db
  325. * phptype://username:password@hostspec/database_name
  326. * phptype://username:password@hostspec
  327. * phptype://username@hostspec
  328. * phptype://hostspec/database
  329. * phptype://hostspec
  330. * phptype(dbsyntax)
  331. * phptype
  332. *
  333. * @author Tomas V.V.Cox <cox@idecnet.com>
  334. */
  335. function parseDSN($dsn)
  336. {
  337. if (is_array($dsn)) {
  338. return $dsn;
  339. }
  340. $parsed = array(
  341. 'phptype' => false,
  342. 'dbsyntax' => false,
  343. 'protocol' => false,
  344. 'hostspec' => false,
  345. 'database' => false,
  346. 'username' => false,
  347. 'password' => false
  348. );
  349. // Find phptype and dbsyntax
  350. if (($pos = strpos($dsn, '://')) !== false) {
  351. $str = substr($dsn, 0, $pos);
  352. $dsn = substr($dsn, $pos + 3);
  353. } else {
  354. $str = $dsn;
  355. $dsn = NULL;
  356. }
  357. // Get phptype and dbsyntax
  358. // $str => phptype(dbsyntax)
  359. if (preg_match('|^([^(]+)\(([^(]*)\)$|', $str, $arr)) {
  360. $parsed['phptype'] = $arr[1];
  361. $parsed['dbsyntax'] = (empty($arr[2])) ? $arr[1] : $arr[2];
  362. } else {
  363. $parsed['phptype'] = $str;
  364. $parsed['dbsyntax'] = $str;
  365. }
  366. if (empty($dsn)) {
  367. return $parsed;
  368. }
  369. // Get (if found): username and password
  370. // $dsn => username:password@protocol+hostspec/database
  371. if (($at = strpos($dsn,'@')) !== false) {
  372. $str = substr($dsn, 0, $at);
  373. $dsn = substr($dsn, $at + 1);
  374. if (($pos = strpos($str, ':')) !== false) {
  375. $parsed['username'] = urldecode(substr($str, 0, $pos));
  376. $parsed['password'] = urldecode(substr($str, $pos + 1));
  377. } else {
  378. $parsed['username'] = urldecode($str);
  379. }
  380. }
  381. // Find protocol and hostspec
  382. // $dsn => protocol+hostspec/database
  383. if (($pos=strpos($dsn, '/')) !== false) {
  384. $str = substr($dsn, 0, $pos);
  385. $dsn = substr($dsn, $pos + 1);
  386. } else {
  387. $str = $dsn;
  388. $dsn = NULL;
  389. }
  390. // Get protocol + hostspec
  391. // $str => protocol+hostspec
  392. if (($pos=strpos($str, '+')) !== false) {
  393. $parsed['protocol'] = substr($str, 0, $pos);
  394. $parsed['hostspec'] = urldecode(substr($str, $pos + 1));
  395. } else {
  396. $parsed['hostspec'] = urldecode($str);
  397. }
  398. // Get dabase if any
  399. // $dsn => database
  400. if (!empty($dsn)) {
  401. $parsed['database'] = $dsn;
  402. }
  403. return $parsed;
  404. }
  405. /**
  406. * Load a PHP database extension if it is not loaded already.
  407. *
  408. * @access public
  409. *
  410. * @param $name the base name of the extension (without the .so or
  411. * .dll suffix)
  412. *
  413. * @return bool true if the extension was already or successfully
  414. * loaded, false if it could not be loaded
  415. */
  416. function assertExtension($name)
  417. {
  418. if (!extension_loaded($name)) {
  419. $dlext = (substr(PHP_OS, 0, 3) == "WIN") ? ".dll" : ".so";
  420. @dl($name . $dlext);
  421. }
  422. if (!extension_loaded($name)) {
  423. return false;
  424. }
  425. return true;
  426. }
  427. }
  428. /**
  429. * DB_Error implements a class for reporting portable database error
  430. * messages.
  431. *
  432. * @author Stig Bakken <ssb@fast.no>
  433. */
  434. class DB_Error extends PEAR_Error
  435. {
  436. /**
  437. * DB_Error constructor.
  438. *
  439. * @param $code mixed DB error code, or string with error message.
  440. * @param $mode int what "error mode" to operate in
  441. * @param $level what error level to use for $mode & PEAR_ERROR_TRIGGER
  442. * @param $debuginfo additional debug info, such as the last query
  443. *
  444. * @access public
  445. *
  446. * @see PEAR_Error
  447. */
  448. function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
  449. $level = E_USER_NOTICE, $debuginfo = null)
  450. {
  451. if (is_int($code)) {
  452. $this->PEAR_Error("DB Error: " . DB::errorMessage( $code ), $code, $mode, $level, $debuginfo);
  453. } else {
  454. $this->PEAR_Error("DB Error: $code", DB_ERROR, $mode, $level, $debuginfo);
  455. }
  456. }
  457. }
  458. /**
  459. * DB_Warning implements a class for reporting portable database
  460. * warning messages.
  461. *
  462. * @author Stig Bakken <ssb@fast.no>
  463. */
  464. class DB_Warning extends PEAR_Error
  465. {
  466. /**
  467. * DB_Warning constructor.
  468. *
  469. * @param $code mixed DB error code, or string with error message.
  470. * @param $mode int what "error mode" to operate in
  471. * @param $level what error level to use for $mode == PEAR_ERROR_TRIGGER
  472. * @param $debuginfo additional debug info, such as the last query
  473. *
  474. * @access public
  475. *
  476. * @see PEAR_Error
  477. */
  478. function DB_Warning($code = DB_WARNING, $mode = PEAR_ERROR_RETURN,
  479. $level = E_USER_NOTICE, $debuginfo = null)
  480. {
  481. if (is_int($code)) {
  482. $this->PEAR_Error("DB Warning: " . DB::errorMessage( $code ), $code, $mode, $level, $debuginfo);
  483. } else {
  484. $this->PEAR_Error("DB Warning: $code", 0, $mode, $level, $debuginfo);
  485. }
  486. }
  487. }
  488. /**
  489. * This class implements a wrapper for a DB result set.
  490. * A new instance of this class will be returned by the DB implementation
  491. * after processing a query that returns data.
  492. *
  493. * @author Stig Bakken <ssb@fast.no>
  494. */
  495. class DB_result
  496. {
  497. var $dbh;
  498. var $result;
  499. /**
  500. * DB_result constructor.
  501. * @param $dbh DB object reference
  502. * @param $result result resource id
  503. */
  504. function DB_result(&$dbh, $result)
  505. {
  506. $this->dbh = &$dbh;
  507. $this->result = $result;
  508. }
  509. /**
  510. * Fetch and return a row of data.
  511. * @return array a row of data, or false on error
  512. */
  513. function fetchRow($fetchmode = DB_FETCHMODE_DEFAULT)
  514. {
  515. if ($fetchmode == DB_FETCHMODE_DEFAULT) {
  516. $fetchmode = $this->dbh->fetchmode;
  517. }
  518. return $this->dbh->fetchRow($this->result, $fetchmode);
  519. }
  520. /**
  521. * Fetch a row of data into an existing array.
  522. *
  523. * @param $arr reference to data array
  524. * @return int error code
  525. */
  526. function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT)
  527. {
  528. if ($fetchmode == DB_FETCHMODE_DEFAULT) {
  529. $fetchmode = $this->dbh->fetchmode;
  530. }
  531. return $this->dbh->fetchInto($this->result, $arr, $fetchmode);
  532. }
  533. /**
  534. * Get the the number of columns in a result set.
  535. *
  536. * @return int the number of columns, or a DB error
  537. */
  538. function numCols()
  539. {
  540. return $this->dbh->numCols($this->result);
  541. }
  542. /**
  543. * Get the number of rows in a result set.
  544. *
  545. * @return int the number of rows, or a DB error
  546. */
  547. function numRows()
  548. {
  549. return $this->dbh->numRows($this->result);
  550. }
  551. /**
  552. * Frees the resources allocated for this result set.
  553. * @return int error code
  554. */
  555. function free()
  556. {
  557. $err = $this->dbh->freeResult($this->result);
  558. if(DB::isError($err)) {
  559. return $err;
  560. }
  561. $this->result = false;
  562. return true;
  563. }
  564. }
  565. ?>