Add custom Doctrine Driver
[MAILPOET-3150]
This commit is contained in:
BIN
composer.phar
Executable file
BIN
composer.phar
Executable file
Binary file not shown.
@ -17,6 +17,6 @@ class DatabaseInitializer {
|
|||||||
|
|
||||||
// pass the same PDO connection to legacy Database object
|
// pass the same PDO connection to legacy Database object
|
||||||
$database = new Database();
|
$database = new Database();
|
||||||
$database->init($connection->getWrappedConnection());
|
$database->init($connection->getWrappedConnection()->getConnection());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace MailPoet\Doctrine;
|
namespace MailPoet\Doctrine;
|
||||||
|
|
||||||
use MailPoet\Config\Env;
|
use MailPoet\Config\Env;
|
||||||
|
use MailPoet\Doctrine\Driver\Driver;
|
||||||
use MailPoet\Doctrine\Types\BigIntType;
|
use MailPoet\Doctrine\Types\BigIntType;
|
||||||
use MailPoet\Doctrine\Types\JsonOrSerializedType;
|
use MailPoet\Doctrine\Types\JsonOrSerializedType;
|
||||||
use MailPoet\Doctrine\Types\JsonType;
|
use MailPoet\Doctrine\Types\JsonType;
|
||||||
@ -30,6 +31,7 @@ class ConnectionFactory {
|
|||||||
$connectionParams = [
|
$connectionParams = [
|
||||||
'wrapperClass' => SerializableConnection::class,
|
'wrapperClass' => SerializableConnection::class,
|
||||||
'driver' => self::DRIVER,
|
'driver' => self::DRIVER,
|
||||||
|
'driverClass' => Driver::class,
|
||||||
'platform' => new $platformClass,
|
'platform' => new $platformClass,
|
||||||
'user' => Env::$dbUsername,
|
'user' => Env::$dbUsername,
|
||||||
'password' => Env::$dbPassword,
|
'password' => Env::$dbPassword,
|
||||||
|
22
lib/Doctrine/Driver/Driver.php
Normal file
22
lib/Doctrine/Driver/Driver.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MailPoet\Doctrine\Driver;
|
||||||
|
|
||||||
|
use MailPoetVendor\Doctrine\DBAL\DBALException;
|
||||||
|
|
||||||
|
class Driver extends \MailPoetVendor\Doctrine\DBAL\Driver\PDOMySql\Driver {
|
||||||
|
public function connect(array $params, $username = null, $password = null, array $driverOptions = []) {
|
||||||
|
try {
|
||||||
|
$conn = new PDOConnection(
|
||||||
|
$this->constructPdoDsn($params),
|
||||||
|
$username,
|
||||||
|
$password,
|
||||||
|
$driverOptions
|
||||||
|
);
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
throw DBALException::driverException($this, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $conn;
|
||||||
|
}
|
||||||
|
}
|
131
lib/Doctrine/Driver/PDOConnection.php
Normal file
131
lib/Doctrine/Driver/PDOConnection.php
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MailPoet\Doctrine\Driver;
|
||||||
|
|
||||||
|
use MailPoet\InvalidStateException;
|
||||||
|
|
||||||
|
class PDOConnection implements \MailPoetVendor\Doctrine\DBAL\Driver\Connection, \MailPoetVendor\Doctrine\DBAL\Driver\ServerInfoAwareConnection {
|
||||||
|
/** @var \PDO */
|
||||||
|
private $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $dsn
|
||||||
|
* @param string|null $user
|
||||||
|
* @param string|null $password
|
||||||
|
* @param array|null $options
|
||||||
|
*/
|
||||||
|
public function __construct($dsn, $user = null, $password = null, array $options = null) {
|
||||||
|
try {
|
||||||
|
$this->connection = new \PDO($dsn, (string)$user, (string)$password, (array)$options);
|
||||||
|
$this->connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new \MailPoetVendor\Doctrine\DBAL\Driver\PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConnection(): \PDO {
|
||||||
|
return $this->connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
* @return int|false
|
||||||
|
*/
|
||||||
|
public function exec($statement) {
|
||||||
|
try {
|
||||||
|
return $this->connection->exec($statement);
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new \MailPoetVendor\Doctrine\DBAL\Driver\PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getServerVersion() {
|
||||||
|
return $this->connection->getAttribute(\PDO::ATTR_SERVER_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prepare($prepareString, $driverOptions = []) {
|
||||||
|
try {
|
||||||
|
return $this->createStatement(
|
||||||
|
$this->connection->prepare($prepareString, $driverOptions)
|
||||||
|
);
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new \MailPoetVendor\Doctrine\DBAL\Driver\PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a wrapped statement
|
||||||
|
*/
|
||||||
|
protected function createStatement(\PDOStatement $stmt): PDOStatement {
|
||||||
|
return new PDOStatement($stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function query() {
|
||||||
|
$args = func_get_args();
|
||||||
|
$argsCount = count($args);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($argsCount == 4) {
|
||||||
|
$stmt = $this->connection->query($args[0], $args[1], $args[2], $args[3]);
|
||||||
|
} elseif ($argsCount == 3) {
|
||||||
|
$stmt = $this->connection->query($args[0], $args[1], $args[2]);
|
||||||
|
} elseif ($argsCount == 2) {
|
||||||
|
$stmt = $this->connection->query($args[0], $args[1]);
|
||||||
|
} else {
|
||||||
|
$stmt = $this->connection->query($args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($stmt !== false) {
|
||||||
|
return $this->createStatement($stmt);
|
||||||
|
} else {
|
||||||
|
throw new InvalidStateException('Statement is missing');
|
||||||
|
}
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new \MailPoetVendor\Doctrine\DBAL\Driver\PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function quote($input, $type = \PDO::PARAM_STR) {
|
||||||
|
return $this->connection->quote($input, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lastInsertId($name = null) {
|
||||||
|
try {
|
||||||
|
if ($name === null) {
|
||||||
|
return $this->connection->lastInsertId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->connection->lastInsertId($name);
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new \MailPoetVendor\Doctrine\DBAL\Driver\PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAttribute($attribute) {
|
||||||
|
return $this->connection->getAttribute($attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requiresQueryForServerVersion() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beginTransaction() {
|
||||||
|
return $this->connection->beginTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function commit() {
|
||||||
|
return $this->connection->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rollBack() {
|
||||||
|
return $this->connection->rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function errorCode() {
|
||||||
|
return $this->connection->errorCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function errorInfo() {
|
||||||
|
return $this->connection->errorInfo();
|
||||||
|
}
|
||||||
|
}
|
187
lib/Doctrine/Driver/PDOStatement.php
Normal file
187
lib/Doctrine/Driver/PDOStatement.php
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MailPoet\Doctrine\Driver;
|
||||||
|
|
||||||
|
use MailPoet\InvalidStateException;
|
||||||
|
use MailPoetVendor\Doctrine\DBAL\Driver\PDOException;
|
||||||
|
use MailPoetVendor\Doctrine\DBAL\Driver\Statement;
|
||||||
|
use MailPoetVendor\Doctrine\DBAL\Exception\InvalidArgumentException;
|
||||||
|
use MailPoetVendor\Doctrine\DBAL\ParameterType;
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
class PDOStatement implements \IteratorAggregate, Statement {
|
||||||
|
private const PARAM_TYPE_MAP = [
|
||||||
|
ParameterType::NULL => PDO::PARAM_NULL,
|
||||||
|
ParameterType::INTEGER => PDO::PARAM_INT,
|
||||||
|
ParameterType::STRING => PDO::PARAM_STR,
|
||||||
|
ParameterType::BINARY => PDO::PARAM_LOB,
|
||||||
|
ParameterType::LARGE_OBJECT => PDO::PARAM_LOB,
|
||||||
|
ParameterType::BOOLEAN => PDO::PARAM_BOOL,
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var \PDOStatement */
|
||||||
|
private $statement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected constructor.
|
||||||
|
*/
|
||||||
|
public function __construct(\PDOStatement $stmt) {
|
||||||
|
$this->statement = $stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) {
|
||||||
|
// This thin wrapper is necessary to shield against the weird signature
|
||||||
|
// of PDOStatement::setFetchMode(): even if the second and third
|
||||||
|
// parameters are optional, PHP will not let us remove it from this
|
||||||
|
// declaration.
|
||||||
|
try {
|
||||||
|
if ($arg2 === null && $arg3 === null) {
|
||||||
|
return $this->statement->setFetchMode($fetchMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($arg3 === null) {
|
||||||
|
return $this->statement->setFetchMode($fetchMode, $arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->statement->setFetchMode($fetchMode, $arg2, $arg3);
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bindValue($param, $value, $type = \PDO::PARAM_STR) {
|
||||||
|
$type = $this->convertParamType($type);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $this->statement->bindValue($param, $value, $type);
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null) {
|
||||||
|
if (is_null($type)) $type = ParameterType::STRING;
|
||||||
|
$type = $this->convertParamType($type);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $this->statement->bindParam($param, $variable, $type, ...array_slice(func_get_args(), 3));
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function closeCursor() {
|
||||||
|
try {
|
||||||
|
return $this->statement->closeCursor();
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
// Exceptions not allowed by the interface.
|
||||||
|
// In case driver implementations do not adhere to the interface, silence exceptions here.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($params = null) {
|
||||||
|
try {
|
||||||
|
return $this->statement->execute($params);
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetch($fetchMode = null, $cursorOrientation = null, $cursorOffset = null) {
|
||||||
|
try {
|
||||||
|
if ($fetchMode === null && $cursorOrientation === null && $cursorOffset === null) {
|
||||||
|
return $this->statement->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fetchMode !== null && $cursorOrientation === null && $cursorOffset === null) {
|
||||||
|
return $this->statement->fetch($fetchMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fetchMode !== null && $cursorOrientation !== null && $cursorOffset === null) {
|
||||||
|
return $this->statement->fetch($fetchMode, $cursorOrientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fetchMode !== null && $cursorOrientation !== null && $cursorOffset !== null) {
|
||||||
|
return $this->statement->fetch($fetchMode, $cursorOrientation, $cursorOffset);
|
||||||
|
}
|
||||||
|
throw new InvalidStateException('Invalid arguments for fetch');
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) {
|
||||||
|
try {
|
||||||
|
$data = null;
|
||||||
|
if ($fetchMode === null && $fetchArgument === null && $ctorArgs === null) {
|
||||||
|
$data = $this->statement->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fetchMode !== null && $fetchArgument === null && $ctorArgs === null) {
|
||||||
|
$data = $this->statement->fetchAll($fetchMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fetchMode !== null && $fetchArgument !== null && $ctorArgs === null) {
|
||||||
|
$data = $this->statement->fetchAll($fetchMode, $fetchArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fetchMode !== null && $fetchArgument !== null && $ctorArgs !== null) {
|
||||||
|
$data = $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($data)) {
|
||||||
|
return $data;
|
||||||
|
} else {
|
||||||
|
throw new InvalidStateException('Invalid data returned in fetchAll');
|
||||||
|
}
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetchColumn($columnIndex = 0) {
|
||||||
|
try {
|
||||||
|
return $this->statement->fetchColumn($columnIndex);
|
||||||
|
} catch (\PDOException $exception) {
|
||||||
|
throw new PDOException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function columnCount() {
|
||||||
|
return $this->statement->columnCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function errorCode() {
|
||||||
|
return $this->statement->errorCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function errorInfo() {
|
||||||
|
return $this->statement->errorInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rowCount() {
|
||||||
|
return $this->statement->rowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIterator() {
|
||||||
|
while (($result = $this->statement->fetch()) !== false) {
|
||||||
|
yield $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts DBAL parameter type to PDO parameter type
|
||||||
|
*
|
||||||
|
* @param int $type Parameter type
|
||||||
|
*
|
||||||
|
* @throws PDOException
|
||||||
|
*/
|
||||||
|
private function convertParamType(int $type): int {
|
||||||
|
if (!isset(self::PARAM_TYPE_MAP[$type])) {
|
||||||
|
throw new InvalidArgumentException('Unknown parameter ' . $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::PARAM_TYPE_MAP[$type];
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ namespace MailPoet\Test\Config;
|
|||||||
|
|
||||||
use MailPoet\Config\Database;
|
use MailPoet\Config\Database;
|
||||||
use MailPoet\Config\Env;
|
use MailPoet\Config\Env;
|
||||||
|
use MailPoetVendor\Doctrine\DBAL\Connection;
|
||||||
use MailPoetVendor\Idiorm\ORM;
|
use MailPoetVendor\Idiorm\ORM;
|
||||||
|
|
||||||
class DatabaseTest extends \MailPoetTest {
|
class DatabaseTest extends \MailPoetTest {
|
||||||
@ -28,7 +29,8 @@ class DatabaseTest extends \MailPoetTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function testItSetsDBDriverOptions() {
|
public function testItSetsDBDriverOptions() {
|
||||||
$this->database->init($this->connection->getWrappedConnection());
|
$connection = $this->diContainer->get(Connection::class);
|
||||||
|
$this->database->init($connection->getWrappedConnection()->getConnection());
|
||||||
$result = ORM::for_table("")
|
$result = ORM::for_table("")
|
||||||
->raw_query(
|
->raw_query(
|
||||||
'SELECT ' .
|
'SELECT ' .
|
||||||
|
@ -29,7 +29,7 @@ class ConnectionFactoryTest extends \MailPoetTest {
|
|||||||
$connection = $connectionFactory->createConnection();
|
$connection = $connectionFactory->createConnection();
|
||||||
|
|
||||||
expect($connection)->isInstanceOf(SerializableConnection::class);
|
expect($connection)->isInstanceOf(SerializableConnection::class);
|
||||||
expect($connection->getWrappedConnection())->isInstanceOf(PDO::class);
|
expect($connection->getWrappedConnection()->getConnection())->isInstanceOf(PDO::class);
|
||||||
expect($connection->getDriver())->isInstanceOf(PDOMySql\Driver::class);
|
expect($connection->getDriver())->isInstanceOf(PDOMySql\Driver::class);
|
||||||
expect($connection->getDatabasePlatform())->isInstanceOf(MySqlPlatform::class);
|
expect($connection->getDatabasePlatform())->isInstanceOf(MySqlPlatform::class);
|
||||||
expect($connection->getHost())->equals(Env::$dbHost);
|
expect($connection->getHost())->equals(Env::$dbHost);
|
||||||
@ -80,7 +80,7 @@ class ConnectionFactoryTest extends \MailPoetTest {
|
|||||||
Env::$dbHost = '::ffff:' . gethostbyname($this->envBackup['db_host']);
|
Env::$dbHost = '::ffff:' . gethostbyname($this->envBackup['db_host']);
|
||||||
$connectionFactory = new ConnectionFactory();
|
$connectionFactory = new ConnectionFactory();
|
||||||
$connection = $connectionFactory->createConnection();
|
$connection = $connectionFactory->createConnection();
|
||||||
expect($connection->getWrappedConnection())->isInstanceOf(PDO::class);
|
expect($connection->getWrappedConnection()->getConnection())->isInstanceOf(PDO::class);
|
||||||
expect($connection->executeQuery('SELECT 1')->fetchColumn())->same('1');
|
expect($connection->executeQuery('SELECT 1')->fetchColumn())->same('1');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user