Passed
Push — dbal ( 9482be...c9bdec )
by Greg
14:20
created

DB::varchar()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 7
rs 10
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2023 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees;
21
22
use Doctrine\DBAL\Configuration as DBALConfiguration;
23
use Doctrine\DBAL\Connection as DBALConnection;
24
use Doctrine\DBAL\Driver as DBALDriver;
25
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
26
use Doctrine\DBAL\Query\QueryBuilder;
27
use DomainException;
28
use Fisharebest\Webtrees\DB\Column;
29
use Fisharebest\Webtrees\DB\ColumnType;
30
use Fisharebest\Webtrees\DB\Drivers\DriverInterface;
31
use Fisharebest\Webtrees\DB\Drivers\MySQLDriver;
32
use Fisharebest\Webtrees\DB\Drivers\PostgreSQLDriver;
33
use Fisharebest\Webtrees\DB\Drivers\SQLiteDriver;
34
use Fisharebest\Webtrees\DB\Drivers\SQLServerDriver;
35
use Fisharebest\Webtrees\DB\ForeignKey;
36
use Fisharebest\Webtrees\DB\Index;
37
use Fisharebest\Webtrees\DB\PrimaryKey;
38
use Fisharebest\Webtrees\DB\UniqueIndex;
39
use Illuminate\Database\Capsule\Manager;
40
use Illuminate\Database\Query\Builder;
41
use PDO;
42
43
use function str_starts_with;
44
45
/**
46
 * Static access to doctrine/dbal and laravel database
47
 */
