Completed
Pull Request — develop (#3565)
by Jonathan
13:21 queued 10:24
created

AbstractSchemaManager   F

Complexity

Total Complexity 110

Size/Duplication

Total Lines 941
Duplicated Lines 0 %

Test Coverage

Coverage 83.23%

Importance

Changes 0
Metric Value
wmc 110
eloc 240
dl 0
loc 941
rs 2
c 0
b 0
f 0
ccs 268
cts 322
cp 0.8323

62 Methods

Rating   Name   Duplication   Size   Complexity  
A listTableColumns() 0 11 2
A getDatabasePlatform() 0 3 1
A listNamespaceNames() 0 7 1
A listDatabases() 0 7 1
A tryMethod() 0 14 2
A __construct() 0 4 2
A listSequences() 0 10 2
A tablesExist() 0 5 1
A listTableIndexes() 0 7 1
A createDatabase() 0 3 1
A createSchema() 0 17 3
A _getPortableSequenceDefinition() 0 3 1
A dropAndCreateSequence() 0 4 1
A dropAndCreateConstraint() 0 4 1
A createSequence() 0 3 1
A _getPortableTriggerDefinition() 0 3 1
A filterAssetNames() 0 8 2
A _getPortableUserDefinition() 0 3 1
A dropIndex() 0 7 2
A _getPortableDatabasesList() 0 14 3
A dropDatabase() 0 3 1
A _getPortableSequencesList() 0 9 2
A listTableDetails() 0 12 2
A _getPortableTableDefinition() 0 3 1
A _getPortableTableForeignKeyDefinition() 0 3 1
A dropAndCreateTable() 0 4 1
A dropTable() 0 3 1
A _getPortableTableColumnList() 0 30 6
A alterTable() 0 10 4
A _getPortableTriggersList() 0 14 3
A createForeignKey() 0 3 1
A tableExists() 0 3 1
A dropConstraint() 0 3 1
A dropView() 0 3 1
B _getPortableTableIndexesList() 0 60 10
A dropAndCreateDatabase() 0 4 1
A _getPortableTableForeignKeysList() 0 9 2
A getPortableNamespacesList() 0 9 2
A createTable() 0 4 1
A createIndex() 0 3 1
A _getPortableTablesList() 0 14 3
A _getPortableViewDefinition() 0 3 1
A dropAndCreateForeignKey() 0 4 1
A dropAndCreateIndex() 0 4 1
A _execSql() 0 4 2
A createSchemaConfig() 0 20 5
A _getPortableDatabaseDefinition() 0 3 1
A dropSequence() 0 3 1
A renameTable() 0 5 1
A getSchemaSearchPaths() 0 3 1
A _getPortableViewsList() 0 15 3
A getPortableNamespaceDefinition() 0 3 1
A extractDoctrineTypeFromComment() 0 9 3
A listTableForeignKeys() 0 9 2
A _getPortableUsersList() 0 14 3
A dropForeignKey() 0 3 1
A listViews() 0 7 1
A listTables() 0 10 2
A dropAndCreateView() 0 4 1
A createView() 0 3 1
A createConstraint() 0 3 1
A listTableNames() 0 8 1

How to fix   Complexity   

Complex Class

Complex classes like AbstractSchemaManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractSchemaManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Schema;
6
7
use Doctrine\DBAL\Connection;
8
use Doctrine\DBAL\ConnectionException;
9
use Doctrine\DBAL\DBALException;
10
use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs;
11
use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs;
12
use Doctrine\DBAL\Events;
13
use Doctrine\DBAL\Platforms\AbstractPlatform;
14
use Doctrine\DBAL\Platforms\Exception\NotSupported;
15
use Throwable;
16
use function array_filter;
17
use function array_intersect;
18
use function array_map;
19
use function array_shift;
20
use function array_values;
21
use function assert;
22
use function call_user_func_array;
23
use function count;
24
use function func_get_args;
25
use function is_array;
26
use function is_callable;
27
use function preg_match;
28
use function strtolower;
29
30
/**
31
 * Base class for schema managers. Schema managers are used to inspect and/or
32
 * modify the database schema/structure.
33
 */
34
abstract class AbstractSchemaManager
35
{
36
    /**
37
     * Holds instance of the Doctrine connection for this schema manager.
38
     *
39
     * @var Connection
40
     */
41
    protected $_conn;
42
43
    /**
44
     * Holds instance of the database platform used for this schema manager.
45
     *
46
     * @var AbstractPlatform
47
     */
48
    protected $_platform;
49
50
    /**
51
     * Constructor. Accepts the Connection instance to manage the schema for.
52
     */
53 5838
    public function __construct(Connection $conn, ?AbstractPlatform $platform = null)
54
    {
55 5838
        $this->_conn     = $conn;
56 5838
        $this->_platform = $platform ?: $this->_conn->getDatabasePlatform();
57 5838
    }
58
59
    /**
60
     * Returns the associated platform.
61
     */
62 3808
    public function getDatabasePlatform() : AbstractPlatform
63
    {
64 3808
        return $this->_platform;
65
    }
66
67
    /**
68
     * Tries any method on the schema manager. Normally a method throws an
69
     * exception when your DBMS doesn't support it or if an error occurs.
70
     * This method allows you to try and method on your SchemaManager
71
     * instance and will return false if it does not work or is not supported.
72
     *
73
     * <code>
74
     * $result = $sm->tryMethod('dropView', 'view_name');
75
     * </code>
76
     *
77
     * @return mixed
78
     */
79 5718
    public function tryMethod()
80
    {
81 5718
        $args   = func_get_args();
82 5718
        $method = $args[0];
83 5718
        unset($args[0]);
84 5718
        $args = array_values($args);
85
86 5718
        $callback = [$this, $method];
87 5718
        assert(is_callable($callback));
88
89
        try {
90 5718
            return call_user_func_array($callback, $args);
91 5704
        } catch (Throwable $e) {
92 5704
            return false;
93
        }
94
    }
95
96
    /**
97
     * Lists the available databases for this connection.
98
     *
99
     * @return array<int, string>
100
     */
101 3401
    public function listDatabases() : array
102
    {
103 3401
        $sql = $this->_platform->getListDatabasesSQL();
104
105 3257
        $databases = $this->_conn->fetchAll($sql);
106
107 3257
        return $this->_getPortableDatabasesList($databases);
108
    }
109
110
    /**
111
     * Returns a list of all namespaces in the current database.
112
     *
113
     * @return array<int, string>
114
     */
115 1269
    public function listNamespaceNames() : array
116
    {
117 1269
        $sql = $this->_platform->getListNamespacesSQL();
118
119 1269
        $namespaces = $this->_conn->fetchAll($sql);
120
121 1269
        return $this->getPortableNamespacesList($namespaces);
122
    }
123
124
    /**
125
     * Lists the available sequences for this connection.
126
     *
127
     * @return array<int, Sequence>
128
     */
129 1301
    public function listSequences(?string $database = null) : array
130
    {
131 1301
        if ($database === null) {
132 1301
            $database = $this->_conn->getDatabase();
133
        }
134 1301
        $sql = $this->_platform->getListSequencesSQL($database);
135
136 1301
        $sequences = $this->_conn->fetchAll($sql);
137
138 1301
        return $this->filterAssetNames($this->_getPortableSequencesList($sequences));
139
    }
140
141
    /**
142
     * Lists the columns for a given table.
143
     *
144
     * In contrast to other libraries and to the old version of Doctrine,
145
     * this column definition does try to contain the 'primary' field for
146
     * the reason that it is not portable across different RDBMS. Use
147
     * {@see listTableIndexes($tableName)} to retrieve the primary key
148
     * of a table. We're a RDBMS specifies more details these are held
149
     * in the platformDetails array.
150
     *
151
     * @return array<string, Column>
152
     */
153 4703
    public function listTableColumns(string $table, ?string $database = null) : array
154
    {
155 4703
        if (! $database) {
156 4702
            $database = $this->_conn->getDatabase();
157
        }
158
159 4703
        $sql = $this->_platform->getListTableColumnsSQL($table, $database);
160
161 4703
        $tableColumns = $this->_conn->fetchAll($sql);
162
163 4703
        return $this->_getPortableTableColumnList($table, $database, $tableColumns);
164
    }
165
166
    /**
167
     * Lists the indexes for a given table returning an array of Index instances.
168
     *
169
     * Keys of the portable indexes list are all lower-cased.
170
     *
171
     * @return array<string, Index>
172
     */
173 4199
    public function listTableIndexes(string $table) : array
174
    {
175 4199
        $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase());
176
177 4199
        $tableIndexes = $this->_conn->fetchAll($sql);
178
179 4199
        return $this->_getPortableTableIndexesList($tableIndexes, $table);
180
    }
181
182
    /**
183
     * Returns true if all the given tables exist.
184
     *
185
     * @param array<int, string> $tableNames
186
     */
187 5122
    public function tablesExist(array $tableNames) : bool
188
    {
189 5122
        $tableNames = array_map('strtolower', $tableNames);
190
191 5122
        return count($tableNames) === count(array_intersect($tableNames, array_map('strtolower', $this->listTableNames())));
192
    }
193
194 5122
    public function tableExists(string $tableName) : bool
195
    {
196 5122
        return $this->tablesExist([$tableName]);
197
    }
198
199
    /**
200
     * Returns a list of all tables in the current database.
201
     *
202
     * @return array<int, string>
203
     */
204 4950
    public function listTableNames() : array
205
    {
206 4950
        $sql = $this->_platform->getListTablesSQL();
207
208 4950
        $tables     = $this->_conn->fetchAll($sql);
209 4950
        $tableNames = $this->_getPortableTablesList($tables);
210
211 4950
        return $this->filterAssetNames($tableNames);
212
    }
213
214
    /**
215
     * Filters asset names if they are configured to return only a subset of all
216
     * the found elements.
217
     *
218
     * @param array<int, mixed> $assetNames
219
     *
220
     * @return array<int, mixed>
221
     */
222 5140
    protected function filterAssetNames(array $assetNames)
223
    {
224 5140
        $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter();
225 5140
        if (! $filter) {
226 5140
            return $assetNames;
227
        }
228
229 1705
        return array_values(array_filter($assetNames, $filter));
230
    }
231
232
    /**
233
     * Lists the tables for this connection.
234
     *
235
     * @return array<int, Table>
236
     */
237 4398
    public function listTables() : array
238
    {
239 4398
        $tableNames = $this->listTableNames();
240
241 4398
        $tables = [];
242 4398
        foreach ($tableNames as $tableName) {
243 4358
            $tables[] = $this->listTableDetails($tableName);
244
        }
245
246 4398
        return $tables;
247
    }
248
249 4691
    public function listTableDetails(string $tableName) : Table
250
    {
251 4691
        $columns     = $this->listTableColumns($tableName);
252 4691
        $foreignKeys = [];
253
254 4691
        if ($this->_platform->supportsForeignKeyConstraints()) {
255 4529
            $foreignKeys = $this->listTableForeignKeys($tableName);
256
        }
257
258 4691
        $indexes = $this->listTableIndexes($tableName);
259
260 4691
        return new Table($tableName, $columns, $indexes, [], $foreignKeys, []);
261
    }
262
263
    /**
264
     * Lists the views this connection has.
265
     *
266
     * @return array<string, View>
267
     */
268 3160
    public function listViews() : array
269
    {
270 3160
        $database = $this->_conn->getDatabase();
271 3160
        $sql      = $this->_platform->getListViewsSQL($database);
272 3160
        $views    = $this->_conn->fetchAll($sql);
273
274 3160
        return $this->_getPortableViewsList($views);
275
    }
276
277
    /**
278
     * Lists the foreign keys for the given table.
279
     *
280
     * @return array<int|string, ForeignKeyConstraint>
281
     */
282 4569
    public function listTableForeignKeys(string $table, ?string $database = null) : array
283
    {
284 4569
        if ($database === null) {
285 4569
            $database = $this->_conn->getDatabase();
286
        }
287 4569
        $sql              = $this->_platform->getListTableForeignKeysSQL($table, $database);
288 4569
        $tableForeignKeys = $this->_conn->fetchAll($sql);
289
290 4569
        return $this->_getPortableTableForeignKeysList($tableForeignKeys);
291
    }
292
293
    /* drop*() Methods */
294
295
    /**
296
     * Drops a database.
297
     *
298
     * NOTE: You can not drop the database this SchemaManager is currently connected to.
299
     */
300 5215
    public function dropDatabase(string $database) : void
301
    {
302 5215
        $this->_execSql($this->_platform->getDropDatabaseSQL($database));
303 4529
    }
304
305
    /**
306
     * Drops the given table.
307
     */
308 5698
    public function dropTable(string $tableName) : void
309
    {
310 5698
        $this->_execSql($this->_platform->getDropTableSQL($tableName));
311 5624
    }
312
313
    /**
314
     * Drops the index from the given table.
315
     *
316
     * @param Index|string $index The name of the index.
317
     * @param Table|string $table The name of the table.
318
     */
319 3286
    public function dropIndex($index, $table) : void
320
    {
321 3286
        if ($index instanceof Index) {
322
            $index = $index->getQuotedName($this->_platform);
323
        }
324
325 3286
        $this->_execSql($this->_platform->getDropIndexSQL($index, $table));
326 3286
    }
327
328
    /**
329
     * Drops the constraint from the given table.
330
     *
331
     * @param Table|string $table The name of the table.
332
     */
333
    public function dropConstraint(Constraint $constraint, $table) : void
334
    {
335
        $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table));
