Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
124 / 124
98.10% covered (success)
98.10%
103 / 105
44.19% covered (danger)
44.19%
38 / 86
91.67% covered (success)
91.67%
22 / 24
CRAP
0.00% covered (danger)
0.00%
0 / 1
Query
100.00% covered (success)
100.00%
124 / 124
98.10% covered (success)
98.10%
103 / 105
44.19% covered (danger)
44.19%
38 / 86
100.00% covered (success)
100.00%
24 / 24
561.01
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 one
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
7 / 7
25.00% covered (danger)
25.00%
1 / 4
100.00% covered (success)
100.00%
1 / 1
6.80
 first
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
6 / 6
0.00% covered (danger)
0.00%
0 / 4
100.00% covered (success)
100.00%
1 / 1
2
 fetch
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 all
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
9 / 9
0.00% covered (danger)
0.00%
0 / 8
100.00% covered (success)
100.00%
1 / 1
3
 lazy
100.00% covered (success)
100.00%
8 / 8
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 6
100.00% covered (success)
100.00%
1 / 1
3
 terminalOptions
100.00% covered (success)
100.00%
8 / 8
92.31% covered (success)
92.31%
12 / 13
15.00% covered (danger)
15.00%
3 / 20
100.00% covered (success)
100.00%
1 / 1
20.35
 executeFresh
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 executeForFetch
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 executeStatement
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 fail
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hydrateRecord
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 hydrator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 run
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 len
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
0.00% covered (danger)
0.00%
0 / 2
100.00% covered (success)
100.00%
1 / 1
1
 interpolate
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 prepare
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
6 / 6
50.00% covered (danger)
50.00%
2 / 4
100.00% covered (success)
100.00%
1 / 1
4.12
 useCurrentConnection
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 bindArgs
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 bindValue
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
17 / 17
53.85% covered (warning)
53.85%
7 / 13
100.00% covered (success)
100.00%
1 / 1
14.29
 fetchArrayRecord
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 fetchAllRecords
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 nullIfNot
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Celema\Quma;
6
7use Celema\Quma\Exception\UnexpectedResultCount;
8use Celema\Quma\Hydration\Hydrator;
9use Closure;
10use Generator;
11use InvalidArgumentException;
12use JsonException;
13use PDO;
14use PDOException;
15use PDOStatement;
16
17/** @api */
18class Query
19{
20    // Matches multi line single and double quotes and handles \' \" escapes
21    public const string PATTERN_STRING = '/([\'"])(?:\\\1|[\s\S])*?\1/';
22
23    // PostgreSQL blocks delimited with $$
24    public const string PATTERN_BLOCK = '/(\$\$)[\s\S]*?\1/';
25
26    // Multi line comments /* */
27    public const string PATTERN_COMMENT_MULTI = '/\/\*([\s\S]*?)\*\//';
28
29    // Single line comments --
30    public const string PATTERN_COMMENT_SINGLE = '/--.*$/m';
31
32    protected PDOStatement $stmt;
33    protected bool $executed = false;
34    protected ?Hydrator $hydrator = null;
35
36    /** The connection the statement was prepared on. */
37    private PDO $pdo;
38
39    public function __construct(
40        protected Database $db,
41        protected string $query,
42        protected Args $args,
43        protected ?string $sourcePath = null,
44    ) {
45        $this->prepare($this->db->getConn());
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        $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        $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        $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        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
136                return $records;
137            }
138
139            $result = [];
140
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
146        } finally {
147            $this->stmt->closeCursor();
148        }
149    }
150
151    /**
152     * @template T of object
153     *
154     * @param class-string<T>|Closure(array<string, mixed>):class-string<T>|null $map
155     * @return ($map is null
156     *     ? Generator<int, array<array-key, mixed>, mixed, void>
157     *     : Generator<int, T, mixed, void>)
158     */
159    public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator
160    {
161        $fetchMode = $this->terminalOptions($map, $fetchMode);
162        $this->executeFresh();
163
164        try {
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
166                if ($map === null) {
167                    yield $record;
168
169                    continue;
170                }
171
172                yield $this->hydrator()->hydrate($record, $map, $this->sourcePath);
173            }
174        } finally {
175            $this->stmt->closeCursor();
176        }
177    }
178
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
184            throw new InvalidArgumentException('Hydration requires PDO::FETCH_ASSOC.');
185        }
186
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
188            throw new InvalidArgumentException(
189                'Unsupported fetch mode. Use PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, or PDO::FETCH_NAMED.',
190            );
191        }
192
193        return $mode;
194    }
195
196    private function executeFresh(): void
197    {
198        $this->executeStatement();
199        $this->executed = false;
200    }
201
202    private function executeForFetch(): void
203    {
204        if (!$this->executed) {
205            $this->executeStatement();
206            $this->executed = true;
207        }
208    }
209
210    private function executeStatement(): bool
211    {
212        $this->useCurrentConnection();
213
214        try {
215            $this->stmt->closeCursor();
216
217            return $this->stmt->execute();
218        } catch (PDOException $e) {
219            $this->fail($this->pdo, $e);
220        }
221    }
222
223    /**
224     * Lets the database verify the connection before it is reused, as the
225     * failure may mean that the connection was lost. The failed operation
226     * itself is never retried.
227     */
228    private function fail(PDO $pdo, PDOException $e): never
229    {
230        $this->db->markSuspect($pdo);
231
232        throw $e;
233    }
234
235    /**
236     * @template T of object
237     *
238     * @param array<array-key, mixed> $record
239     * @param class-string<T>|Closure(array<string, mixed>):class-string<T>|null $map
240     * @return ($map is null ? array<array-key, mixed> : T)
241     */
242    private function hydrateRecord(array $record, string|Closure|null $map): array|object
243    {
244        if ($map === null) {
245            return $record;
246        }
247
248        return $this->hydrator()->hydrate($record, $map, $this->sourcePath);
249    }
250
251    private function hydrator(): Hydrator
252    {
253        return $this->hydrator ??= Hydrator::default();
254    }
255
256    public function run(): bool
257    {
258        $this->executed = false;
259
260        return $this->executeStatement();
261    }
262
263    public function len(): int
264    {
265        $this->executeFresh();
266
267        try {
268            return $this->stmt->rowCount();
269        } finally {
270            $this->stmt->closeCursor();
271        }
272    }
273
274    /**
275     * For debugging purposes only.
276     *
277     * Replaces any parameter placeholders in a query with the
278     * value of that parameter and returns the query as string.
279     *
280     * Covers most of the cases but is not perfect.
281     */
282    public function interpolate(): string
283    {
284        return Debug::interpolate($this->query, $this->args);
285    }
286
287    private function prepare(PDO $pdo): void
288    {
289        try {
290            $stmt = $pdo->prepare($this->query);
291        } catch (PDOException $e) {
292            $this->fail($pdo, $e);
293        }
294
295        // Only a prepared statement switches the query to the connection;
296        // otherwise its next execution would not prepare it again.
297        $this->pdo = $pdo;
298        $this->stmt = $stmt;
299
300        if ($this->args->count() > 0) {
301            $this->bindArgs($this->args->get(), $this->args->type());
302        }
303    }
304
305    /**
306     * Before an execution, prepares the statement again if the database
307     * replaced its connection since, for example after the maximum age. The
308     * statement would otherwise still run on the old connection, outside a
309     * transaction begun on the new one.
310     */
311    private function useCurrentConnection(): void
312    {
313        $pdo = $this->db->getConn();
314
315        if ($pdo !== $this->pdo) {
316            $this->prepare($pdo);
317        }
318    }
319
320    /** @param array<array-key, mixed> $args */
321    protected function bindArgs(array $args, ArgType $argType): void
322    {
323        array_walk(
324            $args,
325            function (mixed $value, int|string $index) use ($argType): void {
326                if ($argType === ArgType::Named) {
327                    $arg = ':' . $index;
328                } else {
329                    $arg = (int) $index + 1; // question mark placeholders are 1-indexed
330                }
331
332                $this->bindValue($arg, $value);
333            },
334        );
335    }
336
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
340            case 'boolean':
341                $this->stmt->bindValue($arg, $value, PDO::PARAM_BOOL);
342
343                break;
344
345            case 'integer':
346                $this->stmt->bindValue($arg, $value, PDO::PARAM_INT);
347
348                break;
349
350            case 'string':
351                $this->stmt->bindValue($arg, $value, PDO::PARAM_STR);
352
353                break;
354
355            case 'NULL':
356                $this->stmt->bindValue($arg, $value, PDO::PARAM_NULL);
357
358                break;
359
360            case 'array':
361                try {
362                    $json = json_encode($value, JSON_THROW_ON_ERROR);
363                } catch (JsonException $e) {
364                    throw new InvalidArgumentException(
365                        'Array parameters must be JSON-encodable.',
366                        previous: $e,
367                    );
368                }
369
370                $this->stmt->bindValue($arg, $json, PDO::PARAM_STR);
371
372                break;
373
374            default:
375                throw new InvalidArgumentException(
376                    'Only the types bool, int, string, null and array are supported',
377                );
378        }
379    }
380
381    /** @return array<array-key, mixed>|null */
382    protected function fetchArrayRecord(int $fetchMode): ?array
383    {
384        try {
385            return $this->nullIfNot($this->stmt->fetch($fetchMode));
386        } catch (PDOException $e) {
387            $this->fail($this->pdo, $e);
388        }
389    }
390
391    /** @return list<array<array-key, mixed>> */
392    private function fetchAllRecords(int $fetchMode): array
393    {
394        try {
395            /**
396             * @mago-expect lint:inline-variable-return Psalm makes this necessary
397             * @var list<array<array-key, mixed>> $records
398             */
399            $records = $this->stmt->fetchAll($fetchMode);
400
401            return $records;
402        } catch (PDOException $e) {
403            $this->fail($this->pdo, $e);
404        }
405    }
406
407    /** @return array<array-key, mixed>|null */
408    protected function nullIfNot(mixed $value): ?array
409    {
410        if (is_array($value)) {
411            return $value;
412        }
413
414        return null;
415    }
416}

