PageRenderTime 25ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/joomla/database/driver.php

https://bitbucket.org/pastor399/newcastleunifc
PHP | 1779 lines | 841 code | 194 blank | 744 comment | 82 complexity | f8a235a0cd79f0671c0348b16dc25267 MD5 | raw file
  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage Database
  5. *
  6. * @copyright Copyright (C) 2005 - 2013 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. * Joomla Platform Database Interface
  12. *
  13. * @package Joomla.Platform
  14. * @subpackage Database
  15. * @since 11.2
  16. */
  17. interface JDatabaseInterface
  18. {
  19. /**
  20. * Test to see if the connector is available.
  21. *
  22. * @return boolean True on success, false otherwise.
  23. *
  24. * @since 11.2
  25. */
  26. public static function isSupported();
  27. }
  28. /**
  29. * Joomla Platform Database Driver Class
  30. *
  31. * @package Joomla.Platform
  32. * @subpackage Database
  33. * @since 12.1
  34. *
  35. * @method string q() q($text, $escape = true) Alias for quote method
  36. * @method string qn() qn($name, $as = null) Alias for quoteName method
  37. */
  38. abstract class JDatabaseDriver extends JDatabase implements JDatabaseInterface
  39. {
  40. /**
  41. * The name of the database.
  42. *
  43. * @var string
  44. * @since 11.4
  45. */
  46. private $_database;
  47. /**
  48. * The name of the database driver.
  49. *
  50. * @var string
  51. * @since 11.1
  52. */
  53. public $name;
  54. /**
  55. * @var resource The database connection resource.
  56. * @since 11.1
  57. */
  58. protected $connection;
  59. /**
  60. * @var integer The number of SQL statements executed by the database driver.
  61. * @since 11.1
  62. */
  63. protected $count = 0;
  64. /**
  65. * @var resource The database connection cursor from the last query.
  66. * @since 11.1
  67. */
  68. protected $cursor;
  69. /**
  70. * @var boolean The database driver debugging state.
  71. * @since 11.1
  72. */
  73. protected $debug = false;
  74. /**
  75. * @var integer The affected row limit for the current SQL statement.
  76. * @since 11.1
  77. */
  78. protected $limit = 0;
  79. /**
  80. * @var array The log of executed SQL statements by the database driver.
  81. * @since 11.1
  82. */
  83. protected $log = array();
  84. /**
  85. * @var string The character(s) used to quote SQL statement names such as table names or field names,
  86. * etc. The child classes should define this as necessary. If a single character string the
  87. * same character is used for both sides of the quoted name, else the first character will be
  88. * used for the opening quote and the second for the closing quote.
  89. * @since 11.1
  90. */
  91. protected $nameQuote;
  92. /**
  93. * @var string The null or zero representation of a timestamp for the database driver. This should be
  94. * defined in child classes to hold the appropriate value for the engine.
  95. * @since 11.1
  96. */
  97. protected $nullDate;
  98. /**
  99. * @var integer The affected row offset to apply for the current SQL statement.
  100. * @since 11.1
  101. */
  102. protected $offset = 0;
  103. /**
  104. * @var array Passed in upon instantiation and saved.
  105. * @since 11.1
  106. */
  107. protected $options;
  108. /**
  109. * @var mixed The current SQL statement to execute.
  110. * @since 11.1
  111. */
  112. protected $sql;
  113. /**
  114. * @var string The common database table prefix.
  115. * @since 11.1
  116. */
  117. protected $tablePrefix;
  118. /**
  119. * @var boolean True if the database engine supports UTF-8 character encoding.
  120. * @since 11.1
  121. */
  122. protected $utf = true;
  123. /**
  124. * @var integer The database error number
  125. * @since 11.1
  126. * @deprecated 12.1
  127. */
  128. protected $errorNum = 0;
  129. /**
  130. * @var string The database error message
  131. * @since 11.1
  132. * @deprecated 12.1
  133. */
  134. protected $errorMsg;
  135. /**
  136. * @var array JDatabaseDriver instances container.
  137. * @since 11.1
  138. */
  139. protected static $instances = array();
  140. /**
  141. * @var string The minimum supported database version.
  142. * @since 12.1
  143. */
  144. protected static $dbMinimum;
  145. /**
  146. * Get a list of available database connectors. The list will only be populated with connectors that both
  147. * the class exists and the static test method returns true. This gives us the ability to have a multitude
  148. * of connector classes that are self-aware as to whether or not they are able to be used on a given system.
  149. *
  150. * @return array An array of available database connectors.
  151. *
  152. * @since 11.1
  153. */
  154. public static function getConnectors()
  155. {
  156. $connectors = array();
  157. // Get an iterator and loop trough the driver classes.
  158. $iterator = new DirectoryIterator(__DIR__ . '/driver');
  159. foreach ($iterator as $file)
  160. {
  161. $fileName = $file->getFilename();
  162. // Only load for php files.
  163. // Note: DirectoryIterator::getExtension only available PHP >= 5.3.6
  164. if (!$file->isFile() || substr($fileName, strrpos($fileName, '.') + 1) != 'php')
  165. {
  166. continue;
  167. }
  168. // Derive the class name from the type.
  169. $class = str_ireplace('.php', '', 'JDatabaseDriver' . ucfirst(trim($fileName)));
  170. // If the class doesn't exist we have nothing left to do but look at the next type. We did our best.
  171. if (!class_exists($class))
  172. {
  173. continue;
  174. }
  175. // Sweet! Our class exists, so now we just need to know if it passes its test method.
  176. if ($class::isSupported())
  177. {
  178. // Connector names should not have file extensions.
  179. $connectors[] = str_ireplace('.php', '', $fileName);
  180. }
  181. }
  182. return $connectors;
  183. }
  184. /**
  185. * Method to return a JDatabaseDriver instance based on the given options. There are three global options and then
  186. * the rest are specific to the database driver. The 'driver' option defines which JDatabaseDriver class is
  187. * used for the connection -- the default is 'mysqli'. The 'database' option determines which database is to
  188. * be used for the connection. The 'select' option determines whether the connector should automatically select
  189. * the chosen database.
  190. *
  191. * Instances are unique to the given options and new objects are only created when a unique options array is
  192. * passed into the method. This ensures that we don't end up with unnecessary database connection resources.
  193. *
  194. * @param array $options Parameters to be passed to the database driver.
  195. *
  196. * @return JDatabaseDriver A database object.
  197. *
  198. * @since 11.1
  199. */
  200. public static function getInstance($options = array())
  201. {
  202. // Sanitize the database connector options.
  203. $options['driver'] = (isset($options['driver'])) ? preg_replace('/[^A-Z0-9_\.-]/i', '', $options['driver']) : 'mysqli';
  204. $options['database'] = (isset($options['database'])) ? $options['database'] : null;
  205. $options['select'] = (isset($options['select'])) ? $options['select'] : true;
  206. // Get the options signature for the database connector.
  207. $signature = md5(serialize($options));
  208. // If we already have a database connector instance for these options then just use that.
  209. if (empty(self::$instances[$signature]))
  210. {
  211. // Derive the class name from the driver.
  212. $class = 'JDatabaseDriver' . ucfirst(strtolower($options['driver']));
  213. // If the class still doesn't exist we have nothing left to do but throw an exception. We did our best.
  214. if (!class_exists($class))
  215. {
  216. throw new RuntimeException(sprintf('Unable to load Database Driver: %s', $options['driver']));
  217. }
  218. // Create our new JDatabaseDriver connector based on the options given.
  219. try
  220. {
  221. $instance = new $class($options);
  222. }
  223. catch (RuntimeException $e)
  224. {
  225. throw new RuntimeException(sprintf('Unable to connect to the Database: %s', $e->getMessage()));
  226. }
  227. // Set the new connector to the global instances based on signature.
  228. self::$instances[$signature] = $instance;
  229. }
  230. return self::$instances[$signature];
  231. }
  232. /**
  233. * Splits a string of multiple queries into an array of individual queries.
  234. *
  235. * @param string $query Input SQL string with which to split into individual queries.
  236. *
  237. * @return array The queries from the input string separated into an array.
  238. *
  239. * @since 11.1
  240. */
  241. public static function splitSql($query)
  242. {
  243. $start = 0;
  244. $open = false;
  245. $char = '';
  246. $end = strlen($query);
  247. $queries = array();
  248. for ($i = 0; $i < $end; $i++)
  249. {
  250. $current = substr($query, $i, 1);
  251. if (($current == '"' || $current == '\''))
  252. {
  253. $n = 2;
  254. while (substr($query, $i - $n + 1, 1) == '\\' && $n < $i)
  255. {
  256. $n++;
  257. }
  258. if ($n % 2 == 0)
  259. {
  260. if ($open)
  261. {
  262. if ($current == $char)
  263. {
  264. $open = false;
  265. $char = '';
  266. }
  267. }
  268. else
  269. {
  270. $open = true;
  271. $char = $current;
  272. }
  273. }
  274. }
  275. if (($current == ';' && !$open) || $i == $end - 1)
  276. {
  277. $queries[] = substr($query, $start, ($i - $start + 1));
  278. $start = $i + 1;
  279. }
  280. }
  281. return $queries;
  282. }
  283. /**
  284. * Magic method to provide method alias support for quote() and quoteName().
  285. *
  286. * @param string $method The called method.
  287. * @param array $args The array of arguments passed to the method.
  288. *
  289. * @return string The aliased method's return value or null.
  290. *
  291. * @since 11.1
  292. */
  293. public function __call($method, $args)
  294. {
  295. if (empty($args))
  296. {
  297. return;
  298. }
  299. switch ($method)
  300. {
  301. case 'q':
  302. return $this->quote($args[0], isset($args[1]) ? $args[1] : true);
  303. break;
  304. case 'qn':
  305. return $this->quoteName($args[0], isset($args[1]) ? $args[1] : null);
  306. break;
  307. }
  308. }
  309. /**
  310. * Constructor.
  311. *
  312. * @param array $options List of options used to configure the connection
  313. *
  314. * @since 11.1
  315. */
  316. public function __construct($options)
  317. {
  318. // Initialise object variables.
  319. $this->_database = (isset($options['database'])) ? $options['database'] : '';
  320. $this->tablePrefix = (isset($options['prefix'])) ? $options['prefix'] : 'jos_';
  321. $this->count = 0;
  322. $this->errorNum = 0;
  323. $this->log = array();
  324. // Set class options.
  325. $this->options = $options;
  326. }
  327. /**
  328. * Alter database's character set, obtaining query string from protected member.
  329. *
  330. * @param string $dbName The database name that will be altered
  331. *
  332. * @return string The query that alter the database query string
  333. *
  334. * @since 12.2
  335. * @throws RuntimeException
  336. */
  337. public function alterDbCharacterSet($dbName)
  338. {
  339. if (is_null($dbName))
  340. {
  341. throw new RuntimeException('Database name must not be null.');
  342. }
  343. $this->setQuery($this->getAlterDbCharacterSet($dbName));
  344. return $this->execute();
  345. }
  346. /**
  347. * Connects to the database if needed.
  348. *
  349. * @return void Returns void if the database connected successfully.
  350. *
  351. * @since 12.1
  352. * @throws RuntimeException
  353. */
  354. abstract public function connect();
  355. /**
  356. * Determines if the connection to the server is active.
  357. *
  358. * @return boolean True if connected to the database engine.
  359. *
  360. * @since 11.1
  361. */
  362. abstract public function connected();
  363. /**
  364. * Create a new database using information from $options object, obtaining query string
  365. * from protected member.
  366. *
  367. * @param stdClass $options Object used to pass user and database name to database driver.
  368. * This object must have "db_name" and "db_user" set.
  369. * @param boolean $utf True if the database supports the UTF-8 character set.
  370. *
  371. * @return string The query that creates database
  372. *
  373. * @since 12.2
  374. * @throws RuntimeException
  375. */
  376. public function createDatabase($options, $utf = true)
  377. {
  378. if (is_null($options))
  379. {
  380. throw new RuntimeException('$options object must not be null.');
  381. }
  382. elseif (empty($options->db_name))
  383. {
  384. throw new RuntimeException('$options object must have db_name set.');
  385. }
  386. elseif (empty($options->db_user))
  387. {
  388. throw new RuntimeException('$options object must have db_user set.');
  389. }
  390. $this->setQuery($this->getCreateDatabaseQuery($options, $utf));
  391. return $this->execute();
  392. }
  393. /**
  394. * Disconnects the database.
  395. *
  396. * @return void
  397. *
  398. * @since 12.1
  399. */
  400. abstract public function disconnect();
  401. /**
  402. * Drops a table from the database.
  403. *
  404. * @param string $table The name of the database table to drop.
  405. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped.
  406. *
  407. * @return JDatabaseDriver Returns this object to support chaining.
  408. *
  409. * @since 11.4
  410. * @throws RuntimeException
  411. */
  412. public abstract function dropTable($table, $ifExists = true);
  413. /**
  414. * Method to escape a string for usage in an SQL statement.
  415. *
  416. * @param string $text The string to be escaped.
  417. * @param boolean $extra Optional parameter to provide extra escaping.
  418. *
  419. * @return string The escaped string.
  420. *
  421. * @since 11.1
  422. */
  423. abstract public function escape($text, $extra = false);
  424. /**
  425. * Method to fetch a row from the result set cursor as an array.
  426. *
  427. * @param mixed $cursor The optional result set cursor from which to fetch the row.
  428. *
  429. * @return mixed Either the next row from the result set or false if there are no more rows.
  430. *
  431. * @since 11.1
  432. */
  433. abstract protected function fetchArray($cursor = null);
  434. /**
  435. * Method to fetch a row from the result set cursor as an associative array.
  436. *
  437. * @param mixed $cursor The optional result set cursor from which to fetch the row.
  438. *
  439. * @return mixed Either the next row from the result set or false if there are no more rows.
  440. *
  441. * @since 11.1
  442. */
  443. abstract protected function fetchAssoc($cursor = null);
  444. /**
  445. * Method to fetch a row from the result set cursor as an object.
  446. *
  447. * @param mixed $cursor The optional result set cursor from which to fetch the row.
  448. * @param string $class The class name to use for the returned row object.
  449. *
  450. * @return mixed Either the next row from the result set or false if there are no more rows.
  451. *
  452. * @since 11.1
  453. */
  454. abstract protected function fetchObject($cursor = null, $class = 'stdClass');
  455. /**
  456. * Method to free up the memory used for the result set.
  457. *
  458. * @param mixed $cursor The optional result set cursor from which to fetch the row.
  459. *
  460. * @return void
  461. *
  462. * @since 11.1
  463. */
  464. abstract protected function freeResult($cursor = null);
  465. /**
  466. * Get the number of affected rows for the previous executed SQL statement.
  467. *
  468. * @return integer The number of affected rows.
  469. *
  470. * @since 11.1
  471. */
  472. abstract public function getAffectedRows();
  473. /**
  474. * Return the query string to alter the database character set.
  475. *
  476. * @param string $dbName The database name
  477. *
  478. * @return string The query that alter the database query string
  479. *
  480. * @since 12.2
  481. */
  482. protected function getAlterDbCharacterSet($dbName)
  483. {
  484. $query = 'ALTER DATABASE ' . $this->quoteName($dbName) . ' CHARACTER SET `utf8`';
  485. return $query;
  486. }
  487. /**
  488. * Return the query string to create new Database.
  489. * Each database driver, other than MySQL, need to override this member to return correct string.
  490. *
  491. * @param stdClass $options Object used to pass user and database name to database driver.
  492. * This object must have "db_name" and "db_user" set.
  493. * @param boolean $utf True if the database supports the UTF-8 character set.
  494. *
  495. * @return string The query that creates database
  496. *
  497. * @since 12.2
  498. */
  499. protected function getCreateDatabaseQuery($options, $utf)
  500. {
  501. if ($utf)
  502. {
  503. $query = 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' CHARACTER SET `utf8`';
  504. }
  505. else
  506. {
  507. $query = 'CREATE DATABASE ' . $this->quoteName($options->db_name);
  508. }
  509. return $query;
  510. }
  511. /**
  512. * Method to get the database collation in use by sampling a text field of a table in the database.
  513. *
  514. * @return mixed The collation in use by the database or boolean false if not supported.
  515. *
  516. * @since 11.1
  517. */
  518. abstract public function getCollation();
  519. /**
  520. * Method that provides access to the underlying database connection. Useful for when you need to call a
  521. * proprietary method such as postgresql's lo_* methods.
  522. *
  523. * @return resource The underlying database connection resource.
  524. *
  525. * @since 11.1
  526. */
  527. public function getConnection()
  528. {
  529. return $this->connection;
  530. }
  531. /**
  532. * Get the total number of SQL statements executed by the database driver.
  533. *
  534. * @return integer
  535. *
  536. * @since 11.1
  537. */
  538. public function getCount()
  539. {
  540. return $this->count;
  541. }
  542. /**
  543. * Gets the name of the database used by this conneciton.
  544. *
  545. * @return string
  546. *
  547. * @since 11.4
  548. */
  549. protected function getDatabase()
  550. {
  551. return $this->_database;
  552. }
  553. /**
  554. * Returns a PHP date() function compliant date format for the database driver.
  555. *
  556. * @return string The format string.
  557. *
  558. * @since 11.1
  559. */
  560. public function getDateFormat()
  561. {
  562. return 'Y-m-d H:i:s';
  563. }
  564. /**
  565. * Get the database driver SQL statement log.
  566. *
  567. * @return array SQL statements executed by the database driver.
  568. *
  569. * @since 11.1
  570. */
  571. public function getLog()
  572. {
  573. return $this->log;
  574. }
  575. /**
  576. * Get the minimum supported database version.
  577. *
  578. * @return string The minimum version number for the database driver.
  579. *
  580. * @since 12.1
  581. */
  582. public function getMinimum()
  583. {
  584. return static::$dbMinimum;
  585. }
  586. /**
  587. * Get the null or zero representation of a timestamp for the database driver.
  588. *
  589. * @return string Null or zero representation of a timestamp.
  590. *
  591. * @since 11.1
  592. */
  593. public function getNullDate()
  594. {
  595. return $this->nullDate;
  596. }
  597. /**
  598. * Get the number of returned rows for the previous executed SQL statement.
  599. *
  600. * @param resource $cursor An optional database cursor resource to extract the row count from.
  601. *
  602. * @return integer The number of returned rows.
  603. *
  604. * @since 11.1
  605. */
  606. abstract public function getNumRows($cursor = null);
  607. /**
  608. * Get the common table prefix for the database driver.
  609. *
  610. * @return string The common database table prefix.
  611. *
  612. * @since 11.1
  613. */
  614. public function getPrefix()
  615. {
  616. return $this->tablePrefix;
  617. }
  618. /**
  619. * Gets an exporter class object.
  620. *
  621. * @return JDatabaseExporter An exporter object.
  622. *
  623. * @since 12.1
  624. * @throws RuntimeException
  625. */
  626. public function getExporter()
  627. {
  628. // Derive the class name from the driver.
  629. $class = 'JDatabaseExporter' . ucfirst($this->name);
  630. // Make sure we have an exporter class for this driver.
  631. if (!class_exists($class))
  632. {
  633. // If it doesn't exist we are at an impasse so throw an exception.
  634. throw new RuntimeException('Database Exporter not found.');
  635. }
  636. $o = new $class;
  637. $o->setDbo($this);
  638. return $o;
  639. }
  640. /**
  641. * Gets an importer class object.
  642. *
  643. * @return JDatabaseImporter An importer object.
  644. *
  645. * @since 12.1
  646. * @throws RuntimeException
  647. */
  648. public function getImporter()
  649. {
  650. // Derive the class name from the driver.
  651. $class = 'JDatabaseImporter' . ucfirst($this->name);
  652. // Make sure we have an importer class for this driver.
  653. if (!class_exists($class))
  654. {
  655. // If it doesn't exist we are at an impasse so throw an exception.
  656. throw new RuntimeException('Database Importer not found');
  657. }
  658. $o = new $class;
  659. $o->setDbo($this);
  660. return $o;
  661. }
  662. /**
  663. * Get the current query object or a new JDatabaseQuery object.
  664. *
  665. * @param boolean $new False to return the current query object, True to return a new JDatabaseQuery object.
  666. *
  667. * @return JDatabaseQuery The current query object or a new object extending the JDatabaseQuery class.
  668. *
  669. * @since 11.1
  670. * @throws RuntimeException
  671. */
  672. public function getQuery($new = false)
  673. {
  674. if ($new)
  675. {
  676. // Derive the class name from the driver.
  677. $class = 'JDatabaseQuery' . ucfirst($this->name);
  678. // Make sure we have a query class for this driver.
  679. if (!class_exists($class))
  680. {
  681. // If it doesn't exist we are at an impasse so throw an exception.
  682. throw new RuntimeException('Database Query Class not found.');
  683. }
  684. return new $class($this);
  685. }
  686. else
  687. {
  688. return $this->sql;
  689. }
  690. }
  691. /**
  692. * Get a new iterator on the current query.
  693. *
  694. * @param string $column An option column to use as the iterator key.
  695. * @param string $class The class of object that is returned.
  696. *
  697. * @return JDatabaseIterator A new database iterator.
  698. *
  699. * @since 12.1
  700. * @throws RuntimeException
  701. */
  702. public function getIterator($column = null, $class = 'stdClass')
  703. {
  704. // Derive the class name from the driver.
  705. $iteratorClass = 'JDatabaseIterator' . ucfirst($this->name);
  706. // Make sure we have an iterator class for this driver.
  707. if (!class_exists($iteratorClass))
  708. {
  709. // If it doesn't exist we are at an impasse so throw an exception.
  710. throw new RuntimeException(sprintf('class *%s* is not defined', $iteratorClass));
  711. }
  712. // Return a new iterator
  713. return new $iteratorClass($this->execute(), $column, $class);
  714. }
  715. /**
  716. * Retrieves field information about the given tables.
  717. *
  718. * @param string $table The name of the database table.
  719. * @param boolean $typeOnly True (default) to only return field types.
  720. *
  721. * @return array An array of fields by table.
  722. *
  723. * @since 11.1
  724. * @throws RuntimeException
  725. */
  726. abstract public function getTableColumns($table, $typeOnly = true);
  727. /**
  728. * Shows the table CREATE statement that creates the given tables.
  729. *
  730. * @param mixed $tables A table name or a list of table names.
  731. *
  732. * @return array A list of the create SQL for the tables.
  733. *
  734. * @since 11.1
  735. * @throws RuntimeException
  736. */
  737. abstract public function getTableCreate($tables);
  738. /**
  739. * Retrieves field information about the given tables.
  740. *
  741. * @param mixed $tables A table name or a list of table names.
  742. *
  743. * @return array An array of keys for the table(s).
  744. *
  745. * @since 11.1
  746. * @throws RuntimeException
  747. */
  748. abstract public function getTableKeys($tables);
  749. /**
  750. * Method to get an array of all tables in the database.
  751. *
  752. * @return array An array of all the tables in the database.
  753. *
  754. * @since 11.1
  755. * @throws RuntimeException
  756. */
  757. abstract public function getTableList();
  758. /**
  759. * Determine whether or not the database engine supports UTF-8 character encoding.
  760. *
  761. * @return boolean True if the database engine supports UTF-8 character encoding.
  762. *
  763. * @since 11.1
  764. * @deprecated 12.3 Use hasUTFSupport() instead
  765. */
  766. public function getUTFSupport()
  767. {
  768. JLog::add('JDatabase::getUTFSupport() is deprecated. Use JDatabase::hasUTFSupport() instead.', JLog::WARNING, 'deprecated');
  769. return $this->hasUTFSupport();
  770. }
  771. /**
  772. * Determine whether or not the database engine supports UTF-8 character encoding.
  773. *
  774. * @return boolean True if the database engine supports UTF-8 character encoding.
  775. *
  776. * @since 12.1
  777. */
  778. public function hasUTFSupport()
  779. {
  780. return $this->utf;
  781. }
  782. /**
  783. * Get the version of the database connector
  784. *
  785. * @return string The database connector version.
  786. *
  787. * @since 11.1
  788. */
  789. abstract public function getVersion();
  790. /**
  791. * Method to get the auto-incremented value from the last INSERT statement.
  792. *
  793. * @return integer The value of the auto-increment field from the last inserted row.
  794. *
  795. * @since 11.1
  796. */
  797. abstract public function insertid();
  798. /**
  799. * Inserts a row into a table based on an object's properties.
  800. *
  801. * @param string $table The name of the database table to insert into.
  802. * @param object &$object A reference to an object whose public properties match the table fields.
  803. * @param string $key The name of the primary key. If provided the object property is updated.
  804. *
  805. * @return boolean True on success.
  806. *
  807. * @since 11.1
  808. * @throws RuntimeException
  809. */
  810. public function insertObject($table, &$object, $key = null)
  811. {
  812. $fields = array();
  813. $values = array();
  814. // Iterate over the object variables to build the query fields and values.
  815. foreach (get_object_vars($object) as $k => $v)
  816. {
  817. // Only process non-null scalars.
  818. if (is_array($v) or is_object($v) or $v === null)
  819. {
  820. continue;
  821. }
  822. // Ignore any internal fields.
  823. if ($k[0] == '_')
  824. {
  825. continue;
  826. }
  827. // Prepare and sanitize the fields and values for the database query.
  828. $fields[] = $this->quoteName($k);
  829. $values[] = $this->quote($v);
  830. }
  831. // Create the base insert statement.
  832. $query = $this->getQuery(true)
  833. ->insert($this->quoteName($table))
  834. ->columns($fields)
  835. ->values(implode(',', $values));
  836. // Set the query and execute the insert.
  837. $this->setQuery($query);
  838. if (!$this->execute())
  839. {
  840. return false;
  841. }
  842. // Update the primary key if it exists.
  843. $id = $this->insertid();
  844. if ($key && $id && is_string($key))
  845. {
  846. $object->$key = $id;
  847. }
  848. return true;
  849. }
  850. /**
  851. * Method to check whether the installed database version is supported by the database driver
  852. *
  853. * @return boolean True if the database version is supported
  854. *
  855. * @since 12.1
  856. */
  857. public function isMinimumVersion()
  858. {
  859. return version_compare($this->getVersion(), static::$dbMinimum) >= 0;
  860. }
  861. /**
  862. * Method to get the first row of the result set from the database query as an associative array
  863. * of ['field_name' => 'row_value'].
  864. *
  865. * @return mixed The return value or null if the query failed.
  866. *
  867. * @since 11.1
  868. * @throws RuntimeException
  869. */
  870. public function loadAssoc()
  871. {
  872. $this->connect();
  873. $ret = null;
  874. // Execute the query and get the result set cursor.
  875. if (!($cursor = $this->execute()))
  876. {
  877. return null;
  878. }
  879. // Get the first row from the result set as an associative array.
  880. if ($array = $this->fetchAssoc($cursor))
  881. {
  882. $ret = $array;
  883. }
  884. // Free up system resources and return.
  885. $this->freeResult($cursor);
  886. return $ret;
  887. }
  888. /**
  889. * Method to get an array of the result set rows from the database query where each row is an associative array
  890. * of ['field_name' => 'row_value']. The array of rows can optionally be keyed by a field name, but defaults to
  891. * a sequential numeric array.
  892. *
  893. * NOTE: Chosing to key the result array by a non-unique field name can result in unwanted
  894. * behavior and should be avoided.
  895. *
  896. * @param string $key The name of a field on which to key the result array.
  897. * @param string $column An optional column name. Instead of the whole row, only this column value will be in
  898. * the result array.
  899. *
  900. * @return mixed The return value or null if the query failed.
  901. *
  902. * @since 11.1
  903. * @throws RuntimeException
  904. */
  905. public function loadAssocList($key = null, $column = null)
  906. {
  907. $this->connect();
  908. $array = array();
  909. // Execute the query and get the result set cursor.
  910. if (!($cursor = $this->execute()))
  911. {
  912. return null;
  913. }
  914. // Get all of the rows from the result set.
  915. while ($row = $this->fetchAssoc($cursor))
  916. {
  917. $value = ($column) ? (isset($row[$column]) ? $row[$column] : $row) : $row;
  918. if ($key)
  919. {
  920. $array[$row[$key]] = $value;
  921. }
  922. else
  923. {
  924. $array[] = $value;
  925. }
  926. }
  927. // Free up system resources and return.
  928. $this->freeResult($cursor);
  929. return $array;
  930. }
  931. /**
  932. * Method to get an array of values from the <var>$offset</var> field in each row of the result set from
  933. * the database query.
  934. *
  935. * @param integer $offset The row offset to use to build the result array.
  936. *
  937. * @return mixed The return value or null if the query failed.
  938. *
  939. * @since 11.1
  940. * @throws RuntimeException
  941. */
  942. public function loadColumn($offset = 0)
  943. {
  944. $this->connect();
  945. $array = array();
  946. // Execute the query and get the result set cursor.
  947. if (!($cursor = $this->execute()))
  948. {
  949. return null;
  950. }
  951. // Get all of the rows from the result set as arrays.
  952. while ($row = $this->fetchArray($cursor))
  953. {
  954. $array[] = $row[$offset];
  955. }
  956. // Free up system resources and return.
  957. $this->freeResult($cursor);
  958. return $array;
  959. }
  960. /**
  961. * Method to get the next row in the result set from the database query as an object.
  962. *
  963. * @param string $class The class name to use for the returned row object.
  964. *
  965. * @return mixed The result of the query as an array, false if there are no more rows.
  966. *
  967. * @since 11.1
  968. * @throws RuntimeException
  969. */
  970. public function loadNextObject($class = 'stdClass')
  971. {
  972. JLog::add(__METHOD__ . '() is deprecated. Use JDatabase::getIterator() instead.', JLog::WARNING, 'deprecated');
  973. $this->connect();
  974. static $cursor = null;
  975. // Execute the query and get the result set cursor.
  976. if ( is_null($cursor) )
  977. {
  978. if (!($cursor = $this->execute()))
  979. {
  980. return $this->errorNum ? null : false;
  981. }
  982. }
  983. // Get the next row from the result set as an object of type $class.
  984. if ($row = $this->fetchObject($cursor, $class))
  985. {
  986. return $row;
  987. }
  988. // Free up system resources and return.
  989. $this->freeResult($cursor);
  990. $cursor = null;
  991. return false;
  992. }
  993. /**
  994. * Method to get the next row in the result set from the database query as an array.
  995. *
  996. * @return mixed The result of the query as an array, false if there are no more rows.
  997. *
  998. * @since 11.1
  999. * @throws RuntimeException
  1000. */
  1001. public function loadNextRow()
  1002. {
  1003. JLog::add('JDatabase::loadNextRow() is deprecated. Use JDatabase::getIterator() instead.', JLog::WARNING, 'deprecated');
  1004. $this->connect();
  1005. static $cursor = null;
  1006. // Execute the query and get the result set cursor.
  1007. if ( is_null($cursor) )
  1008. {
  1009. if (!($cursor = $this->execute()))
  1010. {
  1011. return $this->errorNum ? null : false;
  1012. }
  1013. }
  1014. // Get the next row from the result set as an object of type $class.
  1015. if ($row = $this->fetchArray($cursor))
  1016. {
  1017. return $row;
  1018. }
  1019. // Free up system resources and return.
  1020. $this->freeResult($cursor);
  1021. $cursor = null;
  1022. return false;
  1023. }
  1024. /**
  1025. * Method to get the first row of the result set from the database query as an object.
  1026. *
  1027. * @param string $class The class name to use for the returned row object.
  1028. *
  1029. * @return mixed The return value or null if the query failed.
  1030. *
  1031. * @since 11.1
  1032. * @throws RuntimeException
  1033. */
  1034. public function loadObject($class = 'stdClass')
  1035. {
  1036. $this->connect();
  1037. $ret = null;
  1038. // Execute the query and get the result set cursor.
  1039. if (!($cursor = $this->execute()))
  1040. {
  1041. return null;
  1042. }
  1043. // Get the first row from the result set as an object of type $class.
  1044. if ($object = $this->fetchObject($cursor, $class))
  1045. {
  1046. $ret = $object;
  1047. }
  1048. // Free up system resources and return.
  1049. $this->freeResult($cursor);
  1050. return $ret;
  1051. }
  1052. /**
  1053. * Method to get an array of the result set rows from the database query where each row is an object. The array
  1054. * of objects can optionally be keyed by a field name, but defaults to a sequential numeric array.
  1055. *
  1056. * NOTE: Choosing to key the result array by a non-unique field name can result in unwanted
  1057. * behavior and should be avoided.
  1058. *
  1059. * @param string $key The name of a field on which to key the result array.
  1060. * @param string $class The class name to use for the returned row objects.
  1061. *
  1062. * @return mixed The return value or null if the query failed.
  1063. *
  1064. * @since 11.1
  1065. * @throws RuntimeException
  1066. */
  1067. public function loadObjectList($key = '', $class = 'stdClass')
  1068. {
  1069. $this->connect();
  1070. $array = array();
  1071. // Execute the query and get the result set cursor.
  1072. if (!($cursor = $this->execute()))
  1073. {
  1074. return null;
  1075. }
  1076. // Get all of the rows from the result set as objects of type $class.
  1077. while ($row = $this->fetchObject($cursor, $class))
  1078. {
  1079. if ($key)
  1080. {
  1081. $array[$row->$key] = $row;
  1082. }
  1083. else
  1084. {
  1085. $array[] = $row;
  1086. }
  1087. }
  1088. // Free up system resources and return.
  1089. $this->freeResult($cursor);
  1090. return $array;
  1091. }
  1092. /**
  1093. * Method to get the first field of the first row of the result set from the database query.
  1094. *
  1095. * @return mixed The return value or null if the query failed.
  1096. *
  1097. * @since 11.1
  1098. * @throws RuntimeException
  1099. */
  1100. public function loadResult()
  1101. {
  1102. $this->connect();
  1103. $ret = null;
  1104. // Execute the query and get the result set cursor.
  1105. if (!($cursor = $this->execute()))
  1106. {
  1107. return null;
  1108. }
  1109. // Get the first row from the result set as an array.
  1110. if ($row = $this->fetchArray($cursor))
  1111. {
  1112. $ret = $row[0];
  1113. }
  1114. // Free up system resources and return.
  1115. $this->freeResult($cursor);
  1116. return $ret;
  1117. }
  1118. /**
  1119. * Method to get the first row of the result set from the database query as an array. Columns are indexed
  1120. * numerically so the first column in the result set would be accessible via <var>$row[0]</var>, etc.
  1121. *
  1122. * @return mixed The return value or null if the query failed.
  1123. *
  1124. * @since 11.1
  1125. * @throws RuntimeException
  1126. */
  1127. public function loadRow()
  1128. {
  1129. $this->connect();
  1130. $ret = null;
  1131. // Execute the query and get the result set cursor.
  1132. if (!($cursor = $this->execute()))
  1133. {
  1134. return null;
  1135. }
  1136. // Get the first row from the result set as an array.
  1137. if ($row = $this->fetchArray($cursor))
  1138. {
  1139. $ret = $row;
  1140. }
  1141. // Free up system resources and return.
  1142. $this->freeResult($cursor);
  1143. return $ret;
  1144. }
  1145. /**
  1146. * Method to get an array of the result set rows from the database query where each row is an array. The array
  1147. * of objects can optionally be keyed by a field offset, but defaults to a sequential numeric array.
  1148. *
  1149. * NOTE: Choosing to key the result array by a non-unique field can result in unwanted
  1150. * behavior and should be avoided.
  1151. *
  1152. * @param string $key The name of a field on which to key the result array.
  1153. *
  1154. * @return mixed The return value or null if the query failed.
  1155. *
  1156. * @since 11.1
  1157. * @throws RuntimeException
  1158. */
  1159. public function loadRowList($key = null)
  1160. {
  1161. $this->connect();
  1162. $array = array();
  1163. // Execute the query and get the result set cursor.
  1164. if (!($cursor = $this->execute()))
  1165. {
  1166. return null;
  1167. }
  1168. // Get all of the rows from the result set as arrays.
  1169. while ($row = $this->fetchArray($cursor))
  1170. {
  1171. if ($key !== null)
  1172. {
  1173. $array[$row[$key]] = $row;
  1174. }
  1175. else
  1176. {
  1177. $array[] = $row;
  1178. }
  1179. }
  1180. // Free up system resources and return.
  1181. $this->freeResult($cursor);
  1182. return $array;
  1183. }
  1184. /**
  1185. * Locks a table in the database.
  1186. *
  1187. * @param string $tableName The name of the table to unlock.
  1188. *
  1189. * @return JDatabaseDriver Returns this object to support chaining.
  1190. *
  1191. * @since 11.4
  1192. * @throws RuntimeException
  1193. */
  1194. public abstract function lockTable($tableName);
  1195. /**
  1196. * Method to quote and optionally escape a string to database requirements for insertion into the database.
  1197. *
  1198. * @param string $text The string to quote.
  1199. * @param boolean $escape True (default) to escape the string, false to leave it unchanged.
  1200. *
  1201. * @return string The quoted input string.
  1202. *
  1203. * @since 11.1
  1204. */
  1205. public function quote($text, $escape = true)
  1206. {
  1207. return '\'' . ($escape ? $this->escape($text) : $text) . '\'';
  1208. }
  1209. /**
  1210. * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection
  1211. * risks and reserved word conflicts.
  1212. *
  1213. * @param mixed $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
  1214. * Each type supports dot-notation name.
  1215. * @param mixed $as The AS query part associated to $name. It can be string or array, in latter case it has to be
  1216. * same length of $name; if is null there will not be any AS part for string or array element.
  1217. *
  1218. * @return mixed The quote wrapped name, same type of $name.
  1219. *
  1220. * @since 11.1
  1221. */
  1222. public function quoteName($name, $as = null)
  1223. {
  1224. if (is_string($name))
  1225. {
  1226. $quotedName = $this->quoteNameStr(explode('.', $name));
  1227. $quotedAs = '';
  1228. if (!is_null($as))
  1229. {
  1230. settype($as, 'array');
  1231. $quotedAs .= ' AS ' . $this->quoteNameStr($as);
  1232. }
  1233. return $quotedName . $quotedAs;
  1234. }
  1235. else
  1236. {
  1237. $fin = array();
  1238. if (is_null($as))
  1239. {
  1240. foreach ($name as $str)
  1241. {
  1242. $fin[] = $this->quoteName($str);
  1243. }
  1244. }
  1245. elseif (is_array($name) && (count($name) == count($as)))
  1246. {
  1247. $count = count($name);
  1248. for ($i = 0; $i < $count; $i++)
  1249. {
  1250. $fin[] = $this->quoteName($name[$i], $as[$i]);
  1251. }
  1252. }
  1253. return $fin;
  1254. }
  1255. }
  1256. /**
  1257. * Quote strings coming from quoteName call.
  1258. *
  1259. * @param array $strArr Array of strings coming from quoteName dot-explosion.
  1260. *
  1261. * @return string Dot-imploded string of quoted parts.
  1262. *
  1263. * @since 11.3
  1264. */
  1265. protected function quoteNameStr($strArr)
  1266. {
  1267. $parts = array();
  1268. $q = $this->nameQuote;
  1269. foreach ($strArr as $part)
  1270. {
  1271. if (is_null($part))
  1272. {
  1273. continue;
  1274. }
  1275. if (strlen($q) == 1)
  1276. {
  1277. $parts[] = $q . $part . $q;
  1278. }
  1279. else
  1280. {
  1281. $parts[] = $q{0} . $part . $q{1};
  1282. }
  1283. }
  1284. return implode('.', $parts);
  1285. }
  1286. /**
  1287. * This function replaces a string identifier <var>$prefix</var> with the string held is the
  1288. * <var>tablePrefix</var> class variable.
  1289. *
  1290. * @param string $query The SQL statement to prepare.
  1291. * @param string $prefix The common table prefix.
  1292. *
  1293. * @return string The processed SQL statement.
  1294. *
  1295. * @since 11.1
  1296. */
  1297. public function replacePrefix($query, $prefix = '#__')
  1298. {
  1299. $escaped = false;
  1300. $startPos = 0;
  1301. $quoteChar = '';
  1302. $literal = '';
  1303. $query = trim($query);
  1304. $n = strlen($query);
  1305. while ($startPos < $n)
  1306. {
  1307. $ip = strpos($query, $prefix, $startPos);
  1308. if ($ip === false)
  1309. {
  1310. break;
  1311. }
  1312. $j = strpos($query, "'", $startPos);
  1313. $k = strpos($query, '"', $startPos);
  1314. if (($k !== false) && (($k < $j) || ($j === false)))
  1315. {
  1316. $quoteChar = '"';
  1317. $j = $k;
  1318. }
  1319. else
  1320. {
  1321. $quoteChar = "'";
  1322. }
  1323. if ($j === false)
  1324. {
  1325. $j = $n;
  1326. }
  1327. $literal .= str_replace($prefix, $this->tablePrefix, substr($query, $startPos, $j - $startPos));
  1328. $startPos = $j;
  1329. $j = $startPos + 1;
  1330. if ($j >= $n)
  1331. {
  1332. break;
  1333. }
  1334. // Quote comes first, find end of quote
  1335. while (true)
  1336. {
  1337. $k = strpos($query, $quoteChar, $j);
  1338. $escaped = false;
  1339. if ($k === false)
  1340. {
  1341. break;
  1342. }
  1343. $l = $k - 1;
  1344. while ($l >= 0 && $query{$l} == '\\')
  1345. {
  1346. $l--;
  1347. $escaped = !$escaped;
  1348. }
  1349. if ($escaped)
  1350. {
  1351. $j = $k + 1;
  1352. continue;
  1353. }
  1354. break;
  1355. }
  1356. if ($k === false)
  1357. {
  1358. // Error in the query - no end quote; ignore it
  1359. break;
  1360. }
  1361. $literal .= substr($query, $startPos, $k - $startPos + 1);
  1362. $startPos = $k + 1;
  1363. }
  1364. if ($startPos < $n)
  1365. {
  1366. $literal .= substr($query, $startPos, $n - $startPos);
  1367. }
  1368. return $literal;
  1369. }
  1370. /**
  1371. * Renames a table in the database.
  1372. *
  1373. * @param string $oldTable The name of the table to be renamed
  1374. * @param string $newTable The new name for the table.
  1375. * @param string $backup Table prefix
  1376. * @param string $prefix For the table - used to rename constraints in non-mysql databases
  1377. *
  1378. * @return JDatabaseDriver Returns this object to support chaining.
  1379. *
  1380. * @since 11.4
  1381. * @throws RuntimeException
  1382. */
  1383. public abstract function renameTable($oldTable, $newTable, $backup = null, $prefix = null);
  1384. /**
  1385. * Select a database for use.
  1386. *
  1387. * @param string $database The name of the database to select for use.
  1388. *
  1389. * @return boolean True if the database was successfully selected.
  1390. *
  1391. * @since 11.1
  1392. * @throws RuntimeException
  1393. */
  1394. abstract public function select($database);
  1395. /**
  1396. * Sets the database debugging state for the driver.
  1397. *
  1398. * @param boolean $level True to enable debugging.
  1399. *
  1400. * @return boolean The old debugging level.
  1401. *
  1402. * @since 11.1
  1403. */
  1404. public function setDebug($level)
  1405. {
  1406. $previous = $this->debug;
  1407. $this->debug = (bool) $level;
  1408. return $previous;
  1409. }
  1410. /**
  1411. * Sets the SQL statement string for later execution.
  1412. *
  1413. * @param mixed $query The SQL statement to set either as a JDatabaseQuery object or a string.
  1414. * @param integer $offset The affected row offset to set.
  1415. * @param integer $limit The maximum affected rows to set.
  1416. *
  1417. * @return JDatabaseDriver This object to support method chaining.
  1418. *
  1419. * @since 11.1
  1420. */
  1421. public function setQuery($query, $offset = 0, $limit = 0)
  1422. {
  1423. $this->sql = $query;
  1424. $this->limit = (int) max(0, $limit);
  1425. $this->offset = (int) max(0, $offset);
  1426. return $this;
  1427. }
  1428. /**
  1429. * Set the connection to use UTF-8 character encoding.
  1430. *
  1431. * @return boolean True on success.
  1432. *
  1433. * @since 11.1
  1434. */
  1435. abstract public function setUTF();
  1436. /**
  1437. * Method to commit a transaction.
  1438. *
  1439. * @return void
  1440. *
  1441. * @since 11.1
  1442. * @throws RuntimeException
  1443. */
  1444. abstract public function transactionCommit();
  1445. /**
  1446. * Method to roll back a transaction.
  1447. *
  1448. * @return void
  1449. *
  1450. * @since 11.1
  1451. * @throws RuntimeException
  1452. */
  1453. abstract public function transactionRollback();
  1454. /**
  1455. * Method to initialize a transaction.
  1456. *
  1457. * @return void
  1458. *
  1459. * @since 11.1
  1460. * @throws RuntimeException
  1461. */
  1462. abstract public function transactionStart();
  1463. /**
  1464. * Method to truncate a table.
  1465. *
  1466. * @param string $table The table to truncate
  1467. *
  1468. * @return void
  1469. *
  1470. * @since 11.3
  1471. * @throws RuntimeException
  1472. */
  1473. public function truncateTable($table)
  1474. {
  1475. $this->setQuery('TRUNCATE TABLE ' . $this->quoteName($table));
  1476. $this->execute();
  1477. }
  1478. /**
  1479. * Updates a row in a table based on an object's properties.
  1480. *
  1481. * @param string $table The name of the database table to update.
  1482. * @param object &$object A reference to an object whose public properties match the table fields.
  1483. * @param array $key The name of the primary key.
  1484. * @param boolean $nulls True to update null fields or false to ignore them.
  1485. *
  1486. * @return boolean True on success.
  1487. *
  1488. * @since 11.1
  1489. * @throws RuntimeException
  1490. */
  1491. public function updateObject($table, &$object, $key, $nulls = false)
  1492. {
  1493. $fields = array();
  1494. $where = array();
  1495. if (is_string($key))
  1496. {
  1497. $key = array($key);
  1498. }
  1499. if (is_object($key))
  1500. {
  1501. $key = (array) $key;
  1502. }
  1503. // Create the base update statement.
  1504. $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s';
  1505. // Iterate over the object variables to build the query fields/value pairs.
  1506. foreach (get_object_vars($object) as $k => $v)
  1507. {
  1508. // Only process scalars that are not internal fields.
  1509. if (is_array($v) or is_object($v) or $k[0] == '_')
  1510. {
  1511. continue;
  1512. }
  1513. // Set the primary key to the WHERE clause instead of a field to update.
  1514. if (in_array($k, $key))
  1515. {
  1516. $where[] = $this->quoteName($k) . '=' . $this->quote($v);
  1517. continue;
  1518. }
  1519. // Prepare and sanitize the fields and values for the database query.
  1520. if ($v === null)
  1521. {
  1522. // If the value is null and we want to update nulls then set it.
  1523. if ($nulls)
  1524. {
  1525. $val = 'NULL';
  1526. }
  1527. // If the value is null and we do not want to update nulls then ignore this field.
  1528. else
  1529. {
  1530. continue;
  1531. }
  1532. }
  1533. // The field is not null so we prep it for update.
  1534. else
  1535. {
  1536. $val = $this->quote($v);
  1537. }
  1538. // Add the field to be updated.
  1539. $fields[] = $this->quoteName($k) . '=' . $val;
  1540. }
  1541. // We don't have any fields to update.
  1542. if (empty($fields))
  1543. {
  1544. return true;
  1545. }
  1546. // Set the query and execute the update.
  1547. $this->setQuery(sprintf($statement, implode(",", $fields), implode(' AND ', $where)));
  1548. return $this->execute();
  1549. }
  1550. /**
  1551. * Execute the SQL statement.
  1552. *
  1553. * @return mixed A database cursor resource on success, boolean false on failure.
  1554. *
  1555. * @since 12.1
  1556. * @throws RuntimeException
  1557. */
  1558. abstract public function execute();
  1559. /**
  1560. * Unlocks tables in the database.
  1561. *
  1562. * @return JDatabaseDriver Returns this object to support chaining.
  1563. *
  1564. * @since 11.4
  1565. * @throws RuntimeException
  1566. */
  1567. public abstract function unlockTables();
  1568. }