[DBAL-3548] Remove user provided PDO functionality

parent d9c4e3d8
# Upgrade to 3.0
## BC BREAK User-provided `PDO` instance is no longer supported
In order to share the same `PDO` instances between DBAL and other components, initialize the connection in DBAL and access it using `Connection::getWrappedConnection()->getWrappedConnection()`.
## BC BREAK: the PDO symbols are no longer part of the DBAL API
1. The support of `PDO::PARAM_*`, `PDO::FETCH_*`, `PDO::CASE_*` and `PDO::PARAM_INPUT_OUTPUT` constants in the DBAL API is removed.
......
......@@ -187,12 +187,6 @@ class Connection implements DriverConnection
$this->_driver = $driver;
$this->params = $params;
if (isset($params['pdo'])) {
$this->_conn = $params['pdo'];
$this->isConnected = true;
unset($this->params['pdo']);
}
if (isset($params['platform'])) {
if (! $params['platform'] instanceof Platforms\AbstractPlatform) {
throw DBALException::invalidPlatformType($params['platform']);
......
......@@ -14,7 +14,6 @@ use Doctrine\DBAL\Driver\PDOSqlite\Driver as PDOSQLiteDriver;
use Doctrine\DBAL\Driver\PDOSqlsrv\Driver as PDOSQLSrvDriver;
use Doctrine\DBAL\Driver\SQLAnywhere\Driver as SQLAnywhereDriver;
use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver;
use PDO;
use function array_keys;
use function array_map;
use function array_merge;
......@@ -171,17 +170,7 @@ final class DriverManager
}
}
// check for existing pdo object
if (isset($params['pdo']) && ! $params['pdo'] instanceof PDO) {
throw DBALException::invalidPdoInstance();
}
if (isset($params['pdo'])) {
$params['pdo']->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$params['driver'] = 'pdo_' . $params['pdo']->getAttribute(PDO::ATTR_DRIVER_NAME);
} else {
self::_checkParams($params);
}
self::_checkParams($params);
$className = $params['driverClass'] ?? self::$_driverMap[$params['driver']];
......@@ -277,10 +266,6 @@ final class DriverManager
$url = array_map('rawurldecode', $url);
// If we have a connection URL, we have to unset the default PDO instance connection parameter (if any)
// as we cannot merge connection details from the URL into the PDO instance (URL takes precedence).
unset($params['pdo']);
$params = self::parseDatabaseUrlScheme($url, $params);
if (isset($url['host'])) {
......
......@@ -27,7 +27,6 @@ use Doctrine\Tests\DbalTestCase;
use Exception;
use PHPUnit\Framework\MockObject\MockObject;
use stdClass;
use function call_user_func_array;
/**
* @requires extension pdo_mysql
......@@ -686,82 +685,28 @@ class ConnectionTest extends DbalTestCase
self::assertSame($result, $conn->fetchAll($statement, $params, $types));
}
public function testConnectionDoesNotMaintainTwoReferencesToExternalPDO() : void
{
$params['pdo'] = new stdClass();
$driverMock = $this->createMock(Driver::class);
$conn = new Connection($params, $driverMock);
self::assertArrayNotHasKey('pdo', $conn->getParams(), 'Connection is maintaining additional reference to the PDO connection');
}
public function testPassingExternalPDOMeansConnectionIsConnected() : void
{
$params['pdo'] = new stdClass();
$driverMock = $this->createMock(Driver::class);
$conn = new Connection($params, $driverMock);
self::assertTrue($conn->isConnected(), 'Connection is not connected after passing external PDO');
}
public function testCallingDeleteWithNoDeletionCriteriaResultsInInvalidArgumentException() : void
{
/** @var Driver $driver */
$driver = $this->createMock(Driver::class);
$pdoMock = $this->createMock(\Doctrine\DBAL\Driver\Connection::class);
// should never execute queries with invalid arguments
$pdoMock->expects($this->never())->method('exec');
$pdoMock->expects($this->never())->method('prepare');
$conn = new Connection(['pdo' => $pdoMock], $driver);
$driver = $this->createMock(Driver::class);
$conn = new Connection([], $driver);
$this->expectException(InvalidArgumentException::class);
$conn->delete('kittens', []);
}
/**
* @return array<int, array<int, mixed>>
*/
public static function dataCallConnectOnce() : iterable
{
return [
['delete', ['tbl', ['id' => 12345]]],
['insert', ['tbl', ['data' => 'foo']]],
['update', ['tbl', ['data' => 'bar'], ['id' => 12345]]],
['prepare', ['select * from dual']],
['executeUpdate', ['insert into tbl (id) values (?)'], [123]],
];
}
/**
* @param array<int, mixed> $params
*
* @dataProvider dataCallConnectOnce
*/
public function testCallConnectOnce(string $method, array $params) : void
public function testCallConnectOnce() : void
{
$driverMock = $this->createMock(Driver::class);
$pdoMock = $this->createMock(Connection::class);
$platformMock = $this->createMock(AbstractPlatform::class);
$stmtMock = $this->createMock(Statement::class);
$pdoMock->expects($this->any())
->method('prepare')
->will($this->returnValue($stmtMock));
$conn = $this->getMockBuilder(Connection::class)
->setConstructorArgs([['pdo' => $pdoMock, 'platform' => $platformMock], $driverMock])
->onlyMethods(['connect'])
->getMock();
/** @var Driver|MockObject $driver */
$driver = $this->createMock(Driver::class);
$driver->expects($this->once())
->method('connect');
$conn->expects($this->once())->method('connect');
$platform = $this->createMock(AbstractPlatform::class);
call_user_func_array([$conn, $method], $params);
$conn = new Connection(['platform' => $platform], $driver);
$conn->connect();
$conn->connect();
}
/**
......
......@@ -5,8 +5,10 @@ namespace Doctrine\Tests\DBAL\Driver;
use Doctrine\DBAL\Driver\PDOException;
use Doctrine\Tests\DbalTestCase;
use PHPUnit\Framework\MockObject\MockObject;
use function extension_loaded;
/**
* @requires extension pdo
*/
class PDOExceptionTest extends DbalTestCase
{
public const ERROR_CODE = 666;
......@@ -31,10 +33,6 @@ class PDOExceptionTest extends DbalTestCase
protected function setUp() : void
{
if (! extension_loaded('PDO')) {
$this->markTestSkipped('PDO is not installed.');
}
parent::setUp();
$this->wrappedException = new \PDOException(self::MESSAGE, self::SQLSTATE);
......
......@@ -15,50 +15,13 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Sharding\PoolingShardConnection;
use Doctrine\DBAL\Sharding\ShardChoser\MultiTenantShardChoser;
use Doctrine\Tests\DbalTestCase;
use PDO;
use stdClass;
use function extension_loaded;
use function get_class;
use function in_array;
use function is_array;
class DriverManagerTest extends DbalTestCase
{
/**
* @requires extension pdo_sqlite
*/
public function testInvalidPdoInstance() : void
{
$this->expectException(DBALException::class);
DriverManager::getConnection(['pdo' => 'test']);
}
/**
* @requires extension pdo_sqlite
*/
public function testValidPdoInstance() : void
{
$conn = DriverManager::getConnection([
'pdo' => new PDO('sqlite::memory:'),
]);
self::assertEquals('sqlite', $conn->getDatabasePlatform()->getName());
}
/**
* @group DBAL-32
* @requires extension pdo_sqlite
*/
public function testPdoInstanceSetErrorMode() : void
{
$pdo = new PDO('sqlite::memory:');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$options = ['pdo' => $pdo];
DriverManager::getConnection($options);
self::assertEquals(PDO::ERRMODE_EXCEPTION, $pdo->getAttribute(PDO::ATTR_ERRMODE));
}
public function testCheckParams() : void
{
$this->expectException(DBALException::class);
......@@ -80,7 +43,7 @@ class DriverManagerTest extends DbalTestCase
{
$platform = $this->createMock(AbstractPlatform::class);
$options = [
'pdo' => new PDO('sqlite::memory:'),
'url' => 'sqlite::memory:',
'platform' => $platform,
];
......@@ -97,7 +60,7 @@ class DriverManagerTest extends DbalTestCase
$wrapperClass = get_class($wrapper);
$options = [
'pdo' => new PDO('sqlite::memory:'),
'url' => 'sqlite::memory:',
'wrapperClass' => $wrapperClass,
];
......@@ -113,7 +76,7 @@ class DriverManagerTest extends DbalTestCase
$this->expectException(DBALException::class);
$options = [
'pdo' => new PDO('sqlite::memory:'),
'url' => 'sqlite::memory:',
'wrapperClass' => stdClass::class,
];
......@@ -215,16 +178,6 @@ class DriverManagerTest extends DbalTestCase
{
$options = is_array($url) ? $url : ['url' => $url];
if (isset($options['pdo'])) {
if (! extension_loaded('pdo')) {
$this->markTestSkipped('PDO is not installed');
}
$options['pdo'] = $this->createMock(PDO::class);
}
$options = is_array($url) ? $url : ['url' => $url];
if ($expected === false) {
$this->expectException(DBALException::class);
}
......@@ -233,7 +186,7 @@ class DriverManagerTest extends DbalTestCase
$params = $conn->getParams();
foreach ($expected as $key => $value) {
if (in_array($key, ['pdo', 'driver', 'driverClass'], true)) {
if (in_array($key, ['driver', 'driverClass'], true)) {
self::assertInstanceOf($value, $conn->getDriver());
} else {
self::assertEquals($value, $params[$key]);
......@@ -394,13 +347,6 @@ class DriverManagerTest extends DbalTestCase
['url' => '//foo:bar@localhost/baz'],
false,
],
'URL without scheme but default PDO driver' => [
[
'url' => '//foo:bar@localhost/baz',
'pdo' => true,
],
false,
],
'URL without scheme but default driver' => [
[
'url' => '//foo:bar@localhost/baz',
......@@ -427,20 +373,6 @@ class DriverManagerTest extends DbalTestCase
'driverClass' => $driverClass,
],
],
'URL without scheme but default PDO driver and default driver' => [
[
'url' => '//foo:bar@localhost/baz',
'pdo' => true,
'driver' => 'pdo_mysql',
],
[
'user' => 'foo',
'password' => 'bar',
'host' => 'localhost',
'dbname' => 'baz',
'driver' => PDOMySQLDriver::class,
],
],
'URL without scheme but driver and custom driver' => [
[
'url' => '//foo:bar@localhost/baz',
......@@ -455,19 +387,6 @@ class DriverManagerTest extends DbalTestCase
'driverClass' => $driverClass,
],
],
'URL with default PDO driver' => [
[
'url' => 'mysql://foo:bar@localhost/baz',
'pdo' => true,
],
[
'user' => 'foo',
'password' => 'bar',
'host' => 'localhost',
'dbname' => 'baz',
'driver' => PDOMySQLDriver::class,
],
],
'URL with default driver' => [
[
'url' => 'mysql://foo:bar@localhost/baz',
......@@ -494,20 +413,6 @@ class DriverManagerTest extends DbalTestCase
'driver' => PDOMySQLDriver::class,
],
],
'URL with default PDO driver and default driver' => [
[
'url' => 'mysql://foo:bar@localhost/baz',
'pdo' => true,
'driver' => 'sqlite',
],
[
'user' => 'foo',
'password' => 'bar',
'host' => 'localhost',
'dbname' => 'baz',
'driver' => PDOMySQLDriver::class,
],
],
'URL with default driver and default custom driver' => [
[
'url' => 'mysql://foo:bar@localhost/baz',
......@@ -522,21 +427,6 @@ class DriverManagerTest extends DbalTestCase
'driver' => PDOMySQLDriver::class,
],
],
'URL with default PDO driver and default driver and default custom driver' => [
[
'url' => 'mysql://foo:bar@localhost/baz',
'pdo' => true,
'driver' => 'sqlite',
'driverClass' => $driverClass,
],
[
'user' => 'foo',
'password' => 'bar',
'host' => 'localhost',
'dbname' => 'baz',
'driver' => PDOMySQLDriver::class,
],
],
];
}
}
......@@ -357,18 +357,6 @@ class ConnectionTest extends DbalFunctionalTestCase
$connection->close();
}
/**
* @requires extension pdo_sqlite
*/
public function testUserProvidedPDOConnection() : void
{
self::assertTrue(
DriverManager::getConnection([
'pdo' => new PDO('sqlite::memory:'),
])->ping()
);
}
public function testPersistentConnection() : void
{
$platform = $this->connection->getDatabasePlatform();
......
......@@ -9,10 +9,12 @@ use Doctrine\DBAL\Driver\PDOPgSql\Driver as PDOPgSQLDriver;
use Doctrine\DBAL\Driver\PDOSqlsrv\Driver as PDOSQLSRVDriver;
use Doctrine\Tests\DbalFunctionalTestCase;
use PDO;
use function extension_loaded;
use function get_class;
use function sprintf;
/**
* @requires extension pdo
*/
class PDOConnectionTest extends DbalFunctionalTestCase
{
/**
......@@ -24,10 +26,6 @@ class PDOConnectionTest extends DbalFunctionalTestCase
protected function setUp() : void
{
if (! extension_loaded('PDO')) {
$this->markTestSkipped('PDO is not installed.');
}
parent::setUp();
$this->driverConnection = $this->connection->getWrappedConnection();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment