Skip to content

Commit

Permalink
Rework the portability layer to act as a middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
morozov committed Jul 9, 2020
1 parent d3fd4f6 commit 296fb62
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 140 deletions.
6 changes: 4 additions & 2 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,11 @@ The method no longer accepts the `$username`, `$password` and `$driverOptions` a

This class was deprecated in favor of `PrimaryReadReplicaConnection`

## Removed `Portability\Connection::PORTABILITY_{PLATFORM}` constants`
## BC BREAK: Changes in the portability layer

The platform-specific portability constants were internal implementation details which are longer relevant.
1. The platform-specific portability constants (`Portability\Connection::PORTABILITY_{PLATFORM}`) were internal implementation details which are longer relevant.
2. The `Portability\Connection` class no longer extends the DBAL `Connection`.
3. The `Portability\Class` class has been made final.

## BC BREAK changes in fetching statement results

Expand Down
5 changes: 0 additions & 5 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,5 @@ parameters:
paths:
- %currentWorkingDirectory%/src/Id/TableGenerator.php
- %currentWorkingDirectory%/src/Schema/SqliteSchemaManager.php

-
message: '~Return type \(Doctrine\\DBAL\\Portability\\Statement\) of method Doctrine\\DBAL\\Portability\\Connection::prepare\(\) should be compatible with return type \(Doctrine\\DBAL\\Statement\) of method Doctrine\\DBAL\\Connection::prepare\(\)~'
paths:
- %currentWorkingDirectory%/src/Portability/Connection.php
includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon
128 changes: 49 additions & 79 deletions src/Portability/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,125 +2,95 @@

namespace Doctrine\DBAL\Portability;

use Doctrine\Common\EventManager;
use Doctrine\DBAL\Abstraction\Result as AbstractionResult;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\ColumnCase;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection as BaseConnection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\Result as DriverResult;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\Result as DBALResult;
use PDO;

use const CASE_LOWER;
use const CASE_UPPER;
use Doctrine\DBAL\ParameterType;