336
    }
337
338
    /**
339
     * Drops a foreign key from a table.
340
     *
341
     * @param ForeignKeyConstraint|string $foreignKey The name of the foreign key.
342
     * @param Table|string                $table      The name of the table with the foreign key.
343
     */
344
    public function dropForeignKey($foreignKey, $table) : void
345
    {
346
        $this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table));
347
    }
348
349
    /**
350
     * Drops a sequence with a given name.
351
     */
352 1298
    public function dropSequence(string $name) : void
353
    {
354 1298
        $this->_execSql($this->_platform->getDropSequenceSQL($name));
355
    }
356
357
    /**
358
     * Drops a view.
359
     */
360 3321
    public function dropView(string $name) : void
361
    {
362 3321
        $this->_execSql($this->_platform->getDropViewSQL($name));
363
    }
364
365
    /* create*() Methods */
366
367
    /**
368
     * Creates a new database.
369
     */
370 5213
    public function createDatabase(string $database) : void
371
    {
372 5213
        $this->_execSql($this->_platform->getCreateDatabaseSQL($database));
373 5213
    }
374
375
    /**
376
     * Creates a new table.
377
     */
378 5716
    public function createTable(Table $table) : void
379
    {
380 5716
        $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS;
381 5716
        $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags));
382 5695
    }
