Skip to content

Commit

Permalink
Add Connection::getNativeConnection()
Browse files Browse the repository at this point in the history
  • Loading branch information
derrabus committed Nov 27, 2021
1 parent 8d00257 commit d9f969b
Show file tree
Hide file tree
Showing 19 changed files with 243 additions and 12 deletions.
17 changes: 17 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ awareness about deprecated code.
- Use of our low-overhead runtime deprecation API, details:
https://github.com/doctrine/deprecations/

# Upgrade to 3.3

## Add `Connection::getNativeConnection()`

Driver and middleware connections need to implement a new method `getNativeConnection()` that gives access to the
native database connection. Not doing so is deprecated.

## Deprecate accessors for the native connection in favor of `getNativeConnection()`

The following methods have been deprecated:

* `Doctrine\DBAL\Driver\PDO\Connection::getWrappedConnection()`
* `Doctrine\DBAL\Driver\PDO\SQLSrv\Connection::getWrappedConnection()`
* `Doctrine\DBAL\Driver\Mysqli\Connection::getWrappedResourceHandle()`

Call `getNativeConnection()` to access the underlying PDO or MySQLi connection.

# Upgrade to 3.2

## Deprecated `SQLLogger` and its implementations.
Expand Down
4 changes: 4 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,9 @@ parameters:
message: '~Method Doctrine\\DBAL\\Driver\\Mysqli\\Result::rowCount\(\) should return int but returns int\|string\.~'
paths:
- src/Driver/Mysqli/Result.php

# Type check for legacy implementations of the Connection interface
# TODO: remove in 4.0.0
- "~Call to function method_exists\\(\\) with Doctrine\\\\DBAL\\\\Driver\\\\Connection and 'getNativeConnection' will always evaluate to true\\.~"
includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon
4 changes: 4 additions & 0 deletions psalm.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@
See https://github.com/doctrine/dbal/pull/4897
-->
<referencedMethod name="Doctrine\DBAL\Schema\AbstractSchemaManager::tryMethod"/>

<!-- TODO: remove in 4.0.0 -->
<referencedMethod name="Doctrine\DBAL\Driver\PDO\Connection::getWrappedConnection"/>
</errorLevel>
</DeprecatedMethod>
<DeprecatedProperty>
Expand Down Expand Up @@ -376,6 +379,7 @@
or breaking API changes.
-->
<file name="src/Platforms/MySQLPlatform.php"/>
<file name="tests/Functional/Driver/AbstractDriverTest.php"/>
</errorLevel>
</RedundantConditionGivenDocblockType>
<ReferenceConstraintViolation>
Expand Down
21 changes: 21 additions & 0 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Doctrine\DBAL\SQL\Parser;
use Doctrine\DBAL\Types\Type;
use Doctrine\Deprecations\Deprecation;
use LogicException;
use Throwable;
use Traversable;

Expand All @@ -34,6 +35,8 @@
use function is_int;
use function is_string;
use function key;
use function method_exists;
use function sprintf;

/**
* A database abstraction-level connection that implements features like events, transaction isolation levels,
Expand Down Expand Up @@ -1511,6 +1514,24 @@ public function getWrappedConnection()
return $this->_conn;
}

/**
* @return resource|object
*/
public function getNativeConnection()
{
$this->connect();

assert($this->_conn !== null);
if (! method_exists($this->_conn, 'getNativeConnection')) {
throw new LogicException(sprintf(
'The driver connection %s does not support accessing the native connection.',
get_class($this->_conn)
));
}

return $this->_conn->getNativeConnection();
}

/**
* Creates a SchemaManager that can be used to inspect or change the
* database schema through the connection.
Expand Down
2 changes: 2 additions & 0 deletions src/Driver/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
/**
* Connection interface.
* Driver connections must implement this interface.
*
* @method resource|object getNativeConnection()
*/
interface Connection
{
Expand Down
8 changes: 8 additions & 0 deletions src/Driver/IBMDB2/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,12 @@ public function rollBack(): bool

return $result;
}

