Passed
Pull Request — master (#3070)
by Sergei
07:45
created

_getPortableTableColumnList()   A

Complexity

Conditions 6
Paths 9

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6.0061

Importance

Changes 0
Metric Value
eloc 17
dl 0
loc 30
ccs 17
cts 18
cp 0.9444
rs 9.0777
c 0
b 0
f 0
cc 6
nc 9
nop 3
crap 6.0061
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 1644
    public function __construct(Connection $conn, ?AbstractPlatform $platform = null)
52
    {
53 1644
        $this->_conn     = $conn;
54 1644
        $this->_platform = $platform ?: $this->_conn->getDatabasePlatform();
55 1644
    }
56
57
    /**
58
     * Returns the associated platform.
59
     */
60 488
    public function getDatabasePlatform() : AbstractPlatform
61
    {
62 488
        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 4110
    public function tryMethod(string $method, ...$arguments)
80
    {
81
        try {
82 4110
            return $this->$method(...$arguments);
83 2293
        } catch (Throwable $e) {
84 2293
            return false;
85
        }
86
    }
87
88
    /**
89
     * Lists the available databases for this connection.
90
     *
91
     * @return array<int, string>
92
     */
93 53
    public function listDatabases() : array
94
    {
95 53
        $sql = $this->_platform->getListDatabasesSQL();
96
97 52
        $databases = $this->_conn->fetchAll($sql);
98
99 52
        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 20
    public function listNamespaceNames() : array
108
    {
109 20
        $sql = $this->_platform->getListNamespacesSQL();
110
111 20
        $namespaces = $this->_conn->fetchAll($sql);
112
113 20
        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 1854
    public function listTableColumns(string $table, ?string $database = null) : array
148
    {
149 1854
        $database = $this->ensureDatabase(
150 1854
            $database ?? $this->_conn->getDatabase(),
151 1854
            __METHOD__
152
        );
153
154 1840
        $sql = $this->_platform->getListTableColumnsSQL($table, $database);
155
156 1840
        $tableColumns = $this->_conn->fetchAll($sql);
157
158 1840
        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 1511
    public function listTableIndexes(string $table) : array
169
    {
170 1511
        $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase());
171
172 1511
        $tableIndexes = $this->_conn->fetchAll($sql);
173
174 1511
        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 220
    public function tablesExist(array $tableNames) : bool
183
    {
184 220
        $tableNames = array_map('strtolower', $tableNames);
185
186 220
        return count($tableNames) === count(array_intersect($tableNames, array_map('strtolower', $this->listTableNames())));
187
    }
188
189 220
    public function tableExists(string $tableName) : bool
190
    {
191 220
        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 406
    public function listTableNames() : array
200
    {
201 406
        $sql = $this->_platform->getListTablesSQL();
202
203 406
        $tables     = $this->_conn->fetchAll($sql);
204 406
        $tableNames = $this->_getPortableTablesList($tables);
205
206 406
        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 463
    protected function filterAssetNames(array $assetNames) : array
218
    {
219 463
        $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter();
220 463
        if (! $filter) {
221 455
            return $assetNames;
222
        }
223
224 8
        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 159
    public function listTables() : array
233
    {
234 159
        $tableNames = $this->listTableNames();
235
236 159
        $tables = [];
237 159
        foreach ($tableNames as $tableName) {
238 132
            $tables[] = $this->listTableDetails($tableName);
239
        }
240
241 159
        return $tables;
242
    }
243
244 1439
    public function listTableDetails(string $tableName) : Table
245
    {
246 1439
        $columns     = $this->listTableColumns($tableName);
247 1439
        $foreignKeys = [];
248
249 1439
        if ($this->_platform->supportsForeignKeyConstraints()) {
250 1340
            $foreignKeys = $this->listTableForeignKeys($tableName);
251
        }
252
253 1439
        $indexes = $this->listTableIndexes($tableName);
254
255 1439
        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 1429
    public function listTableForeignKeys(string $table, ?string $database = null) : array
282
    {
283 1429
        if ($database === null) {
284 1429
            $database = $this->_conn->getDatabase();
285
        }
286
287 1429
        $sql              = $this->_platform->getListTableForeignKeysSQL($table, $database);
288 1429
        $tableForeignKeys = $this->_conn->fetchAll($sql);
289
290 1429
        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 66
    public function dropDatabase(string $database) : void
301
    {
302 66
        $this->_execSql($this->_platform->getDropDatabaseSQL($database));
303 48
    }
304
305
    /**
306
     * Drops the given table.
307
     */
308 4188
    public function dropTable(string $tableName) : void
309
    {
310 4188
        $this->_execSql($this->_platform->getDropTableSQL($tableName));
311 2356
    }
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 27
    public function dropIndex($index, $table) : void
320
    {
321 27
        if ($index instanceof Index) {
322
            $index = $index->getQuotedName($this->_platform);
323
        }
324
325 27
        $this->_execSql($this->_platform->getDropIndexSQL($index, $table));
326 27
    }
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 24
    public function dropSequence(string $name) : void
353
    {
354 24
        $this->_execSql($this->_platform->getDropSequenceSQL($name));
355
    }
356
357
    /**
358
     * Drops a view.
359
     */
360 35
    public function dropView(string $name) : void
361
    {
362 35
        $this->_execSql($this->_platform->getDropViewSQL($name));
363
    }
364
365
    /* create*() Methods */
366
367
    /**
368
     * Creates a new database.
369
     */
370 62
    public function createDatabase(string $database) : void
371
    {
372 62
        $this->_execSql($this->_platform->getCreateDatabaseSQL($database));
373 62
    }
374
375
    /**
376
     * Creates a new table.
377
     */
378 4132
    public function createTable(Table $table) : void
379
    {
380 4132
        $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS;
381 4132
        $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags));
382 4020
    }
383
384
    /**
385
     * Creates a new sequence.
386
     *
387
     * @throws ConnectionException If something fails at database level.
388
     */
389 57
    public function createSequence(Sequence $sequence) : void
390
    {
391 57
        $this->_execSql($this->_platform->getCreateSequenceSQL($sequence));
392 49
    }
393
394
    /**
395
     * Creates a constraint on a table.
396
     *
397
     * @param Table|string $table
398
     */
399 2
    public function createConstraint(Constraint $constraint, $table) : void
400
    {
401 2
        $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table));
402 2
    }
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 27
    public function createIndex(Index $index, $table) : void
410
    {
411 27
        $this->_execSql($this->_platform->getCreateIndexSQL($index, $table));
412 27
    }
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 52
    public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) : void
421
    {
422 52
        $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table));
423 52
    }
424
425
    /**
426
     * Creates a new view.
427
     */
428 35
    public function createView(View $view) : void
429
    {
430 35
        $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql()));
431 35
    }
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 27
    public function dropAndCreateIndex(Index $index, $table) : void
455
    {
456 27
        $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table);
457 27
        $this->createIndex($index, $table);
458 27
    }
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 24
    public function dropAndCreateSequence(Sequence $sequence) : void