383
384
    /**
385
     * Creates a new sequence.
386
     *
387
     * @throws ConnectionException If something fails at database level.
388
     */
389 1300
    public function createSequence(Sequence $sequence) : void
390
    {
391 1300
        $this->_execSql($this->_platform->getCreateSequenceSQL($sequence));
392 1300
    }
393
394
    /**
395
     * Creates a constraint on a table.
396
     *
397
     * @param Table|string $table
398
     */
399 1
    public function createConstraint(Constraint $constraint, $table) : void
400
    {
401 1
        $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table));
402 1
    }
403
404
    /**
405
     * Creates a new index on a table.
406
     *
407
     * @param Table|string $table The name of the table on which the index is to be created.
408
     */
409 3286
    public function createIndex(Index $index, $table) : void
410
    {
411 3286
        $this->_execSql($this->_platform->getCreateIndexSQL($index, $table));
412 3286
    }
413
414
    /**
415
     * Creates a new foreign key.
416
     *
417
     * @param ForeignKeyConstraint $foreignKey The ForeignKey instance.
418
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
419
     */
420 3291
    public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) : void
421
    {
422 3291
        $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table));
423 3291
    }
424
425
    /**
426
     * Creates a new view.
427
     */
428 3321
    public function createView(View $view) : void
429
    {
430 3321
        $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql()));
