summaryrefslogtreecommitdiff
path: root/vendor/doctrine/dbal/src/Portability
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/dbal/src/Portability')
-rw-r--r--vendor/doctrine/dbal/src/Portability/Connection.php41
-rw-r--r--vendor/doctrine/dbal/src/Portability/Converter.php280
-rw-r--r--vendor/doctrine/dbal/src/Portability/Driver.php69
-rw-r--r--vendor/doctrine/dbal/src/Portability/Middleware.php25
-rw-r--r--vendor/doctrine/dbal/src/Portability/OptimizeFlags.php42
-rw-r--r--vendor/doctrine/dbal/src/Portability/Result.php68
-rw-r--r--vendor/doctrine/dbal/src/Portability/Statement.php31
7 files changed, 556 insertions, 0 deletions
diff --git a/vendor/doctrine/dbal/src/Portability/Connection.php b/vendor/doctrine/dbal/src/Portability/Connection.php
new file mode 100644
index 0000000..4a821d8
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Portability/Connection.php
@@ -0,0 +1,41 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Portability;
6
7use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
8use Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware;
9
10/**
11 * Portability wrapper for a Connection.
12 */
13final class Connection extends AbstractConnectionMiddleware
14{
15 public const PORTABILITY_ALL = 255;
16 public const PORTABILITY_NONE = 0;
17 public const PORTABILITY_RTRIM = 1;
18 public const PORTABILITY_EMPTY_TO_NULL = 4;
19 public const PORTABILITY_FIX_CASE = 8;
20
21 public function __construct(ConnectionInterface $connection, private readonly Converter $converter)
22 {
23 parent::__construct($connection);
24 }
25
26 public function prepare(string $sql): Statement
27 {
28 return new Statement(
29 parent::prepare($sql),
30 $this->converter,
31 );
32 }
33
34 public function query(string $sql): Result
35 {
36 return new Result(
37 parent::query($sql),
38 $this->converter,
39 );
40 }
41}
diff --git a/vendor/doctrine/dbal/src/Portability/Converter.php b/vendor/doctrine/dbal/src/Portability/Converter.php
new file mode 100644
index 0000000..09a4712
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Portability/Converter.php
@@ -0,0 +1,280 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Portability;
6
7use Closure;
8
9use function array_change_key_case;
10use function array_map;
11use function array_reduce;
12use function is_string;
13use function rtrim;
14
15use const CASE_LOWER;
16use const CASE_UPPER;
17
18final class Converter
19{
20 public const CASE_LOWER = CASE_LOWER;
21 public const CASE_UPPER = CASE_UPPER;
22
23 private readonly Closure $convertNumeric;
24 private readonly Closure $convertAssociative;
25 private readonly Closure $convertOne;
26 private readonly Closure $convertAllNumeric;
27 private readonly Closure $convertAllAssociative;
28 private readonly Closure $convertFirstColumn;
29
30 /**
31 * @param bool $convertEmptyStringToNull Whether each empty string should
32 * be converted to NULL
33 * @param bool $rightTrimString Whether each string should right-trimmed
34 * @param self::CASE_LOWER|self::CASE_UPPER|null $case Convert the case of the column names
35 * (one of {@see self::CASE_LOWER} and
36 * {@see self::CASE_UPPER})
37 */
38 public function __construct(bool $convertEmptyStringToNull, bool $rightTrimString, ?int $case)
39 {
40 $convertValue = $this->createConvertValue($convertEmptyStringToNull, $rightTrimString);
41 $convertNumeric = $this->createConvertRow($convertValue, null);
42 $convertAssociative = $this->createConvertRow($convertValue, $case);
43
44 $this->convertNumeric = $this->createConvert($convertNumeric);
45 $this->convertAssociative = $this->createConvert($convertAssociative);
46 $this->convertOne = $this->createConvert($convertValue);
47
48 $this->convertAllNumeric = $this->createConvertAll($convertNumeric);
49 $this->convertAllAssociative = $this->createConvertAll($convertAssociative);
50 $this->convertFirstColumn = $this->createConvertAll($convertValue);
51 }
52
53 /**
54 * @param array<int,mixed>|false $row
55 *
56 * @return list<mixed>|false
57 */
58 public function convertNumeric(array|false $row): array|false
59 {
60 return ($this->convertNumeric)($row);
61 }
62
63 /**
64 * @param array<string,mixed>|false $row
65 *
66 * @return array<string,mixed>|false
67 */
68 public function convertAssociative(array|false $row): array|false
69 {
70 return ($this->convertAssociative)($row);
71 }
72
73 public function convertOne(mixed $value): mixed
74 {
75 return ($this->convertOne)($value);
76 }
77
78 /**
79 * @param list<list<mixed>> $data
80 *
81 * @return list<list<mixed>>
82 */
83 public function convertAllNumeric(array $data): array
84 {
85 return ($this->convertAllNumeric)($data);
86 }
87
88 /**
89 * @param list<array<string,mixed>> $data
90 *
91 * @return list<array<string,mixed>>
92 */
93 public function convertAllAssociative(array $data): array
94 {
95 return ($this->convertAllAssociative)($data);
96 }
97
98 /**
99 * @param list<mixed> $data
100 *
101 * @return list<mixed>
102 */
103 public function convertFirstColumn(array $data): array
104 {
105 return ($this->convertFirstColumn)($data);
106 }
107
108 /**
109 * @param T $value
110 *
111 * @return T
112 *
113 * @template T
114 */
115 private static function id(mixed $value): mixed
116 {
117 return $value;
118 }
119
120 /**
121 * @param T $value
122 *
123 * @return T|null
124 *
125 * @template T
126 */
127 private static function convertEmptyStringToNull(mixed $value): mixed
128 {
129 if ($value === '') {
130 return null;
131 }
132
133 return $value;
134 }
135
136 /**
137 * @param T $value
138 *
139 * @return T|string
140 * @psalm-return (T is string ? string : T)
141 *
142 * @template T
143 */
144 private static function rightTrimString(mixed $value): mixed
145 {
146 if (! is_string($value)) {
147 return $value;
148 }
149
150 return rtrim($value);
151 }
152
153 /**
154 * Creates a function that will convert each individual value retrieved from the database
155 *
156 * @param bool $convertEmptyStringToNull Whether each empty string should be converted to NULL
157 * @param bool $rightTrimString Whether each string should right-trimmed
158 *
159 * @return Closure|null The resulting function or NULL if no conversion is needed
160 */
161 private function createConvertValue(bool $convertEmptyStringToNull, bool $rightTrimString): ?Closure
162 {
163 $functions = [];
164
165 if ($convertEmptyStringToNull) {
166 $functions[] = self::convertEmptyStringToNull(...);
167 }
168
169 if ($rightTrimString) {
170 $functions[] = self::rightTrimString(...);
171 }
172
173 return $this->compose(...$functions);
174 }
175
176 /**
177 * Creates a function that will convert each array-row retrieved from the database
178 *
179 * @param Closure|null $function The function that will convert each value
180 * @param self::CASE_LOWER|self::CASE_UPPER|null $case Column name case
181 *
182 * @return Closure|null The resulting function or NULL if no conversion is needed
183 */
184 private function createConvertRow(?Closure $function, ?int $case): ?Closure
185 {
186 $functions = [];
187
188 if ($function !== null) {
189 $functions[] = $this->createMapper($function);
190 }
191
192 if ($case !== null) {
193 $functions[] = static fn (array $row): array => array_change_key_case($row, $case);
194 }
195
196 return $this->compose(...$functions);
197 }
198
199 /**
200 * Creates a function that will be applied to the return value of Statement::fetch*()
201 * or an identity function if no conversion is needed
202 *
203 * @param Closure|null $function The function that will convert each tow
204 */
205 private function createConvert(?Closure $function): Closure
206 {
207 if ($function === null) {
208 return self::id(...);
209 }
210
211 return /**
212 * @param T $value
213 *
214 * @psalm-return (T is false ? false : T)
215 *
216 * @template T
217 */
218 static function (mixed $value) use ($function): mixed {
219 if ($value === false) {
220 return false;
221 }
222
223 return $function($value);
224 };
225 }
226
227 /**
228 * Creates a function that will be applied to the return value of Statement::fetchAll*()
229 * or an identity function if no transformation is required
230 *
231 * @param Closure|null $function The function that will transform each value
232 */
233 private function createConvertAll(?Closure $function): Closure
234 {
235 if ($function === null) {
236 return self::id(...);
237 }
238
239 return $this->createMapper($function);
240 }
241
242 /**
243 * Creates a function that maps each value of the array using the given function
244 *
245 * @param Closure $function The function that maps each value of the array
246 *
247 * @return Closure(array<mixed>):array<mixed>
248 */
249 private function createMapper(Closure $function): Closure
250 {
251 return static fn (array $array): array => array_map($function, $array);
252 }
253
254 /**
255 * Creates a composition of the given set of functions
256 *
257 * @param Closure(T):T ...$functions The functions to compose
258 *
259 * @return Closure(T):T|null
260 *
261 * @template T
262 */
263 private function compose(Closure ...$functions): ?Closure
264 {
265 return array_reduce($functions, static function (?Closure $carry, Closure $item): Closure {
266 if ($carry === null) {
267 return $item;
268 }
269
270 return /**
271 * @param T $value
272 *
273 * @return T
274 *
275 * @template T
276 */
277 static fn (mixed $value): mixed => $item($carry($value));
278 });
279 }
280}
diff --git a/vendor/doctrine/dbal/src/Portability/Driver.php b/vendor/doctrine/dbal/src/Portability/Driver.php
new file mode 100644
index 0000000..bb67c25
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Portability/Driver.php
@@ -0,0 +1,69 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Portability;
6
7use Doctrine\DBAL\ColumnCase;
8use Doctrine\DBAL\Driver as DriverInterface;
9use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
10use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
11use PDO;
12use SensitiveParameter;
13
14use const CASE_LOWER;
15use const CASE_UPPER;
16
17final class Driver extends AbstractDriverMiddleware
18{
19 public function __construct(
20 DriverInterface $driver,
21 private readonly int $mode,
22 private readonly ?ColumnCase $case,
23 ) {
24 parent::__construct($driver);
25 }
26
27 /**
28 * {@inheritDoc}
29 */
30 public function connect(
31 #[SensitiveParameter]
32 array $params,
33 ): ConnectionInterface {
34 $connection = parent::connect($params);
35
36 $portability = (new OptimizeFlags())(
37 $this->getDatabasePlatform($connection),
38 $this->mode,
39 );
40
41 $case = null;
42
43 if ($this->case !== null && ($portability & Connection::PORTABILITY_FIX_CASE) !== 0) {
44 $nativeConnection = $connection->getNativeConnection();
45
46 $case = match ($this->case) {
47 ColumnCase::LOWER => CASE_LOWER,
48 ColumnCase::UPPER => CASE_UPPER,
49 };
50
51 if ($nativeConnection instanceof PDO) {
52 $portability &= ~Connection::PORTABILITY_FIX_CASE;
53 $nativeConnection->setAttribute(PDO::ATTR_CASE, $case);
54 }
55 }
56
57 $convertEmptyStringToNull = ($portability & Connection::PORTABILITY_EMPTY_TO_NULL) !== 0;
58 $rightTrimString = ($portability & Connection::PORTABILITY_RTRIM) !== 0;
59
60 if (! $convertEmptyStringToNull && ! $rightTrimString && $case === null) {
61 return $connection;
62 }
63
64 return new Connection(
65 $connection,
66 new Converter($convertEmptyStringToNull, $rightTrimString, $case),
67 );
68 }
69}
diff --git a/vendor/doctrine/dbal/src/Portability/Middleware.php b/vendor/doctrine/dbal/src/Portability/Middleware.php
new file mode 100644
index 0000000..b97897c
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Portability/Middleware.php
@@ -0,0 +1,25 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Portability;
6
7use Doctrine\DBAL\ColumnCase;
8use Doctrine\DBAL\Driver as DriverInterface;
9use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;
10
11final class Middleware implements MiddlewareInterface
12{
13 public function __construct(private readonly int $mode, private readonly ?ColumnCase $case)
14 {
15 }
16
17 public function wrap(DriverInterface $driver): DriverInterface
18 {
19 if ($this->mode !== 0) {
20 return new Driver($driver, $this->mode, $this->case);
21 }
22
23 return $driver;
24 }
25}
diff --git a/vendor/doctrine/dbal/src/Portability/OptimizeFlags.php b/vendor/doctrine/dbal/src/Portability/OptimizeFlags.php
new file mode 100644
index 0000000..c985d4b
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Portability/OptimizeFlags.php
@@ -0,0 +1,42 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Portability;
6
7use Doctrine\DBAL\Platforms\AbstractPlatform;
8use Doctrine\DBAL\Platforms\DB2Platform;
9use Doctrine\DBAL\Platforms\OraclePlatform;
10use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
11use Doctrine\DBAL\Platforms\SQLitePlatform;
12use Doctrine\DBAL\Platforms\SQLServerPlatform;
13
14final class OptimizeFlags
15{
16 /**
17 * Platform-specific portability flags that need to be excluded from the user-provided mode
18 * since the platform already operates in this mode to avoid unnecessary conversion overhead.
19 *
20 * @var array<class-string, int>
21 */
22 private static array $platforms = [
23 DB2Platform::class => 0,
24 OraclePlatform::class => Connection::PORTABILITY_EMPTY_TO_NULL,
25 PostgreSQLPlatform::class => 0,
26 SQLitePlatform::class => 0,
27 SQLServerPlatform::class => 0,
28 ];
29
30 public function __invoke(AbstractPlatform $platform, int $flags): int
31 {
32 foreach (self::$platforms as $class => $mask) {
33 if ($platform instanceof $class) {
34 $flags &= ~$mask;
35
36 break;
37 }
38 }
39
40 return $flags;
41 }
42}
diff --git a/vendor/doctrine/dbal/src/Portability/Result.php b/vendor/doctrine/dbal/src/Portability/Result.php
new file mode 100644
index 0000000..d158683
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Portability/Result.php
@@ -0,0 +1,68 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Portability;
6
7use Doctrine\DBAL\Driver\Middleware\AbstractResultMiddleware;
8use Doctrine\DBAL\Driver\Result as ResultInterface;
9
10final class Result extends AbstractResultMiddleware
11{
12 /** @internal The result can be only instantiated by the portability connection or statement. */
13 public function __construct(ResultInterface $result, private readonly Converter $converter)
14 {
15 parent::__construct($result);
16 }
17
18 public function fetchNumeric(): array|false
19 {
20 return $this->converter->convertNumeric(
21 parent::fetchNumeric(),
22 );
23 }
24
25 public function fetchAssociative(): array|false
26 {
27 return $this->converter->convertAssociative(
28 parent::fetchAssociative(),
29 );
30 }
31
32 public function fetchOne(): mixed
33 {
34 return $this->converter->convertOne(
35 parent::fetchOne(),
36 );
37 }
38
39 /**
40 * {@inheritDoc}
41 */
42 public function fetchAllNumeric(): array
43 {
44 return $this->converter->convertAllNumeric(
45 parent::fetchAllNumeric(),
46 );
47 }
48
49 /**
50 * {@inheritDoc}
51 */
52 public function fetchAllAssociative(): array
53 {
54 return $this->converter->convertAllAssociative(
55 parent::fetchAllAssociative(),
56 );
57 }
58
59 /**
60 * {@inheritDoc}
61 */
62 public function fetchFirstColumn(): array
63 {
64 return $this->converter->convertFirstColumn(
65 parent::fetchFirstColumn(),
66 );
67 }
68}
diff --git a/vendor/doctrine/dbal/src/Portability/Statement.php b/vendor/doctrine/dbal/src/Portability/Statement.php
new file mode 100644
index 0000000..de0c76f
--- /dev/null
+++ b/vendor/doctrine/dbal/src/Portability/Statement.php
@@ -0,0 +1,31 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Portability;
6
7use Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware;
8use Doctrine\DBAL\Driver\Result as ResultInterface;
9use Doctrine\DBAL\Driver\Statement as DriverStatement;
10
11/**
12 * Portability wrapper for a Statement.
13 */
14final class Statement extends AbstractStatementMiddleware
15{
16 /**
17 * Wraps <tt>Statement</tt> and applies portability measures.
18 */
19 public function __construct(DriverStatement $stmt, private readonly Converter $converter)
20 {
21 parent::__construct($stmt);
22 }
23
24 public function execute(): ResultInterface
25 {
26 return new Result(
27 parent::execute(),
28 $this->converter,
29 );
30 }
31}