PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/db/Db.php

https://bitbucket.org/enurkov/prestashop
PHP | 704 lines | 339 code | 83 blank | 282 comment | 94 complexity | f88949df9346b8ff917440a2a46f1450 MD5 | raw file
  1. <?php
  2. /*
  3. * 2007-2012 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2012 PrestaShop SA
  23. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  24. * International Registered Trademark & Property of PrestaShop SA
  25. */
  26. if (file_exists(dirname(__FILE__).'/../../config/settings.inc.php'))
  27. include_once(dirname(__FILE__).'/../../config/settings.inc.php');
  28. abstract class DbCore
  29. {
  30. /**
  31. * Constants used by insert() method
  32. */
  33. const INSERT = 1;
  34. const INSERT_IGNORE = 2;
  35. const REPLACE = 3;
  36. /**
  37. * @var string Server (eg. localhost)
  38. */
  39. protected $server;
  40. /**
  41. * @var string Database user (eg. root)
  42. */
  43. protected $user;
  44. /**
  45. * @var string Database password (eg. can be empty !)
  46. */
  47. protected $password;
  48. /**
  49. * @var string Database name
  50. */
  51. protected $database;
  52. /**
  53. * @var bool
  54. */
  55. protected $is_cache_enabled;
  56. /**
  57. * @var mixed Ressource link
  58. */
  59. protected $link;
  60. /**
  61. * @var mixed SQL cached result
  62. */
  63. protected $result;
  64. /**
  65. * @var array List of DB instance
  66. */
  67. protected static $instance = array();
  68. /**
  69. * @var array Object instance for singleton
  70. */
  71. protected static $_servers = array(
  72. array('server' => _DB_SERVER_, 'user' => _DB_USER_, 'password' => _DB_PASSWD_, 'database' => _DB_NAME_), /* MySQL Master server */
  73. // Add here your slave(s) server(s)
  74. // array('server' => '192.168.0.15', 'user' => 'rep', 'password' => '123456', 'database' => 'rep'),
  75. // array('server' => '192.168.0.3', 'user' => 'myuser', 'password' => 'mypassword', 'database' => 'mydatabase'),
  76. );
  77. /**
  78. * Store last executed query
  79. *
  80. * @var string
  81. */
  82. protected $last_query;
  83. /**
  84. * Last cached query
  85. *
  86. * @var string
  87. */
  88. protected $last_cached;
  89. /**
  90. * Open a connection
  91. */
  92. abstract public function connect();
  93. /**
  94. * Close a connection
  95. */
  96. abstract public function disconnect();
  97. /**
  98. * Execute a query and get result resource
  99. *
  100. * @param string $sql
  101. * @return mixed
  102. */
  103. abstract protected function _query($sql);
  104. /**
  105. * Get number of rows in a result
  106. *
  107. * @param mixed $result
  108. */
  109. abstract protected function _numRows($result);
  110. /**
  111. * Get the ID generated from the previous INSERT operation
  112. */
  113. abstract public function Insert_ID();
  114. /**
  115. * Get number of affected rows in previous database operation
  116. */
  117. abstract public function Affected_Rows();
  118. /**
  119. * Get next row for a query which doesn't return an array
  120. *
  121. * @param mixed $result
  122. */
  123. abstract public function nextRow($result = false);
  124. /**
  125. * Get database version
  126. *
  127. * @return string
  128. */
  129. abstract public function getVersion();
  130. /**
  131. * Protect string against SQL injections
  132. *
  133. * @param string $str
  134. * @return string
  135. */
  136. abstract public function _escape($str);
  137. /**
  138. * Returns the text of the error message from previous database operation
  139. */
  140. abstract public function getMsgError();
  141. /**
  142. * Returns the number of the error from previous database operation
  143. */
  144. abstract public function getNumberError();
  145. /* do not remove, useful for some modules */
  146. abstract public function set_db($db_name);
  147. /**
  148. * Get Db object instance
  149. *
  150. * @param bool $master Decides whether the connection to be returned by the master server or the slave server
  151. * @return Db instance
  152. */
  153. public static function getInstance($master = true)
  154. {
  155. static $id = 0;
  156. $total_servers = count(self::$_servers);
  157. if ($master || $total_servers == 1)
  158. $id_server = 0;
  159. else
  160. {
  161. $id++;
  162. $id_server = ($total_servers > 2 && ($id % $total_servers) != 0) ? $id : 1;
  163. }
  164. if (!isset(self::$instance[$id_server]))
  165. {
  166. $class = Db::getClass();
  167. self::$instance[$id_server] = new $class(
  168. self::$_servers[$id_server]['server'],
  169. self::$_servers[$id_server]['user'],
  170. self::$_servers[$id_server]['password'],
  171. self::$_servers[$id_server]['database']
  172. );
  173. }
  174. return self::$instance[$id_server];
  175. }
  176. /**
  177. * Get child layer class
  178. *
  179. * @return string
  180. */
  181. public static function getClass()
  182. {
  183. $class = 'MySQL';
  184. if (extension_loaded('pdo_mysql'))
  185. $class = 'DbPDO';
  186. else if (extension_loaded('mysqli'))
  187. $class = 'DbMySQLi';
  188. return $class;
  189. }
  190. /**
  191. * Instantiate database connection
  192. *
  193. * @param string $server Server address
  194. * @param string $user User login
  195. * @param string $password User password
  196. * @param string $database Database name
  197. * @param bool $connect If false, don't connect in constructor (since 1.5.0)
  198. */
  199. public function __construct($server, $user, $password, $database, $connect = true)
  200. {
  201. $this->server = $server;
  202. $this->user = $user;
  203. $this->password = $password;
  204. $this->database = $database;
  205. $this->is_cache_enabled = (defined('_PS_CACHE_ENABLED_')) ? _PS_CACHE_ENABLED_ : false;
  206. if (!defined('_PS_DEBUG_SQL_'))
  207. define('_PS_DEBUG_SQL_', false);
  208. if ($connect)
  209. $this->connect();
  210. }
  211. /**
  212. * Close connection to database
  213. */
  214. public function __destruct()
  215. {
  216. if ($this->link)
  217. $this->disconnect();
  218. }
  219. /**
  220. * @deprecated 1.5.0 use insert() or update() method instead
  221. */
  222. public function autoExecute($table, $data, $type, $where = '', $limit = 0, $use_cache = true, $use_null = false)
  223. {
  224. $type = strtoupper($type);
  225. switch ($type)
  226. {
  227. case 'INSERT' :
  228. return $this->insert($table, $data, $use_null, $use_cache, Db::INSERT, false);
  229. case 'INSERT IGNORE' :
  230. return $this->insert($table, $data, $use_null, $use_cache, Db::INSERT_IGNORE, false);
  231. case 'REPLACE' :
  232. return $this->insert($table, $data, $use_null, $use_cache, Db::REPLACE, false);
  233. case 'UPDATE' :
  234. return $this->update($table, $data, $where, $limit, $use_null, $use_cache, false);
  235. default :
  236. throw new PrestaShopDatabaseException('Wrong argument (miss type) in Db::autoExecute()');
  237. }
  238. }
  239. /**
  240. * Filter SQL query within a blacklist
  241. *
  242. * @param string $table Table where insert/update data
  243. * @param string $values Data to insert/update
  244. * @param string $type INSERT or UPDATE
  245. * @param string $where WHERE clause, only for UPDATE (optional)
  246. * @param int $limit LIMIT clause (optional)
  247. * @return mixed|boolean SQL query result
  248. */
  249. public function autoExecuteWithNullValues($table, $values, $type, $where = '', $limit = 0)
  250. {
  251. return $this->autoExecute($table, $values, $type, $where, $limit, 0, true);
  252. }
  253. /**
  254. * Execute a query and get result ressource
  255. *
  256. * @param string $sql
  257. * @return mixed
  258. */
  259. public function query($sql)
  260. {
  261. if ($sql instanceof DbQuery)
  262. $sql = $sql->build();
  263. $this->result = $this->_query($sql);
  264. if (_PS_DEBUG_SQL_)
  265. $this->displayError($sql);
  266. return $this->result;
  267. }
  268. /**
  269. * Execute an INSERT query
  270. *
  271. * @param string $table Table name without prefix
  272. * @param array $data Data to insert as associative array. If $data is a list of arrays, multiple insert will be done
  273. * @param bool $null_values If we want to use NULL values instead of empty quotes
  274. * @param bool $use_cache
  275. * @param int $type Must be Db::INSERT or Db::INSERT_IGNORE or Db::REPLACE
  276. * @param bool $add_prefix Add or not _DB_PREFIX_ before table name
  277. * @return bool
  278. */
  279. public function insert($table, $data, $null_values = false, $use_cache = true, $type = Db::INSERT, $add_prefix = true)
  280. {
  281. if (!$data && !$null_values)
  282. return true;
  283. if ($add_prefix)
  284. $table = _DB_PREFIX_.$table;
  285. if ($type == Db::INSERT)
  286. $insert_keyword = 'INSERT';
  287. else if ($type == Db::INSERT_IGNORE)
  288. $insert_keyword = 'INSERT IGNORE';
  289. else if ($type == Db::REPLACE)
  290. $insert_keyword = 'REPLACE';
  291. else
  292. throw new PrestaShopDatabaseException('Bad keyword, must be Db::INSERT or Db::INSERT_IGNORE or Db::REPLACE');
  293. // Check if $data is a list of row
  294. $current = current($data);
  295. if (!is_array($current) || isset($current['type']))
  296. $data = array($data);
  297. $keys = array();
  298. $values_stringified = array();
  299. foreach ($data as $row_data)
  300. {
  301. $values = array();
  302. foreach ($row_data as $key => $value)
  303. {
  304. if (isset($keys_stringified))
  305. {
  306. // Check if row array mapping are the same
  307. if (!in_array("`$key`", $keys))
  308. throw new PrestaShopDatabaseException('Keys form $data subarray don\'t match');
  309. }
  310. else
  311. $keys[] = "`$key`";
  312. if (!is_array($value))
  313. $value = array('type' => 'text', 'value' => $value);
  314. if ($value['type'] == 'sql')
  315. $values[] = $value['value'];
  316. else
  317. $values[] = $null_values && ($value['value'] === '' || is_null($value['value'])) ? 'NULL' : "'{$value['value']}'";
  318. }
  319. $keys_stringified = implode(', ', $keys);
  320. $values_stringified[] = '('.implode(', ', $values).')';
  321. }
  322. $sql = $insert_keyword.' INTO `'.$table.'` ('.$keys_stringified.') VALUES '.implode(', ', $values_stringified);
  323. return (bool)$this->q($sql, $use_cache);
  324. }
  325. /**
  326. * @param string $table Table name without prefix
  327. * @param array $data Data to insert as associative array. If $data is a list of arrays, multiple insert will be done
  328. * @param string $where WHERE condition
  329. * @param int $limit
  330. * @param bool $null_values If we want to use NULL values instead of empty quotes
  331. * @param bool $use_cache
  332. * @param bool $add_prefix Add or not _DB_PREFIX_ before table name
  333. * @return bool
  334. */
  335. public function update($table, $data, $where = '', $limit = 0, $null_values = false, $use_cache = true, $add_prefix = true)
  336. {
  337. if (!$data)
  338. return true;
  339. if ($add_prefix)
  340. $table = _DB_PREFIX_.$table;
  341. $sql = 'UPDATE `'.$table.'` SET ';
  342. foreach ($data as $key => $value)
  343. {
  344. if (!is_array($value))
  345. $value = array('type' => 'text', 'value' => $value);
  346. if ($value['type'] == 'sql')
  347. $sql .= "`$key` = {$value['value']},";
  348. else
  349. $sql .= ($null_values && ($value['value'] === '' || is_null($value['value']))) ? "`$key` = NULL," : "`$key` = '{$value['value']}',";
  350. }
  351. $sql = rtrim($sql, ',');
  352. if ($where)
  353. $sql .= ' WHERE '.$where;
  354. if ($limit)
  355. $sql .= ' LIMIT '.(int)$limit;
  356. return (bool)$this->q($sql, $use_cache);
  357. }
  358. /**
  359. * Execute a DELETE query
  360. *
  361. * @param string $table Name of the table to delete
  362. * @param string $where WHERE clause on query
  363. * @param int $limit Number max of rows to delete
  364. * @param bool $use_cache Use cache or not
  365. * @param bool $add_prefix Add or not _DB_PREFIX_ before table name
  366. * @return bool
  367. */
  368. public function delete($table, $where = '', $limit = 0, $use_cache = true, $add_prefix = true)
  369. {
  370. if (_DB_PREFIX_ && !preg_match('#^'._DB_PREFIX_.'#i', $table) && $add_prefix)
  371. $table = _DB_PREFIX_.$table;
  372. $this->result = false;
  373. $sql = 'DELETE FROM `'.bqSQL($table).'`'.($where ? ' WHERE '.$where : '').($limit ? ' LIMIT '.(int)$limit : '');
  374. $res = $this->query($sql);
  375. if ($use_cache && $this->is_cache_enabled)
  376. Cache::getInstance()->deleteQuery($sql);
  377. return (bool)$res;
  378. }
  379. /**
  380. * Execute a query
  381. *
  382. * @param string $sql
  383. * @param bool $use_cache
  384. * @return bool
  385. */
  386. public function execute($sql, $use_cache = true)
  387. {
  388. if ($sql instanceof DbQuery)
  389. $sql = $sql->build();
  390. $this->result = $this->query($sql);
  391. if ($use_cache && $this->is_cache_enabled)
  392. Cache::getInstance()->deleteQuery($sql);
  393. return (bool)$this->result;
  394. }
  395. /**
  396. * ExecuteS return the result of $sql as array
  397. *
  398. * @param string $sql query to execute
  399. * @param boolean $array return an array instead of a mysql_result object (deprecated since 1.5.0, use query method instead)
  400. * @param bool $use_cache if query has been already executed, use its result
  401. * @return array or result object
  402. */
  403. public function executeS($sql, $array = true, $use_cache = true)
  404. {
  405. if ($sql instanceof DbQuery)
  406. $sql = $sql->build();
  407. // This method must be used only with queries which display results
  408. if (!preg_match('#^\s*\(?\s*(select|show|explain|describe|desc)\s#i', $sql))
  409. {
  410. if (defined('_PS_MODE_DEV_') && _PS_MODE_DEV_)
  411. throw new PrestaShopDatabaseException('Db->executeS() must be used only with select, show, explain or describe queries');
  412. return $this->execute($sql, $use_cache);
  413. }
  414. $this->result = false;
  415. $this->last_query = $sql;
  416. if ($use_cache && $this->is_cache_enabled && $array && ($result = Cache::getInstance()->get(md5($sql))))
  417. {
  418. $this->last_cached = true;
  419. return $result;
  420. }
  421. $this->result = $this->query($sql);
  422. if (!$this->result)
  423. return false;
  424. $this->last_cached = false;
  425. if (!$array)
  426. return $this->result;
  427. $result_array = array();
  428. while ($row = $this->nextRow($this->result))
  429. $result_array[] = $row;
  430. if ($use_cache && $this->is_cache_enabled)
  431. Cache::getInstance()->setQuery($sql, $result_array);
  432. return $result_array;
  433. }
  434. /**
  435. * getRow return an associative array containing the first row of the query
  436. * This function automatically add "limit 1" to the query
  437. *
  438. * @param mixed $sql the select query (without "LIMIT 1")
  439. * @param bool $use_cache find it in cache first
  440. * @return array associative array of (field=>value)
  441. */
  442. public function getRow($sql, $use_cache = true)
  443. {
  444. if ($sql instanceof DbQuery)
  445. $sql = $sql->build();
  446. $sql .= ' LIMIT 1';
  447. $this->result = false;
  448. $this->last_query = $sql;
  449. if ($use_cache && $this->is_cache_enabled && ($result = Cache::getInstance()->get(md5($sql))))
  450. {
  451. $this->last_cached = true;
  452. return $result;
  453. }
  454. $this->result = $this->query($sql);
  455. if (!$this->result)
  456. return false;
  457. $this->last_cached = false;
  458. $result = $this->nextRow($this->result);
  459. if ($use_cache && $this->is_cache_enabled)
  460. Cache::getInstance()->setQuery($sql, $result);
  461. return $result;
  462. }
  463. /**
  464. * getValue return the first item of a select query.
  465. *
  466. * @param mixed $sql
  467. * @param bool $use_cache
  468. * @return mixed
  469. */
  470. public function getValue($sql, $use_cache = true)
  471. {
  472. if ($sql instanceof DbQuery)
  473. $sql = $sql->build();
  474. if (!$result = $this->getRow($sql, $use_cache))
  475. return false;
  476. return array_shift($result);
  477. }
  478. /**
  479. * Get number of rows for last result
  480. *
  481. * @return int
  482. */
  483. public function numRows()
  484. {
  485. if (!$this->last_cached && $this->result)
  486. {
  487. $nrows = $this->_numRows($this->result);
  488. if ($this->is_cache_enabled)
  489. Cache::getInstance()->set(md5($this->last_query).'_nrows', $nrows);
  490. return $nrows;
  491. }
  492. else if ($this->is_cache_enabled && $this->last_cached)
  493. return Cache::getInstance()->get(md5($this->last_query).'_nrows');
  494. }
  495. /**
  496. *
  497. * Execute a query
  498. *
  499. * @param string $sql
  500. * @param bool $use_cache
  501. * @return mixed $result
  502. */
  503. protected function q($sql, $use_cache = true)
  504. {
  505. if ($sql instanceof DbQuery)
  506. $sql = $sql->build();
  507. $this->result = false;
  508. $result = $this->query($sql);
  509. if ($use_cache && $this->is_cache_enabled)
  510. Cache::getInstance()->deleteQuery($sql);
  511. return $result;
  512. }
  513. /**
  514. * Display last SQL error
  515. *
  516. * @param bool $sql
  517. */
  518. public function displayError($sql = false)
  519. {
  520. global $webservice_call;
  521. $errno = $this->getNumberError();
  522. if ($webservice_call && $errno)
  523. {
  524. $dbg = debug_backtrace();
  525. WebserviceRequest::getInstance()->setError(500, '[SQL Error] '.$this->getMsgError().'. From '.(isset($dbg[3]['class']) ? $dbg[3]['class'] : '').'->'.$dbg[3]['function'].'() Query was : '.$sql, 97);
  526. }
  527. else if (_PS_DEBUG_SQL_ && $errno && !defined('PS_INSTALLATION_IN_PROGRESS'))
  528. {
  529. if ($sql)
  530. throw new PrestaShopDatabaseException($this->getMsgError().'<br /><br /><pre>'.$sql.'</pre>');
  531. throw new PrestaShopDatabaseException($this->getMsgError());
  532. }
  533. }
  534. /**
  535. * Sanitize data which will be injected into SQL query
  536. *
  537. * @param string $string SQL data which will be injected into SQL query
  538. * @param boolean $html_ok Does data contain HTML code ? (optional)
  539. * @return string Sanitized data
  540. */
  541. public function escape($string, $html_ok = false)
  542. {
  543. if (_PS_MAGIC_QUOTES_GPC_)
  544. $string = stripslashes($string);
  545. if (!is_numeric($string))
  546. {
  547. $string = $this->_escape($string);
  548. if (!$html_ok)
  549. $string = strip_tags(Tools::nl2br($string));
  550. }
  551. return $string;
  552. }
  553. /**
  554. * Try a connection to te database
  555. *
  556. * @param string $server Server address
  557. * @param string $user Login for database connection
  558. * @param string $pwd Password for database connection
  559. * @param string $db Database name
  560. * @param bool $new_db_link
  561. * @param bool $engine
  562. * @return int
  563. */
  564. public static function checkConnection($server, $user, $pwd, $db, $new_db_link = true, $engine = null, $timeout = 5)
  565. {
  566. return call_user_func_array(array(Db::getClass(), 'tryToConnect'), array($server, $user, $pwd, $db, $new_db_link, $engine, $timeout));
  567. }
  568. /**
  569. * Try a connection to te database
  570. *
  571. * @param string $server Server address
  572. * @param string $user Login for database connection
  573. * @param string $pwd Password for database connection
  574. * @return int
  575. */
  576. public static function checkEncoding($server, $user, $pwd)
  577. {
  578. return call_user_func_array(array(Db::getClass(), 'tryUTF8'), array($server, $user, $pwd));
  579. }
  580. /**
  581. * Try a connection to the database and check if at least one table with same prefix exists
  582. *
  583. * @param string $server Server address
  584. * @param string $user Login for database connection
  585. * @param string $pwd Password for database connection
  586. * @param string $db Database name
  587. * @param string $prefix Tables prefix
  588. * @return bool
  589. */
  590. public static function hasTableWithSamePrefix($server, $user, $pwd, $db, $prefix)
  591. {
  592. return call_user_func_array(array(Db::getClass(), 'hasTableWithSamePrefix'), array($server, $user, $pwd, $db, $prefix));
  593. }
  594. /**
  595. * @deprecated 1.5.0
  596. */
  597. public static function s($sql, $use_cache = true)
  598. {
  599. Tools::displayAsDeprecated();
  600. return Db::getInstance()->executeS($sql, true, $use_cache);
  601. }
  602. /**
  603. * @deprecated 1.5.0
  604. */
  605. public static function ps($sql, $use_cache = 1)
  606. {
  607. Tools::displayAsDeprecated();
  608. $ret = Db::s($sql, $use_cache);
  609. p($ret);
  610. return $ret;
  611. }
  612. /**
  613. * @deprecated 1.5.0
  614. */
  615. public static function ds($sql, $use_cache = 1)
  616. {
  617. Tools::displayAsDeprecated();
  618. Db::s($sql, $use_cache);
  619. die();
  620. }
  621. }