PageRenderTime 60ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/libraries/joomla/database/driver.php

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