-
Notifications
You must be signed in to change notification settings - Fork 29
Potential improvement: add support for custom PDO implementation #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
1eec70d
a47ec7c
d25537d
d4e77bd
de7413c
322f5ab
762375a
36bea69
36d39c6
4f15f4d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
/.idea/ | ||
/vendor/ | ||
/composer.lock | ||
composer.phar | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,10 +32,36 @@ class Db | |
* @var array | ||
*/ | ||
protected $primaryKeys = []; | ||
/** | ||
* @var string|null | ||
*/ | ||
protected $pdo_class; | ||
|
||
/** | ||
* @param string|null $pdo_class | ||
* @return string | ||
*/ | ||
private static function pdoClass($pdo_class){ | ||
if (!$pdo_class){ | ||
// If empty or null we use regular PDO | ||
return \PDO::class; | ||
} | ||
|
||
if (!class_exists($pdo_class)){ | ||
throw new ModuleException( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Initially this returned the fallback value of |
||
'Codeception\Module\Db', | ||
"The class with provided config value 'pdo_class' ($pdo_class) does not exist" | ||
); | ||
} | ||
|
||
public static function connect($dsn, $user, $password, $options = null) | ||
return $pdo_class; | ||
} | ||
|
||
public static function connect($dsn, $user, $password, $options = null, $pdo_class = null) | ||
{ | ||
$dbh = new \PDO($dsn, $user, $password, $options); | ||
$class_name = self::pdoClass($pdo_class); | ||
$dbh = new $class_name($dsn, $user, $password, $options); | ||
self::assertIsPdo($dbh, $pdo_class); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assert is necessary so we don't get something that accepts the right arguments but doesn't provided expected methods and/or fails expected typehints down the line. |
||
$dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); | ||
|
||
return $dbh; | ||
|
@@ -48,31 +74,32 @@ public static function connect($dsn, $user, $password, $options = null) | |
* @param $user | ||
* @param $password | ||
* @param [optional] $options | ||
* @param [optional] $pdo_class | ||
* | ||
* @see http://php.net/manual/en/pdo.construct.php | ||
* @see http://php.net/manual/de/ref.pdo-mysql.php#pdo-mysql.constants | ||
* | ||
* @return Db|SqlSrv|MySql|Oci|PostgreSql|Sqlite | ||
*/ | ||
public static function create($dsn, $user, $password, $options = null) | ||
public static function create($dsn, $user, $password, $options = null, $pdo_class = null) | ||
{ | ||
$provider = self::getProvider($dsn); | ||
|
||
switch ($provider) { | ||
case 'sqlite': | ||
return new Sqlite($dsn, $user, $password, $options); | ||
return new Sqlite($dsn, $user, $password, $options, $pdo_class); | ||
case 'mysql': | ||
return new MySql($dsn, $user, $password, $options); | ||
return new MySql($dsn, $user, $password, $options, $pdo_class); | ||
case 'pgsql': | ||
return new PostgreSql($dsn, $user, $password, $options); | ||
return new PostgreSql($dsn, $user, $password, $options, $pdo_class); | ||
case 'mssql': | ||
case 'dblib': | ||
case 'sqlsrv': | ||
return new SqlSrv($dsn, $user, $password, $options); | ||
return new SqlSrv($dsn, $user, $password, $options, $pdo_class); | ||
case 'oci': | ||
return new Oci($dsn, $user, $password, $options); | ||
return new Oci($dsn, $user, $password, $options, $pdo_class); | ||
default: | ||
return new Db($dsn, $user, $password, $options); | ||
return new Db($dsn, $user, $password, $options, $pdo_class); | ||
} | ||
} | ||
|
||
|
@@ -86,19 +113,37 @@ public static function getProvider($dsn) | |
* @param $user | ||
* @param $password | ||
* @param [optional] $options | ||
* @param [optional] $pdo_class | ||
* | ||
* @see http://php.net/manual/en/pdo.construct.php | ||
* @see http://php.net/manual/de/ref.pdo-mysql.php#pdo-mysql.constants | ||
*/ | ||
public function __construct($dsn, $user, $password, $options = null) | ||
public function __construct($dsn, $user, $password, $options = null, $pdo_class = null) | ||
{ | ||
$this->dbh = new \PDO($dsn, $user, $password, $options); | ||
$class_name = self::pdoClass($pdo_class); | ||
$this->dbh = new $class_name($dsn, $user, $password, $options); | ||
self::assertIsPdo($this->dbh, $pdo_class); | ||
$this->dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); | ||
|
||
$this->dsn = $dsn; | ||
$this->user = $user; | ||
$this->password = $password; | ||
$this->options = $options; | ||
$this->pdo_class = $pdo_class; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Storing this the same as the rest so it can be passed back in if necessary (e.g. sqllite reconnection) |
||
} | ||
|
||
/** | ||
* @param $dbh | ||
* @param string|null $pdo_class | ||
*/ | ||
private static function assertIsPdo($dbh, $pdo_class) | ||
{ | ||
if (!$dbh instanceof \PDO){ | ||
throw new ModuleException( | ||
'Codeception\Module\Db', | ||
"The provided config value 'pdo_class' ($pdo_class) did not resolve to a class that implements \\PDO" | ||
); | ||
} | ||
} | ||
|
||
public function __destruct() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,7 @@ | |
* * ssl_cipher - list of one or more permissible ciphers to use for SSL encryption (MySQL specific, @see http://php.net/manual/de/ref.pdo-mysql.php#pdo.constants.mysql-attr-cipher) | ||
* * databases - include more database configs and switch between them in tests. | ||
* * initial_queries - list of queries to be executed right after connection to the database has been initiated, i.e. creating the database if it does not exist or preparing the database collation | ||
* * pdo_class - a fully qualified class name of a class which extends \PDO. This allows for custom stubbing of PDO to provide customised interaction with the database, or alternative implementations which may aid in test debugging or test speed | ||
* | ||
* ## Example | ||
* | ||
|
@@ -570,7 +571,8 @@ private function connect($databaseKey, $databaseConfig) | |
|
||
try { | ||
$this->debugSection('Connecting To Db', ['config' => $databaseConfig, 'options' => $options]); | ||
$this->drivers[$databaseKey] = Driver::create($databaseConfig['dsn'], $databaseConfig['user'], $databaseConfig['password'], $options); | ||
$pdo_class = array_key_exists('pdo_class', $databaseConfig) ? $databaseConfig['pdo_class'] : null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This adds a bit more bulk but without it a lot of tests fail due to notices. |
||
$this->drivers[$databaseKey] = Driver::create($databaseConfig['dsn'], $databaseConfig['user'], $databaseConfig['password'], $options, $pdo_class); | ||
} catch (\PDOException $e) { | ||
$message = $e->getMessage(); | ||
if ($message === 'could not find driver') { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can remove this if needed but seemed sensible to include it