Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
124 / 124
95.79% covered (success)
95.79%
91 / 95
61.76% covered (warning)
61.76%
42 / 68
91.30% covered (success)
91.30%
21 / 23
CRAP
0.00% covered (danger)
0.00%
0 / 1
Config
100.00% covered (success)
100.00%
124 / 124
95.79% covered (success)
95.79%
91 / 95
61.76% covered (warning)
61.76%
42 / 68
100.00% covered (success)
100.00%
23 / 23
269.00
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
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
 setCredentials
100.00% covered (success)
100.00%
1 / 1
n/a
0 / 0
n/a
0 / 0
100.00% covered (success)
100.00%
1 / 1
1
 setPdoOptions
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
 setPdoOption
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
 setFetchMode
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
 setPlaceholders
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
 addSqlDirs
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
 setMigrations
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
 addMigrationDir
100.00% covered (success)
100.00%
6 / 6
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
 setMigrationNamespace
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
13 / 13
36.36% covered (danger)
36.36%
4 / 11
100.00% covered (success)
100.00%
1 / 1
15.28
 setMigrationsTable
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
 setMigrationsColumnMigration
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
 setMigrationsColumnApplied
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
 preparePath
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
5 / 5
50.00% covered (danger)
50.00%
2 / 4
100.00% covered (success)
100.00%
1 / 1
4.12
 readDriver
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
 readFlatDirs
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
19 / 19
45.45% covered (danger)
45.45%
5 / 11
100.00% covered (success)
100.00%
1 / 1
22.15
 readAssocDirs
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
5 / 5
75.00% covered (warning)
75.00%
3 / 4
100.00% covered (success)
100.00%
1 / 1
3.14
 readDirsEntry
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
7
 readMigrationDirs
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
9 / 9
66.67% covered (warning)
66.67%
4 / 6
100.00% covered (success)
100.00%
1 / 1
5.93
 isDriverConfig
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
3 / 3
50.00% covered (danger)
50.00%
1 / 2
100.00% covered (success)
100.00%
1 / 1
2.50
 readNamespacedDirs
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
5
 getMigrationsTableName
100.00% covered (success)
100.00%
6 / 6
76.92% covered (warning)
76.92%
10 / 13
37.50% covered (danger)
37.50%
3 / 8
100.00% covered (success)
100.00%
1 / 1
7.91
 getColumnName
