/libraries/joomla/database/driver/pdo.php

https://gitlab.com/vitaliylukin91/alex-lavka · PHP · 1031 lines · 575 code · 144 blank · 312 comment · 56 complexity · 3a9f5f8737a4edc33c921ab9941ddbe6 MD5 · raw file

  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage Database
  5. *
  6. * @copyright Copyright (C) 2005 - 2015 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 PDO Database Driver Class
  12. *
  13. * @see http://php.net/pdo
  14. * @since 12.1
  15. */
  16. abstract class JDatabaseDriverPdo extends JDatabaseDriver
  17. {
  18. /**
  19. * The name of the database driver.
  20. *
  21. * @var string
  22. * @since 12.1
  23. */
  24. public $name = 'pdo';
  25. /**
  26. * The character(s) used to quote SQL statement names such as table names or field names,
  27. * etc. The child classes should define this as necessary. If a single character string the
  28. * same character is used for both sides of the quoted name, else the first character will be
  29. * used for the opening quote and the second for the closing quote.
  30. *
  31. * @var string
  32. * @since 12.1
  33. */
  34. protected $nameQuote = "'";
  35. /**
  36. * The null or zero representation of a timestamp for the database driver. This should be
  37. * defined in child classes to hold the appropriate value for the engine.
  38. *
  39. * @var string
  40. * @since 12.1
  41. */
  42. protected $nullDate = '0000-00-00 00:00:00';
  43. /**
  44. * @var resource The prepared statement.
  45. * @since 12.1
  46. */
  47. protected $prepared;
  48. /**
  49. * Contains the current query execution status
  50. *
  51. * @var array
  52. * @since 12.1
  53. */
  54. protected $executed = false;
  55. /**
  56. * Constructor.
  57. *
  58. * @param array $options List of options used to configure the connection
  59. *
  60. * @since 12.1
  61. */
  62. public function __construct($options)
  63. {
  64. // Get some basic values from the options.
  65. $options['driver'] = (isset($options['driver'])) ? $options['driver'] : 'odbc';
  66. $options['dsn'] = (isset($options['dsn'])) ? $options['dsn'] : '';
  67. $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost';
  68. $options['database'] = (isset($options['database'])) ? $options['database'] : '';
  69. $options['user'] = (isset($options['user'])) ? $options['user'] : '';
  70. $options['password'] = (isset($options['password'])) ? $options['password'] : '';
  71. $options['driverOptions'] = (isset($options['driverOptions'])) ? $options['driverOptions'] : array();
  72. // Finalize initialisation
  73. parent::__construct($options);
  74. }
  75. /**
  76. * Destructor.
  77. *
  78. * @since 12.1
  79. */
  80. public function __destruct()
  81. {
  82. $this->disconnect();
  83. }
  84. /**
  85. * Connects to the database if needed.
  86. *
  87. * @return void Returns void if the database connected successfully.
  88. *
  89. * @since 12.1
  90. * @throws RuntimeException
  91. */
  92. public function connect()
  93. {
  94. if ($this->connection)
  95. {
  96. return;
  97. }
  98. // Make sure the PDO extension for PHP is installed and enabled.
  99. if (!self::isSupported())
  100. {
  101. throw new RuntimeException('PDO Extension is not available.', 1);
  102. }
  103. $replace = array();
  104. $with = array();
  105. // Find the correct PDO DSN Format to use:
  106. switch ($this->options['driver'])
  107. {
  108. case 'cubrid':
  109. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 33000;
  110. $format = 'cubrid:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
  111. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  112. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  113. break;
  114. case 'dblib':
  115. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433;
  116. $format = 'dblib:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
  117. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  118. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  119. break;
  120. case 'firebird':
  121. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 3050;
  122. $format = 'firebird:dbname=#DBNAME#';
  123. $replace = array('#DBNAME#');
  124. $with = array($this->options['database']);
  125. break;
  126. case 'ibm':
  127. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 56789;
  128. if (!empty($this->options['dsn']))
  129. {
  130. $format = 'ibm:DSN=#DSN#';
  131. $replace = array('#DSN#');
  132. $with = array($this->options['dsn']);
  133. }
  134. else
  135. {
  136. $format = 'ibm:hostname=#HOST#;port=#PORT#;database=#DBNAME#';
  137. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  138. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  139. }
  140. break;
  141. case 'informix':
  142. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1526;
  143. $this->options['protocol'] = (isset($this->options['protocol'])) ? $this->options['protocol'] : 'onsoctcp';
  144. if (!empty($this->options['dsn']))
  145. {
  146. $format = 'informix:DSN=#DSN#';
  147. $replace = array('#DSN#');
  148. $with = array($this->options['dsn']);
  149. }
  150. else
  151. {
  152. $format = 'informix:host=#HOST#;service=#PORT#;database=#DBNAME#;server=#SERVER#;protocol=#PROTOCOL#';
  153. $replace = array('#HOST#', '#PORT#', '#DBNAME#', '#SERVER#', '#PROTOCOL#');
  154. $with = array($this->options['host'], $this->options['port'], $this->options['database'], $this->options['server'], $this->options['protocol']);
  155. }
  156. break;
  157. case 'mssql':
  158. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433;
  159. $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
  160. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  161. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  162. break;
  163. // The pdomysql case is a special case within the CMS environment
  164. case 'pdomysql':
  165. case 'mysql':
  166. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 3306;
  167. $format = 'mysql:host=#HOST#;port=#PORT#;dbname=#DBNAME#;charset=#CHARSET#';
  168. $replace = array('#HOST#', '#PORT#', '#DBNAME#', '#CHARSET#');
  169. $with = array($this->options['host'], $this->options['port'], $this->options['database'], $this->options['charset']);
  170. break;
  171. case 'oci':
  172. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1521;
  173. $this->options['charset'] = (isset($this->options['charset'])) ? $this->options['charset'] : 'AL32UTF8';
  174. if (!empty($this->options['dsn']))
  175. {
  176. $format = 'oci:dbname=#DSN#';
  177. $replace = array('#DSN#');
  178. $with = array($this->options['dsn']);
  179. }
  180. else
  181. {
  182. $format = 'oci:dbname=//#HOST#:#PORT#/#DBNAME#';
  183. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  184. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  185. }
  186. $format .= ';charset=' . $this->options['charset'];
  187. break;
  188. case 'odbc':
  189. $format = 'odbc:DSN=#DSN#;UID:#USER#;PWD=#PASSWORD#';
  190. $replace = array('#DSN#', '#USER#', '#PASSWORD#');
  191. $with = array($this->options['dsn'], $this->options['user'], $this->options['password']);
  192. break;
  193. case 'pgsql':
  194. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 5432;
  195. $format = 'pgsql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
  196. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  197. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  198. break;
  199. case 'sqlite':
  200. if (isset($this->options['version']) && $this->options['version'] == 2)
  201. {
  202. $format = 'sqlite2:#DBNAME#';
  203. }
  204. else
  205. {
  206. $format = 'sqlite:#DBNAME#';
  207. }
  208. $replace = array('#DBNAME#');
  209. $with = array($this->options['database']);
  210. break;
  211. case 'sybase':
  212. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433;
  213. $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
  214. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  215. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  216. break;
  217. }
  218. // Create the connection string:
  219. $connectionString = str_replace($replace, $with, $format);
  220. try
  221. {
  222. $this->connection = new PDO(
  223. $connectionString,
  224. $this->options['user'],
  225. $this->options['password'],
  226. $this->options['driverOptions']
  227. );
  228. }
  229. catch (PDOException $e)
  230. {
  231. throw new RuntimeException('Could not connect to PDO: ' . $e->getMessage(), 2, $e);
  232. }
  233. }
  234. /**
  235. * Disconnects the database.
  236. *
  237. * @return void
  238. *
  239. * @since 12.1
  240. */
  241. public function disconnect()
  242. {
  243. foreach ($this->disconnectHandlers as $h)
  244. {
  245. call_user_func_array($h, array( &$this));
  246. }
  247. $this->freeResult();
  248. unset($this->connection);
  249. }
  250. /**
  251. * Method to escape a string for usage in an SQL statement.
  252. *
  253. * Oracle escaping reference:
  254. * http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F
  255. *
  256. * SQLite escaping notes:
  257. * http://www.sqlite.org/faq.html#q14
  258. *
  259. * Method body is as implemented by the Zend Framework
  260. *
  261. * Note: Using query objects with bound variables is
  262. * preferable to the below.
  263. *
  264. * @param string $text The string to be escaped.
  265. * @param boolean $extra Unused optional parameter to provide extra escaping.
  266. *
  267. * @return string The escaped string.
  268. *
  269. * @since 12.1
  270. */
  271. public function escape($text, $extra = false)
  272. {
  273. if (is_int($text) || is_float($text))
  274. {
  275. return $text;
  276. }
  277. $text = str_replace("'", "''", $text);
  278. return addcslashes($text, "\000\n\r\\\032");
  279. }
  280. /**
  281. * Execute the SQL statement.
  282. *
  283. * @return mixed A database cursor resource on success, boolean false on failure.
  284. *
  285. * @since 12.1
  286. * @throws RuntimeException
  287. * @throws Exception
  288. */
  289. public function execute()
  290. {
  291. $this->connect();
  292. if (!is_object($this->connection))
  293. {
  294. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database');
  295. throw new RuntimeException($this->errorMsg, $this->errorNum);
  296. }
  297. // Take a local copy so that we don't modify the original query and cause issues later
  298. $query = $this->replacePrefix((string) $this->sql);
  299. if (!($this->sql instanceof JDatabaseQuery) && ($this->limit > 0 || $this->offset > 0))
  300. {
  301. // @TODO
  302. $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit;
  303. }
  304. // Increment the query counter.
  305. $this->count++;
  306. // Reset the error values.
  307. $this->errorNum = 0;
  308. $this->errorMsg = '';
  309. // If debugging is enabled then let's log the query.
  310. if ($this->debug)
  311. {
  312. // Add the query to the object queue.
  313. $this->log[] = $query;
  314. JLog::add($query, JLog::DEBUG, 'databasequery');
  315. $this->timings[] = microtime(true);
  316. }
  317. // Execute the query.
  318. $this->executed = false;
  319. if ($this->prepared instanceof PDOStatement)
  320. {
  321. // Bind the variables:
  322. if ($this->sql instanceof JDatabaseQueryPreparable)
  323. {
  324. $bounded = $this->sql->getBounded();
  325. foreach ($bounded as $key => $obj)
  326. {
  327. $this->prepared->bindParam($key, $obj->value, $obj->dataType, $obj->length, $obj->driverOptions);
  328. }
  329. }
  330. $this->executed = $this->prepared->execute();
  331. }
  332. if ($this->debug)
  333. {
  334. $this->timings[] = microtime(true);
  335. if (defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
  336. {
  337. $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
  338. }
  339. else
  340. {
  341. $this->callStacks[] = debug_backtrace();
  342. }
  343. }
  344. // If an error occurred handle it.
  345. if (!$this->executed)
  346. {
  347. // Get the error number and message before we execute any more queries.
  348. $errorNum = (int) $this->connection->errorCode();
  349. $errorMsg = (string) 'SQL: ' . implode(", ", $this->connection->errorInfo());
  350. // Check if the server was disconnected.
  351. if (!$this->connected())
  352. {
  353. try
  354. {
  355. // Attempt to reconnect.
  356. $this->connection = null;
  357. $this->connect();
  358. }
  359. // If connect fails, ignore that exception and throw the normal exception.
  360. catch (RuntimeException $e)
  361. {
  362. // Get the error number and message.
  363. $this->errorNum = (int) $this->connection->errorCode();
  364. $this->errorMsg = (string) 'SQL: ' . implode(", ", $this->connection->errorInfo());
  365. // Throw the normal query exception.
  366. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error');
  367. throw new RuntimeException($this->errorMsg, $this->errorNum);
  368. }
  369. // Since we were able to reconnect, run the query again.
  370. return $this->execute();
  371. }
  372. // The server was not disconnected.
  373. else
  374. {
  375. // Get the error number and message from before we tried to reconnect.
  376. $this->errorNum = $errorNum;
  377. $this->errorMsg = $errorMsg;
  378. // Throw the normal query exception.
  379. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error');
  380. throw new RuntimeException($this->errorMsg, $this->errorNum);
  381. }
  382. }
  383. return $this->prepared;
  384. }
  385. /**
  386. * Retrieve a PDO database connection attribute
  387. * http://www.php.net/manual/en/pdo.getattribute.php
  388. *
  389. * Usage: $db->getOption(PDO::ATTR_CASE);
  390. *
  391. * @param mixed $key One of the PDO::ATTR_* Constants
  392. *
  393. * @return mixed
  394. *
  395. * @since 12.1
  396. */
  397. public function getOption($key)
  398. {
  399. $this->connect();
  400. return $this->connection->getAttribute($key);
  401. }
  402. /**
  403. * Get a query to run and verify the database is operational.
  404. *
  405. * @return string The query to check the health of the DB.
  406. *
  407. * @since 12.2
  408. */
  409. public function getConnectedQuery()
  410. {
  411. return 'SELECT 1';
  412. }
  413. /**
  414. * Sets an attribute on the PDO database handle.
  415. * http://www.php.net/manual/en/pdo.setattribute.php
  416. *
  417. * Usage: $db->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
  418. *
  419. * @param integer $key One of the PDO::ATTR_* Constants
  420. * @param mixed $value One of the associated PDO Constants
  421. * related to the particular attribute
  422. * key.
  423. *
  424. * @return boolean
  425. *
  426. * @since 12.1
  427. */
  428. public function setOption($key, $value)
  429. {
  430. $this->connect();
  431. return $this->connection->setAttribute($key, $value);
  432. }
  433. /**
  434. * Test to see if the PDO extension is available.
  435. * Override as needed to check for specific PDO Drivers.
  436. *
  437. * @return boolean True on success, false otherwise.
  438. *
  439. * @since 12.1
  440. */
  441. public static function isSupported()
  442. {
  443. return defined('PDO::ATTR_DRIVER_NAME');
  444. }
  445. /**
  446. * Determines if the connection to the server is active.
  447. *
  448. * @return boolean True if connected to the database engine.
  449. *
  450. * @since 12.1
  451. */
  452. public function connected()
  453. {
  454. // Flag to prevent recursion into this function.
  455. static $checkingConnected = false;
  456. if ($checkingConnected)
  457. {
  458. // Reset this flag and throw an exception.
  459. $checkingConnected = true;
  460. die('Recursion trying to check if connected.');
  461. }
  462. // Backup the query state.
  463. $query = $this->sql;
  464. $limit = $this->limit;
  465. $offset = $this->offset;
  466. $prepared = $this->prepared;
  467. try
  468. {
  469. // Set the checking connection flag.
  470. $checkingConnected = true;
  471. // Run a simple query to check the connection.
  472. $this->setQuery($this->getConnectedQuery());
  473. $status = (bool) $this->loadResult();
  474. }
  475. // If we catch an exception here, we must not be connected.
  476. catch (Exception $e)
  477. {
  478. $status = false;
  479. }
  480. // Restore the query state.
  481. $this->sql = $query;
  482. $this->limit = $limit;
  483. $this->offset = $offset;
  484. $this->prepared = $prepared;
  485. $checkingConnected = false;
  486. return $status;
  487. }
  488. /**
  489. * Get the number of affected rows for the previous executed SQL statement.
  490. * Only applicable for DELETE, INSERT, or UPDATE statements.
  491. *
  492. * @return integer The number of affected rows.
  493. *
  494. * @since 12.1
  495. */
  496. public function getAffectedRows()
  497. {
  498. $this->connect();
  499. if ($this->prepared instanceof PDOStatement)
  500. {
  501. return $this->prepared->rowCount();
  502. }
  503. else
  504. {
  505. return 0;
  506. }
  507. }
  508. /**
  509. * Get the number of returned rows for the previous executed SQL statement.
  510. *
  511. * @param resource $cursor An optional database cursor resource to extract the row count from.
  512. *
  513. * @return integer The number of returned rows.
  514. *
  515. * @since 12.1
  516. */
  517. public function getNumRows($cursor = null)
  518. {
  519. $this->connect();
  520. if ($cursor instanceof PDOStatement)
  521. {
  522. return $cursor->rowCount();
  523. }
  524. elseif ($this->prepared instanceof PDOStatement)
  525. {
  526. return $this->prepared->rowCount();
  527. }
  528. else
  529. {
  530. return 0;
  531. }
  532. }
  533. /**
  534. * Method to get the auto-incremented value from the last INSERT statement.
  535. *
  536. * @return string The value of the auto-increment field from the last inserted row.
  537. *
  538. * @since 12.1
  539. */
  540. public function insertid()
  541. {
  542. $this->connect();
  543. // Error suppress this to prevent PDO warning us that the driver doesn't support this operation.
  544. return @$this->connection->lastInsertId();
  545. }
  546. /**
  547. * Select a database for use.
  548. *
  549. * @param string $database The name of the database to select for use.
  550. *
  551. * @return boolean True if the database was successfully selected.
  552. *
  553. * @since 12.1
  554. * @throws RuntimeException
  555. */
  556. public function select($database)
  557. {
  558. $this->connect();
  559. return true;
  560. }
  561. /**
  562. * Sets the SQL statement string for later execution.
  563. *
  564. * @param mixed $query The SQL statement to set either as a JDatabaseQuery object or a string.
  565. * @param integer $offset The affected row offset to set.
  566. * @param integer $limit The maximum affected rows to set.
  567. * @param array $driverOptions The optional PDO driver options.
  568. *
  569. * @return JDatabaseDriver This object to support method chaining.
  570. *
  571. * @since 12.1
  572. */
  573. public function setQuery($query, $offset = null, $limit = null, $driverOptions = array())
  574. {
  575. $this->connect();
  576. $this->freeResult();
  577. if (is_string($query))
  578. {
  579. // Allows taking advantage of bound variables in a direct query:
  580. $query = $this->getQuery(true)->setQuery($query);
  581. }
  582. if ($query instanceof JDatabaseQueryLimitable && !is_null($offset) && !is_null($limit))
  583. {
  584. $query = $query->processLimit($query, $limit, $offset);
  585. }
  586. // Create a stringified version of the query (with prefixes replaced):
  587. $sql = $this->replacePrefix((string) $query);
  588. // Use the stringified version in the prepare call:
  589. $this->prepared = $this->connection->prepare($sql, $driverOptions);
  590. // Store reference to the original JDatabaseQuery instance within the class.
  591. // This is important since binding variables depends on it within execute():
  592. parent::setQuery($query, $offset, $limit);
  593. return $this;
  594. }
  595. /**
  596. * Set the connection to use UTF-8 character encoding.
  597. *
  598. * @return boolean True on success.
  599. *
  600. * @since 12.1
  601. */
  602. public function setUtf()
  603. {
  604. return false;
  605. }
  606. /**
  607. * Method to commit a transaction.
  608. *
  609. * @param boolean $toSavepoint If true, commit to the last savepoint.
  610. *
  611. * @return void
  612. *
  613. * @since 12.1
  614. * @throws RuntimeException
  615. */
  616. public function transactionCommit($toSavepoint = false)
  617. {
  618. $this->connect();
  619. if (!$toSavepoint || $this->transactionDepth == 1)
  620. {
  621. $this->connection->commit();
  622. }
  623. $this->transactionDepth--;
  624. }
  625. /**
  626. * Method to roll back a transaction.
  627. *
  628. * @param boolean $toSavepoint If true, rollback to the last savepoint.
  629. *
  630. * @return void
  631. *
  632. * @since 12.1
  633. * @throws RuntimeException
  634. */
  635. public function transactionRollback($toSavepoint = false)
  636. {
  637. $this->connect();
  638. if (!$toSavepoint || $this->transactionDepth == 1)
  639. {
  640. $this->connection->rollBack();
  641. }
  642. $this->transactionDepth--;
  643. }
  644. /**
  645. * Method to initialize a transaction.
  646. *
  647. * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created.
  648. *
  649. * @return void
  650. *
  651. * @since 12.1
  652. * @throws RuntimeException
  653. */
  654. public function transactionStart($asSavepoint = false)
  655. {
  656. $this->connect();
  657. if (!$asSavepoint || !$this->transactionDepth)
  658. {
  659. $this->connection->beginTransaction();
  660. }
  661. $this->transactionDepth++;
  662. }
  663. /**
  664. * Method to fetch a row from the result set cursor as an array.
  665. *
  666. * @param mixed $cursor The optional result set cursor from which to fetch the row.
  667. *
  668. * @return mixed Either the next row from the result set or false if there are no more rows.
  669. *
  670. * @since 12.1
  671. */
  672. protected function fetchArray($cursor = null)
  673. {
  674. if (!empty($cursor) && $cursor instanceof PDOStatement)
  675. {
  676. return $cursor->fetch(PDO::FETCH_NUM);
  677. }
  678. if ($this->prepared instanceof PDOStatement)
  679. {
  680. return $this->prepared->fetch(PDO::FETCH_NUM);
  681. }
  682. }
  683. /**
  684. * Method to fetch a row from the result set cursor as an associative array.
  685. *
  686. * @param mixed $cursor The optional result set cursor from which to fetch the row.
  687. *
  688. * @return mixed Either the next row from the result set or false if there are no more rows.
  689. *
  690. * @since 12.1
  691. */
  692. protected function fetchAssoc($cursor = null)
  693. {
  694. if (!empty($cursor) && $cursor instanceof PDOStatement)
  695. {
  696. return $cursor->fetch(PDO::FETCH_ASSOC);
  697. }
  698. if ($this->prepared instanceof PDOStatement)
  699. {
  700. return $this->prepared->fetch(PDO::FETCH_ASSOC);
  701. }
  702. }
  703. /**
  704. * Method to fetch a row from the result set cursor as an object.
  705. *
  706. * @param mixed $cursor The optional result set cursor from which to fetch the row.
  707. * @param string $class Unused, only necessary so method signature will be the same as parent.
  708. *
  709. * @return mixed Either the next row from the result set or false if there are no more rows.
  710. *
  711. * @since 12.1
  712. */
  713. protected function fetchObject($cursor = null, $class = 'stdClass')
  714. {
  715. if (!empty($cursor) && $cursor instanceof PDOStatement)
  716. {
  717. return $cursor->fetchObject($class);
  718. }
  719. if ($this->prepared instanceof PDOStatement)
  720. {
  721. return $this->prepared->fetchObject($class);
  722. }
  723. }
  724. /**
  725. * Method to free up the memory used for the result set.
  726. *
  727. * @param mixed $cursor The optional result set cursor from which to fetch the row.
  728. *
  729. * @return void
  730. *
  731. * @since 12.1
  732. */
  733. protected function freeResult($cursor = null)
  734. {
  735. $this->executed = false;
  736. if ($cursor instanceof PDOStatement)
  737. {
  738. $cursor->closeCursor();
  739. $cursor = null;
  740. }
  741. if ($this->prepared instanceof PDOStatement)
  742. {
  743. $this->prepared->closeCursor();
  744. $this->prepared = null;
  745. }
  746. }
  747. /**
  748. * Method to get the next row in the result set from the database query as an object.
  749. *
  750. * @param string $class The class name to use for the returned row object.
  751. *
  752. * @return mixed The result of the query as an array, false if there are no more rows.
  753. *
  754. * @since 12.1
  755. * @throws RuntimeException
  756. * @deprecated 4.0 (CMS) Use getIterator() instead
  757. */
  758. public function loadNextObject($class = 'stdClass')
  759. {
  760. JLog::add(__METHOD__ . '() is deprecated. Use JDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated');
  761. $this->connect();
  762. // Execute the query and get the result set cursor.
  763. if (!$this->executed)
  764. {
  765. if (!($this->execute()))
  766. {
  767. return $this->errorNum ? null : false;
  768. }
  769. }
  770. // Get the next row from the result set as an object of type $class.
  771. if ($row = $this->fetchObject(null, $class))
  772. {
  773. return $row;
  774. }
  775. // Free up system resources and return.
  776. $this->freeResult();
  777. return false;
  778. }
  779. /**
  780. * Method to get the next row in the result set from the database query as an array.
  781. *
  782. * @return mixed The result of the query as an array, false if there are no more rows.
  783. *
  784. * @since 12.1
  785. * @throws RuntimeException
  786. */
  787. public function loadNextAssoc()
  788. {
  789. $this->connect();
  790. // Execute the query and get the result set cursor.
  791. if (!$this->executed)
  792. {
  793. if (!($this->execute()))
  794. {
  795. return $this->errorNum ? null : false;
  796. }
  797. }
  798. // Get the next row from the result set as an object of type $class.
  799. if ($row = $this->fetchAssoc())
  800. {
  801. return $row;
  802. }
  803. // Free up system resources and return.
  804. $this->freeResult();
  805. return false;
  806. }
  807. /**
  808. * Method to get the next row in the result set from the database query as an array.
  809. *
  810. * @return mixed The result of the query as an array, false if there are no more rows.
  811. *
  812. * @since 12.1
  813. * @throws RuntimeException
  814. * @deprecated 4.0 (CMS) Use getIterator() instead
  815. */
  816. public function loadNextRow()
  817. {
  818. JLog::add(__METHOD__ . '() is deprecated. Use JDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated');
  819. $this->connect();
  820. // Execute the query and get the result set cursor.
  821. if (!$this->executed)
  822. {
  823. if (!($this->execute()))
  824. {
  825. return $this->errorNum ? null : false;
  826. }
  827. }
  828. // Get the next row from the result set as an object of type $class.
  829. if ($row = $this->fetchArray())
  830. {
  831. return $row;
  832. }
  833. // Free up system resources and return.
  834. $this->freeResult();
  835. return false;
  836. }
  837. /**
  838. * PDO does not support serialize
  839. *
  840. * @return array
  841. *
  842. * @since 12.3
  843. */
  844. public function __sleep()
  845. {
  846. $serializedProperties = array();
  847. $reflect = new ReflectionClass($this);
  848. // Get properties of the current class
  849. $properties = $reflect->getProperties();
  850. foreach ($properties as $property)
  851. {
  852. // Do not serialize properties that are PDO
  853. if ($property->isStatic() == false && !($this->{$property->name} instanceof PDO))
  854. {
  855. array_push($serializedProperties, $property->name);
  856. }
  857. }
  858. return $serializedProperties;
  859. }
  860. /**
  861. * Wake up after serialization
  862. *
  863. * @return array
  864. *
  865. * @since 12.3
  866. */
  867. public function __wakeup()
  868. {
  869. // Get connection back
  870. $this->__construct($this->options);
  871. }
  872. }