478
    {
479 24
        $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform));
480 24
        $this->createSequence($sequence);
481 24
    }
482
483
    /**
484
     * Drops and creates a new table.
485
     */
486 3296
    public function dropAndCreateTable(Table $table) : void
487
    {
488 3296
        $this->tryMethod('dropTable', $table->getQuotedName($this->_platform));
489 3296
        $this->createTable($table);
490 3296
    }
491
492
    /**
493
     * Drops and creates a new database.
494
     */
495 67
    public function dropAndCreateDatabase(string $database) : void
496
    {
497 67
        $this->tryMethod('dropDatabase', $database);
498 67
        $this->createDatabase($database);
499 67
    }
500
501
    /**
502
     * Drops and creates a new view.
503
     */
504 35
    public function dropAndCreateView(View $view) : void
505
    {
506 35
        $this->tryMethod('dropView', $view->getQuotedName($this->_platform));
507 35
        $this->createView($view);
508 35
    }
509
510
    /* alterTable() Methods */
511
512
    /**
513
     * Alters an existing tables schema.
514
     */
515 510
    public function alterTable(TableDiff $tableDiff) : void
516
    {
517 510
        $queries = $this->_platform->getAlterTableSQL($tableDiff);
518
519 510
        if (! is_array($queries) || ! count($queries)) {
520 8
            return;
521
        }
522
523 502
        foreach ($queries as $ddlQuery) {
524 502
            $this->_execSql($ddlQuery);
525
        }
526 502
    }
