Passed
Pull Request — master (#380)
by Wilmer
03:09
created

CommonSchemaTest::normalizeConstraints()   B

Complexity

Conditions 8
Paths 5

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 7
nc 5
nop 2
dl 0
loc 12
rs 8.4444
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Tests\Common;
6
7
use PDO;
8
use Yiisoft\Db\Constraint\CheckConstraint;
9
use Yiisoft\Db\Constraint\Constraint;
10
use Yiisoft\Db\Constraint\DefaultValueConstraint;
11
use Yiisoft\Db\Constraint\ForeignKeyConstraint;
12
use Yiisoft\Db\Constraint\IndexConstraint;
13
use Yiisoft\Db\Exception\Exception;
14
use Yiisoft\Db\Exception\InvalidConfigException;
15
use Yiisoft\Db\Exception\NotSupportedException;
16
use Yiisoft\Db\Schema\Schema;
17
use Yiisoft\Db\Schema\TableSchemaInterface;
18
use Yiisoft\Db\Tests\AbstractSchemaTest;
19
use Yiisoft\Db\Tests\Support\AnyCaseValue;
20
use Yiisoft\Db\Tests\Support\AnyValue;
21
use Yiisoft\Db\Tests\Support\TestTrait;
22
23
use function array_keys;
24
use function count;
25
use function gettype;
26
use function is_array;
27
use function is_object;
28
use function json_encode;
29
use function ksort;
30
use function mb_chr;
31
use function sort;
32
use function strtolower;
33
34
/**
35
 * @group mssql
36
 * @group mysql
37
 * @group pgsql
38
 * @group oracle
39
 * @group sqlite
40
 */
41
abstract class CommonSchemaTest extends AbstractSchemaTest
42
{
43
    use TestTrait;
44
45
    public function testColumnSchema(array $columns): void
46
    {
47
        $db = $this->getConnectionWithData();
48
49
        $table = $db->getTableSchema('type', true);
50
51
        $this->assertNotNull($table);
52
53
        $expectedColNames = array_keys($columns);
54
        sort($expectedColNames);
55
        $colNames = $table->getColumnNames();
56
        sort($colNames);
57
58
        $this->assertSame($expectedColNames, $colNames);
59
60
        foreach ($table->getColumns() as $name => $column) {
61
            $expected = $columns[$name];
62
63
            $this->assertSame(
64
                $expected['dbType'],
65
                $column->getDbType(),
66
                "dbType of column $name does not match. type is {$column->getType()}, dbType is {$column->getDbType()}."
67
            );
68
            $this->assertSame(
69
                $expected['phpType'],
70
                $column->getPhpType(),
71
                "phpType of column $name does not match. type is {$column->getType()}, dbType is {$column->getDbType()}."
72
            );
73
            $this->assertSame($expected['type'], $column->getType(), "type of column $name does not match.");
74
            $this->assertSame(
75
                $expected['allowNull'],
76
                $column->isAllowNull(),
77
                "allowNull of column $name does not match."
78
            );
79
            $this->assertSame(
80
                $expected['autoIncrement'],
81
                $column->isAutoIncrement(),
82
                "autoIncrement of column $name does not match."
83
            );
84
            $this->assertSame(
85
                $expected['enumValues'],
86
                $column->getEnumValues(),
87
                "enumValues of column $name does not match."
88
            );
89
            $this->assertSame($expected['size'], $column->getSize(), "size of column $name does not match.");
90
            $this->assertSame(
91
                $expected['precision'],
92
                $column->getPrecision(),
93
                "precision of column $name does not match."
94
            );
95
            $this->assertSame($expected['scale'], $column->getScale(), "scale of column $name does not match.");
96
            if (is_object($expected['defaultValue'])) {
97
                $this->assertIsObject(
98
                    $column->getDefaultValue(),
99
                    "defaultValue of column $name is expected to be an object but it is not."
100
                );
101
                $this->assertSame(
102
                    (string) $expected['defaultValue'],
103
                    (string) $column->getDefaultValue(),
104
                    "defaultValue of column $name does not match."
105
                );
106
            } else {
107
                $this->assertSame(
108
                    $expected['defaultValue'],
109
                    $column->getDefaultValue(),
110
                    "defaultValue of column $name does not match."
111
                );
112
            }
113
            /* Pgsql only */
114
            if (isset($expected['dimension'])) {
115
                /** @psalm-suppress UndefinedMethod */
116
                $this->assertSame(
117
                    $expected['dimension'],
118
                    $column->getDimension(),
119
                    "dimension of column $name does not match"
120
                );
121
            }
122
        }
123
    }
124
125
    public function testCompositeFk(): void
126
    {
127
        $db = $this->getConnection();
128
129
        $schema = $db->getSchema();
130
        $table = $schema->getTableSchema('composite_fk');
131
132
        $this->assertNotNull($table);
133
134
        $fk = $table->getForeignKeys();
135
136
        $this->assertCount(1, $fk);
137
        $this->assertTrue(isset($fk['FK_composite_fk_order_item']));
138
        $this->assertEquals('order_item', $fk['FK_composite_fk_order_item'][0]);
139
        $this->assertEquals('order_id', $fk['FK_composite_fk_order_item']['order_id']);
140
        $this->assertEquals('item_id', $fk['FK_composite_fk_order_item']['item_id']);
141
    }
142
143
    public function testContraintTablesExistance(): void
144
    {
145
        $db = $this->getConnectionWithData();
146
147
        $tableNames = ['T_constraints_1', 'T_constraints_2', 'T_constraints_3', 'T_constraints_4'];
148
        $schema = $db->getSchema();
149
150
        foreach ($tableNames as $tableName) {
151
            $tableSchema = $schema->getTableSchema($tableName);
152
            $this->assertInstanceOf(TableSchemaInterface::class, $tableSchema, $tableName);
153
        }
154
    }
155
156
    public function testFindUniquesIndex(): void
157
    {
158
        $db = $this->getConnectionWithData();
159
160
        $schema = $db->getSchema();
161
        $tUpsert = $schema->getTableSchema('T_upsert');
162
163
        $this->assertInstanceOf(TableSchemaInterface::class, $tUpsert);
164
        $this->assertContains([0 => 'email', 1 => 'recovery_email'], $schema->findUniqueIndexes($tUpsert));
0 ignored issues
show
Bug introduced by
It seems like $tUpsert can also be of type null; however, parameter $table of Yiisoft\Db\Schema\Schema...ce::findUniqueIndexes() does only seem to accept Yiisoft\Db\Schema\TableSchemaInterface, maybe add an additional type check? ( Ignorable by Annotation )

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

164
        $this->assertContains([0 => 'email', 1 => 'recovery_email'], $schema->findUniqueIndexes(/** @scrutinizer ignore-type */ $tUpsert));
Loading history...
165
        $this->assertContains([0 => 'email'], $schema->findUniqueIndexes($tUpsert));
166
    }
167
168
    public function testGetColumnNoExist(): void
169
    {
170
        $db = $this->getConnectionWithData();
171
172
        $schema = $db->getSchema();
173
        $table = $schema->getTableSchema('negative_default_values');
174
175
        $this->assertNotNull($table);
176
        $this->assertNull($table->getColumn('no_exist'));
177
    }
178
179
    public function testGetDefaultSchema(): void
180
    {
181
        $db = $this->getConnection();
182
183
        $schema = $db->getSchema();
184
185
        $this->assertNull($schema->getDefaultSchema());
186
    }
187
188
    public function testGetNonExistingTableSchema(): void
189
    {
190
        $db = $this->getConnection();
191
192
        $schema = $db->getSchema();
193
194
        $this->assertNull($schema->getTableSchema('nonexisting_table'));
195
    }
196
197
    public function testGetPrimaryKey(): void
198
    {
199
        $db = $this->getConnection();
200
201
        $command = $db->createCommand();
202
203
        if ($db->getSchema()->getTableSchema('testPKTable') !== null) {
204
            $command->dropTable('testPKTable')->execute();
205
        }
206
207
        $command->createTable('testPKTable', ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER])->execute();
208
        $insertResult = $command->insertEx('testPKTable', ['bar' => 1]);
209
        $selectResult = $command->setSql('select [id] from [testPKTable] where [bar]=1')->queryOne();
210
211
        $this->assertIsArray($insertResult);
212
        $this->assertIsArray($selectResult);
213
        $this->assertEquals($selectResult['id'], $insertResult['id']);
214
    }
215
216
    public function testGetSchemaChecks(): void
217
    {
218
        $db = $this->getConnection();
219
220
        $schema = $db->getSchema();
221
        $tableChecks = $schema->getSchemaChecks();
222
223
        $this->assertIsArray($tableChecks);
224
225
        foreach ($tableChecks as $checks) {
226
            $this->assertIsArray($checks);
227
            $this->assertContainsOnlyInstancesOf(CheckConstraint::class, $checks);
228
        }
229
    }
230
231
    public function testGetSchemaDefaultValues(): void
232
    {
233
        $db = $this->getConnection();
234
235
        $schema = $db->getSchema();
236
        $tableDefaultValues = $schema->getSchemaDefaultValues();
237
238
        $this->assertIsArray($tableDefaultValues);
239
240
        foreach ($tableDefaultValues as $defaultValues) {
241
            $this->assertIsArray($defaultValues);
242
            $this->assertContainsOnlyInstancesOf(DefaultValueConstraint::class, $defaultValues);
243
        }
244
    }
245
246
    public function testGetSchemaForeignKeys(): void
247
    {
248
        $db = $this->getConnection();
249
250
        $schema = $db->getSchema();
251
        $tableForeignKeys = $schema->getSchemaForeignKeys();
252
253
        $this->assertIsArray($tableForeignKeys);
254
255
        foreach ($tableForeignKeys as $foreignKeys) {
256
            $this->assertIsArray($foreignKeys);
257
            $this->assertContainsOnlyInstancesOf(ForeignKeyConstraint::class, $foreignKeys);
258
        }
259
    }
260
261
    public function testGetSchemaIndexes(): void
262
    {
263
        $db = $this->getConnection();
264
265
        $schema = $db->getSchema();
266
        $tableIndexes = $schema->getSchemaIndexes();
267
268
        $this->assertIsArray($tableIndexes);
269
270
        foreach ($tableIndexes as $indexes) {
271
            $this->assertIsArray($indexes);
272
            $this->assertContainsOnlyInstancesOf(IndexConstraint::class, $indexes);
273
        }
274
    }
275
276
    public function testGetSchemaPrimaryKeys(): void
277
    {
278
        $db = $this->getConnection();
279
280
        $schema = $db->getSchema();
281
        $tablePks = $schema->getSchemaPrimaryKeys();
282
283
        $this->assertIsArray($tablePks);
284
        $this->assertContainsOnlyInstancesOf(Constraint::class, $tablePks);
285
    }
286
287
    public function testGetSchemaUniques(): void
288
    {
289
        $db = $this->getConnection();
290
291
        $schema = $db->getSchema();
292
        $tableUniques = $schema->getSchemaUniques();
293
294
        $this->assertIsArray($tableUniques);
295
296
        foreach ($tableUniques as $uniques) {
297
            $this->assertIsArray($uniques);
298
            $this->assertContainsOnlyInstancesOf(Constraint::class, $uniques);
299
        }
300
    }
301
302
    public function testGetStringFieldsSize(): void
303
    {
304
        $db = $this->getConnectionWithData();
305
306
        $schema = $db->getSchema();
307
        $tableSchema = $schema->getTableSchema('type');
308
309
        $this->assertInstanceOf(TableSchemaInterface::class, $tableSchema);
310
311
        $columns = $tableSchema->getColumns();
312
        $expectedType = null;
313
        $expectedSize = null;
314
        $expectedDbType = null;
315
316
        foreach ($columns as $name => $column) {
317
            $type = $column->getType();
318
            $size = $column->getSize();
319
            $dbType = $column->getDbType();
320
321
            if (str_starts_with($name, 'char_')) {
322
                switch ($name) {
323
                    case 'char_col':
324
                        $expectedType = 'char';
325
                        $expectedSize = 100;
326
                        $expectedDbType = 'char(100)';
327
                        break;
328
                    case 'char_col2':
329
                        $expectedType = 'string';
330
                        $expectedSize = 100;
331
                        $expectedDbType = 'varchar(100)';
332
                        break;
333
                    case 'char_col3':
334
                        $expectedType = 'text';
335
                        $expectedSize = null;
336
                        $expectedDbType = 'text';
337
                        break;
338
                }
339
340
                $this->assertSame($expectedType, $type);
341
                $this->assertSame($expectedSize, $size);
342
                $this->assertSame($expectedDbType, $dbType);
343
            }
344
        }
345
    }
346
347
    public function testGetTableChecks(): void
348
    {
349
        $db = $this->getConnectionWithData();
350
351
        $schema = $db->getSchema();
352
        $tableChecks = $schema->getTableChecks('T_constraints_1');
353
354
        $this->assertIsArray($tableChecks);
355
        $this->assertContainsOnlyInstancesOf(CheckConstraint::class, $tableChecks);
356
    }
357
358
    /**
359
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::pdoAttributes()
360
     */
361
    public function testGetTableNames(array $pdoAttributes): void
362
    {
363
        $db = $this->getConnectionWithData();
364
365
        foreach ($pdoAttributes as $name => $value) {
366
            if ($name === PDO::ATTR_EMULATE_PREPARES) {
367
                continue;
368
            }
369
370
            $db->getPDO()?->setAttribute($name, $value);
371
        }
372
373
        $schema = $db->getSchema();
374
        $tablesNames = $schema->getTableNames();
375
        $tablesNames = array_map(static fn ($item) => trim($item, '[]'), $tablesNames);
376
377
        $this->assertContains('customer', $tablesNames);
378
        $this->assertContains('category', $tablesNames);
379
        $this->assertContains('item', $tablesNames);
380
        $this->assertContains('order', $tablesNames);
381
        $this->assertContains('order_item', $tablesNames);
382
        $this->assertContains('type', $tablesNames);
383
        $this->assertContains('animal', $tablesNames);
384
        $this->assertContains('animal_view', $tablesNames);
385
    }
386
387
    /**
388
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::tableSchema()
389
     */
390
    public function testGetTableSchema(string $name, string $expectedName): void
391
    {
392
        $db = $this->getConnectionWithData();
393
394
        $tableSchema = $db->getSchema()->getTableSchema($name);
395
396
        $this->assertInstanceOf(TableSchemaInterface::class, $tableSchema);
397
        $this->assertEquals($expectedName, $tableSchema->getName());
398
    }
399
400
    /**
401
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::pdoAttributes()
402
     */
403
    public function testGetTableSchemas(array $pdoAttributes): void
404
    {
405
        $db = $this->getConnectionWithData();
406
407
        foreach ($pdoAttributes as $name => $value) {
408
            if ($name === PDO::ATTR_EMULATE_PREPARES) {
409
                continue;
410
            }
411
412
            $db->getPDO()?->setAttribute($name, $value);
413
        }
414
415
        $schema = $db->getSchema();
416
        $tables = $schema->getTableSchemas();
417
        $this->assertCount(count($schema->getTableNames()), $tables);
418
419
        foreach ($tables as $table) {
420
            $this->assertInstanceOf(TableSchemaInterface::class, $table);
421
        }
422
    }
423
424
    public function testGetTableSchemasWithAttrCase(): void
425
    {
426
        $db = $this->getConnectionWithData();
427
428
        $schema = $db->getSchema();
429
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
430
431
        $this->assertCount(count($schema->getTableNames()), $schema->getTableSchemas());
432
433
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
434
435
        $this->assertCount(count($schema->getTableNames()), $schema->getTableSchemas());
436
    }
437
438
    public function testNegativeDefaultValues(): void
439
    {
440
        $schema = $this->getConnectionWithData();
441
442
        $schema = $schema->getSchema();
443
        $table = $schema->getTableSchema('negative_default_values');
444
445
        $this->assertNotNull($table);
446
        $this->assertSame(-123, $table->getColumn('tinyint_col')?->getDefaultValue());
447
        $this->assertSame(-123, $table->getColumn('smallint_col')?->getDefaultValue());
448
        $this->assertSame(-123, $table->getColumn('int_col')?->getDefaultValue());
449
        $this->assertSame(-123, $table->getColumn('bigint_col')?->getDefaultValue());
450
        $this->assertSame(-12345.6789, $table->getColumn('float_col')?->getDefaultValue());
451
        $this->assertEquals(-33.22, $table->getColumn('numeric_col')?->getDefaultValue());
452
    }
453
454
    public function testQuoterEscapingValue()
455
    {
456
        $db = $this->getConnectionWithData();
457
458
        $quoter = $db->getQuoter();
459
        $db->createCommand(
460
            <<<SQL
461
            DELETE FROM {{quoter}}
462
            SQL
463
        )->execute();
464
        $data = $this->generateQuoterEscapingValues();
465
466
        foreach ($data as $index => $value) {
467
            $quotedName = $quoter->quoteValue('testValue_' . $index);
468
            $quoteValue = $quoter->quoteValue($value);
469
            $db->createCommand(
470
                <<<SQL
471
                INSERT INTO {{quoter}} (name, description) VALUES ($quotedName, $quoteValue)
472
                SQL
473
            )->execute();
474
            $result = $db->createCommand(
475
                <<<SQL
476
                SELECT * FROM {{quoter}} WHERE name=$quotedName
477
                SQL
478
            )->queryOne();
479
480
            $this->assertSame($value, $result['description']);
481
        }
482
    }
483
484
    /**
485
     * @depends testSchemaCache
486
     */
487
    public function testRefreshTableSchema(): void
488
    {
489
        $db = $this->getConnectionWithData();
490
491
        $schema = $db->getSchema();
492
        $schemaCache = $this->getSchemaCache();
493
494
        $this->assertNotNull($schemaCache);
495
496
        $schema->schemaCacheEnable(true);
497
        $noCacheTable = $schema->getTableSchema('type', true);
498
        $schema->refreshTableSchema('type');
499
        $refreshedTable = $schema->getTableSchema('type');
500
501
        $this->assertNotSame($noCacheTable, $refreshedTable);
502
    }
503
504
    public function testSchemaCache(): void
505
    {
506
        $db = $this->getConnectionWithData();
507
508
        $schema = $db->getSchema();
509
        $schemaCache = $this->getSchemaCache();
510
511
        $this->assertNotNull($schemaCache);
512
513
        $schema->schemaCacheEnable(true);
514
        $noCacheTable = $schema->getTableSchema('type', true);
515
        $cachedTable = $schema->getTableSchema('type');
516
517
        $this->assertSame($noCacheTable, $cachedTable);
518
519
        $db->createCommand()->renameTable('type', 'type_test');
520
        $noCacheTable = $schema->getTableSchema('type', true);
521
522
        $this->assertNotSame($noCacheTable, $cachedTable);
523
524
        $db->createCommand()->renameTable('type_test', 'type');
525
    }
526
527
    /**
528
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::tableSchemaCachePrefixes()
529
     */
530
    public function testTableSchemaCacheWithTablePrefixes(
531
        string $tablePrefix,
532
        string $tableName,
533
        string $testTablePrefix,
534
        string $testTableName
535
    ): void {
536
        $db = $this->getConnectionWithData();
537
538
        $schema = $db->getSchema();
539
        $schemaCache = $this->getSchemaCache();
540
541
        $this->assertNotNull($schemaCache);
542
543
        $schema->schemaCacheEnable(true);
544
        $db->setTablePrefix($tablePrefix);
545
        $noCacheTable = $schema->getTableSchema($tableName, true);
546
547
        $this->assertInstanceOf(TableSchemaInterface::class, $noCacheTable);
548
549
        /* Compare */
550
        $db->setTablePrefix($testTablePrefix);
551
        $testNoCacheTable = $schema->getTableSchema($testTableName);
552
553
        $this->assertSame($noCacheTable, $testNoCacheTable);
554
555
        $db->setTablePrefix($tablePrefix);
556
        $schema->refreshTableSchema($tableName);
557
        $refreshedTable = $schema->getTableSchema($tableName);
558
559
        $this->assertInstanceOf(TableSchemaInterface::class, $refreshedTable);
560
        $this->assertNotSame($noCacheTable, $refreshedTable);
561
562
        /* Compare */
563
        $db->setTablePrefix($testTablePrefix);
564
        $schema->refreshTableSchema($testTablePrefix);
565
        $testRefreshedTable = $schema->getTableSchema($testTableName);
566
567
        $this->assertInstanceOf(TableSchemaInterface::class, $testRefreshedTable);
568
        $this->assertEquals($refreshedTable, $testRefreshedTable);
569
        $this->assertNotSame($testNoCacheTable, $testRefreshedTable);
570
    }
571
572
    /**
573
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::constraints()
574
     *
575
     * @throws Exception
576
     */
577
    public function testTableSchemaConstraints(string $tableName, string $type, mixed $expected): void
578
    {
579
        if ($expected === false) {
580
            $this->expectException(NotSupportedException::class);
581
        }
582
583
        $db = $this->getConnectionWithData();
584
        $schema = $db->getSchema();
585
        $constraints = $schema->{'getTable' . ucfirst($type)}($tableName);
586
587
        $this->assertMetadataEquals($expected, $constraints);
588
    }
589
590
    /**
591
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::constraints()
592
     *
593
     * @throws Exception
594
     * @throws InvalidConfigException
595
     */
596
    public function testTableSchemaConstraintsWithPdoLowercase(string $tableName, string $type, mixed $expected): void
597
    {
598
        if ($expected === false) {
599
            $this->expectException(NotSupportedException::class);
600
        }
601
602
        $db = $this->getConnectionWithData();
603
604
        $schema = $db->getSchema();
605
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
606
        $constraints = $schema->{'getTable' . ucfirst($type)}($tableName, true);
607
608
        $this->assertMetadataEquals($expected, $constraints);
609
    }
610
611
    /**
612
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::constraints()
613
     *
614
     * @throws Exception
615
     * @throws InvalidConfigException
616
     */
617
    public function testTableSchemaConstraintsWithPdoUppercase(string $tableName, string $type, mixed $expected): void
618
    {
619
        if ($expected === false) {
620
            $this->expectException(NotSupportedException::class);
621
        }
622
623
        $db = $this->getConnectionWithData();
624
625
        $schema = $db->getSchema();
626
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
627
        $constraints = $schema->{'getTable' . ucfirst($type)}($tableName, true);
628
629
        $this->assertMetadataEquals($expected, $constraints);
630
    }
631
632
    private function generateQuoterEscapingValues(): array
633
    {
634
        $result = [];
635
        $stringLength = 16;
636
637
        for ($i = 32; $i < 128 - $stringLength; $i += $stringLength) {
638
            $str = '';
639
640
            for ($symbol = $i; $symbol < $i + $stringLength; $symbol++) {
641
                $str .= mb_chr($symbol, 'UTF-8');
642
            }
643
644
            $result[] = $str;
645
            $str = '';
646
647
            for ($symbol = $i; $symbol < $i + $stringLength; $symbol++) {
648
                $str .= mb_chr($symbol, 'UTF-8') . mb_chr($symbol, 'UTF-8');
649
            }
650
651
            $result[] = $str;
652
        }
653
654
        return $result;
655
    }
656
657
    protected function assertMetadataEquals($expected, $actual): void
658
    {
659
        switch (strtolower(gettype($expected))) {
660
            case 'object':
661
                $this->assertIsObject($actual);
662
                break;
663
            case 'array':
664
                $this->assertIsArray($actual);
665
                break;
666
            case 'null':
667
                $this->assertNull($actual);
668
                break;
669
        }
670
671
        if (is_array($expected)) {
672
            $this->normalizeArrayKeys($expected, false);
673
            $this->normalizeArrayKeys($actual, false);
674
        }
675
676
        $this->normalizeConstraints($expected, $actual);
677
678
        if (is_array($expected)) {
679
            $this->normalizeArrayKeys($expected, true);
680
            $this->normalizeArrayKeys($actual, true);
681
        }
682
683
        $this->assertEquals($expected, $actual);
684
    }
685
686
    private function normalizeArrayKeys(array &$array, bool $caseSensitive): void
687
    {
688
        $newArray = [];
689
690
        foreach ($array as $value) {
691
            if ($value instanceof Constraint) {
692
                $key = (array) $value;
693
                unset(
694
                    $key["\000Yiisoft\Db\Constraint\Constraint\000name"],
695
                    $key["\u0000Yiisoft\\Db\\Constraint\\ForeignKeyConstraint\u0000foreignSchemaName"]
696
                );
697
698
                foreach ($key as $keyName => $keyValue) {
699
                    if ($keyValue instanceof AnyCaseValue) {
700
                        $key[$keyName] = $keyValue->value;
701
                    } elseif ($keyValue instanceof AnyValue) {
702
                        $key[$keyName] = '[AnyValue]';
703
                    }
704
                }
705
706
                ksort($key, SORT_STRING);
707
                $newArray[$caseSensitive
708
                    ? json_encode($key, JSON_THROW_ON_ERROR)
709
                    : strtolower(json_encode($key, JSON_THROW_ON_ERROR))] = $value;
710
            } else {
711
                $newArray[] = $value;
712
            }
713
        }
714
715
        ksort($newArray, SORT_STRING);
716
        $array = $newArray;
717
    }
718
719
    private function normalizeConstraints($expected, $actual): void
720
    {
721
        if (is_array($expected)) {
722
            foreach ($expected as $key => $value) {
723
                if (!$value instanceof Constraint || !isset($actual[$key]) || !$actual[$key] instanceof Constraint) {
724
                    continue;
725
                }
726
727
                $this->normalizeConstraintPair($value, $actual[$key]);
728
            }
729
        } elseif ($expected instanceof Constraint && $actual instanceof Constraint) {
730
            $this->normalizeConstraintPair($expected, $actual);
731
        }
732
    }
733
734
    private function normalizeConstraintPair(Constraint $expectedConstraint, Constraint $actualConstraint): void
735
    {
736
        if ($expectedConstraint::class !== $actualConstraint::class) {
737
            return;
738
        }
739
740
        foreach (array_keys((array) $expectedConstraint) as $name) {
741
            if ($expectedConstraint->getName() instanceof AnyValue) {
742
                $actualConstraint->name($expectedConstraint->getName());
743
            } elseif ($expectedConstraint->getName() instanceof AnyCaseValue) {
744
                $actualConstraintName = $actualConstraint->getName();
745
746
                $this->assertIsString($actualConstraintName);
747
748
                $actualConstraint->name(new AnyCaseValue($actualConstraintName));
0 ignored issues
show
Bug introduced by
It seems like $actualConstraintName can also be of type null and object; however, parameter $value of Yiisoft\Db\Tests\Support...aseValue::__construct() does only seem to accept array|string, maybe add an additional type check? ( Ignorable by Annotation )

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

748
                $actualConstraint->name(new AnyCaseValue(/** @scrutinizer ignore-type */ $actualConstraintName));
Loading history...
749
            }
750
        }
751
    }
752
}
753