Completed
Push — master ( 9728d9...4d9a08 )
by Sergei
27s queued 16s
created

AbstractSchemaManager::createConstraint()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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