Passed
Push — main ( 9c95cc...5952b6 )
by Thomas
01:49
created

Connection::addMigrationDir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Conia\Quma;
6
7
use Conia\Quma\Util;
8
use PDO;
9
use RuntimeException;
10
use ValueError;
11
12
/**
13
 * @psalm-type MigrationDirs = list<non-empty-string>
14
 * @psalm-type SqlDirs = list<non-empty-string>
15
 * @psalm-type SqlAssoc = array<non-empty-string, non-empty-string>
16
 * @psalm-type SqlMixed = list<non-empty-string|SqlAssoc>
17
 * @psalm-type SqlConfig = non-empty-string|SqlAssoc|SqlMixed
18
 */
19
class Connection
20
{
21
    use GetsSetsPrint;
22
23
    /** @var non-empty-string */
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
24
    public readonly string $driver;
25
26
    /** @var SqlDirs */
0 ignored issues
show
Bug introduced by
The type Conia\Quma\SqlDirs was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
27
    protected array $sql;
28
29
    /** @var MigrationDirs */
0 ignored issues
show
Bug introduced by
The type Conia\Quma\MigrationDirs was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
30
    protected array $migrations;
31
32
    protected string $migrationsTable = 'migrations';
33
    protected string $migrationsColumnMigration = 'migration';
34
    protected string $migrationsColumnApplied = 'applied';
35
36
    /**
37
     * @psalm-param SqlConfig $sql
38
     * @psalm-param MigrationDirs $migrations
39
     * */
40 82
    public function __construct(
41
        public readonly string $dsn,
42
        string|array $sql,
43
        string|array $migrations = null,
44
        public readonly ?string $username = null,
45
        public readonly ?string $password = null,
46
        public readonly array $options = [],
47
        public readonly int $fetchMode = PDO::FETCH_BOTH,
48
        bool $print = false
49
    ) {
50 82
        $this->driver = $this->readDriver($this->dsn);
1 ignored issue
show
Bug introduced by
The property driver is declared read-only in Conia\Quma\Connection.
Loading history...
51 81
        $this->sql = $this->readDirs($sql);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->readDirs($sql) of type array or string[] is incompatible with the declared type Conia\Quma\SqlDirs of property $sql.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
52 80
        $this->migrations = $this->readDirs($migrations ?? []);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->readDirs($migrations ?? array()) of type array or string[] is incompatible with the declared type Conia\Quma\MigrationDirs of property $migrations.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
53 79
        $this->print = $print;
54
    }
55
56 71
    public function setMigrationsTable(string $table): void
57
    {
58 71
        $this->migrationsTable = $table;
59
    }
60
61 2
    public function setMigrationsColumnMigration(string $column): void
62
    {
63 2
        $this->migrationsColumnMigration = $column;
64
    }
65
66 2
    public function setMigrationsColumnApplied(string $column): void
67
    {
68 2
        $this->migrationsColumnApplied = $column;
69
    }
70
71 40
    public function migrationsTable(): string
72
    {
73 40
        if ($this->driver === 'pgsql') {
74
            // PostgreSQL table names can contain a schema
75 9
            if (preg_match('/^([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+$/', $this->migrationsTable)) {
76 9
                return $this->migrationsTable;
77
            }
78
        } else {
79 31
            if (preg_match('/^[a-zA-Z0-9_]+$/', $this->migrationsTable)) {
80 30
                return $this->migrationsTable;
81
            }
82
        }
83
84 1
        throw new ValueError('Invalid migrations table name: ' . $this->migrationsTable);
85
    }
86
87 40
    public function migrationsColumnMigration(): string
88
    {
89 40
        return $this->getColumnName($this->migrationsColumnMigration);
90
    }
91
92 40
    public function migrationsColumnApplied(): string
93
    {
94 40
        return $this->getColumnName($this->migrationsColumnApplied);
95
    }
96
97
    /** @psalm-param non-empty-string $migrations */
98 1
    public function addMigrationDir(string $migrations): void
99
    {
100 1
        $migrations = $this->readDirs($migrations);
101 1
        $this->migrations = array_merge($migrations, $this->migrations);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($migrations, $this->migrations) of type array is incompatible with the declared type Conia\Quma\MigrationDirs of property $migrations.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
102
    }
103
104
    /** @psalm-return MigrationDirs */
105 28
    public function migrations(): array
106
    {
107 28
        return $this->migrations;
108
    }
109
110
    /** @psalm-param SqlConfig $sql */
111 1
    public function addSqlDirs(array|string $sql): void
112
    {
113 1
        $sql = $this->readDirs($sql);
114 1
        $this->sql = array_merge($sql, $this->sql);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($sql, $this->sql) of type array is incompatible with the declared type Conia\Quma\SqlDirs of property $sql.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
115
    }
116
117 30
    public function sql(): array
118
    {
119 30
        return $this->sql;
120
    }
121
122
    /** @return non-empty-string */
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
123 81
    protected function preparePath(string $path): string
124
    {
125 81
        $result = realpath($path);
126
127 81
        if ($result) {
128 81
            return $result;
129
        }
130
131 1
        throw new ValueError("Path does not exist: {$path}");
132
    }
133
134
    /** @return non-empty-string */
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
135 82
    protected function readDriver(string $dsn): string
136
    {
137 82
        $driver = explode(':', $dsn)[0];
138
139 82
        if (in_array($driver, PDO::getAvailableDrivers())) {
140
            assert(!empty($driver));
141
142 81
            return $driver;
143
        }
144
145 1
        throw new RuntimeException('PDO driver not supported: ' . $driver);
146
    }
147
148
    /**
149
     * @psalm-param SqlAssoc $entry
150
     *
151
     * @psalm-return MigrationDirs
152
     */
153 7
    protected function prepareDirs(array $entry): array
154
    {
155
        /** @var MigrationDirs */
156 7
        $dirs = [];
157
158
        // Add sql scripts for the current pdo driver.
159
        // Should be the first in the list as they
160
        // may have platform specific queries.
161 7
        if (array_key_exists($this->driver, $entry)) {
162 7
            $dirs[] = $this->preparePath($entry[$this->driver]);
163
        }
164
165
        // Add sql scripts for all platforms
166 7
        if (array_key_exists('all', $entry)) {
167 6
            $dirs[] = $this->preparePath($entry['all']);
168
        }
169
170 7
        return $dirs;
171
    }
172
173
    /**
174
     * Adds the sql script paths from configuration.
175
     *
176
     * Script paths are ordered last in first out (LIFO).
177
     * Which means the last path added is the first one searched
178
     * for a SQL script.
179
     *
180
     * @psalm-param SqlConfig $sql
181
     *
182
     * @return MigrationDirs
183
     */
184 81
    protected function readDirs(string|array $sql): array
185
    {
186 81
        if (is_string($sql)) {
0 ignored issues
show
introduced by
The condition is_string($sql) is always false.
Loading history...
187
            /** @var MigrationDirs */
188 78
            return [$this->preparePath($sql)];
189
        }
190
191 16
        if (Util::isAssoc($sql)) {
192
            /** @var SqlAssoc $sql */
193 2
            return $this->prepareDirs($sql);
0 ignored issues
show
Bug introduced by
$sql of type Conia\Quma\SqlAssoc is incompatible with the type array expected by parameter $entry of Conia\Quma\Connection::prepareDirs(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

193
            return $this->prepareDirs(/** @scrutinizer ignore-type */ $sql);
Loading history...
Bug Best Practice introduced by
The expression return $this->prepareDirs($sql) returns the type array|string[] which is incompatible with the documented return type Conia\Quma\MigrationDirs.
Loading history...
194
        }
195
196
        /** @var MigrationDirs */
197 16
        $dirs = [];
198
199 16
        foreach ($sql as $entry) {
200 7
            if (is_string($entry)) {
201 7
                array_unshift($dirs, $this->preparePath($entry));
202
203 7
                continue;
204
            }
205
206 6
            if (Util::isAssoc($entry)) {
207 5
                $dirs = array_merge($this->prepareDirs($entry), $dirs);
208
209 5
                continue;
210
            }
211
212 1
            throw new ValueError(
213 1
                "A single 'sql' item must be either a string or an associative array"
214 1
            );
215
        }
216
217 15
        return $dirs;
218
    }
219
220 41
    protected function getColumnName(string $column): string
221
    {
222 41
        if (preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
223 39
            return $column;
224
        }
225
226 2
        throw new ValueError('Invalid migrations table column name: ' . $column);
227
    }
228
}
229