527
528
    /**
529
     * Renames a given table to another name.
530
     */
531 2
    public function renameTable(string $name, string $newName) : void
532
    {
533 2
        $tableDiff          = new TableDiff($name);
534 2
        $tableDiff->newName = $newName;
535 2
        $this->alterTable($tableDiff);
536 2
    }
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 52
    protected function _getPortableDatabasesList(array $databases) : array
549
    {
550 52
        $list = [];
551 52
        foreach ($databases as $value) {
552 52
            $value = $this->_getPortableDatabaseDefinition($value);
553
554 52
            if (! $value) {
555
                continue;
556
            }
557
558 52
            $list[] = $value;
559
        }
560
561 52
        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 20
    protected function getPortableNamespacesList(array $namespaces) : array
572
    {
573 20
        $namespacesList = [];
574
575 20
        foreach ($namespaces as $namespace) {
576 20
            $namespacesList[] = $this->getPortableNamespaceDefinition($namespace);
577
        }
578
579 20
        return $namespacesList;
580
    }
581
582
    /**
583
     * @param array<string, string> $database
584
     */
585
    protected function _getPortableDatabaseDefinition(array $database) : string
586
    {
587
        assert(! empty($database));
588
589
        return array_shift($database);
590
    }
591
592
    /**
593
     * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition.
594
     *
595
     * @param array<string|int, mixed> $namespace The native DBMS namespace definition.
596
     */
597
    protected function getPortableNamespaceDefinition(array $namespace) : string
598
    {
599
        return array_shift($namespace);
600
    }
601
602
    /**
603
     * @param array<int, array<string, mixed>> $sequences
604
     *
605
     * @return array<int, Sequence>
606
     */
607 21
    protected function _getPortableSequencesList(array $sequences) : array
608
    {
609 21
        $list = [];
610
611 21
        foreach ($sequences as $value) {
612 21
            $list[] = $this->_getPortableSequenceDefinition($value);
613
        }
614
615 21
        return $list;
616
    }
617
618
    /**
619
     * @param array<string, mixed> $sequence
620
     *
621
     * @throws DBALException
622
     */
623
    protected function _getPortableSequenceDefinition(array $sequence) : Sequence
624
    {
625
        throw NotSupported::new('Sequences');
626
    }
627
628
    /**
629
     * Independent of the database the keys of the column list result are lowercased.
630
     *
631
     * The name of the created column instance however is kept in its case.
632
     *
633
     * @param array<int, array<string, mixed>> $tableColumns
634
     *
635
     * @return array<string, Column>
636
     */
637 1840
    protected function _getPortableTableColumnList(string $table, string $database, array $tableColumns) : array
638
    {
639 1840
        $eventManager = $this->_platform->getEventManager();
640
641 1840
        $list = [];
642 1840
        foreach ($tableColumns as $tableColumn) {
643 1826
            $column           = null;
644 1826
            $defaultPrevented = false;
645
646 1826
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) {
647 27
                $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn);
648 27
                $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs);
649
650 27
                $defaultPrevented = $eventArgs->isDefaultPrevented();
651 27
                $column           = $eventArgs->getColumn();
652
            }
653
654 1826
            if (! $defaultPrevented) {
655 1826
                $column = $this->_getPortableTableColumnDefinition($tableColumn);
656
            }
657
658 1826
            if (! $column) {
659
                continue;
660
            }
661
662 1826
            $name        = strtolower($column->getQuotedName($this->_platform));
663 1826
            $list[$name] = $column;
664
        }
665
666 1840
        return $list;
667
    }
668
669
    /**
670
     * Gets Table Column Definition.
671
     *
672
     * @param array<string, mixed> $tableColumn
673
     */
674
    abstract protected function _getPortableTableColumnDefinition(array $tableColumn) : Column;
675
676
    /**
677
     * Aggregates and groups the index results according to the required data result.
678
     *
679
     * @param array<int, array<string, mixed>> $tableIndexRows
680
     *
681
     * @return array<string, Index>
682
     */
