diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php index 5b16086..2387bd9 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; } @@ -319,7 +332,7 @@ public function prepareQuery($query) { $query = $this->prefixTables($query); // Call PDO::prepare. - return parent::prepare($query); + return $this->connection->prepare($query); } /** @@ -532,7 +545,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 +934,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 +962,7 @@ public function pushTransaction($name) { $this->query('SAVEPOINT ' . $name); } else { - parent::beginTransaction(); + $this->connection->beginTransaction(); } $this->transactionLayers[$name] = $name; } @@ -1000,7 +1013,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 +1097,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); } /** diff --git a/core/lib/Drupal/Core/Database/Database.php b/core/lib/Drupal/Core/Database/Database.php index a54c9cb..5ae4baf 100644 --- a/core/lib/Drupal/Core/Database/Database.php +++ b/core/lib/Drupal/Core/Database/Database.php @@ -381,7 +381,10 @@ 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]); + + $connection_info =& self::$databaseInfo[$key][$target]; + $pdo_connection = $driver_class::open($connection_info); + $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/pgsql/Connection.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php index 4fd7d0d..3de55f6 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,19 @@ 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 +79,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 +97,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 +135,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..6e0ebf6 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) { @@ -95,7 +87,6 @@ public function __construct(array $connection_options = array()) { $this->attachedDatabases[$prefix] = $prefix; $this->query('ATTACH DATABASE :database AS :prefix', array(':database' => $connection_options['database'] . '-' . $prefix, ':prefix' => $prefix)); } - // Add a ., so queries become prefix.table, which is proper syntax for // querying an attached database. $prefix .= '.'; @@ -107,24 +98,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 +256,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()) { @@ -326,7 +335,7 @@ public function nextId($existing_id = 0) { public function rollback($savepoint_name = 'drupal_transaction') { if ($this->savepointSupport) { - return parent::rollBack($savepoint_name); + return $this->connection->rollBack($savepoint_name); } if (!$this->inTransaction()) { @@ -354,13 +363,13 @@ public function rollback($savepoint_name = 'drupal_transaction') { } } if ($this->supportsTransactions()) { - PDO::rollBack(); + $this->connection->rollBack(); } } public function pushTransaction($name) { if ($this->savepointSupport) { - return parent::pushTransaction($name); + return $this->connection->pushTransaction($name); } if (!$this->supportsTransactions()) { return; @@ -369,14 +378,14 @@ public function pushTransaction($name) { throw new TransactionNameNonUniqueException($name . " is already in use."); } if (!$this->inTransaction()) { - PDO::beginTransaction(); + $this->connection->beginTransaction(); } $this->transactionLayers[$name] = $name; } public function popTransaction($name) { if ($this->savepointSupport) { - return parent::popTransaction($name); + return $this->connection->popTransaction($name); } if (!$this->supportsTransactions()) { return; @@ -394,9 +403,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(); } }