welcome back to dyb-tech

This commit is contained in:
Daniel Guzman
2024-05-18 02:28:01 +02:00
parent 9513cdba09
commit 9f30bc98c7
6149 changed files with 668407 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace Doctrine\DBAL\Portability;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware;
use Doctrine\DBAL\Driver\Result as DriverResult;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
/**
* Portability wrapper for a Connection.
*/
final class Connection extends AbstractConnectionMiddleware
{
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;
private Converter $converter;
public function __construct(ConnectionInterface $connection, Converter $converter)
{
parent::__construct($connection);
$this->converter = $converter;
}
public function prepare(string $sql): DriverStatement
{
return new Statement(
parent::prepare($sql),
$this->converter,
);
}
public function query(string $sql): DriverResult
{
return new Result(
parent::query($sql),
$this->converter,
);
}
}
+300
View File
@@ -0,0 +1,300 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Portability;
use function array_change_key_case;
use function array_map;
use function array_reduce;
use function is_string;
use function rtrim;
use const CASE_LOWER;
use const CASE_UPPER;
final class Converter
{
public const CASE_LOWER = CASE_LOWER;
public const CASE_UPPER = CASE_UPPER;
/** @var callable */
private $convertNumeric;
/** @var callable */
private $convertAssociative;
/** @var callable */
private $convertOne;
/** @var callable */
private $convertAllNumeric;
/** @var callable */
private $convertAllAssociative;
/** @var callable */
private $convertFirstColumn;
/**
* @param bool $convertEmptyStringToNull Whether each empty string should
* be converted to NULL
* @param bool $rightTrimString Whether each string should right-trimmed
* @param self::CASE_LOWER|self::CASE_UPPER|null $case Convert the case of the column names
* (one of {@see self::CASE_LOWER} and
* {@see self::CASE_UPPER})
*/
public function __construct(bool $convertEmptyStringToNull, bool $rightTrimString, ?int $case)
{
$convertValue = $this->createConvertValue($convertEmptyStringToNull, $rightTrimString);
$convertNumeric = $this->createConvertRow($convertValue, null);
$convertAssociative = $this->createConvertRow($convertValue, $case);
$this->convertNumeric = $this->createConvert($convertNumeric, [self::class, 'id']);
$this->convertAssociative = $this->createConvert($convertAssociative, [self::class, 'id']);
$this->convertOne = $this->createConvert($convertValue, [self::class, 'id']);
$this->convertAllNumeric = $this->createConvertAll($convertNumeric, [self::class, 'id']);
$this->convertAllAssociative = $this->createConvertAll($convertAssociative, [self::class, 'id']);
$this->convertFirstColumn = $this->createConvertAll($convertValue, [self::class, 'id']);
}
/**
* @param array<int,mixed>|false $row
*
* @return list<mixed>|false
*/
public function convertNumeric($row)
{
return ($this->convertNumeric)($row);
}
/**
* @param array<string,mixed>|false $row
*
* @return array<string,mixed>|false
*/
public function convertAssociative($row)
{
return ($this->convertAssociative)($row);
}
/**
* @param mixed|false $value
*
* @return mixed|false
*/
public function convertOne($value)
{
return ($this->convertOne)($value);
}
/**
* @param list<list<mixed>> $data
*
* @return list<list<mixed>>
*/
public function convertAllNumeric(array $data): array
{
return ($this->convertAllNumeric)($data);
}
/**
* @param list<array<string,mixed>> $data
*
* @return list<array<string,mixed>>
*/
public function convertAllAssociative(array $data): array
{
return ($this->convertAllAssociative)($data);
}
/**
* @param list<mixed> $data
*
* @return list<mixed>
*/
public function convertFirstColumn(array $data): array
{
return ($this->convertFirstColumn)($data);
}
/**
* @param T $value
*
* @return T
*
* @template T
*/
private static function id($value)
{
return $value;
}
/**
* @param T $value
*
* @return T|null
*
* @template T
*/
private static function convertEmptyStringToNull($value)
{
if ($value === '') {
return null;
}
return $value;
}
/**
* @param T $value
*
* @return T|string
* @psalm-return (T is string ? string : T)
*
* @template T
*/
private static function rightTrimString($value)
{
if (! is_string($value)) {
return $value;
}
return rtrim($value);
}
/**
* Creates a function that will convert each individual value retrieved from the database
*
* @param bool $convertEmptyStringToNull Whether each empty string should be converted to NULL
* @param bool $rightTrimString Whether each string should right-trimmed
*
* @return callable|null The resulting function or NULL if no conversion is needed
*/
private function createConvertValue(bool $convertEmptyStringToNull, bool $rightTrimString): ?callable
{
$functions = [];
if ($convertEmptyStringToNull) {
$functions[] = [self::class, 'convertEmptyStringToNull'];
}
if ($rightTrimString) {
$functions[] = [self::class, 'rightTrimString'];
}
return $this->compose(...$functions);
}
/**
* Creates a function that will convert each array-row retrieved from the database
*
* @param callable|null $function The function that will convert each value
* @param self::CASE_LOWER|self::CASE_UPPER|null $case Column name case
*
* @return callable|null The resulting function or NULL if no conversion is needed
*/
private function createConvertRow(?callable $function, ?int $case): ?callable
{
$functions = [];
if ($function !== null) {
$functions[] = $this->createMapper($function);
}
if ($case !== null) {
$functions[] = static function (array $row) use ($case): array {
return array_change_key_case($row, $case);
};
}
return $this->compose(...$functions);
}
/**
* Creates a function that will be applied to the return value of Statement::fetch*()
* or an identity function if no conversion is needed
*
* @param callable|null $function The function that will convert each tow
* @param callable $id Identity function
*/
private function createConvert(?callable $function, callable $id): callable
{
if ($function === null) {
return $id;
}
return /**
* @param T $value
*
* @psalm-return (T is false ? false : T)
*
* @template T
*/
static function ($value) use ($function) {
if ($value === false) {
return false;
}
return $function($value);
};
}
/**
* Creates a function that will be applied to the return value of Statement::fetchAll*()
* or an identity function if no transformation is required
*
* @param callable|null $function The function that will transform each value
* @param callable $id Identity function
*/
private function createConvertAll(?callable $function, callable $id): callable
{
if ($function === null) {
return $id;
}
return $this->createMapper($function);
}
/**
* Creates a function that maps each value of the array using the given function
*
* @param callable $function The function that maps each value of the array
*/
private function createMapper(callable $function): callable
{
return static function (array $array) use ($function): array {
return array_map($function, $array);
};
}
/**
* Creates a composition of the given set of functions
*
* @param callable(T):T ...$functions The functions to compose
*
* @return callable(T):T|null
*
* @template T
*/
private function compose(callable ...$functions): ?callable
{
return array_reduce($functions, static function (?callable $carry, callable $item): callable {
if ($carry === null) {
return $item;
}
return /**
* @param T $value
*
* @return T
*
* @template T
*/
static function ($value) use ($carry, $item) {
return $item($carry($value));
};
});
}
}
+83
View File
@@ -0,0 +1,83 @@
<?php
namespace Doctrine\DBAL\Portability;
use Doctrine\DBAL\ColumnCase;
use Doctrine\DBAL\Driver as DriverInterface;
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
use LogicException;
use PDO;
use SensitiveParameter;
use function method_exists;
final class Driver extends AbstractDriverMiddleware
{
private int $mode;
/** @var 0|ColumnCase::LOWER|ColumnCase::UPPER */
private int $case;
/**
* @param 0|ColumnCase::LOWER|ColumnCase::UPPER $case Determines how the column case will be treated.
* 0: The case will be left as is in the database.
* {@see ColumnCase::LOWER}: The case will be lowercased.
* {@see ColumnCase::UPPER}: The case will be uppercased.
*/
public function __construct(DriverInterface $driver, int $mode, int $case)
{
parent::__construct($driver);
$this->mode = $mode;
$this->case = $case;
}
/**
* {@inheritDoc}
*/
public function connect(
#[SensitiveParameter]
array $params
) {
$connection = parent::connect($params);
$portability = (new OptimizeFlags())(
$this->getDatabasePlatform(),
$this->mode,
);
$case = null;
if ($this->case !== 0 && ($portability & Connection::PORTABILITY_FIX_CASE) !== 0) {
$nativeConnection = null;
if (method_exists($connection, 'getNativeConnection')) {
try {
$nativeConnection = $connection->getNativeConnection();
} catch (LogicException $e) {
}
}
if ($nativeConnection instanceof PDO) {
$portability &= ~Connection::PORTABILITY_FIX_CASE;
$nativeConnection->setAttribute(
PDO::ATTR_CASE,
$this->case === ColumnCase::LOWER ? PDO::CASE_LOWER : PDO::CASE_UPPER,
);
} else {
$case = $this->case === ColumnCase::LOWER ? Converter::CASE_LOWER : Converter::CASE_UPPER;
}
}
$convertEmptyStringToNull = ($portability & Connection::PORTABILITY_EMPTY_TO_NULL) !== 0;
$rightTrimString = ($portability & Connection::PORTABILITY_RTRIM) !== 0;
if (! $convertEmptyStringToNull && ! $rightTrimString && $case === null) {
return $connection;
}
return new Connection(
$connection,
new Converter($convertEmptyStringToNull, $rightTrimString, $case),
);
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Portability;
use Doctrine\DBAL\ColumnCase;
use Doctrine\DBAL\Driver as DriverInterface;
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;
final class Middleware implements MiddlewareInterface
{
private int $mode;
/** @var 0|ColumnCase::LOWER|ColumnCase::UPPER */
private int $case;
/**
* @param 0|ColumnCase::LOWER|ColumnCase::UPPER $case Determines how the column case will be treated.
* 0: The case will be left as is in the database.
* {@see ColumnCase::LOWER}: The case will be lowercased.
* {@see ColumnCase::UPPER}: The case will be uppercased.
*/
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;
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Portability;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
final class OptimizeFlags
{
/**
* Platform-specific portability flags that need to be excluded from the user-provided mode
* since the platform already operates in this mode to avoid unnecessary conversion overhead.
*
* @var array<class-string, int>
*/
private static array $platforms = [
DB2Platform::class => 0,
OraclePlatform::class => Connection::PORTABILITY_EMPTY_TO_NULL,
PostgreSQLPlatform::class => 0,
SqlitePlatform::class => 0,
SQLServerPlatform::class => 0,
];
public function __invoke(AbstractPlatform $platform, int $flags): int
{
foreach (self::$platforms as $class => $mask) {
if ($platform instanceof $class) {
$flags &= ~$mask;
break;
}
}
return $flags;
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Portability;
use Doctrine\DBAL\Driver\Middleware\AbstractResultMiddleware;
use Doctrine\DBAL\Driver\Result as ResultInterface;
final class Result extends AbstractResultMiddleware
{
private Converter $converter;
/** @internal The result can be only instantiated by the portability connection or statement. */
public function __construct(ResultInterface $result, Converter $converter)
{
parent::__construct($result);
$this->converter = $converter;
}
/**
* {@inheritDoc}
*/
public function fetchNumeric()
{
return $this->converter->convertNumeric(
parent::fetchNumeric(),
);
}
/**
* {@inheritDoc}
*/
public function fetchAssociative()
{
return $this->converter->convertAssociative(
parent::fetchAssociative(),
);
}
/**
* {@inheritDoc}
*/
public function fetchOne()
{
return $this->converter->convertOne(
parent::fetchOne(),
);
}
/**
* {@inheritDoc}
*/
public function fetchAllNumeric(): array
{
return $this->converter->convertAllNumeric(
parent::fetchAllNumeric(),
);
}
/**
* {@inheritDoc}
*/
public function fetchAllAssociative(): array
{
return $this->converter->convertAllAssociative(
parent::fetchAllAssociative(),
);
}
/**
* {@inheritDoc}
*/
public function fetchFirstColumn(): array
{
return $this->converter->convertFirstColumn(
parent::fetchFirstColumn(),
);
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace Doctrine\DBAL\Portability;
use Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
/**
* Portability wrapper for a Statement.
*/
final class Statement extends AbstractStatementMiddleware
{
private Converter $converter;
/**
* Wraps <tt>Statement</tt> and applies portability measures.
*/
public function __construct(DriverStatement $stmt, Converter $converter)
{
parent::__construct($stmt);
$this->converter = $converter;
}
/**
* {@inheritDoc}
*/
public function execute($params = null): ResultInterface
{
return new Result(
parent::execute($params),
$this->converter,
);
}
}