683 1603
    protected function _getPortableTableIndexesList(array $tableIndexRows, string $tableName) : array
684
    {
685 1603
        $result = [];
686 1603
        foreach ($tableIndexRows as $tableIndex) {
687 659
            $indexName = $keyName = $tableIndex['key_name'];
688 659
            if ($tableIndex['primary']) {
689 543
                $keyName = 'primary';
690
            }
691
692 659
            $keyName = strtolower($keyName);
693
694 659
            if (! isset($result[$keyName])) {
695
                $options = [
696 659
                    'lengths' => [],
697
                ];
698
699 659
                if (isset($tableIndex['where'])) {
700 24
                    $options['where'] = $tableIndex['where'];
701
                }
702
703 659
                $result[$keyName] = [
704 659
                    'name' => $indexName,
705
                    'columns' => [],
706 659
                    'unique' => ! $tableIndex['non_unique'],
707 659
                    'primary' => $tableIndex['primary'],
708 659
                    'flags' => $tableIndex['flags'] ?? [],
709 659
                    'options' => $options,
710
                ];
711
            }
712
713 659
            $result[$keyName]['columns'][]            = $tableIndex['column_name'];
714 659
            $result[$keyName]['options']['lengths'][] = $tableIndex['length'] ?? null;
715
        }
716
717 1603
        $eventManager = $this->_platform->getEventManager();
718
719 1603
        $indexes = [];
720 1603
        foreach ($result as $indexKey => $data) {
721 659
            $index            = null;
722 659
            $defaultPrevented = false;
723
724 659
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) {
725 27
                $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn);
726 27
                $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs);
727
728 27
                $defaultPrevented = $eventArgs->isDefaultPrevented();
729 27
                $index            = $eventArgs->getIndex();
730
            }
731
732 659
            if (! $defaultPrevented) {
733 659
                $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags'], $data['options']);
734
            }
735
736 659
            if (! $index) {
737
                continue;
738
            }
739
740 659
            $indexes[$indexKey] = $index;
741
        }
742
743 1603
        return $indexes;
744
    }
745
746
    /**
747
     * @param array<int, array<string, mixed>> $tables
748
     *
749
     * @return array<int, string>
750
     */
751 406
    protected function _getPortableTablesList(array $tables) : array
752
    {
753 406
        $list = [];
754 406
        foreach ($tables as $value) {
755 379
            $value = $this->_getPortableTableDefinition($value);
756
757 379
            if (! $value) {
758
                continue;
759
            }
760
761 379
            $list[] = $value;
762
        }
763
764 406
        return $list;
765
    }
766
767
    /**
768
     * @param array<string, string> $table
769
     */
770 154
    protected function _getPortableTableDefinition(array $table) : string
771
    {
772 154
        assert(! empty($table));
773
774 154
        return array_shift($table);
775
    }
776
777
    /**
778
     * @param array<int, array<string, mixed>> $users
779
     *
780
     * @return array<int, array<string, mixed>>
781
     */
782
    protected function _getPortableUsersList(array $users) : array
783
    {
784
        $list = [];
785
        foreach ($users as $value) {
786
            $value = $this->_getPortableUserDefinition($value);
787
788
            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...
789
                continue;
790
            }
791
792
            $list[] = $value;
793
        }
794
795
        return $list;
796
    }
797
798
    /**
799
     * @param array<string, mixed> $user
800
     *
801
     * @return array<string, mixed>
802
     */
803
    protected function _getPortableUserDefinition(array $user) : array
804
    {
805
        return $user;
806
    }
807
808
    /**
809
     * @param array<int, array<string, mixed>> $views
810
     *
811
     * @return array<string, View>
812
     */
813 27
    protected function _getPortableViewsList(array $views) : array
814
    {
815 27
        $list = [];
816 27
        foreach ($views as $value) {
817 27
            $view        = $this->_getPortableViewDefinition($value);
818 27
            $name        = strtolower($view->getQuotedName($this->_platform));
819 27
            $list[$name] = $view;
820
        }
821
822 27
        return $list;
823
    }
824
825
    /**
826
     * @param array<string, mixed> $view
827
     */
828
    protected function _getPortableViewDefinition(array $view) : View