431 3321
    }
432
433
    /* dropAndCreate*() Methods */
434
435
    /**
436
     * Drops and creates a constraint.
437
     *
438
     * @see dropConstraint()
439
     * @see createConstraint()
440
     *
441
     * @param Table|string $table
442
     */
443
    public function dropAndCreateConstraint(Constraint $constraint, $table) : void
444
    {
445
        $this->tryMethod('dropConstraint', $constraint, $table);
446
        $this->createConstraint($constraint, $table);
447
    }
448
449
    /**
450
     * Drops and creates a new index on a table.
451
     *
452
     * @param Table|string $table The name of the table on which the index is to be created.
453
     */
454 3286
    public function dropAndCreateIndex(Index $index, $table) : void
455
    {
456 3286
        $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table);
457 3286
        $this->createIndex($index, $table);
458 3286
    }
459
460
    /**
461
     * Drops and creates a new foreign key.
462
     *
463
     * @param ForeignKeyConstraint $foreignKey An associative array that defines properties of the foreign key to be created.
464
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
465
     */
466
    public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) : void
467
    {
468
        $this->tryMethod('dropForeignKey', $foreignKey, $table);
469
        $this->createForeignKey($foreignKey, $table);
470
    }
471
472
    /**
473
     * Drops and create a new sequence.
474
     *
475
     * @throws ConnectionException If something fails at database level.
476
     */