48
class DB extends Manager
49
{
50
    private static DBALConnection $dbal_connection;
51
52
    private static string $prefix;
53
54
    private static DBALDriver&DriverInterface $driver;
55
56
    public static function connect(PDO $pdo, string $prefix): void
57
    {
58
        $driver_name = $pdo->getAttribute(attribute: PDO::ATTR_DRIVER_NAME);
59
60
        self::$driver = match ($driver_name) {
61
            'mysql'    => new MySQLDriver(pdo: $pdo),
62
            'postgres' => new PostgreSQLDriver(pdo: $pdo),
63
            'sqlite'   => new SQLiteDriver(pdo: $pdo),
64
            'sqlsrv'   => new SQLServerDriver(pdo: $pdo),
65
            default    => throw new DomainException(message: 'No driver available for ' . $driver_name),
66
        };
67
68
        $prefix_filter = static fn (string $name): bool => str_starts_with(haystack: $name, needle: $prefix);
69
        $configuration = new DBALConfiguration();
70
        $configuration->setSchemaAssetsFilter(schemaAssetsFilter: $prefix_filter);
71
72
        self::$dbal_connection = new DBALConnection(params: [], driver: self::$driver, config: $configuration);
73
        self::$prefix          = $prefix;
74
    }
75
76
    public static function driverName(): string
77
    {
78
        return self::$driver->driverName();
79
    }
80
81
    public static function getDBALConnection(): DBALConnection
82
    {
83
        return self::$dbal_connection;
84
    }
85
86
    public static function prefix(string $identifier = ''): string
87
    {
88
        return self::$prefix . $identifier;
89
    }
90
91
    public static function select(string ...$expressions): QueryBuilder
92
    {
93
        return self::$dbal_connection
94
            ->createQueryBuilder()
95
            ->select(...$expressions);
96
    }
97
98
    public static function update(string $table): QueryBuilder
99
    {
100
        return self::$dbal_connection->update(DB::prefix($table));
0 ignored issues
show
Bug introduced by
The call to Doctrine\DBAL\Connection::update() has too few arguments starting with data. ( Ignorable by Annotation )

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

100
        return self::$dbal_connection->/** @scrutinizer ignore-call */ update(DB::prefix($table));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
101
    }
102
103
    /**
104
     * @param string                                                $table
105
     * @param array<array-key,array<string,int|float|string|null>>  $rows
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key,array<st...int|float|string|null>> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key,array<string,int|float|string|null>>.
Loading history...
106
     */
107
    public static function insert(string $table, array $rows): void
108
    {
109
        foreach ($rows as $row) {
110
            DB::getDBALConnection()->insert(DB::prefix($table), $row);
111
        }
112
    }
113
114
    public static function delete(string ...$expressions): QueryBuilder
115
    {
116
        return self::$dbal_connection
117
            ->createQueryBuilder()
118
            ->delete(...$expressions);
0 ignored issues
show
Bug introduced by
$expressions is expanded, but the parameter $table of Doctrine\DBAL\Query\QueryBuilder::delete() does not expect variable arguments. ( Ignorable by Annotation )

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

118
            ->delete(/** @scrutinizer ignore-type */ ...$expressions);
Loading history...
119
    }
120
121
    public static function expression(): ExpressionBuilder
122
    {
123
        return self::$dbal_connection->createExpressionBuilder();
124
    }
125
126
    public static function char(string $name, int $length): Column
127
    {
128
        return new Column(
129
            name: $name,
130
            type: ColumnType::Char,
131
            length: $length,
132
            fixed: true,
133
            collation: self::$driver->collationASCII(),
134
        );
135
    }
136
137
    public static function varchar(string $name, int $length): Column
138
    {
139
        return new Column(
140
            name: $name,
141
            type: ColumnType::Char,
142
            length: $length,
143
            collation: self::$driver->collationASCII(),
144
        );
145
    }
146
147
    public static function nchar(string $name, int $length): Column
148
    {
149
        return new Column(
150
            name: $name,
151
            type: ColumnType::NChar,
152
            length: $length,
153
            fixed: true,
154
            collation: self::$driver->collationUTF8(),
155
        );
156
    }
157
158
    public static function nvarchar(string $name, int $length): Column
159
    {
160
        return new Column(
161
            name: $name,
162
            type: ColumnType::NVarChar,
163
            length: $length,
164
            collation: self::$driver->collationUTF8(),
165
        );
166
    }
167
168
    public static function integer(string $name): Column
169
    {
170
        return new Column(name: $name, type: ColumnType::Integer);
171
    }
172
173
    public static function float(string $name): Column
174
    {
175
        return new Column(name: $name, type: ColumnType::Float);
176
    }
177
178
    public static function text(string $name): Column
179
    {
180
        return new Column(
181
            name: $name,
182
            type: ColumnType::Text,
183
            collation: self::$driver->collationUTF8(),
184
        );
185
    }
186
187
    public static function timestamp(string $name, int $precision = 0): Column
188
    {
189
        return new Column(name: $name, type: ColumnType::Timestamp, precision: $precision);
190
    }
191
192
    /**
193
     * @param array<array-key,string> $columns
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key,string> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key,string>.
Loading history...
194
     *
195
     * @return PrimaryKey
196
     */
197
    public static function primaryKey(array $columns): PrimaryKey
198
    {
199
        return new PrimaryKey(columns: $columns);
200
    }
201
202
    /**
203
     * @param array<array-key,string> $columns
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key,string> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key,string>.
Loading history...
204
     *
205
     * @return Index
206
     */
207
    public static function index(array $columns): Index
208
    {
209
        return new Index(columns: $columns);
210
    }
211
212
    /**
213
     * @param array<array-key,string> $columns
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key,string> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key,string>.
Loading history...
214
     *
215
     * @return UniqueIndex
216
     */
217
    public static function uniqueIndex(array $columns): UniqueIndex
218
    {
219
        return new UniqueIndex(columns: $columns);
220
    }
221
222
    /**
223
     * @param array<array-key,string> $local_columns
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key,string> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key,string>.
Loading history...
224
     * @param string                  $foreign_table
225
     * @param array<array-key,string> $foreign_columns
226
     *
227
     * @return ForeignKey
228
     */
229
    public static function foreignKey(array $local_columns, string $foreign_table, array $foreign_columns = null): ForeignKey
230
    {
231
        return new ForeignKey(
232
            local_columns: $local_columns,
233
            foreign_table: $foreign_table,
234
            foreign_columns: $foreign_columns ?? $local_columns,
235
        );
236
    }
237
238
    /**
239
     * @internal
240
     */
241
    public static function caseInsensitiveLikeOperator(): string
242
    {
243
        if (self::driverName() === 'pgsql') {
244
            return 'ILIKE';
245
        }
246
247
        if (self::driverName() === 'sqlsrv') {
248
            return 'COLLATE SQL_UTF8_General_CI_AI LIKE';
249
        }
250
251
        return 'LIKE';
252
    }
253
254
    /**
255
     * @internal
256
     */
257
    public static function groupConcat(string $column): string
258
    {
259
        switch (DB::driverName()) {
260
            case 'pgsql':
261
            case 'sqlsrv':
262
                return 'STRING_AGG(' . $column . ", ',')";
263
264
            case 'mysql':
265
            case 'sqlite':
266
            default:
267
                return 'GROUP_CONCAT(' . $column . ')';
268
        }
269
    }
270
271
    /**
272
     * PHPSTAN can't detect the magic methods in the parent class.
273
     */
274
    public static function query(): Builder
275
    {
276
        return self::connection()->query();
277
    }
278
}
279