Paths

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.

Query->__construct
40        protected Database $db,
41        protected string $query,
42        protected Args $args,
43        protected ?string $sourcePath = null,
44    ) {
45        $this->prepare($this->db->getConn());
46
47        if ($this->db->debug) {
 
48            Debug::query($this->db, $this->query, $this->args, $this->sourcePath);
49        }
50    }
 
50    }
40        protected Database $db,
41        protected string $query,
42        protected Args $args,
43        protected ?string $sourcePath = null,
44    ) {
45        $this->prepare($this->db->getConn());
46
47        if ($this->db->debug) {
 
50    }
Query->__toString
54        return $this->interpolate();
55    }
Query->all
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
136                return $records;
 
147            $this->stmt->closeCursor();
148        }
149    }
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
136                return $records;
 
136                return $records;
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
147            $this->stmt->closeCursor();
148        }
149    }
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
145            return $result;
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
147            $this->stmt->closeCursor();
148        }
149    }
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
145            return $result;
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
147            $this->stmt->closeCursor();
148        }
149    }
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
145            return $result;
Query->bindArgs
321    protected function bindArgs(array $args, ArgType $argType): void
322    {
323        array_walk(
324            $args,
325            function (mixed $value, int|string $index) use ($argType): void {
326                if ($argType === ArgType::Named) {
327                    $arg = ':' . $index;
328                } else {
329                    $arg = (int) $index + 1; // question mark placeholders are 1-indexed
330                }
331
332                $this->bindValue($arg, $value);
333            },
334        );
335    }
Query->bindValue
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
341                $this->stmt->bindValue($arg, $value, PDO::PARAM_BOOL);
342
343                break;
 