477 1298
    public function dropAndCreateSequence(Sequence $sequence) : void
478
    {
479 1298
        $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform));
480 1298
        $this->createSequence($sequence);
481 1298
    }
482
483
    /**
484
     * Drops and creates a new table.
485
     */
486 5661
    public function dropAndCreateTable(Table $table) : void
487
    {
488 5661
        $this->tryMethod('dropTable', $table->getQuotedName($this->_platform));
489 5661
        $this->createTable($table);
490 5661
    }
491
492
    /**
493
     * Drops and creates a new database.
494
     */
495 5357
    public function dropAndCreateDatabase(string $database) : void
496
    {
497 5357
        $this->tryMethod('dropDatabase', $database);
498 5357
        $this->createDatabase($database);
499 5357
    }
500
501
    /**
502
     * Drops and creates a new view.
503
     */
504 3321
    public function dropAndCreateView(View $view) : void
505
    {
506 3321
        $this->tryMethod('dropView', $view->getQuotedName($this->_platform));
507 3321
        $this->createView($view);
508 3321
    }
509
510
    /* alterTable() Methods */
511
512
    /**
513
     * Alters an existing tables schema.
514
     */
515 3994
    public function alterTable(TableDiff $tableDiff) : void
516
    {
517 3994
        $queries = $this->_platform->getAlterTableSQL($tableDiff);
518
519 3994
        if (! is_array($queries) || ! count($queries)) {
520 1546
            return;
521
        }
522
523 3994
        foreach ($queries as $ddlQuery) {
524 3994
            $this->_execSql($ddlQuery);
525
        }
526 3994
    }
527
528
    /**
529
     * Renames a given table to another name.
530
     */
531 1
    public function renameTable(string $name, string $newName) : void
532
    {
533 1
        $tableDiff          = new TableDiff($name);
534 1
        $tableDiff->newName = $newName;
535 1
        $this->alterTable($tableDiff);
536 1
    }
537
538
    /**
539
     * Methods for filtering return values of list*() methods to convert
540
     * the native DBMS data definition to a portable Doctrine definition
541
     */
542
543
    /**
544
     * @param array<int, mixed> $databases
545
     *
546
     * @return array<int, string>
547
     */
548 3257
    protected function _getPortableDatabasesList(array $databases) : array
549
    {
550 3257
        $list = [];
551 3257
        foreach ($databases as $value) {
552 3257
            $value = $this->_getPortableDatabaseDefinition($value);
553
554 3257
            if (! $value) {
555
                continue;
556
            }
557
558 3257
            $list[] = $value;
559
        }
560
561 3257
        return $list;
562
    }
563
564
    /**
565
     * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition.
566
     *
567
     * @param array<int, array<int, mixed>> $namespaces The list of namespace names in the native DBMS data definition.
568
     *
569
     * @return array<int, string>
570
     */
571 1269
    protected function getPortableNamespacesList(array $namespaces) : array
