PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/src/libraries/joomla/database/driver/pdomysql.php

https://bitbucket.org/ke2083/transfans.co.uk-website
PHP | 591 lines | 245 code | 80 blank | 266 comment | 25 complexity | 0917ee41aa926c25097ff79e396362af MD5 | raw file
  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage Database
  5. *
  6. * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die;
  10. /**
  11. * MySQL database driver supporting PDO based connections
  12. *
  13. * @link https://secure.php.net/manual/en/ref.pdo-mysql.php
  14. * @since 3.4
  15. */
  16. class JDatabaseDriverPdomysql extends JDatabaseDriverPdo
  17. {
  18. /**
  19. * The name of the database driver.
  20. *
  21. * @var string
  22. * @since 3.4
  23. */
  24. public $name = 'pdomysql';
  25. /**
  26. * The type of the database server family supported by this driver.
  27. *
  28. * @var string
  29. * @since CMS 3.5.0
  30. */
  31. public $serverType = 'mysql';
  32. /**
  33. * The character(s) used to quote SQL statement names such as table names or field names,
  34. * etc. The child classes should define this as necessary. If a single character string the
  35. * same character is used for both sides of the quoted name, else the first character will be
  36. * used for the opening quote and the second for the closing quote.
  37. *
  38. * @var string
  39. * @since 3.4
  40. */
  41. protected $nameQuote = '`';
  42. /**
  43. * The null or zero representation of a timestamp for the database driver. This should be
  44. * defined in child classes to hold the appropriate value for the engine.
  45. *
  46. * @var string
  47. * @since 3.4
  48. */
  49. protected $nullDate = '0000-00-00 00:00:00';
  50. /**
  51. * The minimum supported database version.
  52. *
  53. * @var string
  54. * @since 3.4
  55. */
  56. protected static $dbMinimum = '5.0.4';
  57. /**
  58. * Constructor.
  59. *
  60. * @param array $options Array of database options with keys: host, user, password, database, select.
  61. *
  62. * @since 3.4
  63. */
  64. public function __construct($options)
  65. {
  66. // Get some basic values from the options.
  67. $options['driver'] = 'mysql';
  68. if (!isset($options['charset']) || $options['charset'] == 'utf8')
  69. {
  70. $options['charset'] = 'utf8mb4';
  71. }
  72. /**
  73. * Pre-populate the UTF-8 Multibyte compatibility flag. Unfortunately PDO won't report the server version
  74. * unless we're connected to it, and we cannot connect to it unless we know if it supports utf8mb4, which requires
  75. * us knowing the server version. Because of this chicken and egg issue, we _assume_ it's supported and we'll just
  76. * catch any problems at connection time.
  77. */
  78. $this->utf8mb4 = ($options['charset'] == 'utf8mb4');
  79. // Finalize initialisation.
  80. parent::__construct($options);
  81. }
  82. /**
  83. * Connects to the database if needed.
  84. *
  85. * @return void Returns void if the database connected successfully.
  86. *
  87. * @since 3.4
  88. * @throws RuntimeException
  89. */
  90. public function connect()
  91. {
  92. if ($this->connection)
  93. {
  94. return;
  95. }
  96. try
  97. {
  98. // Try to connect to MySQL
  99. parent::connect();
  100. }
  101. catch (\RuntimeException $e)
  102. {
  103. // If the connection failed, but not because of the wrong character set, then bubble up the exception.
  104. if (!$this->utf8mb4)
  105. {
  106. throw $e;
  107. }
  108. /*
  109. * Otherwise, try connecting again without using
  110. * utf8mb4 and see if maybe that was the problem. If the
  111. * connection succeeds, then we will have learned that the
  112. * client end of the connection does not support utf8mb4.
  113. */
  114. $this->utf8mb4 = false;
  115. $this->options['charset'] = 'utf8';
  116. parent::connect();
  117. }
  118. if ($this->utf8mb4)
  119. {
  120. /*
  121. * At this point we know the client supports utf8mb4. Now
  122. * we must check if the server supports utf8mb4 as well.
  123. */
  124. $serverVersion = $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION);
  125. $this->utf8mb4 = version_compare($serverVersion, '5.5.3', '>=');
  126. if (!$this->utf8mb4)
  127. {
  128. // Reconnect with the utf8 character set.
  129. parent::disconnect();
  130. $this->options['charset'] = 'utf8';
  131. parent::connect();
  132. }
  133. }
  134. $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  135. $this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
  136. // Set sql_mode to non_strict mode
  137. $this->connection->query("SET @@SESSION.sql_mode = '';");
  138. // Disable query cache and turn profiling ON in debug mode.
  139. if ($this->debug)
  140. {
  141. $this->connection->query('SET query_cache_type = 0;');
  142. if ($this->hasProfiling())
  143. {
  144. $this->connection->query('SET profiling_history_size = 100, profiling = 1;');
  145. }
  146. }
  147. }
  148. /**
  149. * Test to see if the MySQL connector is available.
  150. *
  151. * @return boolean True on success, false otherwise.
  152. *
  153. * @since 3.4
  154. */
  155. public static function isSupported()
  156. {
  157. return class_exists('PDO') && in_array('mysql', PDO::getAvailableDrivers());
  158. }
  159. /**
  160. * Drops a table from the database.
  161. *
  162. * @param string $tableName The name of the database table to drop.
  163. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped.
  164. *
  165. * @return JDatabaseDriverPdomysql Returns this object to support chaining.
  166. *
  167. * @since 3.4
  168. * @throws RuntimeException
  169. */
  170. public function dropTable($tableName, $ifExists = true)
  171. {
  172. $this->connect();
  173. $query = $this->getQuery(true);
  174. $query->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($tableName));
  175. $this->setQuery($query);
  176. $this->execute();
  177. return $this;
  178. }
  179. /**
  180. * Select a database for use.
  181. *
  182. * @param string $database The name of the database to select for use.
  183. *
  184. * @return boolean True if the database was successfully selected.
  185. *
  186. * @since 3.4
  187. * @throws RuntimeException
  188. */
  189. public function select($database)
  190. {
  191. $this->connect();
  192. $this->setQuery('USE ' . $this->quoteName($database));
  193. $this->execute();
  194. return $this;
  195. }
  196. /**
  197. * Method to get the database collation in use by sampling a text field of a table in the database.
  198. *
  199. * @return mixed The collation in use by the database (string) or boolean false if not supported.
  200. *
  201. * @since 3.4
  202. * @throws RuntimeException
  203. */
  204. public function getCollation()
  205. {
  206. $this->connect();
  207. // Attempt to get the database collation by accessing the server system variable.
  208. $this->setQuery('SHOW VARIABLES LIKE "collation_database"');
  209. $result = $this->loadObject();
  210. if (property_exists($result, 'Value'))
  211. {
  212. return $result->Value;
  213. }
  214. else
  215. {
  216. return false;
  217. }
  218. }
  219. /**
  220. * Method to get the database connection collation, as reported by the driver. If the connector doesn't support
  221. * reporting this value please return an empty string.
  222. *
  223. * @return string
  224. */
  225. public function getConnectionCollation()
  226. {
  227. $this->connect();
  228. // Attempt to get the database collation by accessing the server system variable.
  229. $this->setQuery('SHOW VARIABLES LIKE "collation_connection"');
  230. $result = $this->loadObject();
  231. if (property_exists($result, 'Value'))
  232. {
  233. return $result->Value;
  234. }
  235. else
  236. {
  237. return false;
  238. }
  239. }
  240. /**
  241. * Shows the table CREATE statement that creates the given tables.
  242. *
  243. * @param mixed $tables A table name or a list of table names.
  244. *
  245. * @return array A list of the create SQL for the tables.
  246. *
  247. * @since 3.4
  248. * @throws RuntimeException
  249. */
  250. public function getTableCreate($tables)
  251. {
  252. $this->connect();
  253. // Initialise variables.
  254. $result = array();
  255. // Sanitize input to an array and iterate over the list.
  256. settype($tables, 'array');
  257. foreach ($tables as $table)
  258. {
  259. $this->setQuery('SHOW CREATE TABLE ' . $this->quoteName($table));
  260. $row = $this->loadRow();
  261. // Populate the result array based on the create statements.
  262. $result[$table] = $row[1];
  263. }
  264. return $result;
  265. }
  266. /**
  267. * Retrieves field information about a given table.
  268. *
  269. * @param string $table The name of the database table.
  270. * @param boolean $typeOnly True to only return field types.
  271. *
  272. * @return array An array of fields for the database table.
  273. *
  274. * @since 3.4
  275. * @throws RuntimeException
  276. */
  277. public function getTableColumns($table, $typeOnly = true)
  278. {
  279. $this->connect();
  280. $result = array();
  281. // Set the query to get the table fields statement.
  282. $this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($table));
  283. $fields = $this->loadObjectList();
  284. // If we only want the type as the value add just that to the list.
  285. if ($typeOnly)
  286. {
  287. foreach ($fields as $field)
  288. {
  289. $result[$field->Field] = preg_replace('/[(0-9)]/', '', $field->Type);
  290. }
  291. }
  292. // If we want the whole field data object add that to the list.
  293. else
  294. {
  295. foreach ($fields as $field)
  296. {
  297. $result[$field->Field] = $field;
  298. }
  299. }
  300. return $result;
  301. }
  302. /**
  303. * Get the details list of keys for a table.
  304. *
  305. * @param string $table The name of the table.
  306. *
  307. * @return array An array of the column specification for the table.
  308. *
  309. * @since 3.4
  310. * @throws RuntimeException
  311. */
  312. public function getTableKeys($table)
  313. {
  314. $this->connect();
  315. // Get the details columns information.
  316. $this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table));
  317. $keys = $this->loadObjectList();
  318. return $keys;
  319. }
  320. /**
  321. * Method to get an array of all tables in the database.
  322. *
  323. * @return array An array of all the tables in the database.
  324. *
  325. * @since 3.4
  326. * @throws RuntimeException
  327. */
  328. public function getTableList()
  329. {
  330. $this->connect();
  331. // Set the query to get the tables statement.
  332. $this->setQuery('SHOW TABLES');
  333. $tables = $this->loadColumn();
  334. return $tables;
  335. }
  336. /**
  337. * Locks a table in the database.
  338. *
  339. * @param string $table The name of the table to unlock.
  340. *
  341. * @return JDatabaseDriverPdomysql Returns this object to support chaining.
  342. *
  343. * @since 3.4
  344. * @throws RuntimeException
  345. */
  346. public function lockTable($table)
  347. {
  348. $this->setQuery('LOCK TABLES ' . $this->quoteName($table) . ' WRITE')->execute();
  349. return $this;
  350. }
  351. /**
  352. * Renames a table in the database.
  353. *
  354. * @param string $oldTable The name of the table to be renamed
  355. * @param string $newTable The new name for the table.
  356. * @param string $backup Not used by MySQL.
  357. * @param string $prefix Not used by MySQL.
  358. *
  359. * @return JDatabaseDriverPdomysql Returns this object to support chaining.
  360. *
  361. * @since 3.4
  362. * @throws RuntimeException
  363. */
  364. public function renameTable($oldTable, $newTable, $backup = null, $prefix = null)
  365. {
  366. $this->setQuery('RENAME TABLE ' . $this->quoteName($oldTable) . ' TO ' . $this->quoteName($newTable));
  367. $this->execute();
  368. return $this;
  369. }
  370. /**
  371. * Method to escape a string for usage in an SQL statement.
  372. *
  373. * Oracle escaping reference:
  374. * http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F
  375. *
  376. * SQLite escaping notes:
  377. * http://www.sqlite.org/faq.html#q14
  378. *
  379. * Method body is as implemented by the Zend Framework
  380. *
  381. * Note: Using query objects with bound variables is
  382. * preferable to the below.
  383. *
  384. * @param string $text The string to be escaped.
  385. * @param boolean $extra Unused optional parameter to provide extra escaping.
  386. *
  387. * @return string The escaped string.
  388. *
  389. * @since 3.4
  390. */
  391. public function escape($text, $extra = false)
  392. {
  393. if (is_int($text))
  394. {
  395. return $text;
  396. }
  397. if (is_float($text))
  398. {
  399. // Force the dot as a decimal point.
  400. return str_replace(',', '.', $text);
  401. }
  402. $this->connect();
  403. $result = substr($this->connection->quote($text), 1, -1);
  404. if ($extra)
  405. {
  406. $result = addcslashes($result, '%_');
  407. }
  408. return $result;
  409. }
  410. /**
  411. * Unlocks tables in the database.
  412. *
  413. * @return JDatabaseDriverPdomysql Returns this object to support chaining.
  414. *
  415. * @since 3.4
  416. * @throws RuntimeException
  417. */
  418. public function unlockTables()
  419. {
  420. $this->setQuery('UNLOCK TABLES')->execute();
  421. return $this;
  422. }
  423. /**
  424. * Method to commit a transaction.
  425. *
  426. * @param boolean $toSavepoint If true, commit to the last savepoint.
  427. *
  428. * @return void
  429. *
  430. * @since 3.4
  431. * @throws RuntimeException
  432. */
  433. public function transactionCommit($toSavepoint = false)
  434. {
  435. $this->connect();
  436. if (!$toSavepoint || $this->transactionDepth <= 1)
  437. {
  438. parent::transactionCommit($toSavepoint);
  439. }
  440. else
  441. {
  442. $this->transactionDepth--;
  443. }
  444. }
  445. /**
  446. * Method to roll back a transaction.
  447. *
  448. * @param boolean $toSavepoint If true, rollback to the last savepoint.
  449. *
  450. * @return void
  451. *
  452. * @since 3.4
  453. * @throws RuntimeException
  454. */
  455. public function transactionRollback($toSavepoint = false)
  456. {
  457. $this->connect();
  458. if (!$toSavepoint || $this->transactionDepth <= 1)
  459. {
  460. parent::transactionRollback($toSavepoint);
  461. }
  462. else
  463. {
  464. $savepoint = 'SP_' . ($this->transactionDepth - 1);
  465. $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint));
  466. if ($this->execute())
  467. {
  468. $this->transactionDepth--;
  469. }
  470. }
  471. }
  472. /**
  473. * Method to initialize a transaction.
  474. *
  475. * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created.
  476. *
  477. * @return void
  478. *
  479. * @since 3.4
  480. * @throws RuntimeException
  481. */
  482. public function transactionStart($asSavepoint = false)
  483. {
  484. $this->connect();
  485. if (!$asSavepoint || !$this->transactionDepth)
  486. {
  487. parent::transactionStart($asSavepoint);
  488. }
  489. else
  490. {
  491. $savepoint = 'SP_' . $this->transactionDepth;
  492. $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint));
  493. if ($this->execute())
  494. {
  495. $this->transactionDepth++;
  496. }
  497. }
  498. }
  499. /**
  500. * Internal function to check if profiling is available.
  501. *
  502. * @return boolean
  503. *
  504. * @since 3.9.1
  505. */
  506. private function hasProfiling()
  507. {
  508. $result = $this->setQuery("SHOW VARIABLES LIKE 'have_profiling'")->loadAssoc();
  509. return isset($result);
  510. }
  511. }