PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Joomla/Database/Pdo/PdoDriver.php

https://github.com/piotr-cz/joomla-framework
PHP | 957 lines | 542 code | 133 blank | 282 comment | 47 complexity | 10e7410681a6520b3a80c91d28ce6225 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * Part of the Joomla Framework Database Package
  4. *
  5. * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  6. * @license GNU General Public License version 2 or later; see LICENSE
  7. */
  8. namespace Joomla\Database\Pdo;
  9. use Psr\Log;
  10. use Joomla\Database\DatabaseDriver;
  11. use Joomla\Database\Query\LimitableInterface;
  12. use Joomla\Database\Query\PreparableInterface;
  13. /**
  14. * Joomla Framework PDO Database Driver Class
  15. *
  16. * @see http://php.net/pdo
  17. * @since 1.0
  18. */
  19. abstract class PdoDriver extends DatabaseDriver
  20. {
  21. /**
  22. * The name of the database driver.
  23. *
  24. * @var string
  25. * @since 1.0
  26. */
  27. public $name = 'pdo';
  28. /**
  29. * The character(s) used to quote SQL statement names such as table names or field names,
  30. * etc. The child classes should define this as necessary. If a single character string the
  31. * same character is used for both sides of the quoted name, else the first character will be
  32. * used for the opening quote and the second for the closing quote.
  33. *
  34. * @var string
  35. * @since 1.0
  36. */
  37. protected $nameQuote = "'";
  38. /**
  39. * The null or zero representation of a timestamp for the database driver. This should be
  40. * defined in child classes to hold the appropriate value for the engine.
  41. *
  42. * @var string
  43. * @since 1.0
  44. */
  45. protected $nullDate = '0000-00-00 00:00:00';
  46. /**
  47. * The prepared statement.
  48. *
  49. * @var resource
  50. * @since 1.0
  51. */
  52. protected $prepared;
  53. /**
  54. * Contains the current query execution status
  55. *
  56. * @var array
  57. * @since 1.0
  58. */
  59. protected $executed = false;
  60. /**
  61. * Constructor.
  62. *
  63. * @param array $options List of options used to configure the connection
  64. *
  65. * @since 1.0
  66. */
  67. public function __construct($options)
  68. {
  69. // Get some basic values from the options.
  70. $options['driver'] = (isset($options['driver'])) ? $options['driver'] : 'odbc';
  71. $options['dsn'] = (isset($options['dsn'])) ? $options['dsn'] : '';
  72. $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost';
  73. $options['database'] = (isset($options['database'])) ? $options['database'] : '';
  74. $options['user'] = (isset($options['user'])) ? $options['user'] : '';
  75. $options['password'] = (isset($options['password'])) ? $options['password'] : '';
  76. $options['driverOptions'] = (isset($options['driverOptions'])) ? $options['driverOptions'] : array();
  77. // Finalize initialisation
  78. parent::__construct($options);
  79. }
  80. /**
  81. * Destructor.
  82. *
  83. * @since 1.0
  84. */
  85. public function __destruct()
  86. {
  87. $this->freeResult();
  88. unset($this->connection);
  89. }
  90. /**
  91. * Connects to the database if needed.
  92. *
  93. * @return void Returns void if the database connected successfully.
  94. *
  95. * @since 1.0
  96. * @throws \RuntimeException
  97. * @throws \UnexpectedValueException
  98. */
  99. public function connect()
  100. {
  101. if ($this->connection)
  102. {
  103. return;
  104. }
  105. // Make sure the PDO extension for PHP is installed and enabled.
  106. if (!self::isSupported())
  107. {
  108. throw new \RuntimeException('PDO Extension is not available.', 1);
  109. }
  110. // Find the correct PDO DSN Format to use:
  111. switch ($this->options['driver'])
  112. {
  113. case 'cubrid':
  114. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 33000;
  115. $format = 'cubrid:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
  116. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  117. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  118. break;
  119. case 'dblib':
  120. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433;
  121. $format = 'dblib:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
  122. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  123. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  124. break;
  125. case 'firebird':
  126. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 3050;
  127. $format = 'firebird:dbname=#DBNAME#';
  128. $replace = array('#DBNAME#');
  129. $with = array($this->options['database']);
  130. break;
  131. case 'ibm':
  132. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 56789;
  133. if (!empty($this->options['dsn']))
  134. {
  135. $format = 'ibm:DSN=#DSN#';
  136. $replace = array('#DSN#');
  137. $with = array($this->options['dsn']);
  138. }
  139. else
  140. {
  141. $format = 'ibm:hostname=#HOST#;port=#PORT#;database=#DBNAME#';
  142. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  143. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  144. }
  145. break;
  146. case 'informix':
  147. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1526;
  148. $this->options['protocol'] = (isset($this->options['protocol'])) ? $this->options['protocol'] : 'onsoctcp';
  149. if (!empty($this->options['dsn']))
  150. {
  151. $format = 'informix:DSN=#DSN#';
  152. $replace = array('#DSN#');
  153. $with = array($this->options['dsn']);
  154. }
  155. else
  156. {
  157. $format = 'informix:host=#HOST#;service=#PORT#;database=#DBNAME#;server=#SERVER#;protocol=#PROTOCOL#';
  158. $replace = array('#HOST#', '#PORT#', '#DBNAME#', '#SERVER#', '#PROTOCOL#');
  159. $with = array($this->options['host'], $this->options['port'], $this->options['database'], $this->options['server'], $this->options['protocol']);
  160. }
  161. break;
  162. case 'mssql':
  163. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433;
  164. $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
  165. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  166. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  167. break;
  168. case 'mysql':
  169. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 3306;
  170. $format = 'mysql:host=#HOST#;port=#PORT#;dbname=#DBNAME#;charset=#CHARSET#';
  171. $replace = array('#HOST#', '#PORT#', '#DBNAME#', '#CHARSET#');
  172. $with = array($this->options['host'], $this->options['port'], $this->options['database'], $this->options['charset']);
  173. break;
  174. case 'oci':
  175. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1521;
  176. $this->options['charset'] = (isset($this->options['charset'])) ? $this->options['charset'] : 'AL32UTF8';
  177. if (!empty($this->options['dsn']))
  178. {
  179. $format = 'oci:dbname=#DSN#';
  180. $replace = array('#DSN#');
  181. $with = array($this->options['dsn']);
  182. }
  183. else
  184. {
  185. $format = 'oci:dbname=//#HOST#:#PORT#/#DBNAME#';
  186. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  187. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  188. }
  189. $format .= ';charset=' . $this->options['charset'];
  190. break;
  191. case 'odbc':
  192. $format = 'odbc:DSN=#DSN#;UID:#USER#;PWD=#PASSWORD#';
  193. $replace = array('#DSN#', '#USER#', '#PASSWORD#');
  194. $with = array($this->options['dsn'], $this->options['user'], $this->options['password']);
  195. break;
  196. case 'pgsql':
  197. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 5432;
  198. $format = 'pgsql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
  199. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  200. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  201. break;
  202. case 'sqlite':
  203. if (isset($this->options['version']) && $this->options['version'] == 2)
  204. {
  205. $format = 'sqlite2:#DBNAME#';
  206. }
  207. else
  208. {
  209. $format = 'sqlite:#DBNAME#';
  210. }
  211. $replace = array('#DBNAME#');
  212. $with = array($this->options['database']);
  213. break;
  214. case 'sybase':
  215. $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433;
  216. $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
  217. $replace = array('#HOST#', '#PORT#', '#DBNAME#');
  218. $with = array($this->options['host'], $this->options['port'], $this->options['database']);
  219. break;
  220. default:
  221. throw new \UnexpectedValueException('The ' . $this->options['driver'] . ' driver is not supported.');
  222. }
  223. // Create the connection string:
  224. $connectionString = str_replace($replace, $with, $format);
  225. try
  226. {
  227. $this->connection = new \PDO(
  228. $connectionString,
  229. $this->options['user'],
  230. $this->options['password'],
  231. $this->options['driverOptions']
  232. );
  233. }
  234. catch (\PDOException $e)
  235. {
  236. throw new \RuntimeException('Could not connect to PDO' . ': ' . $e->getMessage(), 2, $e);
  237. }
  238. }
  239. /**
  240. * Disconnects the database.
  241. *
  242. * @return void
  243. *
  244. * @since 1.0
  245. */
  246. public function disconnect()
  247. {
  248. $this->freeResult();
  249. unset($this->connection);
  250. }
  251. /**
  252. * Method to escape a string for usage in an SQL statement.
  253. *
  254. * Oracle escaping reference:
  255. * http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F
  256. *
  257. * SQLite escaping notes:
  258. * http://www.sqlite.org/faq.html#q14
  259. *
  260. * Method body is as implemented by the Zend Framework
  261. *
  262. * Note: Using query objects with bound variables is
  263. * preferable to the below.
  264. *
  265. * @param string $text The string to be escaped.
  266. * @param boolean $extra Unused optional parameter to provide extra escaping.
  267. *
  268. * @return string The escaped string.
  269. *
  270. * @since 1.0
  271. */
  272. public function escape($text, $extra = false)
  273. {
  274. if (is_int($text) || is_float($text))
  275. {
  276. return $text;
  277. }
  278. $text = str_replace("'", "''", $text);
  279. return addcslashes($text, "\000\n\r\\\032");
  280. }
  281. /**
  282. * Execute the SQL statement.
  283. *
  284. * @return mixed A database cursor resource on success, boolean false on failure.
  285. *
  286. * @since 1.0
  287. * @throws \Exception
  288. * @throws \RuntimeException
  289. */
  290. public function execute()
  291. {
  292. $this->connect();
  293. if (!is_object($this->connection))
  294. {
  295. $this->log(
  296. Log\LogLevel::ERROR,
  297. 'Database query failed (error #{code}): {message}',
  298. array('code' => $this->errorNum, 'message' => $this->errorMsg)
  299. );
  300. throw new \RuntimeException($this->errorMsg, $this->errorNum);
  301. }
  302. // Take a local copy so that we don't modify the original query and cause issues later
  303. $sql = $this->replacePrefix((string) $this->sql);
  304. if ($this->limit > 0 || $this->offset > 0)
  305. {
  306. // @TODO
  307. $sql .= ' LIMIT ' . $this->offset . ', ' . $this->limit;
  308. }
  309. // Increment the query counter.
  310. $this->count++;
  311. // If debugging is enabled then let's log the query.
  312. if ($this->debug)
  313. {
  314. // Add the query to the object queue.
  315. $this->log(
  316. Log\LogLevel::DEBUG,
  317. '{sql}',
  318. array('sql' => $sql, 'category' => 'databasequery', 'trace' => debug_backtrace())
  319. );
  320. }
  321. // Reset the error values.
  322. $this->errorNum = 0;
  323. $this->errorMsg = '';
  324. // Execute the query.
  325. $this->executed = false;
  326. if ($this->prepared instanceof \PDOStatement)
  327. {
  328. // Bind the variables:
  329. if ($this->sql instanceof PreparableInterface)
  330. {
  331. $bounded =& $this->sql->getBounded();
  332. foreach ($bounded as $key => $obj)
  333. {
  334. $this->prepared->bindParam($key, $obj->value, $obj->dataType, $obj->length, $obj->driverOptions);
  335. }
  336. }
  337. $this->executed = $this->prepared->execute();
  338. }
  339. // If an error occurred handle it.
  340. if (!$this->executed)
  341. {
  342. // Get the error number and message before we execute any more queries.
  343. $errorNum = (int) $this->connection->errorCode();
  344. $errorMsg = (string) 'SQL: ' . implode(", ", $this->connection->errorInfo());
  345. // Check if the server was disconnected.
  346. if (!$this->connected())
  347. {
  348. try
  349. {
  350. // Attempt to reconnect.
  351. $this->connection = null;
  352. $this->connect();
  353. }
  354. catch (\RuntimeException $e)
  355. // If connect fails, ignore that exception and throw the normal exception.
  356. {
  357. // Get the error number and message.
  358. $this->errorNum = (int) $this->connection->errorCode();
  359. $this->errorMsg = (string) 'SQL: ' . implode(", ", $this->connection->errorInfo());
  360. // Throw the normal query exception.
  361. $this->log(
  362. Log\LogLevel::ERROR,
  363. 'Database query failed (error #{code}): {message}',
  364. array('code' => $this->errorNum, 'message' => $this->errorMsg)
  365. );
  366. throw new \RuntimeException($this->errorMsg, $this->errorNum);
  367. }
  368. // Since we were able to reconnect, run the query again.
  369. return $this->execute();
  370. }
  371. else
  372. // The server was not disconnected.
  373. {
  374. // Get the error number and message from before we tried to reconnect.
  375. $this->errorNum = $errorNum;
  376. $this->errorMsg = $errorMsg;
  377. // Throw the normal query exception.
  378. $this->log(
  379. Log\LogLevel::ERROR,
  380. 'Database query failed (error #{code}): {message}',
  381. array('code' => $this->errorNum, 'message' => $this->errorMsg)
  382. );
  383. throw new \RuntimeException($this->errorMsg, $this->errorNum);
  384. }
  385. }
  386. return $this->prepared;
  387. }
  388. /**
  389. * Retrieve a PDO database connection attribute
  390. * http://www.php.net/manual/en/pdo.getattribute.php
  391. *
  392. * Usage: $db->getOption(PDO::ATTR_CASE);
  393. *
  394. * @param mixed $key One of the PDO::ATTR_* Constants
  395. *
  396. * @return mixed
  397. *
  398. * @since 1.0
  399. */
  400. public function getOption($key)
  401. {
  402. $this->connect();
  403. return $this->connection->getAttribute($key);
  404. }
  405. /**
  406. * Get a query to run and verify the database is operational.
  407. *
  408. * @return string The query to check the health of the DB.
  409. *
  410. * @since 1.0
  411. */
  412. public function getConnectedQuery()
  413. {
  414. return 'SELECT 1';
  415. }
  416. /**
  417. * Sets an attribute on the PDO database handle.
  418. * http://www.php.net/manual/en/pdo.setattribute.php
  419. *
  420. * Usage: $db->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
  421. *
  422. * @param integer $key One of the PDO::ATTR_* Constants
  423. * @param mixed $value One of the associated PDO Constants
  424. * related to the particular attribute
  425. * key.
  426. *
  427. * @return boolean
  428. *
  429. * @since 1.0
  430. */
  431. public function setOption($key, $value)
  432. {
  433. $this->connect();
  434. return $this->connection->setAttribute($key, $value);
  435. }
  436. /**
  437. * Test to see if the PDO extension is available.
  438. * Override as needed to check for specific PDO Drivers.
  439. *
  440. * @return boolean True on success, false otherwise.
  441. *
  442. * @since 1.0
  443. */
  444. public static function isSupported()
  445. {
  446. return defined('\\PDO::ATTR_DRIVER_NAME');
  447. }
  448. /**
  449. * Determines if the connection to the server is active.
  450. *
  451. * @return boolean True if connected to the database engine.
  452. *
  453. * @since 1.0
  454. */
  455. public function connected()
  456. {
  457. // Flag to prevent recursion into this function.
  458. static $checkingConnected = false;
  459. if ($checkingConnected)
  460. {
  461. // Reset this flag and throw an exception.
  462. $checkingConnected = true;
  463. die('Recursion trying to check if connected.');
  464. }
  465. // Backup the query state.
  466. $sql = $this->sql;
  467. $limit = $this->limit;
  468. $offset = $this->offset;
  469. $prepared = $this->prepared;
  470. try
  471. {
  472. // Set the checking connection flag.
  473. $checkingConnected = true;
  474. // Run a simple query to check the connection.
  475. $this->setQuery($this->getConnectedQuery());
  476. $status = (bool) $this->loadResult();
  477. }
  478. catch (\Exception $e)
  479. // If we catch an exception here, we must not be connected.
  480. {
  481. $status = false;
  482. }
  483. // Restore the query state.
  484. $this->sql = $sql;
  485. $this->limit = $limit;
  486. $this->offset = $offset;
  487. $this->prepared = $prepared;
  488. $checkingConnected = false;
  489. return $status;
  490. }
  491. /**
  492. * Get the number of affected rows for the previous executed SQL statement.
  493. * Only applicable for DELETE, INSERT, or UPDATE statements.
  494. *
  495. * @return integer The number of affected rows.
  496. *
  497. * @since 1.0
  498. */
  499. public function getAffectedRows()
  500. {
  501. $this->connect();
  502. if ($this->prepared instanceof \PDOStatement)
  503. {
  504. return $this->prepared->rowCount();
  505. }
  506. else
  507. {
  508. return 0;
  509. }
  510. }
  511. /**
  512. * Get the number of returned rows for the previous executed SQL statement.
  513. *
  514. * @param resource $cursor An optional database cursor resource to extract the row count from.
  515. *
  516. * @return integer The number of returned rows.
  517. *
  518. * @since 1.0
  519. */
  520. public function getNumRows($cursor = null)
  521. {
  522. $this->connect();
  523. if ($cursor instanceof \PDOStatement)
  524. {
  525. return $cursor->rowCount();
  526. }
  527. elseif ($this->prepared instanceof \PDOStatement)
  528. {
  529. return $this->prepared->rowCount();
  530. }
  531. else
  532. {
  533. return 0;
  534. }
  535. }
  536. /**
  537. * Method to get the auto-incremented value from the last INSERT statement.
  538. *
  539. * @return string The value of the auto-increment field from the last inserted row.
  540. *
  541. * @since 1.0
  542. */
  543. public function insertid()
  544. {
  545. $this->connect();
  546. // Error suppress this to prevent PDO warning us that the driver doesn't support this operation.
  547. return @$this->connection->lastInsertId();
  548. }
  549. /**
  550. * Select a database for use.
  551. *
  552. * @param string $database The name of the database to select for use.
  553. *
  554. * @return boolean True if the database was successfully selected.
  555. *
  556. * @since 1.0
  557. * @throws \RuntimeException
  558. */
  559. public function select($database)
  560. {
  561. $this->connect();
  562. return true;
  563. }
  564. /**
  565. * Sets the SQL statement string for later execution.
  566. *
  567. * @param mixed $query The SQL statement to set either as a JDatabaseQuery object or a string.
  568. * @param integer $offset The affected row offset to set.
  569. * @param integer $limit The maximum affected rows to set.
  570. * @param array $driverOptions The optional PDO driver options
  571. *
  572. * @return PdoDriver This object to support method chaining.
  573. *
  574. * @since 1.0
  575. */
  576. public function setQuery($query, $offset = null, $limit = null, $driverOptions = array())
  577. {
  578. $this->connect();
  579. $this->freeResult();
  580. if (is_string($query))
  581. {
  582. // Allows taking advantage of bound variables in a direct query:
  583. $query = $this->getQuery(true)->setQuery($query);
  584. }
  585. if ($query instanceof LimitableInterface && !is_null($offset) && !is_null($limit))
  586. {
  587. $query->setLimit($limit, $offset);
  588. }
  589. $sql = $this->replacePrefix((string) $query);
  590. $this->prepared = $this->connection->prepare($sql, $driverOptions);
  591. // Store reference to the DatabaseQuery instance:
  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 1.0
  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 1.0
  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 1.0
  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 1.0
  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 1.0
  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 1.0
  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 1.0
  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 1.0
  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 array.
  749. *
  750. * @return mixed The result of the query as an array, false if there are no more rows.
  751. *
  752. * @since 1.0
  753. * @throws \RuntimeException
  754. */
  755. public function loadNextAssoc()
  756. {
  757. $this->connect();
  758. // Execute the query and get the result set cursor.
  759. if (!$this->executed)
  760. {
  761. if (!($this->execute()))
  762. {
  763. return $this->errorNum ? null : false;
  764. }
  765. }
  766. // Get the next row from the result set as an object of type $class.
  767. $row = $this->fetchAssoc();
  768. if ($row)
  769. {
  770. return $row;
  771. }
  772. // Free up system resources and return.
  773. $this->freeResult();
  774. return false;
  775. }
  776. /**
  777. * PDO does not support serialize
  778. *
  779. * @return array
  780. *
  781. * @since 1.0
  782. */
  783. public function __sleep()
  784. {
  785. $serializedProperties = array();
  786. $reflect = new \ReflectionClass($this);
  787. // Get properties of the current class
  788. $properties = $reflect->getProperties();
  789. foreach ($properties as $property)
  790. {
  791. // Do not serialize properties that are PDO
  792. if ($property->isStatic() == false && !($this->{$property->name} instanceof \PDO))
  793. {
  794. array_push($serializedProperties, $property->name);
  795. }
  796. }
  797. return $serializedProperties;
  798. }
  799. /**
  800. * Wake up after serialization
  801. *
  802. * @return array
  803. *
  804. * @since 1.0
  805. */
  806. public function __wakeup()
  807. {
  808. // Get connection back
  809. $this->__construct($this->options);
  810. }
  811. }