summaryrefslogtreecommitdiff
path: root/vendor/doctrine/dbal/src/Portability/Converter.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/dbal/src/Portability/Converter.php')
-rw-r--r--vendor/doctrine/dbal/src/Portability/Converter.php280
1 files changed, 280 insertions, 0 deletions
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}