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