diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php index 5b16086..70cb296 100644 --- a/core/lib/Drupal/Core/Database/Connection.php +++ b/core/lib/Drupal/Core/Database/Connection.php @@ -23,7 +23,7 @@ * * @see http://php.net/manual/book.pdo.php */ -abstract class Connection extends PDO { +abstract class Connection { /** * The database target this connection is for. @@ -135,20 +135,33 @@ */ protected $prefixReplace = array(); - function __construct($dsn, $username, $password, $driver_options = array()) { + /** + * The actual PDO connection. + * + * @var \PDO + */ + protected $connection; + + /** + * Constructs a Connection object. + */ + public function __construct(PDO $connection, array $connection_options) { // Initialize and prepare the connection prefix. $this->setPrefix(isset($this->connectionOptions['prefix']) ? $this->connectionOptions['prefix'] : ''); - // Because the other methods don't seem to work right. - $driver_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION; - - // Call PDO::__construct and PDO::setAttribute. - parent::__construct($dsn, $username, $password, $driver_options); - // Set a Statement class, unless the driver opted out. if (!empty($this->statementClass)) { - $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array($this->statementClass, array($this))); + $connection->setAttribute(PDO::ATTR_STATEMENT_CLASS, array($this->statementClass, array($this))); } + + $this->connection = $connection; + } + + /** + * Opens a PDO connection. + */ + public static function open(array &$connection_options = array()) { + // @TODO WHy is there no abstract for static methods. } /** @@ -163,7 +176,7 @@ public function destroy() { // Destroy all references to this connection by setting them to NULL. // The Statement class attribute only accepts a new value that presents a // proper callable, so we reset it to PDOStatement. - $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDOStatement', array())); + $this->connection->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDOStatement', array())); $this->schema = NULL; } @@ -318,8 +331,7 @@ public function tablePrefix($table = 'default') { public function prepareQuery($query) { $query = $this->prefixTables($query); - // Call PDO::prepare. - return parent::prepare($query); + return $this->connection->prepare($query); } /** @@ -532,7 +544,7 @@ public function query($query, array $args = array(), $options = array()) { case Database::RETURN_AFFECTED: return $stmt->rowCount(); case Database::RETURN_INSERT_ID: - return $this->lastInsertId(); + return $this->connection->lastInsertId(); case Database::RETURN_NULL: return; default: @@ -921,7 +933,7 @@ public function rollback($savepoint_name = 'drupal_transaction') { $rolled_back_other_active_savepoints = TRUE; } } - parent::rollBack(); + $this->connection->rollBack(); if ($rolled_back_other_active_savepoints) { throw new TransactionOutOfOrderException(); } @@ -949,7 +961,7 @@ public function pushTransaction($name) { $this->query('SAVEPOINT ' . $name); } else { - parent::beginTransaction(); + $this->connection->beginTransaction(); } $this->transactionLayers[$name] = $name; } @@ -1000,7 +1012,7 @@ protected function popCommittableTransactions() { // If there are no more layers left then we should commit. unset($this->transactionLayers[$name]); if (empty($this->transactionLayers)) { - if (!parent::commit()) { + if (!$this->connection->commit()) { throw new TransactionCommitFailedException(); } } @@ -1084,7 +1096,7 @@ protected function generateTemporaryTableName() { * Returns the version of the database server. */ public function version() { - return $this->getAttribute(PDO::ATTR_SERVER_VERSION); + return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); } /** @@ -1178,4 +1190,24 @@ public function commit() { * also larger than the $existing_id if one was passed in. */ abstract public function nextId($existing_id = 0); + + /** + * Quotes a string for use in a query. + * + * @param string $string + * The string to be quoted. + * @param int $parameter_type + * Provides a data type hint for drivers that have alternate quoting styles. + * + * @return string|bool + * A quoted string that is theoretically safe to pass into an SQL statement. + * Returns FALSE if the driver does not support quoting in this way. + * + * @see \PDO::quote() + */ + public function quote($string, $parameter_type = \PDO::PARAM_STR) { + return $this->connection->quote($string, $parameter_type); + } + } + diff --git a/core/lib/Drupal/Core/Database/Database.php b/core/lib/Drupal/Core/Database/Database.php index a54c9cb..d6c28ca 100644 --- a/core/lib/Drupal/Core/Database/Database.php +++ b/core/lib/Drupal/Core/Database/Database.php @@ -381,7 +381,9 @@ public static function addConnectionInfo($key, $target, $info) { // Fallback for Drupal 7 settings.php. $driver_class = "Drupal\\Core\\Database\\Driver\\{$driver}\\Connection"; } - $new_connection = new $driver_class(self::$databaseInfo[$key][$target]); + + $pdo_connection = $driver_class::open(self::$databaseInfo[$key][$target]); + $new_connection = new $driver_class($pdo_connection, self::$databaseInfo[$key][$target]); $new_connection->setTarget($target); $new_connection->setKey($key); diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php index 128780f..06cce2c 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php @@ -36,7 +36,12 @@ class Connection extends DatabaseConnection { */ protected $needsCleanup = FALSE; - public function __construct(array $connection_options = array()) { + /** + * Constructs a Connection object. + */ + public function __construct(PDO $connection, array $connection_options = array()) { + parent::__construct($connection, $connection_options); + // This driver defaults to transaction support, except if explicitly passed FALSE. $this->transactionSupport = !isset($connection_options['transactions']) || ($connection_options['transactions'] !== FALSE); @@ -44,7 +49,12 @@ public function __construct(array $connection_options = array()) { $this->transactionalDDLSupport = FALSE; $this->connectionOptions = $connection_options; + } + /** + * Overrides Connection::open(). + */ + public static function open(array &$connection_options = array()) { // The DSN should use either a socket or a host/port. if (isset($connection_options['unix_socket'])) { $dsn = 'mysql:unix_socket=' . $connection_options['unix_socket']; @@ -67,16 +77,16 @@ public function __construct(array $connection_options = array()) { PDO::ATTR_EMULATE_PREPARES => TRUE, ); - parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']); + $pdo = new PDO($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']); // Force MySQL to use the UTF-8 character set. Also set the collation, if a // certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci' // for UTF-8. if (!empty($connection_options['collation'])) { - $this->exec('SET NAMES utf8 COLLATE ' . $connection_options['collation']); + $pdo->exec('SET NAMES utf8 COLLATE ' . $connection_options['collation']); } else { - $this->exec('SET NAMES utf8'); + $pdo->exec('SET NAMES utf8'); } // Set MySQL init_commands if not already defined. Default Drupal's MySQL @@ -94,7 +104,9 @@ public function __construct(array $connection_options = array()) { 'sql_mode' => "SET sql_mode = 'ANSI,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'", ); // Set connection options. - $this->exec(implode('; ', $connection_options['init_commands'])); + $pdo->exec(implode('; ', $connection_options['init_commands'])); + + return $pdo; } public function __destruct() { @@ -135,8 +147,8 @@ public function createDatabase($database) { try { // Create the database and set it as active. - $this->exec("CREATE DATABASE $database"); - $this->exec("USE $database"); + $this->connection->exec("CREATE DATABASE $database"); + $this->connection->exec("USE $database"); } catch (\Exception $e) { throw new DatabaseNotFoundException($e->getMessage()); @@ -204,7 +216,7 @@ protected function popCommittableTransactions() { // If there are no more layers left then we should commit. unset($this->transactionLayers[$name]); if (empty($this->transactionLayers)) { - if (!PDO::commit()) { + if (!$this->connection->commit()) { throw new TransactionCommitFailedException(); } } @@ -227,7 +239,7 @@ protected function popCommittableTransactions() { $this->transactionLayers = array(); // We also have to explain to PDO that the transaction stack has // been cleaned-up. - PDO::commit(); + $this->connection->commit(); } else { throw $e; diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php index 73785fd..4bf29e4 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php @@ -513,8 +513,7 @@ public function tableExists($table) { // @todo This override should be removed once we require a version of MySQL // that has that bug fixed. try { - $this->connection->queryRange("SELECT 1 FROM {" . $table . "}", 0, 1); - return TRUE; + return (bool) $this->connection->queryRange("SELECT 1 FROM {" . $table . "}", 0, 1)->fetchField(); } catch (Exception $e) { return FALSE; diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php index 4fd7d0d..c1d211b 100644 --- a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php @@ -34,7 +34,12 @@ class Connection extends DatabaseConnection { */ const DATABASE_NOT_FOUND = 7; - public function __construct(array $connection_options = array()) { + /** + * Constructs a connection object. + */ + public function __construct(PDO $connection, array $connection_options) { + parent::__construct($connection, $connection_options); + // This driver defaults to transaction support, except if explicitly passed FALSE. $this->transactionSupport = !isset($connection_options['transactions']) || ($connection_options['transactions'] !== FALSE); @@ -42,6 +47,18 @@ public function __construct(array $connection_options = array()) { // but we'll only enable it if standard transactions are. $this->transactionalDDLSupport = $this->transactionSupport; + $this->connectionOptions = $connection_options; + + // Force PostgreSQL to use the UTF-8 character set by default. + $this->connection->exec("SET NAMES 'UTF8'"); + + // Execute PostgreSQL init_commands. + if (isset($connection_options['init_commands'])) { + $this->connection->exec(implode('; ', $connection_options['init_commands'])); + } + } + + public static function open(array &$connection_options = array()) { // Default to TCP connection on port 5432. if (empty($connection_options['port'])) { $connection_options['port'] = 5432; @@ -61,8 +78,6 @@ public function __construct(array $connection_options = array()) { $connection_options['password'] = str_replace('\\', '\\\\', $connection_options['password']); } - $this->connectionOptions = $connection_options; - $connection_options['database'] = (!empty($connection_options['database']) ? $connection_options['database'] : 'template1'); $dsn = 'pgsql:host=' . $connection_options['host'] . ' dbname=' . $connection_options['database'] . ' port=' . $connection_options['port']; @@ -81,17 +96,12 @@ public function __construct(array $connection_options = array()) { // Convert numeric values to strings when fetching. PDO::ATTR_STRINGIFY_FETCHES => TRUE, ); - parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']); - - // Force PostgreSQL to use the UTF-8 character set by default. - $this->exec("SET NAMES 'UTF8'"); + $pdo = new PDO($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']); - // Execute PostgreSQL init_commands. - if (isset($connection_options['init_commands'])) { - $this->exec(implode('; ', $connection_options['init_commands'])); - } + return $pdo; } + public function query($query, array $args = array(), $options = array()) { $options += $this->defaultOptions(); @@ -124,7 +134,7 @@ public function query($query, array $args = array(), $options = array()) { case Database::RETURN_AFFECTED: return $stmt->rowCount(); case Database::RETURN_INSERT_ID: - return $this->lastInsertId($options['sequence_name']); + return $this->connection->lastInsertId($options['sequence_name']); case Database::RETURN_NULL: return; default: diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php index abda556..9dfa753 100644 --- a/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php @@ -66,7 +66,9 @@ class Connection extends DatabaseConnection { */ var $tableDropped = FALSE; - public function __construct(array $connection_options = array()) { + public function __construct(PDO $connection, array $connection_options) { + parent::__construct($connection, $connection_options); + // We don't need a specific PDOStatement class here, we simulate it below. $this->statementClass = NULL; @@ -75,16 +77,6 @@ public function __construct(array $connection_options = array()) { $this->connectionOptions = $connection_options; - // Allow PDO options to be overridden. - $connection_options += array( - 'pdo' => array(), - ); - $connection_options['pdo'] += array( - // Convert numeric values to strings when fetching. - PDO::ATTR_STRINGIFY_FETCHES => TRUE, - ); - parent::__construct('sqlite:' . $connection_options['database'], '', '', $connection_options['pdo']); - // Attach one database for each registered prefix. $prefixes = $this->prefixes; foreach ($prefixes as $table => &$prefix) { @@ -107,24 +99,42 @@ public function __construct(array $connection_options = array()) { // Detect support for SAVEPOINT. $version = $this->query('SELECT sqlite_version()')->fetchField(); $this->savepointSupport = (version_compare($version, '3.6.8') >= 0); + } + + /** + * Overrides Connection::open(). + */ + public static function open(array &$connection_options = array()) { + // Allow PDO options to be overridden. + $connection_options += array( + 'pdo' => array(), + ); + $connection_options['pdo'] += array( + // Convert numeric values to strings when fetching. + PDO::ATTR_STRINGIFY_FETCHES => TRUE, + ); + $pdo = new PDO('sqlite:' . $connection_options['database'], '', '', $connection_options['pdo']); // Create functions needed by SQLite. - $this->sqliteCreateFunction('if', array($this, 'sqlFunctionIf')); - $this->sqliteCreateFunction('greatest', array($this, 'sqlFunctionGreatest')); - $this->sqliteCreateFunction('pow', 'pow', 2); - $this->sqliteCreateFunction('length', 'strlen', 1); - $this->sqliteCreateFunction('md5', 'md5', 1); - $this->sqliteCreateFunction('concat', array($this, 'sqlFunctionConcat')); - $this->sqliteCreateFunction('substring', array($this, 'sqlFunctionSubstring'), 3); - $this->sqliteCreateFunction('substring_index', array($this, 'sqlFunctionSubstringIndex'), 3); - $this->sqliteCreateFunction('rand', array($this, 'sqlFunctionRand')); + $pdo->sqliteCreateFunction('if', array($this, 'sqlFunctionIf')); + $pdo->sqliteCreateFunction('greatest', array($this, 'sqlFunctionGreatest')); + $pdo->sqliteCreateFunction('pow', 'pow', 2); + $pdo->sqliteCreateFunction('length', 'strlen', 1); + $pdo->sqliteCreateFunction('md5', 'md5', 1); + $pdo->sqliteCreateFunction('concat', array($this, 'sqlFunctionConcat')); + $pdo->sqliteCreateFunction('substring', array($this, 'sqlFunctionSubstring'), 3); + $pdo->sqliteCreateFunction('substring_index', array($this, 'sqlFunctionSubstringIndex'), 3); + $pdo->sqliteCreateFunction('rand', array($this, 'sqlFunctionRand')); // Execute sqlite init_commands. if (isset($connection_options['init_commands'])) { - $this->exec(implode('; ', $connection_options['init_commands'])); + $pdo->exec(implode('; ', $connection_options['init_commands'])); } + + return $pdo; } + /** * Destructor for the SQLite connection. * @@ -247,7 +257,7 @@ public function prepare($query, $options = array()) { * the world. */ public function PDOPrepare($query, array $options = array()) { - return parent::prepare($query, $options); + return $this->connection->prepare($query, $options); } public function queryRange($query, $from, $count, array $args = array(), array $options = array()) { @@ -354,7 +364,7 @@ public function rollback($savepoint_name = 'drupal_transaction') { } } if ($this->supportsTransactions()) { - PDO::rollBack(); + $this->connection->rollBack(); } } @@ -394,9 +404,9 @@ public function popTransaction($name) { // If there was any rollback() we should roll back whole transaction. if ($this->willRollback) { $this->willRollback = FALSE; - PDO::rollBack(); + $this->connection->rollBack(); } - elseif (!PDO::commit()) { + elseif (!$this->connection->commit()) { throw new TransactionCommitFailedException(); } }