/**
* @return resource
*/
public function getNativeConnection()
{
return $this->connection;
}
}
19 changes: 19 additions & 0 deletions src/Driver/Middleware/AbstractConnectionMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
use Doctrine\Deprecations\Deprecation;
use LogicException;

use function get_class;
use function method_exists;
use function sprintf;

abstract class AbstractConnectionMiddleware implements ServerInfoAwareConnection
{
/** @var Connection */
Expand Down Expand Up @@ -94,4 +98,19 @@ public function getServerVersion()

return $this->wrappedConnection->getServerVersion();
}

/**
* @return resource|object
*/
public function getNativeConnection()
{
if (! method_exists($this->wrappedConnection, 'getNativeConnection')) {
throw new LogicException(sprintf(
'The driver connection %s does not support accessing the native connection.',
get_class($this->wrappedConnection)
));
}

return $this->wrappedConnection->getNativeConnection();
}
}
16 changes: 15 additions & 1 deletion src/Driver/Mysqli/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,19 @@ public function __construct(mysqli $connection)
* Retrieves mysqli native resource handle.
*
* Could be used if part of your application is not using DBAL.
*
* @deprecated Call {@see getNativeConnection()} instead.
*/
public function getWrappedResourceHandle(): mysqli
{
return $this->connection;
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5037',
'%s is deprecated, call getNativeConnection() instead.',
__METHOD__
);

return $this->getNativeConnection();
}

/**
Expand Down Expand Up @@ -147,4 +156,9 @@ public function rollBack(): bool
return false;
}
}

public function getNativeConnection(): mysqli
{
return $this->connection;
}
}
8 changes: 8 additions & 0 deletions src/Driver/OCI8/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,12 @@ public function rollBack(): bool

return true;
}

/**
* @return resource
*/
public function getNativeConnection()
{
return $this->connection;
}
}
17 changes: 16 additions & 1 deletion src/Driver/PDO/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,23 @@ public function rollBack(): bool
return $this->connection->rollBack();
}

public function getWrappedConnection(): PDO
public function getNativeConnection(): PDO
{
return $this->connection;
}

/**
* @deprecated Call {@see getNativeConnection()} instead.
*/
public function getWrappedConnection(): PDO
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5037',
'%s is deprecated, call getNativeConnection() instead.',
__METHOD__
);

return $this->getNativeConnection();
}
}
15 changes: 15 additions & 0 deletions src/Driver/PDO/SQLSrv/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,23 @@ public function getServerVersion()
return $this->connection->getServerVersion();
}

public function getNativeConnection(): PDO
{
return $this->connection->getNativeConnection();
}

/**
* @deprecated Call {@see getNativeConnection()} instead.
*/
public function getWrappedConnection(): PDO
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5037',
'%s is deprecated, call getNativeConnection() instead.',
__METHOD__
);

return $this->connection->getWrappedConnection();
}
}
10 changes: 9 additions & 1 deletion src/Driver/SQLSrv/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
final class Connection implements ServerInfoAwareConnection
{
/** @var resource */
protected $connection;
private $connection;

/**
* @internal The connection can be only instantiated by its driver.
Expand Down Expand Up @@ -135,4 +135,12 @@ public function rollBack(): bool

return true;
}

/**
* @return resource
*/
public function getNativeConnection()
{
return $this->connection;
}
}
18 changes: 14 additions & 4 deletions src/Portability/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
use Doctrine\DBAL\ColumnCase;
use Doctrine\DBAL\Driver as DriverInterface;
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
use Doctrine\DBAL\Driver\PDO;
use LogicException;
use PDO;

use function method_exists;

use const CASE_LOWER;
use const CASE_UPPER;
Expand Down Expand Up @@ -41,10 +44,17 @@ public function connect(array $params)
$case = 0;

