PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/system/db/db.mysql.php

https://github.com/danboy/Croissierd
PHP | 797 lines | 465 code | 174 blank | 158 comment | 113 complexity | 35ab95f66eb1fdf80a2a1d2df873f1b9 MD5 | raw file
  1. <?php
  2. /*
  3. =====================================================
  4. ExpressionEngine - by EllisLab
  5. -----------------------------------------------------
  6. http://expressionengine.com/
  7. -----------------------------------------------------
  8. Copyright (c) 2003 - 2010 EllisLab, Inc.
  9. =====================================================
  10. THIS IS COPYRIGHTED SOFTWARE
  11. PLEASE READ THE LICENSE AGREEMENT
  12. http://expressionengine.com/docs/license.html
  13. =====================================================
  14. File: db.mysql.php
  15. -----------------------------------------------------
  16. Purpose: SQL database abstraction: MySQL
  17. =====================================================
  18. */
  19. if ( ! defined('EXT'))
  20. {
  21. exit('Invalid file request');
  22. }
  23. //---------------------------------------
  24. // DB Cache Class
  25. //---------------------------------------
  26. // This object gets serialized and cached.
  27. // It provides a simple mechanism to store queries
  28. // that are portable as objects
  29. class DB_Cache {
  30. var $result = array();
  31. var $row = array();
  32. var $num_rows = 0;
  33. var $q_count = 0;
  34. }
  35. // END CLASS
  36. //---------------------------------------
  37. // DB Class
  38. //---------------------------------------
  39. class DB {
  40. // Public variables
  41. var $hostname = 'localhost';
  42. var $username = 'root';
  43. var $password = '';
  44. var $database = '';
  45. var $prefix = 'exp_'; // Table prefix
  46. var $conntype = 1; // 1 = persistent. 0 = non
  47. var $cache_dir = 'db_cache/'; // Cache directory/path with trailing slash.
  48. var $debug = 0; // Manually turns on debugging
  49. var $enable_cache = TRUE; // true/false Enables query caching
  50. var $error_footer = ''; // This is used by the update script
  51. var $error_header = '';
  52. // Private variables.
  53. var $exp_prefix = 'exp_';
  54. var $cache_path = '';
  55. var $cache_file = '';
  56. var $sql_table = '';
  57. var $insert_id = '';
  58. var $q_count = 0;
  59. var $affected_rows = 0;
  60. var $conn_id = FALSE;
  61. var $query_id = FALSE;
  62. var $fetch_fields = FALSE;
  63. var $cache_enabled = FALSE;
  64. var $field_names = array();
  65. var $tables_list = array();
  66. var $show_queries = FALSE; // Enables queries to be shown for debugging
  67. var $queries = array(); // Stores the queries
  68. var $server_info = ''; // MySQL Server Info, like version
  69. /** ---------------------------------------
  70. /** Constructor
  71. /** ---------------------------------------*/
  72. function DB($settings)
  73. {
  74. global $PREFS;
  75. $db_settings = array(
  76. 'hostname',
  77. 'username',
  78. 'password',
  79. 'database',
  80. 'conntype',
  81. 'prefix',
  82. 'debug',
  83. 'show_queries',
  84. 'enable_cache'
  85. );
  86. foreach ($db_settings as $item)
  87. {
  88. if (isset($settings[$item]))
  89. {
  90. $this->$item = $settings[$item];
  91. }
  92. }
  93. if ($this->prefix != '' && substr($this->prefix, -1) != '_')
  94. {
  95. $this->prefix .= '_';
  96. }
  97. }
  98. /* END */
  99. /** ---------------------------------------
  100. /** Forces a Reconnect On Next Query
  101. /** ---------------------------------------*/
  102. function reconnect()
  103. {
  104. if (function_exists('mysql_ping'))
  105. {
  106. if (mysql_ping($this->conn_id) === FALSE)
  107. {
  108. $this->conn_id = FALSE;
  109. }
  110. }
  111. }
  112. /* END */
  113. /** ---------------------------------------
  114. /** Connect to database
  115. /** ---------------------------------------*/
  116. function db_connect($select_db = TRUE)
  117. {
  118. $this->conn_id = ($this->conntype == 0) ?
  119. @mysql_connect ($this->hostname, $this->username, $this->password):
  120. @mysql_pconnect($this->hostname, $this->username, $this->password);
  121. if ( ! $this->conn_id)
  122. {
  123. return FALSE;
  124. }
  125. if ($select_db == TRUE)
  126. {
  127. if ( ! $this->select_db())
  128. {
  129. return FALSE;
  130. }
  131. }
  132. $this->server_info = @mysql_get_server_info();
  133. return TRUE;
  134. }
  135. /* END */
  136. /** ---------------------------------------
  137. /** Select database
  138. /** ---------------------------------------*/
  139. function select_db()
  140. {
  141. if ( ! @mysql_select_db($this->database, $this->conn_id))
  142. {
  143. return FALSE;
  144. }
  145. return TRUE;
  146. }
  147. /* END */
  148. /** ---------------------------------------
  149. /** Close database connection
  150. /** ---------------------------------------*/
  151. function db_close()
  152. {
  153. if ($this->conn_id)
  154. mysql_close($this->conn_id);
  155. }
  156. /* END */
  157. /** ---------------------------------------
  158. /** Enable SQL Query Caching
  159. /** ---------------------------------------*/
  160. function enable_cache()
  161. {
  162. global $PREFS;
  163. if ($this->enable_cache == TRUE)
  164. {
  165. $this->cache_enabled = TRUE;
  166. $this->cache_dir = rtrim(PATH_CACHE.$this->cache_dir, '/').'/';
  167. // We limit the total number of cache files in order to
  168. // keep some sanity with large sites or ones that get
  169. // hit by overambitious crawlers.
  170. if ($dh = @opendir($this->cache_dir))
  171. {
  172. $i = 0;
  173. while (false !== (readdir($dh)))
  174. {
  175. $i++;
  176. }
  177. //$max = ( ! $PREFS->ini('max_caches') OR ! is_numeric($PREFS->ini('max_caches')) OR $PREFS->ini('max_caches') > 1000) ? 100 : $PREFS->ini('max_caches');
  178. if ($i > 150)
  179. {
  180. $this->delete_directory($this->cache_dir);
  181. }
  182. }
  183. }
  184. }
  185. /* END */
  186. /** ---------------------------------------
  187. /** DB Query
  188. /** ---------------------------------------*/
  189. function query($sql)
  190. {
  191. if ($sql == '')
  192. return;
  193. $sql = trim($sql);
  194. $this->affected_rows = 0;
  195. $this->insert_id = 0;
  196. // Store the query for debugging
  197. if ($this->show_queries == TRUE)
  198. {
  199. $this->queries[] = $sql;
  200. }
  201. // Verify table prefix and replace if necessary.
  202. if ($this->prefix != $this->exp_prefix)
  203. {
  204. $sql = preg_replace("/(\W)".$this->exp_prefix."(\S+?)/", "\\1".$this->prefix."\\2", $sql);
  205. // If the custom prefix includes 'exp_' the above can sometimes cause partial doubling.
  206. // This is a quick fix to prevent this from causing errors in 1.x.
  207. if (strncmp($this->prefix, 'exp_', 4) == 0)
  208. {
  209. $sql = str_replace($this->prefix.str_replace('exp_', '', $this->prefix), $this->prefix, $sql);
  210. }
  211. }
  212. /**
  213. * The Cache Cannot be enabled until AFTER the Input class is insantiated.
  214. */
  215. if ($this->enable_cache == TRUE && $this->cache_enabled == FALSE && isset($GLOBALS['IN']) && is_object($GLOBALS['IN']))
  216. {
  217. $this->enable_cache();
  218. }
  219. if ($this->cache_enabled == TRUE)
  220. {
  221. global $IN;
  222. // The URI being requested will become the name of the cache directory
  223. $this->cache_path = ($IN->URI == '') ? $this->cache_dir.md5('index').'/' : $this->cache_path = $this->cache_dir.md5($IN->URI).'/';
  224. // Convert the SQL query into a hash. This will become the cache file name.
  225. $this->cache_file = md5($sql);
  226. // Is this query a read type?
  227. // If so, return the previously cached data if it exists and bail out.
  228. if (stristr($sql, 'SELECT'))
  229. {
  230. if (FALSE !== ($cache = $this->get_cache()))
  231. {
  232. return $cache;
  233. }
  234. }
  235. }
  236. // Connect to the DB if we haven't done so on a previous query
  237. if ( ! $this->conn_id)
  238. {
  239. if ( ! $this->db_connect(0))
  240. {
  241. exit("Database Error: Unable to connect to your database. Your database appears to be turned off or the database connection settings in your config file are not correct. Please contact your hosting provider if the problem persists.");
  242. }
  243. if ( ! $this->select_db())
  244. {
  245. exit("Database Error: Unable to select your database");
  246. }
  247. }
  248. // Execute the query
  249. if ( ! $this->query_id = mysql_query($sql, $this->conn_id))
  250. {
  251. if ($this->debug)
  252. {
  253. return $this->db_error("MySQL ERROR:", $this->conn_id, $sql);
  254. }
  255. return FALSE;
  256. }
  257. // Increment the query counter
  258. $this->q_count++;
  259. // Determine if the query is one of the 'write' types. If so, gather the
  260. // affected rows and insert ID, and delete the existing cache file.
  261. $qtypes = array('INSERT', 'UPDATE', 'DELETE', 'CREATE', 'ALTER', 'DROP', 'REPLACE', 'GRANT', 'REVOKE', 'LOCK', 'UNLOCK', 'TRUNCATE');
  262. if (preg_match("#^(".implode('|', $qtypes).")#i", $sql, $qmatches))
  263. {
  264. $type = $qmatches[1];
  265. $this->affected_rows = mysql_affected_rows($this->conn_id);
  266. if ($type == 'INSERT' || $type == 'REPLACE')
  267. {
  268. $this->insert_id = mysql_insert_id($this->conn_id);
  269. }
  270. // Delete the cache file since the data in it is no longer current.
  271. if ($this->cache_enabled == TRUE)
  272. {
  273. $this->delete_cache();
  274. }
  275. // Bail out. We are done
  276. if ($type == 'INSERT' OR $type == 'UPDATE' OR $type == 'DELETE')
  277. {
  278. return ($this->affected_rows == 0 AND $this->insert_id == 0) ? FALSE : TRUE;
  279. }
  280. else
  281. {
  282. return TRUE;
  283. }
  284. }
  285. // Fetch the field names, but only if explicitly requested
  286. // We use this in our SQL utilities functions
  287. if ($this->fetch_fields == TRUE)
  288. {
  289. $this->field_names = array();
  290. while ($field = mysql_fetch_field($this->query_id))
  291. {
  292. $this->field_names[] = $field->name;
  293. }
  294. }
  295. // Fetch the result of the query and assign it to an array.
  296. // I know, the result *is* an array. But we want our own
  297. // numerically indexed array so we can cache it.
  298. $i = 0;
  299. $result = array();
  300. while ($row = mysql_fetch_array($this->query_id, MYSQL_ASSOC))
  301. {
  302. $result[$i] = $row;
  303. $i++;
  304. }
  305. // Free the result. Optional with MySQL, but might as well be thorough
  306. mysql_free_result($this->query_id);
  307. // Instantiate the cache super-class and assign the data
  308. // to it if a subsequent query hasn't already done so
  309. $DBC = new DB_Cache;
  310. $DBC->result = $result;
  311. $DBC->row = (isset($result['0'])) ? $result['0'] : array();
  312. $DBC->num_rows = $i;
  313. // Serialize the class and store it in a cache file
  314. if ($this->cache_enabled == TRUE)
  315. {
  316. $this->store_cache(serialize($DBC));
  317. }
  318. // Assign the query count to the super-class.
  319. // The query count only applies to non-cached queries,
  320. // so we add it after the class has already been cached.
  321. $DBC->q_count = $this->q_count;
  322. $DBC->fields = $this->field_names;
  323. // Return it
  324. return $DBC;
  325. }
  326. /* END */
  327. /** ---------------------------------------
  328. /** Fetch SQL tables
  329. /** ---------------------------------------*/
  330. function fetch_tables()
  331. {
  332. if (sizeof($this->tables_list) > 0)
  333. {
  334. return $this->tables_list;
  335. }
  336. if ( ! $this->conn_id)
  337. {
  338. if ( ! $this->db_connect(0))
  339. {
  340. exit("Database Error: Unable to connect to your database. Your database appears to be turned off or the database connection settings in your config file are not correct. Please contact your hosting provider if the problem persists.");
  341. }
  342. if ( ! $this->select_db())
  343. {
  344. exit("Database Error: Unable to select your database");
  345. }
  346. }
  347. // mysql_list_tables() was depreciated, so we switched to using
  348. // this query, which should work. -Paul
  349. // We use $this->prefix as query() will not match the like escaped exp_prefix.
  350. $query = $this->query("SHOW TABLES FROM `{$this->database}` LIKE '".$this->escape_like_str($this->prefix)."%'");
  351. if ($query->num_rows > 0)
  352. {
  353. foreach($query->result as $row)
  354. {
  355. $this->tables_list[] = array_shift($row);
  356. }
  357. }
  358. return $this->tables_list;
  359. }
  360. /* END */
  361. /** ---------------------------------------
  362. /** Determine if a table exists
  363. /** ---------------------------------------*/
  364. function table_exists($table_name)
  365. {
  366. if ($this->prefix != $this->exp_prefix)
  367. {
  368. $table_name = preg_replace("/".$this->exp_prefix."(\S+?)/", $this->prefix."\\1", $table_name);
  369. }
  370. if ( ! in_array($table_name, $this->fetch_tables()))
  371. {
  372. return FALSE;
  373. }
  374. return TRUE;
  375. }
  376. /* END */
  377. /** ---------------------------------------
  378. /** Cache a query
  379. /** ---------------------------------------*/
  380. function store_cache($object)
  381. {
  382. $dirs = array(PATH_CACHE.'db_cache', substr($this->cache_path, 0, -1));
  383. foreach ($dirs as $dir)
  384. {
  385. if ( ! @is_dir($dir))
  386. {
  387. if ( ! @mkdir($dir, 0777))
  388. {
  389. return;
  390. }
  391. if ($dir == PATH_CACHE.'db_cache' && $fp = @fopen($dir.'/index.html', 'wb'))
  392. {
  393. fclose($fp);
  394. }
  395. @chmod($dir, 0777);
  396. }
  397. }
  398. if ( ! $fp = @fopen($this->cache_path.$this->cache_file, 'wb'))
  399. return;
  400. flock($fp, LOCK_EX);
  401. fwrite($fp, $object);
  402. flock($fp, LOCK_UN);
  403. fclose($fp);
  404. @chmod($this->cache_path.$this->cache_file, 0777);
  405. }
  406. /* END */
  407. /** ---------------------------------------
  408. /** Retreive a cached query
  409. /** ---------------------------------------*/
  410. function get_cache()
  411. {
  412. if ( ! @is_dir($this->cache_path))
  413. return false;
  414. if ( ! file_exists($this->cache_path.$this->cache_file))
  415. return false;
  416. if ( ! $fp = @fopen($this->cache_path.$this->cache_file, 'rb'))
  417. return false;
  418. flock($fp, LOCK_SH);
  419. $cachedata = @fread($fp, filesize($this->cache_path.$this->cache_file));
  420. flock($fp, LOCK_UN);
  421. fclose($fp);
  422. if ( ! is_string($cachedata)) return FALSE;
  423. return unserialize($cachedata);
  424. }
  425. /* END */
  426. /** ---------------------------------------
  427. /** Delete cache files
  428. /** ---------------------------------------*/
  429. function delete_cache()
  430. {
  431. if ( ! @is_dir($this->cache_path))
  432. return FALSE;
  433. if ( ! $fp = @opendir($this->cache_path))
  434. {
  435. return FALSE;
  436. }
  437. while (false !== ($file = @readdir($fp)))
  438. {
  439. if ($file != "." AND $file != "..")
  440. {
  441. if ( ! @unlink($this->cache_path.$file))
  442. {
  443. return FALSE;
  444. }
  445. }
  446. }
  447. if ( ! @rmdir($this->cache_path))
  448. {
  449. return FALSE;
  450. }
  451. closedir($fp);
  452. }
  453. /* END */
  454. /** -----------------------------------------
  455. /** Delete Direcories
  456. /** -----------------------------------------*/
  457. function delete_directory($path, $del_root = FALSE)
  458. {
  459. if ( ! $current_dir = @opendir($path))
  460. {
  461. return;
  462. }
  463. while($filename = @readdir($current_dir))
  464. {
  465. if (@is_dir($path.'/'.$filename) and ($filename != "." and $filename != ".."))
  466. {
  467. $this->delete_directory($path.'/'.$filename, TRUE);
  468. }
  469. elseif($filename != "." and $filename != "..")
  470. {
  471. @unlink($path.'/'.$filename);
  472. }
  473. }
  474. closedir($current_dir);
  475. if ($del_root == TRUE)
  476. {
  477. @rmdir($path);
  478. }
  479. }
  480. /* END */
  481. /** ---------------------------------------
  482. /** MySQL escape string
  483. /** ---------------------------------------*/
  484. function escape_str($str, $like = FALSE)
  485. {
  486. if (is_array($str))
  487. {
  488. foreach($str as $key => $val)
  489. {
  490. $str[$key] = $this->escape_str($val, $like);
  491. }
  492. return $str;
  493. }
  494. if (function_exists('mysql_real_escape_string') AND is_resource($this->conn_id))
  495. {
  496. $str = mysql_real_escape_string(stripslashes($str), $this->conn_id);
  497. }
  498. elseif (function_exists('mysql_escape_string'))
  499. {
  500. $str = mysql_escape_string(stripslashes($str));
  501. }
  502. else
  503. {
  504. $str = addslashes(stripslashes($str));
  505. }
  506. if ($like === TRUE)
  507. {
  508. $replace_characters = array('%', '_');
  509. $escaped_characters = array('\\%', '\\_');
  510. $str = str_replace($replace_characters, $escaped_characters, $str);
  511. }
  512. return $str;
  513. }
  514. /* END */
  515. /** ---------------------------------------
  516. /** MySQL escape plus LIKE wildcards
  517. /** ---------------------------------------*/
  518. function escape_like_str($str)
  519. {
  520. return $this->escape_str($str, TRUE);
  521. }
  522. /** ---------------------------------------
  523. /** Error Message
  524. /** ---------------------------------------*/
  525. function db_error($msg, $id="", $sql="")
  526. {
  527. if ($this->error_header != '')
  528. {
  529. $msg = $this->error_header.$msg;
  530. }
  531. if ($id)
  532. {
  533. $msg .= "<br /><br />";
  534. $msg .= "Error Number: " . mysql_errno($id);
  535. $msg .= "<br /><br />";
  536. $msg .= "Description: " . mysql_error($id);
  537. }
  538. if ($sql)
  539. $msg .= "<br /><br />Query: ".$sql;
  540. if ($this->error_footer != '')
  541. {
  542. $msg .= $this->error_footer;
  543. }
  544. exit($msg);
  545. }
  546. /** ---------------------------------------
  547. /** Write an INSERT string
  548. /** ---------------------------------------*/
  549. // This function simplifies the process of writing database inserts.
  550. // It returns a correctly formatted SQL insert string.
  551. //
  552. // Example:
  553. //
  554. // $data = array('name' => $name, 'email' => $email, 'url' => $url);
  555. //
  556. // $str = $DB->insert_string('exp_weblog', $data);
  557. //
  558. // Produces: INSERT INTO exp_weblog (name, email, url) VALUES ('Joe', 'joe@joe.com', 'www.joe.com')
  559. function insert_string($table, $data, $addslashes = FALSE)
  560. {
  561. $fields = '';
  562. $values = '';
  563. if (stristr($table, '.'))
  564. {
  565. $x = explode('.', $table, 3);
  566. $table = $x['0'].'`.`'.$x['1'];
  567. }
  568. foreach($data as $key => $val)
  569. {
  570. $fields .= '`' . $key . '`, ';
  571. $val = ($addslashes === TRUE) ? addslashes($val) : $val;
  572. $values .= "'".$this->escape_str($val)."'".', ';
  573. }
  574. $fields = preg_replace( "/, $/" , "" , $fields);
  575. $values = preg_replace( "/, $/" , "" , $values);
  576. return 'INSERT INTO `'.$table.'` ('.$fields.') VALUES ('.$values.')';
  577. }
  578. /* END */
  579. /** ---------------------------------------
  580. /** Write an UPDATE string
  581. /** ---------------------------------------*/
  582. // This function simplifies the process of writing database updates.
  583. // It returns a correctly formatted SQL update string.
  584. //
  585. // Example:
  586. //
  587. // $data = array('name' => $name, 'email' => $email, 'url' => $url);
  588. //
  589. // $str = $DB->update_string('exp_weblog', $data, "author_id = '1'");
  590. //
  591. // Produces: UPDATE exp_weblog SET name = 'Joe', email = 'joe@joe.com', url = 'www.joe.com' WHERE author_id = '1'
  592. function update_string($table, $data, $where)
  593. {
  594. if ($where == '')
  595. return false;
  596. $str = '';
  597. $dest = '';
  598. if (stristr($table, '.'))
  599. {
  600. $x = explode('.', $table, 3);
  601. $table = $x['0'].'`.`'.$x['1'];
  602. }
  603. foreach($data as $key => $val)
  604. {
  605. $str .= '`'.$key."` = '".$this->escape_str($val)."', ";
  606. }
  607. $str = preg_replace( "/, $/" , "" , $str);
  608. if (is_array($where))
  609. {
  610. foreach ($where as $key => $val)
  611. {
  612. $dest .= $key." = '".$this->escape_str($val)."' AND ";
  613. }
  614. $dest = preg_replace( "/AND $/" , "" , $dest);
  615. }
  616. else
  617. $dest = $where;
  618. return 'UPDATE `'.$table.'` SET '.$str.' WHERE '.$dest;
  619. }
  620. /* END */
  621. }
  622. // END CLASS
  623. ?>