100.00% covered (success)
100.00%
3 / 3
83.33% covered (warning)
83.33%
5 / 6
50.00% covered (danger)
50.00%
2 / 4
100.00% covered (success)
100.00%
1 / 1
2.50
1<?php
2
3declare(strict_types=1);
4
5namespace Celemas\Quma;
6
7use PDO;
8use RuntimeException;
9use ValueError;
10
11/**
12 * @api
13 *
14 * @psalm-type SqlDirs = list<non-empty-string>
15 * @psalm-type SqlAssoc = array<non-empty-string, non-empty-string|list<non-empty-string>>
16 * @psalm-type SqlMixed = list<non-empty-string|SqlAssoc>
17 * @psalm-type SqlConfig = non-empty-string|SqlAssoc|SqlMixed
18 * @psalm-type MigrationDirsFlat = list<non-empty-string>
19 * @psalm-type MigrationDirsNamespaced = array<non-empty-string, non-empty-string|list<non-empty-string>>
20 * @psalm-type MigrationDirs = MigrationDirsFlat|MigrationDirsNamespaced
21 * @psalm-type PlaceholderMap = array<non-empty-string, string>
22 * @psalm-type PlaceholderConfig = array<non-empty-string, PlaceholderMap>
23 */
24final class Config
25{
26    /** @var non-empty-string */
27    public readonly string $driver;
28
29    /** @psalm-var SqlDirs */
30    public private(set) array $sql;
31
32    /** @psalm-var MigrationDirs */
33    public private(set) array $migrations = [];
34
35    public private(set) PdoConfig $pdo;
36    public private(set) ?Placeholders $placeholders = null;
37
38    public private(set) string $migrationsTable = 'migrations';
39    public private(set) string $migrationsColumnMigration = 'migration';
40    public private(set) string $migrationsColumnApplied = 'applied';
41
42    /** @psalm-param SqlConfig $sql */
43    public function __construct(
44        public readonly string $dsn,
45        string|array $sql,
46    ) {
47        $this->driver = $this->readDriver($this->dsn);
48        $this->sql = $this->readFlatDirs($sql);
49        $this->pdo = new PdoConfig();
50    }
51
52    public function setCredentials(
53        string $username,
54        #[\SensitiveParameter]
55        ?string $password = null,
56    ): void {
57        $this->pdo = $this->pdo->credentials($username, $password);
58    }
59
60    /** @param array<array-key, mixed> $options */
61    public function setPdoOptions(array $options): void
62    {
63        $this->pdo = $this->pdo->options($options);
64    }
65
66    public function setPdoOption(int $attribute, mixed $value): void
67    {
68        $this->pdo = $this->pdo->option($attribute, $value);
69    }
70
71    public function setFetchMode(int $fetchMode): void
72    {
73        $this->pdo = $this->pdo->fetch($fetchMode);
74    }
75
76    /** @psalm-param PlaceholderConfig $placeholders */
77    public function setPlaceholders(Delimiters $delimiters, array $placeholders): void
78    {
79        $this->placeholders = new Placeholders($this->driver, $placeholders, $delimiters);
80    }
81
82    /** @psalm-param SqlConfig $sql */
83    public function addSqlDirs(array|string $sql): void
84    {
85        $dirs = $this->readFlatDirs($sql);
86        $this->sql = array_merge($dirs, $this->sql);
87    }
88
89    /** @psalm-param SqlConfig $migrations */
90    public function setMigrations(array|string $migrations): void
91    {
92        $this->migrations = $this->readMigrationDirs($migrations);
93    }
94
95    /** @param non-empty-string $migrations */
96    public function addMigrationDir(string $migrations): void
97    {
98        if (!array_is_list($this->migrations)) {
99            throw new ValueError(
100                'Cannot add a flat migration directory when migrations are namespaced. Use migrationNamespace().',
101            );
102        }
103
104        $dirs = $this->readFlatDirs($migrations);
105        $this->migrations = array_merge($dirs, $this->migrations);
106    }
107
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
111            throw new ValueError('Migration namespace must not be empty.');
112        }
113
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
115            throw new ValueError(
116                'Cannot add a namespaced migration directory when migrations are configured as a flat list.',
117            );
118        }
119
120        /** @psalm-var MigrationDirsNamespaced $migrations */
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
125        $this->migrations = $migrations;
126    }
127
128    public function setMigrationsTable(string $table): void
129    {
130        $this->migrationsTable = $this->getMigrationsTableName($table);
131    }
132
133    public function setMigrationsColumnMigration(string $column): void
134    {
135        $this->migrationsColumnMigration = $this->getColumnName($column);
136    }
137
138    public function setMigrationsColumnApplied(string $column): void
139    {
140        $this->migrationsColumnApplied = $this->getColumnName($column);
141    }
142
143    /** @return non-empty-string */
144    private function preparePath(string $path): string
145    {
146        $result = realpath($path);
147
148        if ($result !== false && $result !== '') {
149            return $result;
150        }
151
152        throw new ValueError("Path does not exist: {$path}");
153    }
154
155    /** @return non-empty-string */
156    private function readDriver(string $dsn): string
157    {
158        $driver = explode(':', $dsn)[0];
159
160        if (in_array($driver, PDO::getAvailableDrivers(), strict: true)) {
161            assert($driver !== '', 'PDO driver name must not be empty.');
162
163            return $driver;
164        }
165
166        throw new RuntimeException('PDO driver not supported: ' . $driver);
167    }
168
169    /**
170     * Reads directories from configuration into a flat list.
171     *
172     * @psalm-param SqlConfig $config
173     *
174     * @return list<non-empty-string>
175     */
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
179            return [$this->preparePath($config)];
180        }
181
182        if (count($config) === 0) {
183            return [];
184        }
185
186        if (Util::isAssoc($config)) {
187            return $this->readAssocDirs($config);
188        }
189
190        $dirs = [];
191
192        foreach ($config as $entry) {
193            if (is_string($entry)) {
194                array_unshift($dirs, $this->preparePath($entry));
195
196                continue;
197            }
198
199            if (array_is_list($entry)) {
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
209            }
210
211            $dirs = array_merge($this->readAssocDirs($entry), $dirs);
212        }
213
214        return $dirs;
215    }
216
217    /**
218     * Reads directories from an associative array config.
219     *
220     * @param array<array-key, mixed> $entry
221     *
222     * @return list<non-empty-string>
223     */
224    private function readAssocDirs(array $entry): array
225    {
226        $hasDriver = array_key_exists($this->driver, $entry);
227        $hasAll = array_key_exists('all', $entry);
228        $dirs = [];
229
230        if ($hasDriver) {
231            $dirs = array_merge($dirs, $this->readDirsEntry($entry[$this->driver]));
232        }
233
234        if ($hasAll) {
235            $dirs = array_merge($dirs, $this->readDirsEntry($entry['all']));
236        }
237
238        return $dirs;
239    }
240
241    /** @return list<non-empty-string> */
242    private function readDirsEntry(mixed $entry): array
243    {
244        if (is_string($entry)) {
245            return [$this->preparePath($entry)];
246        }
247
248        if (!is_array($entry)) {
249            return [];
250        }
251
252        $dirs = [];
253
254        array_walk(
255            $entry,
256            function (mixed $value) use (&$dirs): void {
257                if (is_string($value)) {
258                    $dirs[] = $this->preparePath($value);
259
260                    return;
261                }
262
263                if (!is_array($value)) {
264                    return;
265                }
266
267                if (Util::isAssoc($value)) {
268                    $dirs = array_merge($dirs, $this->readAssocDirs($value));
269
270                    return;
271                }
272
273                array_walk(
274                    $value,
275                    function (mixed $path) use (&$dirs): void {
276                        if (is_string($path)) {
277                            $dirs[] = $this->preparePath($path);
278                        }
279                    },
280                );
281            },
282        );
283
284        return $dirs;
285    }
286
287    /**
288     * Reads migration directories from configuration.
289     *
290     * Migrations can be configured as:
291     * - A flat list of directories
292     * - A namespaced structure with string keys mapping to directories
293     *
294     * @psalm-param SqlConfig $config
295     *
296     * @psalm-return MigrationDirs
297     */
298    private function readMigrationDirs(string|array $config): array
299    {
300        if (is_string($config)) {
301            return [$this->preparePath($config)];
302        }
303
304        if (count($config) === 0) {
305            return [];
306        }
307
308        if (Util::isAssoc($config) && !$this->isDriverConfig($config)) {
309            return $this->readNamespacedDirs($config);
310        }
311
312        return $this->readFlatDirs($config);
313    }
314
315    /** @param array<array-key, mixed> $config */
316    private function isDriverConfig(array $config): bool
317    {
318        return array_key_exists($this->driver, $config) || array_key_exists('all', $config);
319    }
320
321    /**
322     * Reads namespaced migration directories.
323     *
324     * @param array<array-key, mixed> $config
325     *
326     * @psalm-return MigrationDirsNamespaced
327     */
328    private function readNamespacedDirs(array $config): array
329    {
330        $result = [];
331
332        array_walk(
333            $config,
334            function (mixed $dirs, int|string $namespace) use (&$result): void {
335                if (!is_string($namespace) || $namespace === '') {
336                    return;
337                }
338
339                if (is_string($dirs)) {
340                    $result[$namespace] = $this->preparePath($dirs);
341
342                    return;
343                }
344
345                if (!is_array($dirs)) {
346                    return;
347                }
348
349                $result[$namespace] = $this->readDirsEntry($dirs);
350            },
351        );
352
353        return $result;
354    }
355
356    private function getMigrationsTableName(string $table): string
357    {
358        if ($this->driver === 'pgsql') {
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
360                return $table;
361            }
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
363            return $table;
364        }
365
366        throw new ValueError('Invalid migrations table name: ' . $table);
367    }
368
369    private function getColumnName(string $column): string
370    {
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
372            return $column;
373        }
374
375        throw new ValueError('Invalid migrations table column name: ' . $column);
376    }
377}

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.