572
    {
573 1269
        $namespacesList = [];
574
575 1269
        foreach ($namespaces as $namespace) {
576 1269
            $namespacesList[] = $this->getPortableNamespaceDefinition($namespace);
577
        }
578
579 1269
        return $namespacesList;
580
    }
581
582
    /**
583
     * @param array<string, string> $database
584
     */
585
    protected function _getPortableDatabaseDefinition(array $database) : string
586
    {
587
        return array_shift($database);
588
    }
589
590
    /**
591
     * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition.
592
     *
593
     * @param array<string|int, mixed> $namespace The native DBMS namespace definition.
594
     */
595
    protected function getPortableNamespaceDefinition(array $namespace) : string
596
    {
597
        return array_shift($namespace);
598
    }
599
600
    /**
601
     * @param array<int, array<int, mixed>> $triggers
602
     *
603
     * @return array<int, string>
604
     */
605
    protected function _getPortableTriggersList(array $triggers)
606
    {
607
        $list = [];
608
        foreach ($triggers as $value) {
609
            $value = $this->_getPortableTriggerDefinition($value);
610
611
            if (! $value) {
612
                continue;
613
            }
614
615
            $list[] = $value;
616
        }
617
618
        return $list;
619
    }
620
621
    /**
622
     * @param array<string|int, mixed> $trigger
623
     */
624
    protected function _getPortableTriggerDefinition(array $trigger) : string
625
    {
626
        return array_shift($trigger);
627
    }
628
629
    /**
630
     * @param array<int, array<string, mixed>> $sequences
631
     *
632
     * @return array<int, Sequence>
633
     */
634 438
    protected function _getPortableSequencesList(array $sequences) : array
635
    {
636 438
        $list = [];
637
638 438
        foreach ($sequences as $value) {
639 438
            $list[] = $this->_getPortableSequenceDefinition($value);
640
        }
641
642 438
        return $list;
643
    }
644
645
    /**
646
     * @param array<string, mixed> $sequence
647
     *
648
     * @throws DBALException
649
     */
650
    protected function _getPortableSequenceDefinition(array $sequence) : Sequence
651
    {
652
        throw NotSupported::new('Sequences');
653
    }
654
655
    /**
656
     * Independent of the database the keys of the column list result are lowercased.
657
     *
658
     * The name of the created column instance however is kept in its case.
659
     *
660
     * @param array<int, array<string, mixed>> $tableColumns
661
     *
662
     * @return array<string, Column>
663
     */
664 4703
    protected function _getPortableTableColumnList(string $table, ?string $database, array $tableColumns) : array
665
    {
666 4703
        $eventManager = $this->_platform->getEventManager();
667
668 4703
        $list = [];
669 4703
        foreach ($tableColumns as $tableColumn) {
670 4703
            $column           = null;
671 4703
            $defaultPrevented = false;
672
673 4703
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) {
674 3386
                $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn);
675 3386
                $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs);
676
677 3386
                $defaultPrevented = $eventArgs->isDefaultPrevented();
678 3386
                $column           = $eventArgs->getColumn();
679
            }
680
681 4703
            if (! $defaultPrevented) {
682 4703
                $column = $this->_getPortableTableColumnDefinition($tableColumn);
683
            }
684
685 4703
            if (! $column) {
686
                continue;
687
            }
688
689 4703
            $name        = strtolower($column->getQuotedName($this->_platform));
690 4703
            $list[$name] = $column;
691
        }
692
693 4703
        return $list;
694
    }
695
696
    /**
697
     * Gets Table Column Definition.
698
     *
699
     * @param array<string, mixed> $tableColumn
700
     */
701
    abstract protected function _getPortableTableColumnDefinition(array $tableColumn) : Column;
702
703
    /**
704
     * Aggregates and groups the index results according to the required data result.
705
     *
706
     * @param array<int, array<string, mixed>> $tableIndexRows
707
     *
708
     * @return array<string, Index>
709
     */
710 4696
    protected function _getPortableTableIndexesList(array $tableIndexRows, string $tableName) : array
