Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
100.00% |
109 / 109 |
|
98.81% |
83 / 84 |
|
43.75% |
28 / 64 |
|
94.74% |
18 / 19 |
CRAP | |
0.00% |
0 / 1 |
| Query | |
100.00% |
109 / 109 |
|
98.81% |
83 / 84 |
|
43.75% |
28 / 64 |
|
100.00% |
19 / 19 |
372.08 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
5 / 5 |
|
100.00% |
5 / 5 |
|
75.00% |
3 / 4 |
|
100.00% |
1 / 1 |
3.14 | |||
| __toString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| one | |
100.00% |
9 / 9 |
|
100.00% |
7 / 7 |
|
25.00% |
1 / 4 |
|
100.00% |
1 / 1 |
6.80 | |||
| first | |
100.00% |
5 / 5 |
|
100.00% |
6 / 6 |
|
0.00% |
0 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| fetch | |
100.00% |
4 / 4 |
|
100.00% |
4 / 4 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| all | |
100.00% |
12 / 12 |
|
100.00% |
9 / 9 |
|
0.00% |
0 / 8 |
|
100.00% |
1 / 1 |
3 | |||
| lazy | |
100.00% |
9 / 9 |
|
88.89% |
8 / 9 |
|
0.00% |
0 / 6 |
|
100.00% |
1 / 1 |
3 | |||
| terminalOptions | |
100.00% |
4 / 4 |
|
100.00% |
8 / 8 |
|
25.00% |
2 / 8 |
|
100.00% |
1 / 1 |
10.75 | |||
| executeFresh | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| executeForFetch | |
100.00% |
5 / 5 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| hydrateRecord | |
100.00% |
4 / 4 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| hydrator | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| run | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| len | |
100.00% |
3 / 3 |
|
100.00% |
3 / 3 |
|
0.00% |
0 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| interpolate | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| bindArgs | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| bindValue | |
100.00% |
25 / 25 |
|
100.00% |
17 / 17 |
|
53.85% |
7 / 13 |
|
100.00% |
1 / 1 |
14.29 | |||
| fetchArrayRecord | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| nullIfNot | |
100.00% |
3 / 3 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Celemas\Quma; |
| 6 | |
| 7 | use Celemas\Quma\Exception\UnexpectedResultCount; |
| 8 | use Celemas\Quma\Hydration\Hydrator; |
| 9 | use Closure; |
| 10 | use Generator; |
| 11 | use InvalidArgumentException; |
| 12 | use JsonException; |
| 13 | use PDO; |
| 14 | use PDOStatement; |
| 15 | |
| 16 | /** @api */ |
| 17 | class Query |
| 18 | { |
| 19 | // Matches multi line single and double quotes and handles \' \" escapes |
| 20 | public const string PATTERN_STRING = '/([\'"])(?:\\\1|[\s\S])*?\1/'; |
| 21 | |
| 22 | // PostgreSQL blocks delimited with $$ |
| 23 | public const string PATTERN_BLOCK = '/(\$\$)[\s\S]*?\1/'; |
| 24 | |
| 25 | // Multi line comments /* */ |
| 26 | public const string PATTERN_COMMENT_MULTI = '/\/\*([\s\S]*?)\*\//'; |
| 27 | |
| 28 | // Single line comments -- |
| 29 | public const string PATTERN_COMMENT_SINGLE = '/--.*$/m'; |
| 30 | |
| 31 | protected PDOStatement $stmt; |
| 32 | protected bool $executed = false; |
| 33 | protected ?Hydrator $hydrator = null; |
| 34 | |
| 35 | public function __construct( |
| 36 | protected Database $db, |
| 37 | protected string $query, |
| 38 | protected Args $args, |
| 39 | protected ?string $sourcePath = null, |
| 40 | ) { |
| 41 | $this->stmt = $this->db->getConn()->prepare($query); |
| 42 | |
| 43 | if ($args->count() > 0) { |
| 44 | $this->bindArgs($args->get(), $args->type()); |
| 45 | } |
| 46 | |
| 47 | if ($this->db->debug) { |
| 48 | Debug::query($this->db, $this->query, $this->args, $this->sourcePath); |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | public function __toString(): string |
| 53 | { |
| 54 | return $this->interpolate(); |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * @template T of object |
| 59 | * |
| 60 | * @param class-string<T>|Closure(array<string, mixed>):class-string<T>|null $map |
| 61 | * @return ($map is null ? array<array-key, mixed> : T) |
| 62 | */ |
| 63 | public function one(string|Closure|null $map = null, ?int $fetchMode = null): array|object |
| 64 | { |
| 65 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 66 | $this->executeFresh(); |
| 67 | |
| 68 | try { |
| 69 | $record = $this->fetchArrayRecord($fetchMode); |
| 70 | |
| 71 | if ($record === null) { |
| 72 | throw UnexpectedResultCount::none(); |
| 73 | } |
| 74 | |
| 75 | if ($this->fetchArrayRecord($fetchMode) !== null) { |
| 76 | throw UnexpectedResultCount::multiple(); |
| 77 | } |
| 78 | |
| 79 | return $this->hydrateRecord($record, $map); |
| 80 | } finally { |
| 81 | $this->stmt->closeCursor(); |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * @template T of object |
| 87 | * |
| 88 | * @param class-string<T>|Closure(array<string, mixed>):class-string<T>|null $map |
| 89 | * @return ($map is null ? array<array-key, mixed>|null : T|null) |
| 90 | */ |
| 91 | public function first(string|Closure|null $map = null, ?int $fetchMode = null): array|object|null |
| 92 | { |
| 93 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 94 | $this->executeFresh(); |
| 95 | |
| 96 | try { |
| 97 | $record = $this->fetchArrayRecord($fetchMode); |
| 98 | |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 100 | } finally { |
| 101 | $this->stmt->closeCursor(); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * @template T of object |
| 107 | * |
| 108 | * @param class-string<T>|Closure(array<string, mixed>):class-string<T>|null $map |
| 109 | * @return ($map is null ? array<array-key, mixed>|null : T|null) |
| 110 | */ |
| 111 | public function fetch(string|Closure|null $map = null, ?int $fetchMode = null): array|object|null |
| 112 | { |
| 113 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 114 | $this->executeForFetch(); |
| 115 | |
| 116 | $record = $this->fetchArrayRecord($fetchMode); |
| 117 | |
| 118 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * @template T of object |
| 123 | * |
| 124 | * @param class-string<T>|Closure(array<string, mixed>):class-string<T>|null $map |
| 125 | * @return ($map is null ? list<array<array-key, mixed>> : list<T>) |
| 126 | */ |
| 127 | public function all(string|Closure|null $map = null, ?int $fetchMode = null): array |
| 128 | { |
| 129 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 130 | $this->executeFresh(); |
| 131 | |
| 132 | try { |
| 133 | if ($map === null) { |
| 134 | /** |
| 135 | * @mago-expect lint:inline-variable-return Psalm makes this necessary |
| 136 | * @var list<array<array-key, mixed>> $records |
| 137 | */ |
| 138 | $records = $this->stmt->fetchAll($fetchMode); |
| 139 | |
| 140 | return $records; |
| 141 | } |
| 142 | |
| 143 | /** @var list<T> $result */ |
| 144 | $result = []; |
| 145 | /** @var list<array<array-key, mixed>> $records */ |
| 146 | $records = $this->stmt->fetchAll($fetchMode); |
| 147 | |
| 148 | foreach ($records as $record) { |
| 149 | /** @var T $object */ |
| 150 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 151 | $result[] = $object; |
| 152 | } |
| 153 | |
| 154 | return $result; |
| 155 | } finally { |
| 156 | $this->stmt->closeCursor(); |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | /** |
| 161 | * @template T of object |
| 162 | * |
| 163 | * @param class-string<T>|Closure(array<string, mixed>):class-string<T>|null $map |
| 164 | * @return ($map is null |
| 165 | * ? Generator<int, array<array-key, mixed>, mixed, void> |
| 166 | * : Generator<int, T, mixed, void>) |
| 167 | */ |
| 168 | public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator |
| 169 | { |
| 170 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 171 | $this->executeFresh(); |
| 172 | |
| 173 | try { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 175 | if ($map === null) { |
| 176 | yield $record; |
| 177 | |
| 178 | continue; |
| 179 | } |
| 180 | |
| 181 | /** @var T $object */ |
| 182 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 183 | |
| 184 | yield $object; |
| 185 | } |
| 186 | } finally { |
| 187 | $this->stmt->closeCursor(); |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | /** |
| 192 | * @return array{0: string|Closure|null, 1: int} |
| 193 | */ |
| 194 | private function terminalOptions(string|Closure|null $map, ?int $fetchMode): array |
| 195 | { |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 197 | |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 199 | throw new InvalidArgumentException('Hydration requires PDO::FETCH_ASSOC.'); |
| 200 | } |
| 201 | |
| 202 | return [$map, $mode]; |
| 203 | } |
| 204 | |
| 205 | private function executeFresh(): void |
| 206 | { |
| 207 | $this->db->connect(); |
| 208 | $this->stmt->closeCursor(); |
| 209 | $this->stmt->execute(); |
| 210 | $this->executed = false; |
| 211 | } |
| 212 | |
| 213 | private function executeForFetch(): void |
| 214 | { |
| 215 | $this->db->connect(); |
| 216 | |
| 217 | if (!$this->executed) { |
| 218 | $this->stmt->closeCursor(); |
| 219 | $this->stmt->execute(); |
| 220 | $this->executed = true; |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | /** |
| 225 | * @template T of object |
| 226 | * |
| 227 | * @param array<array-key, mixed> $record |
| 228 | * @param string|Closure(array<string, mixed>):class-string<T>|null $map |
| 229 | * @return ($map is null ? array<array-key, mixed> : T) |
| 230 | */ |
| 231 | private function hydrateRecord(array $record, string|Closure|null $map): array|object |
| 232 | { |
| 233 | if ($map === null) { |
| 234 | return $record; |
| 235 | } |
| 236 | |
| 237 | /** |
| 238 | * @mago-expect lint:inline-variable-return Psalm makes this necessary |
| 239 | * @var T $object |
| 240 | */ |
| 241 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 242 | |
| 243 | return $object; |
| 244 | } |
| 245 | |
| 246 | private function hydrator(): Hydrator |
| 247 | { |
| 248 | return $this->hydrator ??= Hydrator::default(); |
| 249 | } |
| 250 | |
| 251 | public function run(): bool |
| 252 | { |
| 253 | $this->db->connect(); |
| 254 | $this->stmt->closeCursor(); |
| 255 | $this->executed = false; |
| 256 | |
| 257 | return $this->stmt->execute(); |
| 258 | } |
| 259 | |
| 260 | public function len(): int |
| 261 | { |
| 262 | $this->executeFresh(); |
| 263 | |
| 264 | try { |
| 265 | return $this->stmt->rowCount(); |
| 266 | } finally { |
| 267 | $this->stmt->closeCursor(); |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | /** |
| 272 | * For debugging purposes only. |
| 273 | * |
| 274 | * Replaces any parameter placeholders in a query with the |
| 275 | * value of that parameter and returns the query as string. |
| 276 | * |
| 277 | * Covers most of the cases but is not perfect. |
| 278 | */ |
| 279 | public function interpolate(): string |
| 280 | { |
| 281 | return Debug::interpolate($this->query, $this->args); |
| 282 | } |
| 283 | |
| 284 | protected function bindArgs(array $args, ArgType $argType): void |
| 285 | { |
| 286 | array_walk( |
| 287 | $args, |
| 288 | function (mixed $value, int|string $index) use ($argType): void { |
| 289 | if ($argType === ArgType::Named) { |
| 290 | $arg = ':' . $index; |
| 291 | } else { |
| 292 | $arg = (int) $index + 1; // question mark placeholders are 1-indexed |
| 293 | } |
| 294 | |
| 295 | $this->bindValue($arg, $value); |
| 296 | }, |
| 297 | ); |
| 298 | } |
| 299 | |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 303 | case 'boolean': |
| 304 | $this->stmt->bindValue($arg, $value, PDO::PARAM_BOOL); |
| 305 | |
| 306 | break; |
| 307 | |
| 308 | case 'integer': |
| 309 | $this->stmt->bindValue($arg, $value, PDO::PARAM_INT); |
| 310 | |
| 311 | break; |
| 312 | |
| 313 | case 'string': |
| 314 | $this->stmt->bindValue($arg, $value, PDO::PARAM_STR); |
| 315 | |
| 316 | break; |
| 317 | |
| 318 | case 'NULL': |
| 319 | $this->stmt->bindValue($arg, $value, PDO::PARAM_NULL); |
| 320 | |
| 321 | break; |
| 322 | |
| 323 | case 'array': |
| 324 | try { |
| 325 | $json = json_encode($value, JSON_THROW_ON_ERROR); |
| 326 | } catch (JsonException $e) { |
| 327 | throw new InvalidArgumentException( |
| 328 | 'Array parameters must be JSON-encodable.', |
| 329 | previous: $e, |
| 330 | ); |
| 331 | } |
| 332 | |
| 333 | $this->stmt->bindValue($arg, $json, PDO::PARAM_STR); |
| 334 | |
| 335 | break; |
| 336 | |
| 337 | default: |
| 338 | throw new InvalidArgumentException( |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 340 | ); |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | protected function fetchArrayRecord(int $fetchMode): ?array |
| 345 | { |
| 346 | return $this->nullIfNot($this->stmt->fetch($fetchMode)); |
| 347 | } |
| 348 | |
| 349 | protected function nullIfNot(mixed $value): ?array |
| 350 | { |
| 351 | if (is_array($value)) { |
| 352 | return $value; |
| 353 | } |
| 354 | |
| 355 | return null; |
| 356 | } |
| 357 | } |
Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not
necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once.
Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 36 | protected Database $db, |
| 37 | protected string $query, |
| 38 | protected Args $args, |
| 39 | protected ?string $sourcePath = null, |
| 40 | ) { |
| 41 | $this->stmt = $this->db->getConn()->prepare($query); |
| 42 | |
| 43 | if ($args->count() > 0) { |
| 44 | $this->bindArgs($args->get(), $args->type()); |
| 45 | } |
| 46 | |
| 47 | if ($this->db->debug) { |
| 47 | if ($this->db->debug) { |
| 48 | Debug::query($this->db, $this->query, $this->args, $this->sourcePath); |
| 49 | } |
| 50 | } |
| 50 | } |
| 36 | protected Database $db, |
| 37 | protected string $query, |
| 38 | protected Args $args, |
| 39 | protected ?string $sourcePath = null, |
| 40 | ) { |
| 41 | $this->stmt = $this->db->getConn()->prepare($query); |
| 42 | |
| 43 | if ($args->count() > 0) { |
| 44 | $this->bindArgs($args->get(), $args->type()); |
| 45 | } |
| 46 | |
| 47 | if ($this->db->debug) { |
| 47 | if ($this->db->debug) { |
| 50 | } |
| 36 | protected Database $db, |
| 37 | protected string $query, |
| 38 | protected Args $args, |
| 39 | protected ?string $sourcePath = null, |
| 40 | ) { |
| 41 | $this->stmt = $this->db->getConn()->prepare($query); |
| 42 | |
| 43 | if ($args->count() > 0) { |
| 47 | if ($this->db->debug) { |
| 48 | Debug::query($this->db, $this->query, $this->args, $this->sourcePath); |
| 49 | } |
| 50 | } |
| 50 | } |
| 36 | protected Database $db, |
| 37 | protected string $query, |
| 38 | protected Args $args, |
| 39 | protected ?string $sourcePath = null, |
| 40 | ) { |
| 41 | $this->stmt = $this->db->getConn()->prepare($query); |
| 42 | |
| 43 | if ($args->count() > 0) { |
| 47 | if ($this->db->debug) { |
| 50 | } |
| 54 | return $this->interpolate(); |
| 55 | } |
| 127 | public function all(string|Closure|null $map = null, ?int $fetchMode = null): array |
| 128 | { |
| 129 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 130 | $this->executeFresh(); |
| 131 | |
| 132 | try { |
| 133 | if ($map === null) { |
| 138 | $records = $this->stmt->fetchAll($fetchMode); |
| 139 | |
| 140 | return $records; |
| 156 | $this->stmt->closeCursor(); |
| 157 | } |
| 158 | } |
| 127 | public function all(string|Closure|null $map = null, ?int $fetchMode = null): array |
| 128 | { |
| 129 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 130 | $this->executeFresh(); |
| 131 | |
| 132 | try { |
| 133 | if ($map === null) { |
| 138 | $records = $this->stmt->fetchAll($fetchMode); |
| 139 | |
| 140 | return $records; |
| 140 | return $records; |
| 127 | public function all(string|Closure|null $map = null, ?int $fetchMode = null): array |
| 128 | { |
| 129 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 130 | $this->executeFresh(); |
| 131 | |
| 132 | try { |
| 133 | if ($map === null) { |
| 144 | $result = []; |
| 145 | /** @var list<array<array-key, mixed>> $records */ |
| 146 | $records = $this->stmt->fetchAll($fetchMode); |
| 147 | |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 149 | /** @var T $object */ |
| 150 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 149 | /** @var T $object */ |
| 150 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 151 | $result[] = $object; |
| 152 | } |
| 153 | |
| 154 | return $result; |
| 156 | $this->stmt->closeCursor(); |
| 157 | } |
| 158 | } |
| 127 | public function all(string|Closure|null $map = null, ?int $fetchMode = null): array |
| 128 | { |
| 129 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 130 | $this->executeFresh(); |
| 131 | |
| 132 | try { |
| 133 | if ($map === null) { |
| 144 | $result = []; |
| 145 | /** @var list<array<array-key, mixed>> $records */ |
| 146 | $records = $this->stmt->fetchAll($fetchMode); |
| 147 | |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 149 | /** @var T $object */ |
| 150 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 149 | /** @var T $object */ |
| 150 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 151 | $result[] = $object; |
| 152 | } |
| 153 | |
| 154 | return $result; |
| 154 | return $result; |
| 127 | public function all(string|Closure|null $map = null, ?int $fetchMode = null): array |
| 128 | { |
| 129 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 130 | $this->executeFresh(); |
| 131 | |
| 132 | try { |
| 133 | if ($map === null) { |
| 144 | $result = []; |
| 145 | /** @var list<array<array-key, mixed>> $records */ |
| 146 | $records = $this->stmt->fetchAll($fetchMode); |
| 147 | |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 149 | /** @var T $object */ |
| 150 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 151 | $result[] = $object; |
| 152 | } |
| 153 | |
| 154 | return $result; |
| 156 | $this->stmt->closeCursor(); |
| 157 | } |
| 158 | } |
| 127 | public function all(string|Closure|null $map = null, ?int $fetchMode = null): array |
| 128 | { |
| 129 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 130 | $this->executeFresh(); |
| 131 | |
| 132 | try { |
| 133 | if ($map === null) { |
| 144 | $result = []; |
| 145 | /** @var list<array<array-key, mixed>> $records */ |
| 146 | $records = $this->stmt->fetchAll($fetchMode); |
| 147 | |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 149 | /** @var T $object */ |
| 150 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 151 | $result[] = $object; |
| 152 | } |
| 153 | |
| 154 | return $result; |
| 154 | return $result; |
| 127 | public function all(string|Closure|null $map = null, ?int $fetchMode = null): array |
| 128 | { |
| 129 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 130 | $this->executeFresh(); |
| 131 | |
| 132 | try { |
| 133 | if ($map === null) { |
| 144 | $result = []; |
| 145 | /** @var list<array<array-key, mixed>> $records */ |
| 146 | $records = $this->stmt->fetchAll($fetchMode); |
| 147 | |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 149 | /** @var T $object */ |
| 150 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 151 | $result[] = $object; |
| 152 | } |
| 153 | |
| 154 | return $result; |
| 156 | $this->stmt->closeCursor(); |
| 157 | } |
| 158 | } |
| 127 | public function all(string|Closure|null $map = null, ?int $fetchMode = null): array |
| 128 | { |
| 129 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 130 | $this->executeFresh(); |
| 131 | |
| 132 | try { |
| 133 | if ($map === null) { |
| 144 | $result = []; |
| 145 | /** @var list<array<array-key, mixed>> $records */ |
| 146 | $records = $this->stmt->fetchAll($fetchMode); |
| 147 | |
| 148 | foreach ($records as $record) { |
| 148 | foreach ($records as $record) { |
| 149 | /** @var T $object */ |
| 150 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 151 | $result[] = $object; |
| 152 | } |
| 153 | |
| 154 | return $result; |
| 154 | return $result; |
| 284 | protected function bindArgs(array $args, ArgType $argType): void |
| 285 | { |
| 286 | array_walk( |
| 287 | $args, |
| 288 | function (mixed $value, int|string $index) use ($argType): void { |
| 289 | if ($argType === ArgType::Named) { |
| 290 | $arg = ':' . $index; |
| 291 | } else { |
| 292 | $arg = (int) $index + 1; // question mark placeholders are 1-indexed |
| 293 | } |
| 294 | |
| 295 | $this->bindValue($arg, $value); |
| 296 | }, |
| 297 | ); |
| 298 | } |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 304 | $this->stmt->bindValue($arg, $value, PDO::PARAM_BOOL); |
| 305 | |
| 306 | break; |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 340 | ); |
| 341 | } |
| 342 | } |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 309 | $this->stmt->bindValue($arg, $value, PDO::PARAM_INT); |
| 310 | |
| 311 | break; |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 340 | ); |
| 341 | } |
| 342 | } |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 314 | $this->stmt->bindValue($arg, $value, PDO::PARAM_STR); |
| 315 | |
| 316 | break; |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 340 | ); |
| 341 | } |
| 342 | } |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 319 | $this->stmt->bindValue($arg, $value, PDO::PARAM_NULL); |
| 320 | |
| 321 | break; |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 340 | ); |
| 341 | } |
| 342 | } |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 324 | try { |
| 325 | $json = json_encode($value, JSON_THROW_ON_ERROR); |
| 333 | $this->stmt->bindValue($arg, $json, PDO::PARAM_STR); |
| 334 | |
| 335 | break; |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 340 | ); |
| 341 | } |
| 342 | } |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 338 | throw new InvalidArgumentException( |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 303 | case 'boolean': |
| 308 | case 'integer': |
| 313 | case 'string': |
| 318 | case 'NULL': |
| 323 | case 'array': |
| 323 | case 'array': |
| 338 | throw new InvalidArgumentException( |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 303 | case 'boolean': |
| 308 | case 'integer': |
| 313 | case 'string': |
| 318 | case 'NULL': |
| 323 | case 'array': |
| 324 | try { |
| 325 | $json = json_encode($value, JSON_THROW_ON_ERROR); |
| 333 | $this->stmt->bindValue($arg, $json, PDO::PARAM_STR); |
| 334 | |
| 335 | break; |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 340 | ); |
| 341 | } |
| 342 | } |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 303 | case 'boolean': |
| 308 | case 'integer': |
| 313 | case 'string': |
| 318 | case 'NULL': |
| 319 | $this->stmt->bindValue($arg, $value, PDO::PARAM_NULL); |
| 320 | |
| 321 | break; |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 340 | ); |
| 341 | } |
| 342 | } |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 303 | case 'boolean': |
| 308 | case 'integer': |
| 313 | case 'string': |
| 314 | $this->stmt->bindValue($arg, $value, PDO::PARAM_STR); |
| 315 | |
| 316 | break; |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 340 | ); |
| 341 | } |
| 342 | } |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 303 | case 'boolean': |
| 308 | case 'integer': |
| 309 | $this->stmt->bindValue($arg, $value, PDO::PARAM_INT); |
| 310 | |
| 311 | break; |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 340 | ); |
| 341 | } |
| 342 | } |
| 300 | protected function bindValue(string|int $arg, mixed $value): void |
| 301 | { |
| 302 | switch (gettype($value)) { |
| 303 | case 'boolean': |
| 304 | $this->stmt->bindValue($arg, $value, PDO::PARAM_BOOL); |
| 305 | |
| 306 | break; |
| 339 | 'Only the types bool, int, string, null and array are supported', |
| 340 | ); |
| 341 | } |
| 342 | } |
| 326 | } catch (JsonException $e) { |
| 327 | throw new InvalidArgumentException( |
| 328 | 'Array parameters must be JSON-encodable.', |
| 215 | $this->db->connect(); |
| 216 | |
| 217 | if (!$this->executed) { |
| 218 | $this->stmt->closeCursor(); |
| 219 | $this->stmt->execute(); |
| 220 | $this->executed = true; |
| 221 | } |
| 222 | } |
| 222 | } |
| 215 | $this->db->connect(); |
| 216 | |
| 217 | if (!$this->executed) { |
| 222 | } |
| 207 | $this->db->connect(); |
| 208 | $this->stmt->closeCursor(); |
| 209 | $this->stmt->execute(); |
| 210 | $this->executed = false; |
| 211 | } |
| 111 | public function fetch(string|Closure|null $map = null, ?int $fetchMode = null): array|object|null |
| 112 | { |
| 113 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 114 | $this->executeForFetch(); |
| 115 | |
| 116 | $record = $this->fetchArrayRecord($fetchMode); |
| 117 | |
| 118 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 118 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 118 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 119 | } |
| 111 | public function fetch(string|Closure|null $map = null, ?int $fetchMode = null): array|object|null |
| 112 | { |
| 113 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 114 | $this->executeForFetch(); |
| 115 | |
| 116 | $record = $this->fetchArrayRecord($fetchMode); |
| 117 | |
| 118 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 118 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 118 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 119 | } |
| 344 | protected function fetchArrayRecord(int $fetchMode): ?array |
| 345 | { |
| 346 | return $this->nullIfNot($this->stmt->fetch($fetchMode)); |
| 347 | } |
| 91 | public function first(string|Closure|null $map = null, ?int $fetchMode = null): array|object|null |
| 92 | { |
| 93 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 94 | $this->executeFresh(); |
| 95 | |
| 96 | try { |
| 97 | $record = $this->fetchArrayRecord($fetchMode); |
| 98 | |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 101 | $this->stmt->closeCursor(); |
| 102 | } |
| 103 | } |
| 91 | public function first(string|Closure|null $map = null, ?int $fetchMode = null): array|object|null |
| 92 | { |
| 93 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 94 | $this->executeFresh(); |
| 95 | |
| 96 | try { |
| 97 | $record = $this->fetchArrayRecord($fetchMode); |
| 98 | |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 91 | public function first(string|Closure|null $map = null, ?int $fetchMode = null): array|object|null |
| 92 | { |
| 93 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 94 | $this->executeFresh(); |
| 95 | |
| 96 | try { |
| 97 | $record = $this->fetchArrayRecord($fetchMode); |
| 98 | |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 101 | $this->stmt->closeCursor(); |
| 102 | } |
| 103 | } |
| 91 | public function first(string|Closure|null $map = null, ?int $fetchMode = null): array|object|null |
| 92 | { |
| 93 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 94 | $this->executeFresh(); |
| 95 | |
| 96 | try { |
| 97 | $record = $this->fetchArrayRecord($fetchMode); |
| 98 | |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 99 | return $record === null ? null : $this->hydrateRecord($record, $map); |
| 231 | private function hydrateRecord(array $record, string|Closure|null $map): array|object |
| 232 | { |
| 233 | if ($map === null) { |
| 234 | return $record; |
| 231 | private function hydrateRecord(array $record, string|Closure|null $map): array|object |
| 232 | { |
| 233 | if ($map === null) { |
| 241 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 242 | |
| 243 | return $object; |
| 244 | } |
| 248 | return $this->hydrator ??= Hydrator::default(); |
| 249 | } |
| 281 | return Debug::interpolate($this->query, $this->args); |
| 282 | } |
| 168 | public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator |
| 169 | { |
| 170 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 171 | $this->executeFresh(); |
| 172 | |
| 173 | try { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 186 | } finally { |
| 187 | $this->stmt->closeCursor(); |
| 168 | public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator |
| 169 | { |
| 170 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 171 | $this->executeFresh(); |
| 172 | |
| 173 | try { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 186 | } finally { |
| 186 | } finally { |
| 189 | } |
| 168 | public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator |
| 169 | { |
| 170 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 171 | $this->executeFresh(); |
| 172 | |
| 173 | try { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 175 | if ($map === null) { |
| 176 | yield $record; |
| 177 | |
| 178 | continue; |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 186 | } finally { |
| 187 | $this->stmt->closeCursor(); |
| 168 | public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator |
| 169 | { |
| 170 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 171 | $this->executeFresh(); |
| 172 | |
| 173 | try { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 175 | if ($map === null) { |
| 176 | yield $record; |
| 177 | |
| 178 | continue; |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 186 | } finally { |
| 186 | } finally { |
| 189 | } |
| 168 | public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator |
| 169 | { |
| 170 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 171 | $this->executeFresh(); |
| 172 | |
| 173 | try { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 175 | if ($map === null) { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 175 | if ($map === null) { |
| 176 | yield $record; |
| 177 | |
| 178 | continue; |
| 179 | } |
| 180 | |
| 181 | /** @var T $object */ |
| 182 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 186 | } finally { |
| 187 | $this->stmt->closeCursor(); |
| 168 | public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator |
| 169 | { |
| 170 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 171 | $this->executeFresh(); |
| 172 | |
| 173 | try { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 175 | if ($map === null) { |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 175 | if ($map === null) { |
| 176 | yield $record; |
| 177 | |
| 178 | continue; |
| 179 | } |
| 180 | |
| 181 | /** @var T $object */ |
| 182 | $object = $this->hydrator()->hydrate($record, $map, $this->sourcePath); |
| 174 | while (($record = $this->fetchArrayRecord($fetchMode)) !== null) { |
| 186 | } finally { |
| 186 | } finally { |
| 189 | } |
| 262 | $this->executeFresh(); |
| 263 | |
| 264 | try { |
| 265 | return $this->stmt->rowCount(); |
| 267 | $this->stmt->closeCursor(); |
| 268 | } |
| 269 | } |
| 262 | $this->executeFresh(); |
| 263 | |
| 264 | try { |
| 265 | return $this->stmt->rowCount(); |
| 265 | return $this->stmt->rowCount(); |
| 349 | protected function nullIfNot(mixed $value): ?array |
| 350 | { |
| 351 | if (is_array($value)) { |
| 352 | return $value; |
| 349 | protected function nullIfNot(mixed $value): ?array |
| 350 | { |
| 351 | if (is_array($value)) { |
| 355 | return null; |
| 356 | } |
| 63 | public function one(string|Closure|null $map = null, ?int $fetchMode = null): array|object |
| 64 | { |
| 65 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 66 | $this->executeFresh(); |
| 67 | |
| 68 | try { |
| 69 | $record = $this->fetchArrayRecord($fetchMode); |
| 70 | |
| 71 | if ($record === null) { |
| 72 | throw UnexpectedResultCount::none(); |
| 63 | public function one(string|Closure|null $map = null, ?int $fetchMode = null): array|object |
| 64 | { |
| 65 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 66 | $this->executeFresh(); |
| 67 | |
| 68 | try { |
| 69 | $record = $this->fetchArrayRecord($fetchMode); |
| 70 | |
| 71 | if ($record === null) { |
| 75 | if ($this->fetchArrayRecord($fetchMode) !== null) { |
| 76 | throw UnexpectedResultCount::multiple(); |
| 63 | public function one(string|Closure|null $map = null, ?int $fetchMode = null): array|object |
| 64 | { |
| 65 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 66 | $this->executeFresh(); |
| 67 | |
| 68 | try { |
| 69 | $record = $this->fetchArrayRecord($fetchMode); |
| 70 | |
| 71 | if ($record === null) { |
| 75 | if ($this->fetchArrayRecord($fetchMode) !== null) { |
| 79 | return $this->hydrateRecord($record, $map); |
| 81 | $this->stmt->closeCursor(); |
| 82 | } |
| 83 | } |
| 63 | public function one(string|Closure|null $map = null, ?int $fetchMode = null): array|object |
| 64 | { |
| 65 | [$map, $fetchMode] = $this->terminalOptions($map, $fetchMode); |
| 66 | $this->executeFresh(); |
| 67 | |
| 68 | try { |
| 69 | $record = $this->fetchArrayRecord($fetchMode); |
| 70 | |
| 71 | if ($record === null) { |
| 75 | if ($this->fetchArrayRecord($fetchMode) !== null) { |
| 79 | return $this->hydrateRecord($record, $map); |
| 79 | return $this->hydrateRecord($record, $map); |
| 253 | $this->db->connect(); |
| 254 | $this->stmt->closeCursor(); |
| 255 | $this->executed = false; |
| 256 | |
| 257 | return $this->stmt->execute(); |
| 258 | } |
| 194 | private function terminalOptions(string|Closure|null $map, ?int $fetchMode): array |
| 195 | { |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 197 | |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 199 | throw new InvalidArgumentException('Hydration requires PDO::FETCH_ASSOC.'); |
| 194 | private function terminalOptions(string|Closure|null $map, ?int $fetchMode): array |
| 195 | { |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 197 | |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 202 | return [$map, $mode]; |
| 203 | } |
| 194 | private function terminalOptions(string|Closure|null $map, ?int $fetchMode): array |
| 195 | { |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 197 | |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 199 | throw new InvalidArgumentException('Hydration requires PDO::FETCH_ASSOC.'); |
| 194 | private function terminalOptions(string|Closure|null $map, ?int $fetchMode): array |
| 195 | { |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 197 | |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 202 | return [$map, $mode]; |
| 203 | } |
| 194 | private function terminalOptions(string|Closure|null $map, ?int $fetchMode): array |
| 195 | { |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 197 | |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 199 | throw new InvalidArgumentException('Hydration requires PDO::FETCH_ASSOC.'); |
| 194 | private function terminalOptions(string|Closure|null $map, ?int $fetchMode): array |
| 195 | { |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 197 | |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 202 | return [$map, $mode]; |
| 203 | } |
| 194 | private function terminalOptions(string|Closure|null $map, ?int $fetchMode): array |
| 195 | { |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 197 | |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 199 | throw new InvalidArgumentException('Hydration requires PDO::FETCH_ASSOC.'); |
| 194 | private function terminalOptions(string|Closure|null $map, ?int $fetchMode): array |
| 195 | { |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 196 | $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC); |
| 197 | |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 198 | if ($map !== null && $mode !== PDO::FETCH_ASSOC) { |
| 202 | return [$map, $mode]; |
| 203 | } |
| 288 | function (mixed $value, int|string $index) use ($argType): void { |
| 289 | if ($argType === ArgType::Named) { |
| 289 | if ($argType === ArgType::Named) { |
| 290 | $arg = ':' . $index; |
| 295 | $this->bindValue($arg, $value); |
| 296 | }, |
| 288 | function (mixed $value, int|string $index) use ($argType): void { |
| 289 | if ($argType === ArgType::Named) { |
| 292 | $arg = (int) $index + 1; // question mark placeholders are 1-indexed |
| 293 | } |
| 294 | |
| 295 | $this->bindValue($arg, $value); |
| 295 | $this->bindValue($arg, $value); |
| 296 | }, |