376                    'Only the types bool, int, string, null and array are supported',
377                );
378        }
379    }
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
346                $this->stmt->bindValue($arg, $value, PDO::PARAM_INT);
347
348                break;
 
376                    'Only the types bool, int, string, null and array are supported',
377                );
378        }
379    }
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
351                $this->stmt->bindValue($arg, $value, PDO::PARAM_STR);
352
353                break;
 
376                    'Only the types bool, int, string, null and array are supported',
377                );
378        }
379    }
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
356                $this->stmt->bindValue($arg, $value, PDO::PARAM_NULL);
357
358                break;
 
376                    'Only the types bool, int, string, null and array are supported',
377                );
378        }
379    }
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
361                try {
362                    $json = json_encode($value, JSON_THROW_ON_ERROR);
 
370                $this->stmt->bindValue($arg, $json, PDO::PARAM_STR);
371
372                break;
 
376                    'Only the types bool, int, string, null and array are supported',
377                );
378        }
379    }
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
375                throw new InvalidArgumentException(
376                    'Only the types bool, int, string, null and array are supported',
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
340            case 'boolean':
 
345            case 'integer':
 
350            case 'string':
 
355            case 'NULL':
 
360            case 'array':
 
360            case 'array':
 
375                throw new InvalidArgumentException(
376                    'Only the types bool, int, string, null and array are supported',
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
340            case 'boolean':
 
345            case 'integer':
 
350            case 'string':
 
355            case 'NULL':
 
360            case 'array':
 
361                try {
362                    $json = json_encode($value, JSON_THROW_ON_ERROR);
 
370                $this->stmt->bindValue($arg, $json, PDO::PARAM_STR);
371
372                break;
 
376                    'Only the types bool, int, string, null and array are supported',
377                );
378        }
379    }
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
340            case 'boolean':
 
