PageRenderTime 47ms CodeModel.GetById 2ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 0ms

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