/**
* Portability wrapper for a Connection.
*/
class Connection extends BaseConnection
final class Connection implements ConnectionInterface
{
public const PORTABILITY_ALL = 255;
public const PORTABILITY_NONE = 0;
public const PORTABILITY_RTRIM = 1;
public const PORTABILITY_EMPTY_TO_NULL = 4;
public const PORTABILITY_FIX_CASE = 8;

/** @var int */
private $portability = self::PORTABILITY_NONE;

/** @var int */
private $case = 0;
/** @var ConnectionInterface */
private $connection;

/** @var Converter */
private $converter;

/** {@inheritDoc} */
public function __construct(
array $params,
Driver $driver,
?Configuration $config = null,
?EventManager $eventManager = null
) {
if (isset($params['portability'])) {
$this->portability = $params['portability'];
}

if (isset($params['fetch_case'])) {
$this->case = $params['fetch_case'];
}

unset($params['portability'], $params['fetch_case']);

parent::__construct($params, $driver, $config, $eventManager);
public function __construct(ConnectionInterface $connection, Converter $converter)
{
$this->connection = $connection;
$this->converter = $converter;
}

/**
* {@inheritdoc}
* @return Statement
*/
public function connect()
public function prepare(string $sql): DriverStatement
{
$ret = parent::connect();
if ($ret) {
$portability = (new OptimizeFlags())(
$this->getDatabasePlatform(),
$this->portability
);

$case = 0;
return new Statement(
$this->connection->prepare($sql),
$this->converter
);
}

if ($this->case !== 0 && ($portability & self::PORTABILITY_FIX_CASE) !== 0) {
if ($this->_conn instanceof PDOConnection) {
// make use of c-level support for case handling
$this->_conn->getWrappedConnection()->setAttribute(PDO::ATTR_CASE, $this->case);
} else {
$case = $this->case === ColumnCase::LOWER ? CASE_LOWER : CASE_UPPER;
}
}
public function query(string $sql): DriverResult
{
return new Result(
$this->connection->query($sql),
$this->converter
);
}

$this->converter = new Converter(
($portability & self::PORTABILITY_EMPTY_TO_NULL) !== 0,
($portability & self::PORTABILITY_RTRIM) !== 0,
$case
);
}
/**
* {@inheritDoc}
*/
public function quote($input, $type = ParameterType::STRING)
{
return $this->connection->quote($input, $type);
}

return $ret;
public function exec(string $statement): int
{
return $this->connection->exec($statement);
}

/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function executeQuery(string $query, array $params = [], $types = [], ?QueryCacheProfile $qcp = null): AbstractionResult
public function lastInsertId($name = null)
{
return $this->wrapResult(
parent::executeQuery($query, $params, $types, $qcp)
);
return $this->connection->lastInsertId($name);
}

/**
* @return Statement
* {@inheritDoc}
*/
public function prepare(string $sql): DriverStatement
public function beginTransaction()
{
return new Statement(parent::prepare($sql), $this->converter);
return $this->connection->beginTransaction();
}

public function query(string $sql): DriverResult
/**
* {@inheritDoc}
*/
public function commit()
{
return $this->wrapResult(
parent::query($sql)
);
return $this->connection->commit();
}

private function wrapResult(DriverResult $result): AbstractionResult
/**
* {@inheritDoc}
*/
public function rollBack()
{
return new DBALResult(
new Result($result, $this->converter),
$this
);
return $this->connection->rollBack();
}
}
90 changes: 90 additions & 0 deletions src/Portability/Driver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

namespace Doctrine\DBAL\Portability;

use Doctrine\DBAL\ColumnCase;
use Doctrine\DBAL\Connection as DBALConnection;
use Doctrine\DBAL\Driver as DriverInterface;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Platforms\AbstractPlatform;

use const CASE_LOWER;
use const CASE_UPPER;

final class Driver implements DriverInterface
{
/** @var int */
private $mode;

/** @var int */
private $case;

public function __construct(DriverInterface $driver, int $mode, int $case)
{
$this->driver = $driver;
$this->mode = $mode;
$this->case = $case;
}

/** @var DriverInterface */
private $driver;

/**
* {@inheritDoc}
*/
public function connect(array $params)
{
$connection = $this->driver->connect($params);

$portability = (new OptimizeFlags())(
$this->getDatabasePlatform(),
$this->mode
);

$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
$portability &= ~Connection::PORTABILITY_FIX_CASE;
$connection->getWrappedConnection()->setAttribute(\PDO::ATTR_CASE, $this->case);
} else {
$case = $this->case === ColumnCase::LOWER ? CASE_LOWER : CASE_UPPER;
}
}

$convertEmptyStringToNull = ($portability & Connection::PORTABILITY_EMPTY_TO_NULL) !== 0;
$rightTrimString = ($portability & Connection::PORTABILITY_RTRIM) !== 0;

if (! $convertEmptyStringToNull && ! $rightTrimString && $case === 0) {
return $connection;
}

return new Connection(
$connection,
new Converter($convertEmptyStringToNull, $rightTrimString, $case)
);
}

/**
* {@inheritDoc}
*/
public function getDatabasePlatform()
{
return $this->driver->getDatabasePlatform();
}

/**
* {@inheritDoc}
*/
public function getSchemaManager(DBALConnection $conn, AbstractPlatform $platform)
{
return $this->driver->getSchemaManager($conn, $platform);
}

public function getExceptionConverter(): ExceptionConverter
{
return $this->driver->getExceptionConverter();
}
}
32 changes: 32 additions & 0 deletions src/Portability/Middleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Portability;

use Doctrine\DBAL\Driver as DriverInterface;
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;

final class Middleware implements MiddlewareInterface
{
/** @var int */
private $mode;

/** @var int */
private $case;

public function __construct(int $mode, int $case)
{
$this->mode = $mode;
$this->case = $case;
}

public function wrap(DriverInterface $driver): DriverInterface
{
if ($this->mode !== 0) {
return new Driver($driver, $this->mode, $this->case);
}

return $driver;
}
}
2 changes: 1 addition & 1 deletion src/Portability/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/**
* Portability wrapper for a Statement.
*/
class Statement implements DriverStatement
final class Statement implements DriverStatement
{
/** @var DriverStatement */
private $stmt;
Expand Down
Loading

0 comments on commit 296fb62

Please sign in to comment.