711
    {
712 4696
        $result = [];
713 4696
        foreach ($tableIndexRows as $tableIndex) {
714 4473
            $indexName = $keyName = $tableIndex['key_name'];
715 4473
            if ($tableIndex['primary']) {
716 4471
                $keyName = 'primary';
717
            }
718 4473
            $keyName = strtolower($keyName);
719
720 4473
            if (! isset($result[$keyName])) {
721
                $options = [
722 4473
                    'lengths' => [],
723
                ];
724
725 4473
                if (isset($tableIndex['where'])) {
726 922
                    $options['where'] = $tableIndex['where'];
727
                }
728
729 4473
                $result[$keyName] = [
730 4473
                    'name' => $indexName,
731
                    'columns' => [],
732 4473
                    'unique' => ! $tableIndex['non_unique'],
733 4473
                    'primary' => $tableIndex['primary'],
734 4473
                    'flags' => $tableIndex['flags'] ?? [],
735 4473
                    'options' => $options,
736
                ];
737
            }
738
739 4473
            $result[$keyName]['columns'][]            = $tableIndex['column_name'];
740 4473
            $result[$keyName]['options']['lengths'][] = $tableIndex['length'] ?? null;
741
        }
742
743 4696
        $eventManager = $this->_platform->getEventManager();
744
745 4696
        $indexes = [];
746 4696
        foreach ($result as $indexKey => $data) {
747 4473
            $index            = null;
748 4473
            $defaultPrevented = false;
749
750 4473
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) {
751 3361
                $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn);
752 3361
                $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs);
753
754 3361
                $defaultPrevented = $eventArgs->isDefaultPrevented();
755 3361
                $index            = $eventArgs->getIndex();
756
            }
757
758 4473
            if (! $defaultPrevented) {
759 4473
                $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags'], $data['options']);
760
            }
761
762 4473
            if (! $index) {
763
                continue;
764
            }
765
766 4473
            $indexes[$indexKey] = $index;
767
        }
768
769 4696
        return $indexes;
770
    }
771
772
    /**
773
     * @param array<int, array<string, mixed>> $tables
774
     *
775
     * @return array<int, string>
776
     */
777 4950
    protected function _getPortableTablesList(array $tables) : array
778
    {
779 4950
        $list = [];
780 4950
        foreach ($tables as $value) {
781 4948
            $value = $this->_getPortableTableDefinition($value);
782
783 4948
            if (! $value) {
784
                continue;
785
            }
786
787 4948
            $list[] = $value;
788
        }
789
790 4950
        return $list;
791
    }
792
793
    /**
794
     * @param array<string, string> $table
795
     */
796
    protected function _getPortableTableDefinition(array $table) : string
797
    {
798
        return array_shift($table);
799
    }
800
801
    /**
802
     * @param array<int, array<string, mixed>> $users
803
     *
804
     * @return array<int, array<string, mixed>>
805
     */
806
    protected function _getPortableUsersList(array $users) : array
807
    {
808
        $list = [];
809
        foreach ($users as $value) {
810
            $value = $this->_getPortableUserDefinition($value);
811
812
            if (! $value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array<string,mixed> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
813
                continue;
814
            }
815
816
            $list[] = $value;
817
        }
818
819
        return $list;
820
    }
821
822
    /**
823
     * @param array<string, mixed> $user
824
     *
825
     * @return array<string, mixed>
826
     */
827
    protected function _getPortableUserDefinition(array $user) : array
828
    {
829
        return $user;
830
    }
831
832
    /**
833
     * @param array<int, array<string, mixed>> $views
834
     *
835
     * @return array<string, View>
836
     */
837 3160
    protected function _getPortableViewsList(array $views) : array
838
    {
839 3160
        $list = [];
840 3160
        foreach ($views as $value) {
841 3160
            $view = $this->_getPortableViewDefinition($value);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $view is correct as $this->_getPortableViewDefinition($value) targeting Doctrine\DBAL\Schema\Abs...ortableViewDefinition() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
842
843 3160
            if ($view === null) {
844
                continue;
845
            }
846
847 3160
            $viewName        = strtolower($view->getQuotedName($this->_platform));
848 3160
            $list[$viewName] = $view;
849
        }
850
851 3160
        return $list;
852
    }
853
854
    /**
855
     * @param array<string, mixed> $view
856
     */
