From 15905fd282b7c144a7526256fda23a09d93b750b Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sun, 5 May 2013 00:00:37 -0400 Subject: [PATCH] Shift connection logic into the drivers. This allows classes that only access the driver (like schema platforms) to have access to lazy connection features. I think this also makes the drivers better encapsulate their behavior. Before some of the connected/connection logic was in the connection class which I think will slowly become the datasource class, or a wrapper for the various SQL based datasources at the very least. --- lib/Cake/Database/Connection.php | 42 ++--------- lib/Cake/Database/Driver.php | 69 +++++++++++++++---- lib/Cake/Database/Driver/Mysql.php | 8 ++- lib/Cake/Database/Driver/PDODriverTrait.php | 28 ++++++-- lib/Cake/Database/Driver/Postgres.php | 19 ++--- lib/Cake/Database/Driver/Sqlite.php | 14 ++-- .../TestCase/Database/Driver/MysqlTest.php | 9 ++- .../TestCase/Database/Driver/PostgresTest.php | 12 +++- .../TestCase/Database/Driver/SqliteTest.php | 6 +- 9 files changed, 128 insertions(+), 79 deletions(-) diff --git a/lib/Cake/Database/Connection.php b/lib/Cake/Database/Connection.php index 38d3850a12c..cff91794010 100644 --- a/lib/Cake/Database/Connection.php +++ b/lib/Cake/Database/Connection.php @@ -46,13 +46,6 @@ class Connection { */ protected $_driver; -/** - * Whether connection was established or not - * - * @var boolean - */ - protected $_connected = false; - /** * Contains how many nested transactions have been started * @@ -103,7 +96,7 @@ public function __construct($config) { throw new MissingDriverException(['driver' => $config['datasource']]); } - $this->driver($config['datasource']); + $this->driver($config['datasource'], $config); if (!$this->_driver->enabled()) { throw new MissingExtensionException(['driver' => get_class($this->_driver)]); } @@ -121,9 +114,6 @@ public function __construct($config) { * @return void */ public function __destruct() { - if ($this->_connected) { - $this->_driver->disconnect(); - } unset($this->_driver); } @@ -143,14 +133,15 @@ public function config() { * If no params are passed it will return the current driver instance * * @param string|Driver $driver + * @param array|null $config Either config for a new driver or null. * @return Driver */ - public function driver($driver = null) { + public function driver($driver = null, $config = null) { if ($driver === null) { return $this->_driver; } if (is_string($driver)) { - $driver = new $driver; + $driver = new $driver($config); } return $this->_driver = $driver; } @@ -162,11 +153,9 @@ public function driver($driver = null) { * @return boolean true on success or false if already connected. */ public function connect() { - if ($this->_connected) { - return false; - } try { - return $this->_connected = $this->_driver->connect($this->_config); + $this->_driver->connect(); + return true; } catch(\Exception $e) { throw new MissingConnectionException(['reason' => $e->getMessage()]); } @@ -179,7 +168,6 @@ public function connect() { */ public function disconnect() { $this->_driver->disconnect(); - $this->_connected = false; } /** @@ -188,7 +176,7 @@ public function disconnect() { * @return boolean */ public function isConnected() { - return $this->_connected; + return $this->_driver->isConnected(); } /** @@ -198,7 +186,6 @@ public function isConnected() { * @return \Cake\Database\Statement */ public function prepare($sql) { - $this->connect(); $statement = $this->_driver->prepare($sql); if ($this->_logQueries) { @@ -218,7 +205,6 @@ public function prepare($sql) { * @return \Cake\Database\Statement executed statement */ public function execute($query, array $params = [], array $types = []) { - $this->connect(); if ($params) { $statement = $this->prepare($query); $statement->bind($params, $types); @@ -236,7 +222,6 @@ public function execute($query, array $params = [], array $types = []) { * @return \Cake\Database\Statement */ public function query($sql) { - $this->connect(); $statement = $this->prepare($sql); $statement->execute(); return $statement; @@ -260,8 +245,6 @@ public function newQuery() { * @return \Cake\Database\Statement */ public function insert($table, array $data, array $types = []) { - $this->connect(); - $columns = array_keys($data); return $this->newQuery()->insert($table, $columns, $types) ->values($data) @@ -278,7 +261,6 @@ public function insert($table, array $data, array $types = []) { * @return \Cake\Database\Statement */ public function update($table, array $data, array $conditions = [], $types = []) { - $this->connect(); $columns = array_keys($data); return $this->newQuery()->update($table) @@ -296,7 +278,6 @@ public function update($table, array $data, array $conditions = [], $types = []) * @return \Cake\Database\Statement */ public function delete($table, $conditions = [], $types = []) { - $this->connect(); return $this->newQuery()->delete($table) ->where($conditions, $types) ->execute(); @@ -308,7 +289,6 @@ public function delete($table, $conditions = [], $types = []) { * @return void */ public function begin() { - $this->connect(); if (!$this->_transactionStarted) { if ($this->_logQueries) { $this->log('BEGIN'); @@ -334,7 +314,6 @@ public function commit() { if (!$this->_transactionStarted) { return false; } - $this->connect(); if ($this->_transactionLevel === 0) { $this->_transactionStarted = false; @@ -360,7 +339,6 @@ public function rollback() { if (!$this->_transactionStarted) { return false; } - $this->connect(); $useSavePoint = $this->useSavePoints(); if ($this->_transactionLevel === 0 || !$useSavePoint) { @@ -414,7 +392,6 @@ public function useSavePoints($enable = null) { * @return void */ public function createSavePoint($name) { - $this->connect(); $this->execute($this->_driver->savePointSQL($name)); } @@ -425,7 +402,6 @@ public function createSavePoint($name) { * @return void */ public function releaseSavePoint($name) { - $this->connect(); $this->execute($this->_driver->releaseSavePointSQL($name)); } @@ -436,7 +412,6 @@ public function releaseSavePoint($name) { * @return void */ public function rollbackSavepoint($name) { - $this->connect(); $this->execute($this->_driver->rollbackSavePointSQL($name)); } @@ -448,7 +423,6 @@ public function rollbackSavepoint($name) { * @return mixed quoted value */ public function quote($value, $type = null) { - $this->connect(); list($value, $type) = $this->cast($value, $type); return $this->_driver->quote($value, $type); } @@ -459,7 +433,6 @@ public function quote($value, $type = null) { * @return boolean */ public function supportsQuoting() { - $this->connect(); return $this->_driver->supportsQuoting(); } @@ -481,7 +454,6 @@ public function quoteIdentifier($identifier) { * @return string|integer */ public function lastInsertId($table) { - $this->connect(); return $this->_driver->lastInsertId($table); } diff --git a/lib/Cake/Database/Driver.php b/lib/Cake/Database/Driver.php index 66f1e120897..42d7fed5066 100644 --- a/lib/Cake/Database/Driver.php +++ b/lib/Cake/Database/Driver.php @@ -23,22 +23,47 @@ * Represents a database diver containing all specificities for * a database engine including its SQL dialect * - **/ + */ abstract class Driver { +/** + * Configuration data. + * + * @var array + */ + protected $_config; + +/** + * Base configuration that is merged into the user + * supplied configuration data. + * + * @var array + */ + protected $_baseConfig = []; + +/** + * Constructor + * + * @param array $config The configuration for the driver. + * @return void + */ + public function __construct($config = []) { + $config += $this->_baseConfig; + $this->_config = $config; + } + /** * Establishes a connection to the database server * - * @param array $config configuration to be used for creating connection * @return boolean true con success - **/ - public abstract function connect(array $config); + */ + public abstract function connect(); /** * Disconnects from database server * * @return void - **/ + */ public abstract function disconnect(); /** @@ -46,14 +71,14 @@ public abstract function disconnect(); * If first argument is passed, * * @return void - **/ + */ public abstract function connection($connection = null); /** * Returns whether php is able to use this driver for connecting to database * * @return boolean true if it is valid to use this driver - **/ + */ public abstract function enabled(); /** @@ -61,28 +86,28 @@ public abstract function enabled(); * * @param string $sql * @return Cake\Database\Statement - **/ + */ public abstract function prepare($sql); /** * Starts a transaction * * @return boolean true on success, false otherwise - **/ + */ public abstract function beginTransaction(); /** * Commits a transaction * * @return boolean true on success, false otherwise - **/ + */ public abstract function commitTransaction(); /** * Rollsback a transaction * * @return boolean true on success, false otherwise - **/ + */ public abstract function rollbackTransaction(); /** @@ -105,7 +130,7 @@ public abstract function quote($value, $type); * Checks if the driver supports quoting * * @return boolean - **/ + */ public function supportsQuoting() { return true; } @@ -115,9 +140,27 @@ public function supportsQuoting() { * * @param string $table table name or sequence to get last insert value from * @return string|integer - **/ + */ public function lastInsertId($table = null) { return $this->_connection->lastInsertId($table); } +/** + * Check whether or not the driver is connected. + * + * @return boolean + */ + public function isConnected() { + return $this->_connection !== null; + } + +/** + * Destructor + * + * @return void + */ + public function __destruct() { + $this->_connection = null; + } + } diff --git a/lib/Cake/Database/Driver/Mysql.php b/lib/Cake/Database/Driver/Mysql.php index 35941f27fe1..77941bf799a 100644 --- a/lib/Cake/Database/Driver/Mysql.php +++ b/lib/Cake/Database/Driver/Mysql.php @@ -46,11 +46,13 @@ class Mysql extends \Cake\Database\Driver { /** * Establishes a connection to the database server * - * @param array $config configuration to be used for creating connection * @return boolean true on success */ - public function connect(array $config) { - $config += $this->_baseConfig; + public function connect() { + if ($this->_connection) { + return true; + } + $config = $this->_config; if ($config['timezone'] === 'UTC') { $config['timezone'] = '+0:00'; diff --git a/lib/Cake/Database/Driver/PDODriverTrait.php b/lib/Cake/Database/Driver/PDODriverTrait.php index 368f05f434e..3d15126991a 100644 --- a/lib/Cake/Database/Driver/PDODriverTrait.php +++ b/lib/Cake/Database/Driver/PDODriverTrait.php @@ -22,6 +22,13 @@ trait PDODriverTrait { +/** + * Instance of PDO. + * + * @var \PDO + */ + protected $_connection; + /** * Establishes a connection to the databse server * @@ -69,7 +76,8 @@ public function disconnect() { * @return Cake\Database\Statement */ public function prepare($sql) { - $statement = $this->connection()->prepare($sql); + $this->connect(); + $statement = $this->_connection->prepare($sql); return new PDOStatement($statement, $this); } @@ -79,7 +87,8 @@ public function prepare($sql) { * @return boolean true on success, false otherwise */ public function beginTransaction() { - return $this->connection()->beginTransaction(); + $this->connect(); + return $this->_connection->beginTransaction(); } /** @@ -88,7 +97,8 @@ public function beginTransaction() { * @return boolean true on success, false otherwise */ public function commitTransaction() { - return $this->connection()->commit(); + $this->connect(); + return $this->_connection->commit(); } /** @@ -97,7 +107,8 @@ public function commitTransaction() { * @return boolean true on success, false otherwise */ public function rollbackTransaction() { - return $this->connection()->rollback(); + $this->connect(); + return $this->_connection->rollback(); } /** @@ -106,7 +117,8 @@ public function rollbackTransaction() { * @return string */ public function quote($value, $type) { - return $this->connection()->quote($value, $type); + $this->connect(); + return $this->_connection->quote($value, $type); } /** @@ -116,7 +128,8 @@ public function quote($value, $type) { * @return string|integer */ public function lastInsertId($table = null) { - return $this->connection()->lastInsertId(); + $this->connect(); + return $this->_connection->lastInsertId(); } /** @@ -125,7 +138,8 @@ public function lastInsertId($table = null) { * @return boolean */ public function supportsQuoting() { - return $this->connection()->getAttribute(PDO::ATTR_DRIVER_NAME) !== 'odbc'; + $this->connect(); + return $this->_connection->getAttribute(PDO::ATTR_DRIVER_NAME) !== 'odbc'; } } diff --git a/lib/Cake/Database/Driver/Postgres.php b/lib/Cake/Database/Driver/Postgres.php index 200977faddb..bb18d4401fc 100644 --- a/lib/Cake/Database/Driver/Postgres.php +++ b/lib/Cake/Database/Driver/Postgres.php @@ -49,11 +49,13 @@ class Postgres extends \Cake\Database\Driver { /** * Establishes a connection to the databse server * - * @param array $config configuration to be used for creating connection * @return boolean true on success */ - public function connect(array $config) { - $config += $this->_baseConfig; + public function connect() { + if ($this->_connection) { + return true; + } + $config = $this->_config; $config['flags'] += [ PDO::ATTR_PERSISTENT => $config['persistent'], PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION @@ -64,7 +66,7 @@ public function connect(array $config) { } $this->_connect($config); - $connection = $this->connection(); + $this->_connection = $connection = $this->connection(); if (!empty($config['encoding'])) { $this->setEncoding($config['encoding']); } @@ -88,7 +90,6 @@ public function connect(array $config) { * * @return boolean true if it is valid to use this driver */ - public function enabled() { return in_array('pgsql', PDO::getAvailableDrivers()); } @@ -99,8 +100,8 @@ public function enabled() { * @return void */ public function setEncoding($encoding) { - $connection = $this->connection(); - $connection->exec('SET NAMES ' . $connection->quote($encoding)); + $this->connect(); + $this->_connection->exec('SET NAMES ' . $this->_connection->quote($encoding)); } /** @@ -110,8 +111,8 @@ public function setEncoding($encoding) { * @return void */ public function setSchema($schema) { - $connection = $this->connection(); - $connection->exec('SET search_path TO ' . $connection->quote($schema)); + $this->connect(); + $this->_connection->exec('SET search_path TO ' . $this->_connection->quote($schema)); } } diff --git a/lib/Cake/Database/Driver/Sqlite.php b/lib/Cake/Database/Driver/Sqlite.php index e7ea8addf9e..522751af5da 100644 --- a/lib/Cake/Database/Driver/Sqlite.php +++ b/lib/Cake/Database/Driver/Sqlite.php @@ -33,6 +33,8 @@ class Sqlite extends \Cake\Database\Driver { */ protected $_baseConfig = [ 'persistent' => false, + 'login' => null, + 'password' => null, 'database' => ':memory:', 'encoding' => 'utf8', 'flags' => [], @@ -43,11 +45,13 @@ class Sqlite extends \Cake\Database\Driver { /** * Establishes a connection to the databse server * - * @param array $config configuration to be used for creating connection * @return boolean true on success */ - public function connect(array $config) { - $config += $this->_baseConfig + ['login' => null, 'password' => null]; + public function connect() { + if ($this->_connection) { + return true; + } + $config = $this->_config; $config['flags'] += [ PDO::ATTR_PERSISTENT => $config['persistent'], PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION @@ -64,7 +68,6 @@ public function connect(array $config) { $this->connection()->exec($command); } } - return true; } @@ -85,7 +88,8 @@ public function enabled() { * @return Cake\Database\Statement */ public function prepare($sql) { - $statement = $this->connection()->prepare($sql); + $this->connect(); + $statement = $this->_connection->prepare($sql); return new SqliteStatement(new PDOStatement($statement, $this), $this); } diff --git a/lib/Cake/Test/TestCase/Database/Driver/MysqlTest.php b/lib/Cake/Test/TestCase/Database/Driver/MysqlTest.php index 68b5b3f7570..8e8ed14c37b 100644 --- a/lib/Cake/Test/TestCase/Database/Driver/MysqlTest.php +++ b/lib/Cake/Test/TestCase/Database/Driver/MysqlTest.php @@ -77,7 +77,6 @@ public function testConnectionConfigDefault() { * @return void */ public function testConnectionConfigCustom() { - $driver = $this->getMock('Cake\Database\Driver\Mysql', ['_connect']); $config = [ 'persistent' => false, 'host' => 'foo', @@ -90,7 +89,11 @@ public function testConnectionConfigCustom() { 'timezone' => 'Antartica', 'init' => ['Execute this', 'this too'] ]; - + $driver = $this->getMock( + 'Cake\Database\Driver\Mysql', + ['_connect'], + [$config] + ); $expected = $config; $expected['dsn'] = 'mysql:host=foo;port=3440;dbname=bar;charset=a-language'; $expected['init'][] = "SET time_zone = 'Antartica'"; @@ -102,7 +105,7 @@ public function testConnectionConfigCustom() { ]; $driver->expects($this->once())->method('_connect') ->with($expected); - $driver->connect($config); + $driver->connect(); } } diff --git a/lib/Cake/Test/TestCase/Database/Driver/PostgresTest.php b/lib/Cake/Test/TestCase/Database/Driver/PostgresTest.php index 04059f45f38..afa31f5f7f7 100644 --- a/lib/Cake/Test/TestCase/Database/Driver/PostgresTest.php +++ b/lib/Cake/Test/TestCase/Database/Driver/PostgresTest.php @@ -74,7 +74,7 @@ public function testConnectionConfigDefault() { $driver->expects($this->any())->method('connection') ->will($this->returnValue($connection)); - $driver->connect([]); + $driver->connect(); } /** @@ -83,7 +83,6 @@ public function testConnectionConfigDefault() { * @return void */ public function testConnectionConfigCustom() { - $driver = $this->getMock('Cake\Database\driver\Postgres', ['_connect', 'connection']); $config = [ 'persistent' => false, 'host' => 'foo', @@ -97,6 +96,11 @@ public function testConnectionConfigCustom() { 'schema' => 'fooblic', 'init' => ['Execute this', 'this too'] ]; + $driver = $this->getMock( + 'Cake\Database\driver\Postgres', + ['_connect', 'connection'], + [$config] + ); $expected = $config; $expected['dsn'] = 'pgsql:host=foo;port=3440;dbname=bar'; @@ -121,12 +125,14 @@ public function testConnectionConfigCustom() { $connection->expects($this->at(7))->method('exec')->with('SET timezone = Antartica'); $connection->expects($this->exactly(5))->method('exec'); + $driver->connection($connection); $driver->expects($this->once())->method('_connect') ->with($expected); + $driver->expects($this->any())->method('connection') ->will($this->returnValue($connection)); - $driver->connect($config); + $driver->connect(); } } diff --git a/lib/Cake/Test/TestCase/Database/Driver/SqliteTest.php b/lib/Cake/Test/TestCase/Database/Driver/SqliteTest.php index 440d88d6980..38a080d8a0a 100644 --- a/lib/Cake/Test/TestCase/Database/Driver/SqliteTest.php +++ b/lib/Cake/Test/TestCase/Database/Driver/SqliteTest.php @@ -61,7 +61,6 @@ public function testConnectionConfigDefault() { * @return void */ public function testConnectionConfigCustom() { - $driver = $this->getMock('Cake\Database\driver\Sqlite', ['_connect', 'connection']); $config = [ 'persistent' => true, 'host' => 'foo', @@ -70,6 +69,11 @@ public function testConnectionConfigCustom() { 'encoding' => 'a-language', 'init' => ['Execute this', 'this too'] ]; + $driver = $this->getMock( + 'Cake\Database\driver\Sqlite', + ['_connect', 'connection'], + [$config] + ); $expected = $config; $expected += ['login' => null, 'password' => null];