if ($this->case !== 0 && ($portability & Connection::PORTABILITY_FIX_CASE) !== 0) {
if ($connection instanceof PDO\Connection) {
// make use of c-level support for case handling
$nativeConnection = null;
if (method_exists($connection, 'getNativeConnection')) {
try {
$nativeConnection = $connection->getNativeConnection();
} catch (LogicException $e) {
}
}

if ($nativeConnection instanceof PDO) {
$portability &= ~Connection::PORTABILITY_FIX_CASE;
$connection->getWrappedConnection()->setAttribute(\PDO::ATTR_CASE, $this->case);
$nativeConnection->setAttribute(PDO::ATTR_CASE, $this->case);
} else {
$case = $this->case === ColumnCase::LOWER ? CASE_LOWER : CASE_UPPER;
}
Expand Down
29 changes: 29 additions & 0 deletions tests/Driver/Middleware/AbstractConnectionMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,38 @@ public function testGetServerVersionFailsOnLegacyConnections(): void
$middleware->getServerVersion();
}

public function testGetNativeConnection(): void
{
$nativeConnection = new class () {
};

$connection = $this->createMock(NativeDriverConnection::class);
$connection->method('getNativeConnection')
->willReturn($nativeConnection);

self::assertSame($nativeConnection, $this->createMiddleware($connection)->getNativeConnection());
}

public function testGetNativeConnectionFailsWithLegacyConnection(): void
{
$connection = $this->createMock(Connection::class);
$middleware = $this->createMiddleware($connection);

$this->expectException(LogicException::class);
$middleware->getNativeConnection();
}

private function createMiddleware(Connection $connection): AbstractConnectionMiddleware
{
return new class ($connection) extends AbstractConnectionMiddleware {
};
}
}

interface NativeDriverConnection extends ServerInfoAwareConnection
{
/**
* @return object|resource
*/
public function getNativeConnection();
}
6 changes: 3 additions & 3 deletions tests/Driver/PDO/PgSQL/DriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function testConnectionDisablesPrepares(): void

self::assertInstanceOf(PDO\Connection::class, $connection);
self::assertTrue(
$connection->getWrappedConnection()->getAttribute(\PDO::PGSQL_ATTR_DISABLE_PREPARES)
$connection->getNativeConnection()->getAttribute(\PDO::PGSQL_ATTR_DISABLE_PREPARES)
);
}

Expand All @@ -42,7 +42,7 @@ public function testConnectionDoesNotDisablePreparesWhenAttributeDefined(): void

self::assertInstanceOf(PDO\Connection::class, $connection);
self::assertNotTrue(
$connection->getWrappedConnection()->getAttribute(\PDO::PGSQL_ATTR_DISABLE_PREPARES)
$connection->getNativeConnection()->getAttribute(\PDO::PGSQL_ATTR_DISABLE_PREPARES)
);
}

Expand All @@ -54,7 +54,7 @@ public function testConnectionDisablePreparesWhenDisablePreparesIsExplicitlyDefi

self::assertInstanceOf(PDO\Connection::class, $connection);
self::assertTrue(
$connection->getWrappedConnection()->getAttribute(\PDO::PGSQL_ATTR_DISABLE_PREPARES)
$connection->getNativeConnection()->getAttribute(\PDO::PGSQL_ATTR_DISABLE_PREPARES)
);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/Functional/ConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ public function testPersistentConnection(): void
self::markTestSkipped('Unable to test if the connection is persistent');
}

$pdo = $driverConnection->getWrappedConnection();
$pdo = $driverConnection->getNativeConnection();

self::assertTrue($pdo->getAttribute(PDO::ATTR_PERSISTENT));
}
Expand Down
11 changes: 11 additions & 0 deletions tests/Functional/Driver/AbstractDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Tests\FunctionalTestCase;
use PHPUnit\Framework\Constraint\IsType;

abstract class AbstractDriverTest extends FunctionalTestCase
{
Expand Down Expand Up @@ -49,6 +50,16 @@ public function testReturnsDatabaseNameWithoutDatabaseNameParameter(): void
);
}

public function testProvidesAccessToTheNativeConnection(): void
{
$nativeConnection = $this->connection->getNativeConnection();

self::assertThat($nativeConnection, self::logicalOr(
new IsType(IsType::TYPE_OBJECT),
new IsType(IsType::TYPE_RESOURCE)
));
}

abstract protected function createDriver(): Driver;

protected static function getDatabaseNameForConnectionWithoutDatabaseNameParameter(): ?string
Expand Down

0 comments on commit d9f969b

Please sign in to comment.