857
    protected function _getPortableViewDefinition(array $view) : ?View
858
    {
859
        return null;
860
    }
861
862
    /**
863
     * @param array<int|string, array<string, mixed>> $tableForeignKeys
864
     *
865
     * @return array<int, ForeignKeyConstraint>
866
     */
867 1765
    protected function _getPortableTableForeignKeysList(array $tableForeignKeys) : array
868
    {
869 1765
        $list = [];
870
871 1765
        foreach ($tableForeignKeys as $value) {
872 1515
            $list[] = $this->_getPortableTableForeignKeyDefinition($value);
873
        }
874
875 1765
        return $list;
876
    }
877
878
    /**
879
     * @param array<string, mixed> $tableForeignKey
880
     */
881
    protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey) : ForeignKeyConstraint
0 ignored issues
show
Unused Code introduced by
The parameter $tableForeignKey is not used and could be removed. ( Ignorable by Annotation )

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

881
    protected function _getPortableTableForeignKeyDefinition(/** @scrutinizer ignore-unused */ array $tableForeignKey) : ForeignKeyConstraint

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
882
    {
883
        throw NotSupported::new('ForeignKey');
884
    }
885
886
    /**
887
     * @param array<int, string>|string $sql
888
     */
889 5747
    protected function _execSql($sql) : void
890
    {
891 5747
        foreach ((array) $sql as $query) {
892 5747
            $this->_conn->executeUpdate($query);
893
        }
894 5720
    }
895
896
    /**
897
     * Creates a schema instance for the current database.
898
     */
899 4215
    public function createSchema() : Schema
900
    {
901 4215
        $namespaces = [];
902
903 4215
        if ($this->_platform->supportsSchemas()) {
904 1176
            $namespaces = $this->listNamespaceNames();
905
        }
906
907 4215
        $sequences = [];
908
909 4215
        if ($this->_platform->supportsSequences()) {
910 1177
            $sequences = $this->listSequences();
911
        }
912
913 4215
        $tables = $this->listTables();
914
915 4215
        return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces);
916
    }
917
918
    /**
919
     * Creates the configuration for this schema.
920
     */
921 4452
    public function createSchemaConfig() : SchemaConfig
922
    {
923 4452
        $schemaConfig = new SchemaConfig();
924 4452
        $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength());
925
926 4452
        $searchPaths = $this->getSchemaSearchPaths();
927 4452
        if (isset($searchPaths[0])) {
928 4451
            $schemaConfig->setName($searchPaths[0]);
929
        }
930
931 4452
        $params = $this->_conn->getParams();
932 4452
        if (! isset($params['defaultTableOptions'])) {
933 4452
            $params['defaultTableOptions'] = [];
934
        }
935 4452
        if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) {
936 951
            $params['defaultTableOptions']['charset'] = $params['charset'];
937
        }
938 4452
        $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']);
939
940 4452
        return $schemaConfig;
941
    }
942
943
    /**
944
     * The search path for namespaces in the currently connected database.
945
     *
946
     * The first entry is usually the default namespace in the Schema. All
947
     * further namespaces contain tables/sequences which can also be addressed
948
     * with a short, not full-qualified name.
949
     *
950
     * For databases that don't support subschema/namespaces this method
951
     * returns the name of the currently connected database.
952
     *
953
     * @return array<int, string>
954
     */
955 3722
    public function getSchemaSearchPaths() : array
956
    {
957 3722
        return [$this->_conn->getDatabase()];
958
    }
959
960
    /**
961
     * Given a table comment this method tries to extract a type hint for Doctrine Type. If the type hint is found,
962
     * it's removed from the comment.
963
     *
964
     * @return string|null The extracted Doctrine type or NULL of the type hint was not found.
965
     */
966 4710
    final protected function extractDoctrineTypeFromComment(?string &$comment) : ?string
967
    {
968 4710
        if ($comment === null || ! preg_match('/(.*)\(DC2Type:(((?!\)).)+)\)(.*)/', $comment, $match)) {
969 4705
            return null;
970
        }
971
972 3526
        $comment = $match[1] . $match[4];
973
974 3526
        return $match[2];
975
    }
976
}
977