Config->__construct
44        public readonly string $dsn,
45        string|array $sql,
46    ) {
47        $this->driver = $this->readDriver($this->dsn);
48        $this->sql = $this->readFlatDirs($sql);
49        $this->pdo = new PdoConfig();
50    }
Config->addMigrationDir
96    public function addMigrationDir(string $migrations): void
97    {
98        if (!array_is_list($this->migrations)) {
 
99            throw new ValueError(
100                'Cannot add a flat migration directory when migrations are namespaced. Use migrationNamespace().',
96    public function addMigrationDir(string $migrations): void
97    {
98        if (!array_is_list($this->migrations)) {
 
104        $dirs = $this->readFlatDirs($migrations);
105        $this->migrations = array_merge($dirs, $this->migrations);
106    }
Config->addSqlDirs
83    public function addSqlDirs(array|string $sql): void
84    {
85        $dirs = $this->readFlatDirs($sql);
86        $this->sql = array_merge($dirs, $this->sql);
87    }
Config->getColumnName
369    private function getColumnName(string $column): string
370    {
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
372            return $column;
369    private function getColumnName(string $column): string
370    {
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
375        throw new ValueError('Invalid migrations table column name: ' . $column);
376    }
369    private function getColumnName(string $column): string
370    {
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
372            return $column;
369    private function getColumnName(string $column): string
370    {
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
371        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
 
375        throw new ValueError('Invalid migrations table column name: ' . $column);
376    }
Config->getMigrationsTableName
356    private function getMigrationsTableName(string $table): string
357    {
358        if ($this->driver === 'pgsql') {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
360                return $table;
356    private function getMigrationsTableName(string $table): string
357    {
358        if ($this->driver === 'pgsql') {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
358        if ($this->driver === 'pgsql') {
 
366        throw new ValueError('Invalid migrations table name: ' . $table);
367    }
356    private function getMigrationsTableName(string $table): string
357    {
358        if ($this->driver === 'pgsql') {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
360                return $table;
356    private function getMigrationsTableName(string $table): string
357    {
358        if ($this->driver === 'pgsql') {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
359            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $table)) {
 
358        if ($this->driver === 'pgsql') {
 
366        throw new ValueError('Invalid migrations table name: ' . $table);
367    }
356    private function getMigrationsTableName(string $table): string
357    {
358        if ($this->driver === 'pgsql') {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
363            return $table;
356    private function getMigrationsTableName(string $table): string
357    {
358        if ($this->driver === 'pgsql') {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
366        throw new ValueError('Invalid migrations table name: ' . $table);
367    }
356    private function getMigrationsTableName(string $table): string
357    {
358        if ($this->driver === 'pgsql') {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
363            return $table;
356    private function getMigrationsTableName(string $table): string
357    {
358        if ($this->driver === 'pgsql') {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
362        } elseif (preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
 
366        throw new ValueError('Invalid migrations table name: ' . $table);
367    }
Config->isDriverConfig
316    private function isDriverConfig(array $config): bool
317    {
318        return array_key_exists($this->driver, $config) || array_key_exists('all', $config);
 
318        return array_key_exists($this->driver, $config) || array_key_exists('all', $config);
 
318        return array_key_exists($this->driver, $config) || array_key_exists('all', $config);
319    }
316    private function isDriverConfig(array $config): bool
317    {
318        return array_key_exists($this->driver, $config) || array_key_exists('all', $config);
 
318        return array_key_exists($this->driver, $config) || array_key_exists('all', $config);
319    }
Config->preparePath
144    private function preparePath(string $path): string
145    {
146        $result = realpath($path);
147
148        if ($result !== false && $result !== '') {
 
148        if ($result !== false && $result !== '') {
 
148        if ($result !== false && $result !== '') {
 
149            return $result;
144    private function preparePath(string $path): string
145    {
146        $result = realpath($path);
147
148        if ($result !== false && $result !== '') {
 
148        if ($result !== false && $result !== '') {
 
148        if ($result !== false && $result !== '') {
 
152        throw new ValueError("Path does not exist: {$path}");
153    }
144    private function preparePath(string $path): string
145    {
146        $result = realpath($path);
147
148        if ($result !== false && $result !== '') {
 
148        if ($result !== false && $result !== '') {
 
149            return $result;
144    private function preparePath(string $path): string
145    {
146        $result = realpath($path);
147
148        if ($result !== false && $result !== '') {
 
148        if ($result !== false && $result !== '') {
 
152        throw new ValueError("Path does not exist: {$path}");
153    }
Config->readAssocDirs
224    private function readAssocDirs(array $entry): array
225    {
226        $hasDriver = array_key_exists($this->driver, $entry);
227        $hasAll = array_key_exists('all', $entry);
228        $dirs = [];
229
230        if ($hasDriver) {
 
231            $dirs = array_merge($dirs, $this->readDirsEntry($entry[$this->driver]));
232        }
233
234        if ($hasAll) {
 
234        if ($hasAll) {
 
235            $dirs = array_merge($dirs, $this->readDirsEntry($entry['all']));
236        }
237
238        return $dirs;
 
238        return $dirs;
239    }
224    private function readAssocDirs(array $entry): array
225    {
226        $hasDriver = array_key_exists($this->driver, $entry);
227        $hasAll = array_key_exists('all', $entry);
228        $dirs = [];
229
230        if ($hasDriver) {
 
231            $dirs = array_merge($dirs, $this->readDirsEntry($entry[$this->driver]));
232        }
233
234        if ($hasAll) {
 
234        if ($hasAll) {
 
238        return $dirs;
239    }
224    private function readAssocDirs(array $entry): array
225    {
226        $hasDriver = array_key_exists($this->driver, $entry);
227        $hasAll = array_key_exists('all', $entry);
228        $dirs = [];
229
230        if ($hasDriver) {
 
234        if ($hasAll) {
 
235            $dirs = array_merge($dirs, $this->readDirsEntry($entry['all']));
236        }
237
238        return $dirs;
 
238        return $dirs;
239    }
224    private function readAssocDirs(array $entry): array
225    {
226        $hasDriver = array_key_exists($this->driver, $entry);
227        $hasAll = array_key_exists('all', $entry);
228        $dirs = [];
229
230        if ($hasDriver) {
 
234        if ($hasAll) {
 
238        return $dirs;
239    }
Config->readDirsEntry
242    private function readDirsEntry(mixed $entry): array
243    {
244        if (is_string($entry)) {
 
245            return [$this->preparePath($entry)];
242    private function readDirsEntry(mixed $entry): array
243    {
244        if (is_string($entry)) {
 
248        if (!is_array($entry)) {
 
249            return [];
242    private function readDirsEntry(mixed $entry): array
243    {
244        if (is_string($entry)) {
 
248        if (!is_array($entry)) {
 
252        $dirs = [];
253
254        array_walk(
255            $entry,
256            function (mixed $value) use (&$dirs): void {
257                if (is_string($value)) {
258                    $dirs[] = $this->preparePath($value);
259
260                    return;
261                }
262
263                if (!is_array($value)) {
264                    return;
265                }
266
267                if (Util::isAssoc($value)) {
268                    $dirs = array_merge($dirs, $this->readAssocDirs($value));
269
270                    return;
271                }
272
273                array_walk(
274                    $value,
275                    function (mixed $path) use (&$dirs): void {
276                        if (is_string($path)) {
277                            $dirs[] = $this->preparePath($path);
278                        }
279                    },
280                );
281            },
282        );
283
284        return $dirs;
285    }
Config->readDriver
156    private function readDriver(string $dsn): string
157    {
158        $driver = explode(':', $dsn)[0];
159
160        if (in_array($driver, PDO::getAvailableDrivers(), strict: true)) {
 
161            assert($driver !== '', 'PDO driver name must not be empty.');
162
163            return $driver;
156    private function readDriver(string $dsn): string
157    {
158        $driver = explode(':', $dsn)[0];
159
160        if (in_array($driver, PDO::getAvailableDrivers(), strict: true)) {
 
166        throw new RuntimeException('PDO driver not supported: ' . $driver);
167    }
Config->readFlatDirs
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
 
179            return [$this->preparePath($config)];
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
 
182        if (count($config) === 0) {
 
183            return [];
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
 
182        if (count($config) === 0) {
 
186        if (Util::isAssoc($config)) {
 
187            return $this->readAssocDirs($config);
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
 
182        if (count($config) === 0) {
 
186        if (Util::isAssoc($config)) {
 
190        $dirs = [];
191
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
 
193            if (is_string($entry)) {
 
194                array_unshift($dirs, $this->preparePath($entry));
195
196                continue;
 
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
193            if (is_string($entry)) {
194                array_unshift($dirs, $this->preparePath($entry));
195
196                continue;
197            }
198
199            if (array_is_list($entry)) {
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
209            }
210
211            $dirs = array_merge($this->readAssocDirs($entry), $dirs);
212        }
213
214        return $dirs;
215    }
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
 
182        if (count($config) === 0) {
 
186        if (Util::isAssoc($config)) {
 
190        $dirs = [];
191
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
 
193            if (is_string($entry)) {
 
199            if (array_is_list($entry)) {
 
200                foreach ($entry as $path) {
 
200                foreach ($entry as $path) {
 
201                    if (!is_string($path)) {
 
202                        continue;
 
200                foreach ($entry as $path) {
 
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
 
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
193            if (is_string($entry)) {
194                array_unshift($dirs, $this->preparePath($entry));
195
196                continue;
197            }
198
199            if (array_is_list($entry)) {
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
209            }
210
211            $dirs = array_merge($this->readAssocDirs($entry), $dirs);
212        }
213
214        return $dirs;
215    }
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
 
182        if (count($config) === 0) {
 
186        if (Util::isAssoc($config)) {
 
190        $dirs = [];
191
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
 
193            if (is_string($entry)) {
 
199            if (array_is_list($entry)) {
 
200                foreach ($entry as $path) {
 
200                foreach ($entry as $path) {
 
201                    if (!is_string($path)) {
 
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
 
200                foreach ($entry as $path) {
 
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
 
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
193            if (is_string($entry)) {
194                array_unshift($dirs, $this->preparePath($entry));
195
196                continue;
197            }
198
199            if (array_is_list($entry)) {
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
209            }
210
211            $dirs = array_merge($this->readAssocDirs($entry), $dirs);
212        }
213
214        return $dirs;
215    }
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
 
182        if (count($config) === 0) {
 
186        if (Util::isAssoc($config)) {
 
190        $dirs = [];
191
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
 
193            if (is_string($entry)) {
 
199            if (array_is_list($entry)) {
 
200                foreach ($entry as $path) {
 
200                foreach ($entry as $path) {
 
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
 
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
193            if (is_string($entry)) {
194                array_unshift($dirs, $this->preparePath($entry));
195
196                continue;
197            }
198
199            if (array_is_list($entry)) {
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
209            }
210
211            $dirs = array_merge($this->readAssocDirs($entry), $dirs);
212        }
213
214        return $dirs;
215    }
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
 
182        if (count($config) === 0) {
 
186        if (Util::isAssoc($config)) {
 
190        $dirs = [];
191
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
 
193            if (is_string($entry)) {
 
199            if (array_is_list($entry)) {
 
200                foreach ($entry as $path) {
 
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
 
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
193            if (is_string($entry)) {
194                array_unshift($dirs, $this->preparePath($entry));
195
196                continue;
197            }
198
199            if (array_is_list($entry)) {
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
209            }
210
211            $dirs = array_merge($this->readAssocDirs($entry), $dirs);
212        }
213
214        return $dirs;
215    }
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
 
182        if (count($config) === 0) {
 
186        if (Util::isAssoc($config)) {
 
190        $dirs = [];
191
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
 
193            if (is_string($entry)) {
 
199            if (array_is_list($entry)) {
 
192        foreach ($config as $entry) {
193            if (is_string($entry)) {
194                array_unshift($dirs, $this->preparePath($entry));
195
196                continue;
197            }
198
199            if (array_is_list($entry)) {
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
209            }
210
211            $dirs = array_merge($this->readAssocDirs($entry), $dirs);
 
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
193            if (is_string($entry)) {
194                array_unshift($dirs, $this->preparePath($entry));
195
196                continue;
197            }
198
199            if (array_is_list($entry)) {
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
209            }
210
211            $dirs = array_merge($this->readAssocDirs($entry), $dirs);
212        }
213
214        return $dirs;
215    }
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
 
182        if (count($config) === 0) {
 
186        if (Util::isAssoc($config)) {
 
190        $dirs = [];
191
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
193            if (is_string($entry)) {
194                array_unshift($dirs, $this->preparePath($entry));
195
196                continue;
197            }
198
199            if (array_is_list($entry)) {
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
209            }
210
211            $dirs = array_merge($this->readAssocDirs($entry), $dirs);
212        }
213
214        return $dirs;
215    }
176    private function readFlatDirs(string|array $config): array
177    {
178        if (is_string($config)) {
 
182        if (count($config) === 0) {
 
186        if (Util::isAssoc($config)) {
 
190        $dirs = [];
191
192        foreach ($config as $entry) {
 
192        foreach ($config as $entry) {
193            if (is_string($entry)) {
194                array_unshift($dirs, $this->preparePath($entry));
195
196                continue;
197            }
198
199            if (array_is_list($entry)) {
200                foreach ($entry as $path) {
201                    if (!is_string($path)) {
202                        continue;
203                    }
204
205                    array_unshift($dirs, $this->preparePath($path));
206                }
207
208                continue;
209            }
210
211            $dirs = array_merge($this->readAssocDirs($entry), $dirs);
212        }
213
214        return $dirs;
215    }
Config->readMigrationDirs
298    private function readMigrationDirs(string|array $config): array
299    {
300        if (is_string($config)) {
 
301            return [$this->preparePath($config)];
298    private function readMigrationDirs(string|array $config): array
299    {
300        if (is_string($config)) {
 
304        if (count($config) === 0) {
 
305            return [];
298    private function readMigrationDirs(string|array $config): array
299    {
300        if (is_string($config)) {
 
304        if (count($config) === 0) {
 
308        if (Util::isAssoc($config) && !$this->isDriverConfig($config)) {
 
308        if (Util::isAssoc($config) && !$this->isDriverConfig($config)) {
 
308        if (Util::isAssoc($config) && !$this->isDriverConfig($config)) {
 
309            return $this->readNamespacedDirs($config);
298    private function readMigrationDirs(string|array $config): array
299    {
300        if (is_string($config)) {
 
304        if (count($config) === 0) {
 
308        if (Util::isAssoc($config) && !$this->isDriverConfig($config)) {
 
308        if (Util::isAssoc($config) && !$this->isDriverConfig($config)) {
 
308        if (Util::isAssoc($config) && !$this->isDriverConfig($config)) {
 
312        return $this->readFlatDirs($config);
313    }
298    private function readMigrationDirs(string|array $config): array
299    {
300        if (is_string($config)) {
 
304        if (count($config) === 0) {
 
308        if (Util::isAssoc($config) && !$this->isDriverConfig($config)) {
 
308        if (Util::isAssoc($config) && !$this->isDriverConfig($config)) {
 
309            return $this->readNamespacedDirs($config);
298    private function readMigrationDirs(string|array $config): array
299    {
300        if (is_string($config)) {
 
304        if (count($config) === 0) {
 
308        if (Util::isAssoc($config) && !$this->isDriverConfig($config)) {
 
308        if (Util::isAssoc($config) && !$this->isDriverConfig($config)) {
 
312        return $this->readFlatDirs($config);
313    }
Config->readNamespacedDirs
328    private function readNamespacedDirs(array $config): array
329    {
330        $result = [];
331
332        array_walk(
333            $config,
334            function (mixed $dirs, int|string $namespace) use (&$result): void {
335                if (!is_string($namespace) || $namespace === '') {
336                    return;
337                }
338
339                if (is_string($dirs)) {
340                    $result[$namespace] = $this->preparePath($dirs);
341
342                    return;
343                }
344
345                if (!is_array($dirs)) {
346                    return;
347                }
348
349                $result[$namespace] = $this->readDirsEntry($dirs);
350            },
351        );
352
353        return $result;
354    }
Config->setFetchMode
71    public function setFetchMode(int $fetchMode): void
72    {
73        $this->pdo = $this->pdo->fetch($fetchMode);
74    }
Config->setMigrationNamespace
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
 
111            throw new ValueError('Migration namespace must not be empty.');
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
115            throw new ValueError(
116                'Cannot add a namespaced migration directory when migrations are configured as a flat list.',
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
122        $migrations[$namespace] = is_string($dirs)
 
123            ? $this->preparePath($dirs)
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
125        $this->migrations = $migrations;
126    }
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
122        $migrations[$namespace] = is_string($dirs)
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
125        $this->migrations = $migrations;
126    }
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
122        $migrations[$namespace] = is_string($dirs)
 
123            ? $this->preparePath($dirs)
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
125        $this->migrations = $migrations;
126    }
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
122        $migrations[$namespace] = is_string($dirs)
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
125        $this->migrations = $migrations;
126    }
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
115            throw new ValueError(
116                'Cannot add a namespaced migration directory when migrations are configured as a flat list.',
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
122        $migrations[$namespace] = is_string($dirs)
 
123            ? $this->preparePath($dirs)
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
125        $this->migrations = $migrations;
126    }
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
122        $migrations[$namespace] = is_string($dirs)
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
125        $this->migrations = $migrations;
126    }
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
122        $migrations[$namespace] = is_string($dirs)
 
123            ? $this->preparePath($dirs)
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
125        $this->migrations = $migrations;
126    }
108    public function setMigrationNamespace(string $namespace, string|array $dirs): void
109    {
110        if ($namespace === '') {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
114        if (array_is_list($this->migrations) && count($this->migrations) > 0) {
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
 
121        $migrations = array_is_list($this->migrations) ? [] : $this->migrations;
122        $migrations[$namespace] = is_string($dirs)
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
 
122        $migrations[$namespace] = is_string($dirs)
123            ? $this->preparePath($dirs)
124            : $this->readDirsEntry($dirs);
125        $this->migrations = $migrations;
126    }
Config->setMigrations
90    public function setMigrations(array|string $migrations): void
91    {
92        $this->migrations = $this->readMigrationDirs($migrations);
93    }
Config->setMigrationsColumnApplied
138    public function setMigrationsColumnApplied(string $column): void
139    {
140        $this->migrationsColumnApplied = $this->getColumnName($column);
141    }
Config->setMigrationsColumnMigration
133    public function setMigrationsColumnMigration(string $column): void
134    {
135        $this->migrationsColumnMigration = $this->getColumnName($column);
136    }
Config->setMigrationsTable
128    public function setMigrationsTable(string $table): void
129    {
130        $this->migrationsTable = $this->getMigrationsTableName($table);
131    }
Config->setPdoOption
66    public function setPdoOption(int $attribute, mixed $value): void
67    {
68        $this->pdo = $this->pdo->option($attribute, $value);
69    }
Config->setPdoOptions
61    public function setPdoOptions(array $options): void
62    {
63        $this->pdo = $this->pdo->options($options);
64    }
Config->setPlaceholders
77    public function setPlaceholders(Delimiters $delimiters, array $placeholders): void
78    {
79        $this->placeholders = new Placeholders($this->driver, $placeholders, $delimiters);
80    }
{closure:/workspace/celemas/quma/src/Config.php:256-281}
256            function (mixed $value) use (&$dirs): void {
257                if (is_string($value)) {
 
258                    $dirs[] = $this->preparePath($value);
259
260                    return;
256            function (mixed $value) use (&$dirs): void {
257                if (is_string($value)) {
 
263                if (!is_array($value)) {
 
264                    return;
256            function (mixed $value) use (&$dirs): void {
257                if (is_string($value)) {
 
263                if (!is_array($value)) {
 
267                if (Util::isAssoc($value)) {
 
268                    $dirs = array_merge($dirs, $this->readAssocDirs($value));
269
270                    return;
256            function (mixed $value) use (&$dirs): void {
257                if (is_string($value)) {
 
263                if (!is_array($value)) {
 
267                if (Util::isAssoc($value)) {
 
273                array_walk(
274                    $value,
275                    function (mixed $path) use (&$dirs): void {
276                        if (is_string($path)) {
277                            $dirs[] = $this->preparePath($path);
278                        }
279                    },
280                );
281            },
{closure:/workspace/celemas/quma/src/Config.php:275-279}
275                    function (mixed $path) use (&$dirs): void {
276                        if (is_string($path)) {
 
277                            $dirs[] = $this->preparePath($path);
278                        }
279                    },
 
279                    },
275                    function (mixed $path) use (&$dirs): void {
276                        if (is_string($path)) {
 
279                    },
{closure:/workspace/celemas/quma/src/Config.php:334-350}
334            function (mixed $dirs, int|string $namespace) use (&$result): void {
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
336                    return;
334            function (mixed $dirs, int|string $namespace) use (&$result): void {
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
339                if (is_string($dirs)) {
 
340                    $result[$namespace] = $this->preparePath($dirs);
341
342                    return;
334            function (mixed $dirs, int|string $namespace) use (&$result): void {
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
339                if (is_string($dirs)) {
 
345                if (!is_array($dirs)) {
 
346                    return;
334            function (mixed $dirs, int|string $namespace) use (&$result): void {
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
339                if (is_string($dirs)) {
 
345                if (!is_array($dirs)) {
 
349                $result[$namespace] = $this->readDirsEntry($dirs);
350            },
334            function (mixed $dirs, int|string $namespace) use (&$result): void {
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
336                    return;
334            function (mixed $dirs, int|string $namespace) use (&$result): void {
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
339                if (is_string($dirs)) {
 
340                    $result[$namespace] = $this->preparePath($dirs);
341
342                    return;
334            function (mixed $dirs, int|string $namespace) use (&$result): void {
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
339                if (is_string($dirs)) {
 
345                if (!is_array($dirs)) {
 
346                    return;
334            function (mixed $dirs, int|string $namespace) use (&$result): void {
335                if (!is_string($namespace) || $namespace === '') {
 
335                if (!is_string($namespace) || $namespace === '') {
 
339                if (is_string($dirs)) {
 
345                if (!is_array($dirs)) {
 
349                $result[$namespace] = $this->readDirsEntry($dirs);
350            },