Completed
Pull Request — master (#3833)
by Benjamin
17:15
created

getPortableNamespaceDefinition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

859
    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...
860
    {
861
        throw NotSupported::new('ForeignKey');
862
    }
863
864
    /**
865
     * @param array<int, string>|string $sql
866
     */
867 4335
    protected function _execSql($sql) : void
868
    {
869 4335
        foreach ((array) $sql as $query) {
870 4335
            $this->_conn->executeUpdate($query);
871
        }
872 3927
    }
873
874
    /**
875
     * Creates a schema instance for the current database.
876
     */
877 118
    public function createSchema() : Schema
878
    {
879 118
        $namespaces = [];
880
881 118
        if ($this->_platform->supportsSchemas()) {
882 7
            $namespaces = $this->listNamespaceNames();
883
        }
884
885 118
        $sequences = [];
886
887 118
        if ($this->_platform->supportsSequences()) {
888 9
            $sequences = $this->listSequences();
889
        }
890
891 118
        $tables = $this->listTables();
892
893 118
        return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces);
894
    }
895
896
    /**
897
     * Creates the configuration for this schema.
898
     */
899 462
    public function createSchemaConfig() : SchemaConfig
900
    {
901 462
        $schemaConfig = new SchemaConfig();
902 462
        $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength());
903
904 462
        $searchPaths = $this->getSchemaSearchPaths();
905 462
        if (isset($searchPaths[0])) {
906 436
            $schemaConfig->setName($searchPaths[0]);
907
        }
908
909 462
        $params = $this->_conn->getParams();
910 462
        if (! isset($params['defaultTableOptions'])) {
911 462
            $params['defaultTableOptions'] = [];
912
        }
913 462
        if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) {
914 26
            $params['defaultTableOptions']['charset'] = $params['charset'];
915
        }
916 462
        $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']);
917
918 462
        return $schemaConfig;
919
    }
920
921
    /**
922
     * The search path for namespaces in the currently connected database.
923
     *
924
     * The first entry is usually the default namespace in the Schema. All
925
     * further namespaces contain tables/sequences which can also be addressed
926
     * with a short, not full-qualified name.
927
     *
928
     * For databases that don't support subschema/namespaces this method
929
     * returns the name of the currently connected database.
930
     *
931
     * @return array<int, string>
932
     */
933 387
    public function getSchemaSearchPaths() : array
934
    {
935 387
        $database = $this->_conn->getDatabase();
936
937 387
        if ($database !== null) {
938 361
            return [$database];
939
        }
940
941 26
        return [];
942
    }
943
944
    /**
945
     * Given a table comment this method tries to extract a type hint for Doctrine Type. If the type hint is found,
946
     * it's removed from the comment.
947
     *
948
     * @return string|null The extracted Doctrine type or NULL of the type hint was not found.
949
     */
950 1897
    final protected function extractDoctrineTypeFromComment(?string &$comment) : ?string
951
    {
952 1897
        if ($comment === null || ! preg_match('/(.*)\(DC2Type:(((?!\)).)+)\)(.*)/', $comment, $match)) {
953 1745
            return null;
954
        }
955
956 240
        $comment = $match[1] . $match[4];
957
958 240
        return $match[2];
959
    }
960
961
    /**
962
     * @throws DatabaseRequired
963
     */
964 1811
    private function ensureDatabase(?string $database, string $methodName) : string
965
    {
966 1811
        if ($database === null) {
967 14
            throw DatabaseRequired::new($methodName);
968
        }
969
970 1797
        return $database;
971
    }
972
}
973