829
    {
830
        throw NotSupported::new('Views');
831
    }
832
833
    /**
834
     * @param array<int|string, array<string, mixed>> $tableForeignKeys
835
     *
836
     * @return array<int, ForeignKeyConstraint>
837
     */
838 558
    protected function _getPortableTableForeignKeysList(array $tableForeignKeys) : array
839
    {
840 558
        $list = [];
841
842 558
        foreach ($tableForeignKeys as $value) {
843 128
            $list[] = $this->_getPortableTableForeignKeyDefinition($value);
844
        }
845
846 558
        return $list;
847
    }
848
849
    /**
850
     * @param array<string, mixed> $tableForeignKey
851
     */
852
    protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey) : ForeignKeyConstraint
853
    {
854
        throw NotSupported::new('ForeignKey');
855
    }
856
857
    /**
858
     * @param array<int, string>|string $sql
859
     */
860 4553
    protected function _execSql($sql) : void
861
    {
862 4553
        foreach ((array) $sql as $query) {
863 4553
            $this->_conn->executeUpdate($query);
864
        }
865 4130
    }
866
867
    /**
868
     * Creates a schema instance for the current database.
869
     */
870 122
    public function createSchema() : Schema
871
    {
872 122
        $namespaces = [];
873
874 122
        if ($this->_platform->supportsSchemas()) {
875 10
            $namespaces = $this->listNamespaceNames();
876
        }
877
878 122
        $sequences = [];
879
880 122
        if ($this->_platform->supportsSequences()) {
881 12
            $sequences = $this->listSequences();
882
        }
883
884 122
        $tables = $this->listTables();
885
886 122
        return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces);
887
    }
888
889
    /**
890
     * Creates the configuration for this schema.
891
     */
892 488
    public function createSchemaConfig() : SchemaConfig
893
    {
894 488
        $schemaConfig = new SchemaConfig();
895 488
        $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength());
896
897 488
        $searchPaths = $this->getSchemaSearchPaths();
898 488
        if (isset($searchPaths[0])) {
899 461
            $schemaConfig->setName($searchPaths[0]);
900
        }
901
902 488
        $params = $this->_conn->getParams();
903 488
        if (! isset($params['defaultTableOptions'])) {
904 488
            $params['defaultTableOptions'] = [];
905
        }
906
907 488
        if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) {
908 27
            $params['defaultTableOptions']['charset'] = $params['charset'];
909
        }
910
911 488
        $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']);
912
913 488
        return $schemaConfig;
914
    }
915
916
    /**
917
     * The search path for namespaces in the currently connected database.
918
     *
919
     * The first entry is usually the default namespace in the Schema. All
920
     * further namespaces contain tables/sequences which can also be addressed
921
     * with a short, not full-qualified name.
922
     *
923
     * For databases that don't support subschema/namespaces this method
924
     * returns the name of the currently connected database.
925
     *
926
     * @return array<int, string>
927
     */
928 368
    public function getSchemaSearchPaths() : array
929
    {
930 368
        $database = $this->_conn->getDatabase();
931
932 368
        if ($database !== null) {
933 341
            return [$database];
934
        }
935
936 27
        return [];
937
    }
938
939
    /**
940
     * Given a table comment this method tries to extract a type hint for Doctrine Type. If the type hint is found,
941
     * it's removed from the comment.
942
     *
943
     * @return string|null The extracted Doctrine type or NULL of the type hint was not found.
944
     */
945 2015
    final protected function extractDoctrineTypeFromComment(?string &$comment) : ?string
946
    {
947 2015
        if ($comment === null || ! preg_match('/(.*)\(DC2Type:(((?!\)).)+)\)(.*)/', $comment, $match)) {
948 1858
            return null;
949
        }
950
951 245
        $comment = $match[1] . $match[4];
952
953 245
        return $match[2];
954
    }
955
956
    /**
957
     * @throws DatabaseRequired
958
     */
959 1938
    private function ensureDatabase(?string $database, string $methodName) : string
960
    {
961 1938
        if ($database === null) {
962 14
            throw DatabaseRequired::new($methodName);
963
        }
964
965 1924
        return $database;
966
    }
967
}
968