PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/mysqldatabase.php

http://github.com/Quixotix/PHP-MySQL-Database-Class
PHP | 653 lines | 238 code | 83 blank | 332 comment | 46 complexity | 3fda13013a224cd742fbc54b9ee39d84 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * @package mysql-database
  4. */
  5. /**
  6. * MySQL Database
  7. *
  8. * A singleton object which provides convenience methods for interfacing with
  9. * a MySQL database in PHP 5. You can get the object's instance using the
  10. * static {@link getInstance()} method. Being a singleton object, this class
  11. * only supports one open database connection at a time and idealy suited to
  12. * single-threaded applications. You can read
  13. * about {@link http://php.net/manual/en/language.oop5.patterns.php the singleton
  14. * pattern in the PHP manual}.
  15. *
  16. * <b>Getting Started</b>
  17. * <code>
  18. * $db = MySqlDatabase::getInstance();
  19. *
  20. * try {
  21. * $db->connect('localhost', 'user', 'password', 'database_name');
  22. * }
  23. * catch (Exception $e) {
  24. * die($e->getMessage());
  25. * }
  26. * </code>
  27. *
  28. * @package mysql-database
  29. * @author Micah Carrick
  30. * @copyright (c) 2010 - Micah Carrick
  31. * @version 2.0
  32. * @license BSD
  33. */
  34. class MySqlDatabase
  35. {
  36. /**
  37. * The MySQL link identifier created by {@link connect()}
  38. *
  39. * @var resource
  40. */
  41. public $link;
  42. /**
  43. * @var string
  44. */
  45. private $conn_str;
  46. /**
  47. * @var MySqlDatabase
  48. */
  49. private static $instance;
  50. const MYSQL_DATE_FORMAT = 'Y-m-d';
  51. const MYSQL_TIME_FORMAT = 'H:i:s';
  52. const MYSQL_DATETIME_FORMAT = 'Y-m-d H:i:s';
  53. const INSERT_GET_AUTO_INCREMENT_ID = 1;
  54. const INSERT_GET_AFFECTED_ROWS = 2;
  55. /**
  56. * Constructor
  57. *
  58. * Private constructor as part of the singleton pattern implementation.
  59. */
  60. private function __construct() {}
  61. /**
  62. * Connect
  63. *
  64. * Establish a connection to a MySQL database. Returns the MySQL link
  65. * link identifier or throws an exception if there is an error.
  66. *
  67. * <code>
  68. * // get an instance of the Database singleton
  69. * $db = MySqlDatabase::getInstance();
  70. *
  71. * // connect to a MySQL database (use your own login information)
  72. * try {
  73. * $db->connect('localhost', 'user', 'password', 'database_name');
  74. * }
  75. * catch (Exception $e) {
  76. * die($e->getMessage());
  77. * }
  78. * </code>
  79. *
  80. * @param string
  81. * @param string
  82. * @param string
  83. * @param string
  84. * @param boolean
  85. * @return resource
  86. */
  87. public function connect($host, $user, $password, $database=false, $persistant=false)
  88. {
  89. if ($persistant) {
  90. $this->link = @mysql_pconnect($host, $user, $password);
  91. } else {
  92. $this->link = @mysql_connect($host, $user, $password);
  93. }
  94. if (!$this->link)
  95. {
  96. throw new Exception('Unable to establish database connection: '
  97. .mysql_error());
  98. }
  99. if ($database) $this->useDatabase($database);
  100. $version = mysql_get_server_info();
  101. $this->conn_str = "'$database' on '$user@$host' (MySQL $version)";
  102. return $this->link;
  103. }
  104. /**
  105. * Delete
  106. *
  107. * Executes the DELETE statement specified in the query and returns the
  108. * value from either the PHP {@link mysql_affected_rows()} function. Throws
  109. * and exception if there is a MySQL error in the query.
  110. *
  111. * Note: With MySQL versions prior to 4.1.2, the affected rows on DELETE
  112. * statements with no WHERE clause is 0. See {@link mysql_affected_rows()}
  113. * for more information.
  114. *
  115. * @param string
  116. * @return integer
  117. */
  118. public function delete($query)
  119. {
  120. return $this->updateOrDelete($query);
  121. }
  122. /**
  123. * Get Connection String
  124. *
  125. * Gets a string representing the connection.
  126. *
  127. * <code>
  128. * echo $db->getConnectionString();
  129. * // example output: 'test_database' on 'web_user@localhost' (MySQL 5.1.47)
  130. * </code>
  131. *
  132. * @return string
  133. */
  134. public function getConnectionString()
  135. {
  136. return $this->conn_str;
  137. }
  138. /**
  139. * Get Instance
  140. *
  141. * Gets the singleton instance for this object. This method should be called
  142. * statically in order to use the Database object:
  143. *
  144. * <code>
  145. * $db = MySqlDatabase::getInstance();
  146. * </code>
  147. *
  148. * @return MySqlDatabase
  149. */
  150. public static function getInstance()
  151. {
  152. if (!isset(self::$instance))
  153. {
  154. self::$instance = new MySqlDatabase();
  155. }
  156. return self::$instance;
  157. }
  158. /**
  159. * Fetch One From Each Row
  160. *
  161. * Convenience method to get a single value from every row in a given
  162. * query. This is usefull in situations where you know that the result will
  163. * only have only one column of data and you need that all in a simple
  164. * array.
  165. *
  166. * <code>
  167. *
  168. * $query = "SELECT name FROM users";
  169. * $names = $db->fetchOneFromEachRow($query);
  170. * echo 'Users: ' . implode(', ', $names);
  171. * </code>
  172. *
  173. * @param string
  174. * @return array
  175. */
  176. public function fetchOneFromEachRow($query)
  177. {
  178. $rval = array();
  179. foreach ($this->iterate($query, MySqlResultSet::DATA_NUMERIC_ARRAY) as $row) {
  180. $rval[] = $row[0];
  181. }
  182. return $rval;
  183. }
  184. /**
  185. * Fetch One Row
  186. *
  187. * Convenience method to get a single row from a given query. This is
  188. * usefull in situations where you know that the result will only contain
  189. * one record and therefore do not need to iterate over it.
  190. *
  191. * You can
  192. * optionally specify the type of data to be returned (object or array)
  193. * using one of the MySqlResultSet Data Constants. The default is
  194. * {@link MySqlResultSet::DATA_OBJECT}.
  195. *
  196. * <code>
  197. * // get one row of data
  198. * $query = "SELECT first, last FROM users WHERE user_id = 24 LIMIT 1";
  199. * $row = $db->fetchOneRow($query);
  200. * echo $row->foo;
  201. * echo $row->bar;
  202. * </code>
  203. *
  204. * @param string
  205. * @param integer
  206. * @return mixed
  207. */
  208. public function fetchOneRow($query, $data_type=MySqlResultSet::DATA_OBJECT)
  209. {
  210. $result = new MySqlResultSet($query, $data_type, $this->link);
  211. $result->rewind();
  212. $row = $result->current();
  213. return $row;
  214. }
  215. /**
  216. * Fetch One
  217. *
  218. * Convenience method to get a single value from a single row. Returns the
  219. * value if the query returned a record, false if there were no results, or
  220. * throws an exception if there was an error with the query.
  221. *
  222. * <code>
  223. * // get the number of records in the 'users' table
  224. * $count = $db->fetchOne("SELECT COUNT(*) FROM users");
  225. * </code>
  226. *
  227. * @param string
  228. * @return mixed
  229. */
  230. public function fetchOne($query)
  231. {
  232. $result = new MySqlResultSet($query, MySqlResultSet::DATA_NUMERIC_ARRAY,
  233. $this->link);
  234. $result->rewind();
  235. $row = $result->current();
  236. if (!$row) return false;
  237. else return $row[0];
  238. }
  239. /**
  240. * Import SQL File
  241. *
  242. * Runs the queries defined in an SQL script file. The double-hyphen style
  243. * comments must have a single space after the hyphens. Hash style comments
  244. * and C-style comments are also supported.
  245. *
  246. * An optional user callback function can be specified to get information
  247. * about each MySQL statement. The user callback function takes 3
  248. * parameters: the line number as an integer, the query as a string, and the
  249. * result of the query as a boolean.
  250. *
  251. * <code>
  252. * function import_sql_callback($line_number, $sql_query, $result)
  253. * {
  254. * echo "Line $line_number: $sql_query ";
  255. * if ($result) echo "(OK)<br/>";
  256. * else echo "(FAIL)<br/>";
  257. * }
  258. * </code>
  259. *
  260. * You can optionally specify whether or not to abort importing statements
  261. * when an SQL error occurs (defaults to 'true') in which case an exception
  262. * will be thrown for any MySQL error.
  263. *
  264. * Returns the number of queries executed from the script or throws an
  265. * exception if there is an error.
  266. *
  267. * <code>
  268. * // no callback, throw exception on MySQL errors
  269. * $number = $db->importSqlFile('queries.sql');
  270. *
  271. * // callback for each query, skip queries with MySQL errors
  272. * $number = $db->importSqlFile('queries.sql', 'import_sql_callback', false);
  273. * </code>
  274. *
  275. * TODO: Ensure this works with huge files. Might need to use fopen()
  276. *
  277. * @param string
  278. * @param string
  279. * @param boolean
  280. * @return integer
  281. */
  282. public function importSqlFile($filename, $callback=false, $abort_on_error=true)
  283. {
  284. if ($callback && !is_callable($callback)) {
  285. throw new Exception("Invalid callback function.");
  286. }
  287. $lines = $this->loadFile($filename);
  288. $num_queries = 0;
  289. $sql_line = 0;
  290. $sql = '';
  291. $in_comment = false;
  292. foreach ($lines as $num => $line) {
  293. $line = trim($line);
  294. $num++;
  295. if (empty($sql)) $sql_line = $num;
  296. // ignore comments
  297. if ($in_comment) {
  298. $comment = strpos($line, '*/');
  299. if ($comment !== false) {
  300. $in_comment = false;
  301. $line = substr($line, $comment+2);
  302. } else {
  303. continue;
  304. }
  305. } else {
  306. $comment = strpos($line, '/*');
  307. if ($comment !== false) {
  308. if (strpos($line, '*/') === false) {
  309. $in_comment = true;
  310. }
  311. $line = substr($line, 0, $comment);
  312. } else {
  313. // single line comments
  314. foreach (array('-- ', '#') as $chars) {
  315. $comment = strpos($line, $chars);
  316. if ($comment !== false) {
  317. $line = substr($line, 0, $comment);
  318. }
  319. }
  320. }
  321. }
  322. // check if the statement is ready to be queried
  323. $end = strpos($line, ';');
  324. if ($end === false) {
  325. $sql .= $line;
  326. } else {
  327. $sql .= substr($line, 0, $end);
  328. $result = $this->quickQuery($sql);
  329. $num_queries++;
  330. if (!$result && $abort_on_error) {
  331. $file = basename($filename);
  332. $error = mysql_error($this->link);
  333. throw new Exception("Error in $file on line $sql_line: $error");
  334. }
  335. if ($callback) {
  336. call_user_func($callback, $sql_line, $sql, $result);
  337. }
  338. $sql = ''; // clear for next statement
  339. }
  340. }
  341. return $num_queries;
  342. }
  343. /**
  344. * Is Connected
  345. *
  346. * Determines if there is a connection open to the database.
  347. *
  348. * @return boolean
  349. */
  350. public function isConnected()
  351. {
  352. if (!empty($this->link)) {
  353. return @mysql_ping($this->link);
  354. } else {
  355. return false;
  356. }
  357. }
  358. // insertPhpArray
  359. // insertSqlArray
  360. // sqlval()
  361. /**
  362. * Insert
  363. *
  364. * Executes the INSERT statement specified in the query and returns the
  365. * value from either the PHP {@link mysql_insert_id()} function or the
  366. * php {@link mysql_affected_rows()} function depending on the value of the
  367. * $return_type parameter.
  368. *
  369. * <code>
  370. * $db = MySqlDatabase::getInstance();
  371. * $query = "INSERT INTO foobar (col1, col2) VALUES (1, 2), (2, 3)";
  372. * $rows = $db->insert($query, MySqlDatabase::INSERT_GET_AFFECTED_ROWS);
  373. * echo $rows; // output: 2
  374. * </code>
  375. *
  376. *
  377. * @param string
  378. * @param integer
  379. * @return integer
  380. */
  381. public function insert($query, $r_type=MySqlDatabase::INSERT_GET_AUTO_INCREMENT_ID)
  382. {
  383. $r = $this->query($query);
  384. if ($r_type == MySqlDatabase::INSERT_GET_AFFECTED_ROWS) {
  385. return @mysql_affected_rows($this->link);
  386. } else {
  387. return @mysql_insert_id($this->link);
  388. }
  389. }
  390. /**
  391. * DO NOT USE
  392. *
  393. * This was never finished... I don't think. The goal was to take a table
  394. * name, an array of column names, and an array of values and generate a
  395. * multiple record insert. You should not use this, but, you could help
  396. * out and finish or rewrite this method.
  397. *
  398. *
  399. * @param deprecated
  400. */
  401. public function smartInsert($table, $columns, $values)
  402. {
  403. if (empty($table) || !is_string($table)) {
  404. throw new Exception('The $table parameter must be specified as a string.');
  405. }
  406. $table_sql = '`' . @mysql_real_escape_string($table) . '`';
  407. $query = "INSERT INTO $table_sql ";
  408. // columns
  409. if (is_string($columns)) {
  410. $columns = explode(',', $columns);
  411. }
  412. if (is_array($columns)) {
  413. foreach ($columns as &$col) {
  414. if (!is_string($col)) {
  415. throw new Exception('The $columns parameter must be a string or an array of strings');
  416. }
  417. $col = @mysql_real_escape_string($col);
  418. }
  419. $column_sql = implode(',', $columns);
  420. $column_count = count($columns);
  421. } else {
  422. throw new Exception('The $columns parameter must be a string or an array of strings.');
  423. }
  424. try {
  425. $column_info = array();
  426. foreach ($this->iterate("SHOW COLUMNS FROM $table_sql") as $row) {
  427. $column_info[] = $row;
  428. }
  429. }
  430. catch (Exception $e) {
  431. throw new Exception("Could not get column information for table $table_sql.");
  432. }
  433. $query .= "($column_sql) ";
  434. // values
  435. if (is_array($values)) {
  436. for ($i=0; $i < count($values); $i++) {
  437. $info = $column_info[$i];
  438. $value = $values[i];
  439. // Where the heck did I leave off?
  440. }
  441. } else {
  442. // TODO: if only 1 column, then this will work
  443. throw new Exception('The $values parameter must be a string or an array.');
  444. }
  445. if (isset($column_count) && $column_count <> $value_count) {
  446. throw new Exception("Column count ($column_count) does not match values count ($value_count).");
  447. }
  448. $query .= "VALUES ($value_sql) ";
  449. echo $query;
  450. }
  451. /**
  452. * Iterate Result Set
  453. *
  454. * Returns a {@link MySQL_ResultSet} iteratable object for a query. The $type
  455. * parameter indicates the data being iterated should be an object,
  456. * a numerically indexed array, an associative array, or an array with
  457. * both numeric and associative indexes. Defaults to objects.
  458. *
  459. * <code>
  460. * $sql_query = "SELECT col1, col2 FROM table";
  461. *
  462. * // iterate as objects
  463. * foreach ($db->iterate("SELECT col1, col2 FROM table") as $row) {
  464. * echo $row->col1 . '<br/>';
  465. * echo $row->col2 . '<br/>';
  466. * }
  467. *
  468. * // iterate as both associative and numerically indexed array
  469. * foreach ($db->iterate($sql_query, MySQL_Db::DATA_ARRAY) as $row) {
  470. * echo $row[0] . '<br/>';
  471. * echo $row['col1'] . '<br/>';
  472. * }
  473. * </code>
  474. *
  475. * @param string
  476. * @param integer
  477. * @return boolean
  478. */
  479. public function iterate($sql, $data_type=MySqlResultSet::DATA_OBJECT)
  480. {
  481. return new MySqlResultSet($sql, $data_type, $this->link);
  482. }
  483. /**
  484. * Load File
  485. *
  486. * Loads the specified filename into an array of lines. Throws an exception
  487. * if there is an error.
  488. *
  489. * @param string
  490. * @return boolean
  491. */
  492. private function loadFile($filename)
  493. {
  494. if (!file_exists($filename)) {
  495. throw new Exception("File does not exist: $filename");
  496. }
  497. $file = @file($filename, FILE_IGNORE_NEW_LINES);
  498. if (!$file) {
  499. throw new Exception("Could not open $filename");
  500. }
  501. return $file;
  502. }
  503. public function query($query)
  504. {
  505. $r = @mysql_query($query, $this->link);
  506. if (!$r) {
  507. throw new Exception("Query Error: " . mysql_error());
  508. }
  509. return $r;
  510. }
  511. /**
  512. * Quick Query
  513. *
  514. * Executes a MySQL query and returns a boolean value indicating success
  515. * or failure. This method will close any resources opened from
  516. * SELECT, SHOW, DESCRIBE, or EXPLAIN statements and would not be very
  517. * usefull for those types of queries. This method is used internally for
  518. * importing SQL scripts.
  519. *
  520. * @param string
  521. * @return boolean
  522. */
  523. public function quickQuery($query)
  524. {
  525. $r = @mysql_query($query, $this->link);
  526. if (!$r) return false;
  527. if (is_resource($r)) mysql_free_result($r);
  528. return true;
  529. }
  530. /**
  531. * Update
  532. *
  533. * Executes the UPDATE statement specified in the query and returns the
  534. * value from either the PHP {@link mysql_affected_rows()} function. Throws
  535. * and exception if there is a MySQL error in the query.
  536. *
  537. * Note: The number of rows affected include only those in which the new
  538. * value was not the same as the old value. See {@link mysql_affected_rows()}
  539. * for more information.
  540. *
  541. * @param string
  542. * @return integer
  543. */
  544. public function update($query)
  545. {
  546. return $this->updateOrDelete($query);
  547. }
  548. private function updateOrDelete($query)
  549. {
  550. $r = $this->query($query);
  551. return @mysql_affected_rows($this->link);
  552. }
  553. /**
  554. * Use Database
  555. *
  556. * Selects the database to use. Throws an exception if there is an error
  557. * using the specified database.
  558. *
  559. * @param string
  560. * @return integer
  561. */
  562. public function useDatabase($database)
  563. {
  564. if (!@mysql_select_db($database, $this->link))
  565. {
  566. throw new Exception('Unable to select database: ' . mysql_error($this->link));
  567. }
  568. }
  569. }
  570. ?>