PageRenderTime 61ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/joomla/database/driver.php

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