345            case 'integer':
 
350            case 'string':
 
355            case 'NULL':
 
356                $this->stmt->bindValue($arg, $value, PDO::PARAM_NULL);
357
358                break;
 
376                    'Only the types bool, int, string, null and array are supported',
377                );
378        }
379    }
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
340            case 'boolean':
 
345            case 'integer':
 
350            case 'string':
 
351                $this->stmt->bindValue($arg, $value, PDO::PARAM_STR);
352
353                break;
 
376                    'Only the types bool, int, string, null and array are supported',
377                );
378        }
379    }
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
340            case 'boolean':
 
345            case 'integer':
 
346                $this->stmt->bindValue($arg, $value, PDO::PARAM_INT);
347
348                break;
 
376                    'Only the types bool, int, string, null and array are supported',
377                );
378        }
379    }
337    protected function bindValue(string|int $arg, mixed $value): void
338    {
339        switch (gettype($value)) {
 
340            case 'boolean':
 
341                $this->stmt->bindValue($arg, $value, PDO::PARAM_BOOL);
342
343                break;
 
376                    'Only the types bool, int, string, null and array are supported',
377                );
378        }
379    }
363                } catch (JsonException $e) {
 
364                    throw new InvalidArgumentException(
365                        'Array parameters must be JSON-encodable.',
Query->executeForFetch
204        if (!$this->executed) {
 
205            $this->executeStatement();
206            $this->executed = true;
207        }
208    }
 
208    }
204        if (!$this->executed) {
 
208    }
Query->executeFresh
198        $this->executeStatement();
199        $this->executed = false;
200    }
Query->executeStatement
212        $this->useCurrentConnection();
213
214        try {
215            $this->stmt->closeCursor();
216
217            return $this->stmt->execute();
218        } catch (PDOException $e) {
 
219            $this->fail($this->pdo, $e);
220        }
221    }
Query->fail
228    private function fail(PDO $pdo, PDOException $e): never
229    {
230        $this->db->markSuspect($pdo);
231
232        throw $e;
233    }
Query->fetch
111    public function fetch(string|Closure|null $map = null, ?int $fetchMode = null): array|object|null
112    {
113        $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        $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    }
Query->fetchAllRecords
392    private function fetchAllRecords(int $fetchMode): array
393    {
394        try {
395            /**
396             * @mago-expect lint:inline-variable-return Psalm makes this necessary
397             * @var list<array<array-key, mixed>> $records
398             */
399            $records = $this->stmt->fetchAll($fetchMode);
400
401            return $records;
402        } catch (PDOException $e) {
 
403            $this->fail($this->pdo, $e);
404        }
405    }
Query->fetchArrayRecord
382    protected function fetchArrayRecord(int $fetchMode): ?array
383    {
384        try {
385            return $this->nullIfNot($this->stmt->fetch($fetchMode));
386        } catch (PDOException $e) {
 
387            $this->fail($this->pdo, $e);
388        }
389    }
Query->first
91    public function first(string|Closure|null $map = null, ?int $fetchMode = null): array|object|null
92    {
93        $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        $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        $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        $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);
Query->hydrateRecord
242    private function hydrateRecord(array $record, string|Closure|null $map): array|object
243    {
244        if ($map === null) {
 
245            return $record;
242    private function hydrateRecord(array $record, string|Closure|null $map): array|object
243    {
244        if ($map === null) {
 
248        return $this->hydrator()->hydrate($record, $map, $this->sourcePath);
249    }
Query->hydrator
253        return $this->hydrator ??= Hydrator::default();
254    }
Query->interpolate
284        return Debug::interpolate($this->query, $this->args);
285    }
Query->lazy
159    public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator
160    {
161        $fetchMode = $this->terminalOptions($map, $fetchMode);
162        $this->executeFresh();
163
164        try {
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
174        } finally {
 
175            $this->stmt->closeCursor();
159    public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator
160    {
161        $fetchMode = $this->terminalOptions($map, $fetchMode);
162        $this->executeFresh();
163
164        try {
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
174        } finally {
 
174        } finally {
 
177    }
159    public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator
160    {
161        $fetchMode = $this->terminalOptions($map, $fetchMode);
162        $this->executeFresh();
163
164        try {
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
166                if ($map === null) {
 
167                    yield $record;
168
169                    continue;
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
174        } finally {
 
175            $this->stmt->closeCursor();
159    public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator
160    {
161        $fetchMode = $this->terminalOptions($map, $fetchMode);
162        $this->executeFresh();
163
164        try {
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
166                if ($map === null) {
 
167                    yield $record;
168
169                    continue;
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
174        } finally {
 
174        } finally {
 
177    }
159    public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator
160    {
161        $fetchMode = $this->terminalOptions($map, $fetchMode);
162        $this->executeFresh();
163
164        try {
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
166                if ($map === null) {
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
166                if ($map === null) {
167                    yield $record;
168
169                    continue;
170                }
171
172                yield $this->hydrator()->hydrate($record, $map, $this->sourcePath);
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
174        } finally {
 
175            $this->stmt->closeCursor();
159    public function lazy(string|Closure|null $map = null, ?int $fetchMode = null): Generator
160    {
161        $fetchMode = $this->terminalOptions($map, $fetchMode);
162        $this->executeFresh();
163
164        try {
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
166                if ($map === null) {
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
166                if ($map === null) {
167                    yield $record;
168
169                    continue;
170                }
171
172                yield $this->hydrator()->hydrate($record, $map, $this->sourcePath);
 
165            while (($record = $this->fetchArrayRecord($fetchMode)) !== null) {
 
174        } finally {
 
174        } finally {
 
177    }
Query->len
265        $this->executeFresh();
266
267        try {
268            return $this->stmt->rowCount();
 
270            $this->stmt->closeCursor();
271        }
272    }
265        $this->executeFresh();
266
267        try {
268            return $this->stmt->rowCount();
 
268            return $this->stmt->rowCount();
Query->nullIfNot
408    protected function nullIfNot(mixed $value): ?array
409    {
410        if (is_array($value)) {
 
411            return $value;
408    protected function nullIfNot(mixed $value): ?array
409    {
410        if (is_array($value)) {
 
414        return null;
415    }
Query->one
63    public function one(string|Closure|null $map = null, ?int $fetchMode = null): array|object
64    {
65        $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        $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        $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        $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);
Query->prepare
287    private function prepare(PDO $pdo): void
288    {
289        try {
290            $stmt = $pdo->prepare($this->query);
 
297        $this->pdo = $pdo;
298        $this->stmt = $stmt;
299
300        if ($this->args->count() > 0) {
 
301            $this->bindArgs($this->args->get(), $this->args->type());
302        }
303    }
 
303    }
287    private function prepare(PDO $pdo): void
288    {
289        try {
290            $stmt = $pdo->prepare($this->query);
 
297        $this->pdo = $pdo;
298        $this->stmt = $stmt;
299
300        if ($this->args->count() > 0) {
 
303    }
291        } catch (PDOException $e) {
 
292            $this->fail($pdo, $e);
293        }
294
295        // Only a prepared statement switches the query to the connection;
296        // otherwise its next execution would not prepare it again.
297        $this->pdo = $pdo;
 
297        $this->pdo = $pdo;
298        $this->stmt = $stmt;
299
300        if ($this->args->count() > 0) {
 
301            $this->bindArgs($this->args->get(), $this->args->type());
302        }
303    }
 
303    }
291        } catch (PDOException $e) {
 
292            $this->fail($pdo, $e);
293        }
294
295        // Only a prepared statement switches the query to the connection;
296        // otherwise its next execution would not prepare it again.
297        $this->pdo = $pdo;
 
297        $this->pdo = $pdo;
298        $this->stmt = $stmt;
299
300        if ($this->args->count() > 0) {
 
303    }
Query->run
258        $this->executed = false;
259
260        return $this->executeStatement();
261    }
Query->terminalOptions
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
184            throw new InvalidArgumentException('Hydration requires PDO::FETCH_ASSOC.');
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
188            throw new InvalidArgumentException(
189                'Unsupported fetch mode. Use PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, or PDO::FETCH_NAMED.',
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
193        return $mode;
194    }
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
188            throw new InvalidArgumentException(
189                'Unsupported fetch mode. Use PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, or PDO::FETCH_NAMED.',
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
193        return $mode;
194    }
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
184            throw new InvalidArgumentException('Hydration requires PDO::FETCH_ASSOC.');
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
188            throw new InvalidArgumentException(
189                'Unsupported fetch mode. Use PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, or PDO::FETCH_NAMED.',
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
193        return $mode;
194    }
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
188            throw new InvalidArgumentException(
189                'Unsupported fetch mode. Use PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, or PDO::FETCH_NAMED.',
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
193        return $mode;
194    }
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
184            throw new InvalidArgumentException('Hydration requires PDO::FETCH_ASSOC.');
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
188            throw new InvalidArgumentException(
189                'Unsupported fetch mode. Use PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, or PDO::FETCH_NAMED.',
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
193        return $mode;
194    }
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
188            throw new InvalidArgumentException(
189                'Unsupported fetch mode. Use PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, or PDO::FETCH_NAMED.',
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
193        return $mode;
194    }
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
184            throw new InvalidArgumentException('Hydration requires PDO::FETCH_ASSOC.');
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
188            throw new InvalidArgumentException(
189                'Unsupported fetch mode. Use PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, or PDO::FETCH_NAMED.',
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
193        return $mode;
194    }
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
188            throw new InvalidArgumentException(
189                'Unsupported fetch mode. Use PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, or PDO::FETCH_NAMED.',
179    private function terminalOptions(string|Closure|null $map, ?int $fetchMode): int
180    {
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
 
181        $mode = $fetchMode ?? ($map === null ? $this->db->getFetchMode() : PDO::FETCH_ASSOC);
182
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
183        if ($map !== null && $mode !== PDO::FETCH_ASSOC) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
187        if (!in_array($mode, [PDO::FETCH_ASSOC, PDO::FETCH_NUM, PDO::FETCH_BOTH, PDO::FETCH_NAMED], true)) {
 
193        return $mode;
194    }
Query->useCurrentConnection
313        $pdo = $this->db->getConn();
314
315        if ($pdo !== $this->pdo) {
 
316            $this->prepare($pdo);
317        }
318    }
 
318    }
313        $pdo = $this->db->getConn();
314
315        if ($pdo !== $this->pdo) {
 
318    }
all
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
136                return $records;
 
147            $this->stmt->closeCursor();
148        }
149    }
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
136                return $records;
 
136                return $records;
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
147            $this->stmt->closeCursor();
148        }
149    }
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
145            return $result;
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
147            $this->stmt->closeCursor();
148        }
149    }
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
145            return $result;
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
147            $this->stmt->closeCursor();
148        }
149    }
127    public function all(string|Closure|null $map = null, ?int $fetchMode = null): array
128    {
129        $fetchMode = $this->terminalOptions($map, $fetchMode);
130        $this->executeFresh();
131
132        try {
133            $records = $this->fetchAllRecords($fetchMode);
134
135            if ($map === null) {
 
139            $result = [];
140
141            foreach ($records as $record) {
 
141            foreach ($records as $record) {
142                $result[] = $this->hydrator()->hydrate($record, $map, $this->sourcePath);
143            }
144
145            return $result;
 
145            return $result;
{closure:/workspace/celema/quma/src/Query.php:325-333}
325            function (mixed $value, int|string $index) use ($argType): void {
326                if ($argType === ArgType::Named) {
 
326                if ($argType === ArgType::Named) {
327                    $arg = ':' . $index;
 
332                $this->bindValue($arg, $value);
333            },
325            function (mixed $value, int|string $index) use ($argType): void {
326                if ($argType === ArgType::Named) {
 
329                    $arg = (int) $index + 1; // question mark placeholders are 1-indexed
330                }
331
332                $this->bindValue($arg, $value);
 
332                $this->bindValue($arg, $value);
333            },