Failed Conditions
Push — 2.6 ( 53af70...fe8241 )
by Luís
08:32
created

testConvertBooleanAsLiteralIntegers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 0
1
<?php
2
3
namespace Doctrine\Tests\DBAL\Platforms;
4
5
use Doctrine\DBAL\Schema\Column;
6
use Doctrine\DBAL\Schema\Comparator;
7
use Doctrine\DBAL\Schema\Table;
8
use Doctrine\DBAL\Schema\TableDiff;
9
use Doctrine\DBAL\Types\Type;
10
11
abstract class AbstractPostgreSqlPlatformTestCase extends AbstractPlatformTestCase
12
{
13
    public function getGenerateTableSql()
14
    {
15
        return 'CREATE TABLE test (id SERIAL NOT NULL, test VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))';
16
    }
17
18
    public function getGenerateTableWithMultiColumnUniqueIndexSql()
19
    {
20
        return array(
21
            'CREATE TABLE test (foo VARCHAR(255) DEFAULT NULL, bar VARCHAR(255) DEFAULT NULL)',
22
            'CREATE UNIQUE INDEX UNIQ_D87F7E0C8C73652176FF8CAA ON test (foo, bar)'
23
        );
24
    }
25
26
    public function getGenerateAlterTableSql()
27
    {
28
        return array(
29
            'ALTER TABLE mytable ADD quota INT DEFAULT NULL',
30
            'ALTER TABLE mytable DROP foo',
31
            'ALTER TABLE mytable ALTER bar TYPE VARCHAR(255)',
32
            "ALTER TABLE mytable ALTER bar SET DEFAULT 'def'",
33
            'ALTER TABLE mytable ALTER bar SET NOT NULL',
34
            'ALTER TABLE mytable ALTER bloo TYPE BOOLEAN',
35
            "ALTER TABLE mytable ALTER bloo SET DEFAULT 'false'",
36
            'ALTER TABLE mytable ALTER bloo SET NOT NULL',
37
            'ALTER TABLE mytable RENAME TO userlist',
38
        );
39
    }
40
41
    public function getGenerateIndexSql()
42
    {
43
        return 'CREATE INDEX my_idx ON mytable (user_name, last_login)';
44
    }
45
46
    public function getGenerateForeignKeySql()
47
    {
48
        return 'ALTER TABLE test ADD FOREIGN KEY (fk_name_id) REFERENCES other_table (id) NOT DEFERRABLE INITIALLY IMMEDIATE';
49
    }
50
51
    public function testGeneratesForeignKeySqlForNonStandardOptions()
52
    {
53
        $foreignKey = new \Doctrine\DBAL\Schema\ForeignKeyConstraint(
54
                array('foreign_id'), 'my_table', array('id'), 'my_fk', array('onDelete' => 'CASCADE')
55
        );
56
        $this->assertEquals(
57
            "CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE",
58
            $this->_platform->getForeignKeyDeclarationSQL($foreignKey)
59
        );
60
61
        $foreignKey = new \Doctrine\DBAL\Schema\ForeignKeyConstraint(
62
            array('foreign_id'), 'my_table', array('id'), 'my_fk', array('match' => 'full')
63
        );
64
        $this->assertEquals(
65
            "CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) MATCH full NOT DEFERRABLE INITIALLY IMMEDIATE",
66
            $this->_platform->getForeignKeyDeclarationSQL($foreignKey)
67
        );
68
69
        $foreignKey = new \Doctrine\DBAL\Schema\ForeignKeyConstraint(
70
            array('foreign_id'), 'my_table', array('id'), 'my_fk', array('deferrable' => true)
71
        );
72
        $this->assertEquals(
73
            "CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) DEFERRABLE INITIALLY IMMEDIATE",
74
            $this->_platform->getForeignKeyDeclarationSQL($foreignKey)
75
        );
76
77
        $foreignKey = new \Doctrine\DBAL\Schema\ForeignKeyConstraint(
78
            array('foreign_id'), 'my_table', array('id'), 'my_fk', array('deferred' => true)
79
        );
80
        $this->assertEquals(
81
            "CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) NOT DEFERRABLE INITIALLY DEFERRED",
82
            $this->_platform->getForeignKeyDeclarationSQL($foreignKey)
83
        );
84
85
        $foreignKey = new \Doctrine\DBAL\Schema\ForeignKeyConstraint(
86
            array('foreign_id'), 'my_table', array('id'), 'my_fk', array('feferred' => true)
87
        );
88
        $this->assertEquals(
89
            "CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) NOT DEFERRABLE INITIALLY DEFERRED",
90
            $this->_platform->getForeignKeyDeclarationSQL($foreignKey)
91
        );
92
93
        $foreignKey = new \Doctrine\DBAL\Schema\ForeignKeyConstraint(
94
            array('foreign_id'), 'my_table', array('id'), 'my_fk', array('deferrable' => true, 'deferred' => true, 'match' => 'full')
95
        );
96
        $this->assertEquals(
97
            "CONSTRAINT my_fk FOREIGN KEY (foreign_id) REFERENCES my_table (id) MATCH full DEFERRABLE INITIALLY DEFERRED",
98
            $this->_platform->getForeignKeyDeclarationSQL($foreignKey)
99
        );
100
    }
101
102 View Code Duplication
    public function testGeneratesSqlSnippets()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
103
    {
104
        $this->assertEquals('SIMILAR TO', $this->_platform->getRegexpExpression(), 'Regular expression operator is not correct');
105
        $this->assertEquals('"', $this->_platform->getIdentifierQuoteCharacter(), 'Identifier quote character is not correct');
106
        $this->assertEquals('column1 || column2 || column3', $this->_platform->getConcatExpression('column1', 'column2', 'column3'), 'Concatenation expression is not correct');
107
        $this->assertEquals('SUBSTRING(column FROM 5)', $this->_platform->getSubstringExpression('column', 5), 'Substring expression without length is not correct');
108
        $this->assertEquals('SUBSTRING(column FROM 1 FOR 5)', $this->_platform->getSubstringExpression('column', 1, 5), 'Substring expression with length is not correct');
109
    }
110
111 View Code Duplication
    public function testGeneratesTransactionCommands()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
112
    {
113
        $this->assertEquals(
114
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED',
115
            $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED)
116
        );
117
        $this->assertEquals(
118
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED',
119
            $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED)
120
        );
121
        $this->assertEquals(
122
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ',
123
            $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ)
124
        );
125
        $this->assertEquals(
126
            'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE',
127
            $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE)
128
        );
129
    }
130
131
    public function testGeneratesDDLSnippets()
132
    {
133
        $this->assertEquals('CREATE DATABASE foobar', $this->_platform->getCreateDatabaseSQL('foobar'));
134
        $this->assertEquals('DROP DATABASE foobar', $this->_platform->getDropDatabaseSQL('foobar'));
135
        $this->assertEquals('DROP TABLE foobar', $this->_platform->getDropTableSQL('foobar'));
136
    }
137
138 View Code Duplication
    public function testGenerateTableWithAutoincrement()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
139
    {
140
        $table = new \Doctrine\DBAL\Schema\Table('autoinc_table');
141
        $column = $table->addColumn('id', 'integer');
142
        $column->setAutoincrement(true);
143
144
        $this->assertEquals(array('CREATE TABLE autoinc_table (id SERIAL NOT NULL)'), $this->_platform->getCreateTableSQL($table));
145
    }
146
147
    public static function serialTypes() : array
148
    {
149
        return [
150
            ['integer', 'SERIAL'],
151
            ['bigint', 'BIGSERIAL'],
152
        ];
153
    }
154
155
    /**
156
     * @dataProvider serialTypes
157
     * @group 2906
158
     */
159 View Code Duplication
    public function testGenerateTableWithAutoincrementDoesNotSetDefault(string $type, string $definition) : void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
160
    {
161
        $table  = new \Doctrine\DBAL\Schema\Table('autoinc_table_notnull');
162
        $column = $table->addColumn('id', $type);
163
        $column->setAutoIncrement(true);
164
        $column->setNotNull(false);
165
166
        $sql = $this->_platform->getCreateTableSQL($table);
167
168
        self::assertEquals(["CREATE TABLE autoinc_table_notnull (id $definition)"], $sql);
169
    }
170
171
    /**
172
     * @dataProvider serialTypes
173
     * @group 2906
174
     */
175 View Code Duplication
    public function testCreateTableWithAutoincrementAndNotNullAddsConstraint(string $type, string $definition) : void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
    {
177
        $table  = new \Doctrine\DBAL\Schema\Table('autoinc_table_notnull_enabled');
178
        $column = $table->addColumn('id', $type);
179
        $column->setAutoIncrement(true);
180
        $column->setNotNull(true);
181
182
        $sql = $this->_platform->getCreateTableSQL($table);
183
184
        self::assertEquals(["CREATE TABLE autoinc_table_notnull_enabled (id $definition NOT NULL)"], $sql);
185
    }
186
187
    /**
188
     * @dataProvider serialTypes
189
     * @group 2906
190
     */
191
    public function testGetDefaultValueDeclarationSQLIgnoresTheDefaultKeyWhenTheFieldIsSerial(string $type) : void
192
    {
193
        $sql = $this->_platform->getDefaultValueDeclarationSQL(
194
            [
195
                'autoincrement' => true,
196
                'type'          => Type::getType($type),
197
                'default'       => 1,
198
            ]
199
        );
200
201
        self::assertSame('', $sql);
202
    }
203
204 View Code Duplication
    public function testGeneratesTypeDeclarationForIntegers()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
205
    {
206
        $this->assertEquals(
207
            'INT',
208
            $this->_platform->getIntegerTypeDeclarationSQL(array())
209
        );
210
        $this->assertEquals(
211
            'SERIAL',
212
            $this->_platform->getIntegerTypeDeclarationSQL(array('autoincrement' => true)
213
        ));
214
        $this->assertEquals(
215
            'SERIAL',
216
            $this->_platform->getIntegerTypeDeclarationSQL(
217
                array('autoincrement' => true, 'primary' => true)
218
        ));
219
    }
220
221 View Code Duplication
    public function testGeneratesTypeDeclarationForStrings()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
222
    {
223
        $this->assertEquals(
224
            'CHAR(10)',
225
            $this->_platform->getVarcharTypeDeclarationSQL(
226
                array('length' => 10, 'fixed' => true))
227
        );
228
        $this->assertEquals(
229
            'VARCHAR(50)',
230
            $this->_platform->getVarcharTypeDeclarationSQL(array('length' => 50)),
231
            'Variable string declaration is not correct'
232
        );
233
        $this->assertEquals(
234
            'VARCHAR(255)',
235
            $this->_platform->getVarcharTypeDeclarationSQL(array()),
236
            'Long string declaration is not correct'
237
        );
238
    }
239
240
    public function getGenerateUniqueIndexSql()
241
    {
242
        return 'CREATE UNIQUE INDEX index_name ON test (test, test2)';
243
    }
244
245
    public function testGeneratesSequenceSqlCommands()
246
    {
247
        $sequence = new \Doctrine\DBAL\Schema\Sequence('myseq', 20, 1);
248
        $this->assertEquals(
249
            'CREATE SEQUENCE myseq INCREMENT BY 20 MINVALUE 1 START 1',
250
            $this->_platform->getCreateSequenceSQL($sequence)
251
        );
252
        $this->assertEquals(
253
            'DROP SEQUENCE myseq CASCADE',
254
            $this->_platform->getDropSequenceSQL('myseq')
255
        );
256
        $this->assertEquals(
257
            "SELECT NEXTVAL('myseq')",
258
            $this->_platform->getSequenceNextValSQL('myseq')
259
        );
260
    }
261
262
    public function testDoesNotPreferIdentityColumns()
263
    {
264
        $this->assertFalse($this->_platform->prefersIdentityColumns());
265
    }
266
267
    public function testPrefersSequences()
268
    {
269
        $this->assertTrue($this->_platform->prefersSequences());
270
    }
271
272
    public function testSupportsIdentityColumns()
273
    {
274
        $this->assertTrue($this->_platform->supportsIdentityColumns());
275
    }
276
277
    public function testSupportsSavePoints()
278
    {
279
        $this->assertTrue($this->_platform->supportsSavepoints());
280
    }
281
282
    public function testSupportsSequences()
283
    {
284
        $this->assertTrue($this->_platform->supportsSequences());
285
    }
286
287
    /**
288
     * {@inheritdoc}
289
     */
290
    protected function supportsCommentOnStatement()
291
    {
292
        return true;
293
    }
294
295
    public function testModifyLimitQuery()
296
    {
297
        $sql = $this->_platform->modifyLimitQuery('SELECT * FROM user', 10, 0);
298
        $this->assertEquals('SELECT * FROM user LIMIT 10 OFFSET 0', $sql);
299
    }
300
301
    public function testModifyLimitQueryWithEmptyOffset()
302
    {
303
        $sql = $this->_platform->modifyLimitQuery('SELECT * FROM user', 10);
304
        $this->assertEquals('SELECT * FROM user LIMIT 10', $sql);
305
    }
306
307
    public function getCreateTableColumnCommentsSQL()
308
    {
309
        return array(
310
            "CREATE TABLE test (id INT NOT NULL, PRIMARY KEY(id))",
311
            "COMMENT ON COLUMN test.id IS 'This is a comment'",
312
        );
313
    }
314
315
    public function getAlterTableColumnCommentsSQL()
316
    {
317
        return array(
318
            "ALTER TABLE mytable ADD quota INT NOT NULL",
319
            "COMMENT ON COLUMN mytable.quota IS 'A comment'",
320
            "COMMENT ON COLUMN mytable.foo IS NULL",
321
            "COMMENT ON COLUMN mytable.baz IS 'B comment'",
322
        );
323
    }
324
325
    public function getCreateTableColumnTypeCommentsSQL()
326
    {
327
        return array(
328
            "CREATE TABLE test (id INT NOT NULL, data TEXT NOT NULL, PRIMARY KEY(id))",
329
            "COMMENT ON COLUMN test.data IS '(DC2Type:array)'"
330
        );
331
    }
332
333
    protected function getQuotedColumnInPrimaryKeySQL()
334
    {
335
        return array(
336
            'CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL, PRIMARY KEY("create"))',
337
        );
338
    }
339
340
    protected function getQuotedColumnInIndexSQL()
341
    {
342
        return array(
343
            'CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL)',
344
            'CREATE INDEX IDX_22660D028FD6E0FB ON "quoted" ("create")',
345
        );
346
    }
347
348
    protected function getQuotedNameInIndexSQL()
349
    {
350
        return array(
351
            'CREATE TABLE test (column1 VARCHAR(255) NOT NULL)',
352
            'CREATE INDEX "key" ON test (column1)',
353
        );
354
    }
355
356
    protected function getQuotedColumnInForeignKeySQL()
357
    {
358
        return array(
359
            'CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL, foo VARCHAR(255) NOT NULL, "bar" VARCHAR(255) NOT NULL)',
360
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_RESERVED_KEYWORD FOREIGN KEY ("create", foo, "bar") REFERENCES "foreign" ("create", bar, "foo-bar") NOT DEFERRABLE INITIALLY IMMEDIATE',
361
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_NON_RESERVED_KEYWORD FOREIGN KEY ("create", foo, "bar") REFERENCES foo ("create", bar, "foo-bar") NOT DEFERRABLE INITIALLY IMMEDIATE',
362
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_INTENDED_QUOTATION FOREIGN KEY ("create", foo, "bar") REFERENCES "foo-bar" ("create", bar, "foo-bar") NOT DEFERRABLE INITIALLY IMMEDIATE',
363
        );
364
    }
365
366
    /**
367
     * @group DBAL-457
368
     * @dataProvider pgBooleanProvider
369
     *
370
     * @param string $databaseValue
371
     * @param string $preparedStatementValue
372
     * @param integer $integerValue
373
     * @param boolean $booleanValue
374
     */
375
    public function testConvertBooleanAsLiteralStrings(
376
        $databaseValue,
377
        $preparedStatementValue,
378
        $integerValue,
0 ignored issues
show
Unused Code introduced by
The parameter $integerValue is not used and could be removed.

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

Loading history...
379
        $booleanValue
0 ignored issues
show
Unused Code introduced by
The parameter $booleanValue is not used and could be removed.

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

Loading history...
380
    ) {
381
        $platform = $this->createPlatform();
382
383
        $this->assertEquals($preparedStatementValue, $platform->convertBooleans($databaseValue));
384
    }
385
386
    /**
387
     * @group DBAL-457
388
     */
389
    public function testConvertBooleanAsLiteralIntegers()
390
    {
391
        $platform = $this->createPlatform();
392
        $platform->setUseBooleanTrueFalseStrings(false);
393
394
        $this->assertEquals(1, $platform->convertBooleans(true));
395
        $this->assertEquals(1, $platform->convertBooleans('1'));
396
397
        $this->assertEquals(0, $platform->convertBooleans(false));
398
        $this->assertEquals(0, $platform->convertBooleans('0'));
399
    }
400
401
    /**
402
     * @group DBAL-630
403
     * @dataProvider pgBooleanProvider
404
     *
405
     * @param string $databaseValue
406
     * @param string $preparedStatementValue
407
     * @param integer $integerValue
408
     * @param boolean $booleanValue
409
     */
410
    public function testConvertBooleanAsDatabaseValueStrings(
411
        $databaseValue,
0 ignored issues
show
Unused Code introduced by
The parameter $databaseValue is not used and could be removed.

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

Loading history...
412
        $preparedStatementValue,
0 ignored issues
show
Unused Code introduced by
The parameter $preparedStatementValue is not used and could be removed.

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

Loading history...
413
        $integerValue,
414
        $booleanValue
415
    )
416
    {
417
        $platform = $this->createPlatform();
418
419
        $this->assertSame($integerValue, $platform->convertBooleansToDatabaseValue($booleanValue));
420
    }
421
422
    /**
423
     * @group DBAL-630
424
     */
425
    public function testConvertBooleanAsDatabaseValueIntegers()
426
    {
427
        $platform = $this->createPlatform();
428
        $platform->setUseBooleanTrueFalseStrings(false);
429
430
        $this->assertSame(1, $platform->convertBooleansToDatabaseValue(true));
431
        $this->assertSame(0, $platform->convertBooleansToDatabaseValue(false));
432
    }
433
434
    /**
435
     * @dataProvider pgBooleanProvider
436
     *
437
     * @param string $databaseValue
438
     * @param string $prepareStatementValue
439
     * @param integer $integerValue
440
     * @param boolean $booleanValue
441
     */
442
    public function testConvertFromBoolean($databaseValue, $prepareStatementValue, $integerValue, $booleanValue)
0 ignored issues
show
Unused Code introduced by
The parameter $prepareStatementValue is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $integerValue is not used and could be removed.

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

Loading history...
443
    {
444
        $platform = $this->createPlatform();
445
446
        $this->assertSame($booleanValue, $platform->convertFromBoolean($databaseValue));
447
    }
448
449
    /**
450
     * @expectedException        UnexpectedValueException
451
     * @expectedExceptionMessage Unrecognized boolean literal 'my-bool'
452
     */
453
    public function testThrowsExceptionWithInvalidBooleanLiteral()
454
    {
455
        $platform = $this->createPlatform()->convertBooleansToDatabaseValue("my-bool");
0 ignored issues
show
Unused Code introduced by
$platform is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
456
    }
457
458
    public function testGetCreateSchemaSQL()
459
    {
460
        $schemaName = 'schema';
461
        $sql = $this->_platform->getCreateSchemaSQL($schemaName);
462
        $this->assertEquals('CREATE SCHEMA ' . $schemaName, $sql);
463
    }
464
465
    public function testAlterDecimalPrecisionScale()
466
    {
467
468
        $table = new Table('mytable');
469
        $table->addColumn('dfoo1', 'decimal');
470
        $table->addColumn('dfoo2', 'decimal', array('precision' => 10, 'scale' => 6));
471
        $table->addColumn('dfoo3', 'decimal', array('precision' => 10, 'scale' => 6));
472
        $table->addColumn('dfoo4', 'decimal', array('precision' => 10, 'scale' => 6));
473
474
        $tableDiff = new TableDiff('mytable');
475
        $tableDiff->fromTable = $table;
476
477
        $tableDiff->changedColumns['dloo1'] = new \Doctrine\DBAL\Schema\ColumnDiff(
478
            'dloo1', new \Doctrine\DBAL\Schema\Column(
479
                'dloo1', \Doctrine\DBAL\Types\Type::getType('decimal'), array('precision' => 16, 'scale' => 6)
480
            ),
481
            array('precision')
482
        );
483
        $tableDiff->changedColumns['dloo2'] = new \Doctrine\DBAL\Schema\ColumnDiff(
484
            'dloo2', new \Doctrine\DBAL\Schema\Column(
485
                'dloo2', \Doctrine\DBAL\Types\Type::getType('decimal'), array('precision' => 10, 'scale' => 4)
486
            ),
487
            array('scale')
488
        );
489
        $tableDiff->changedColumns['dloo3'] = new \Doctrine\DBAL\Schema\ColumnDiff(
490
            'dloo3', new \Doctrine\DBAL\Schema\Column(
491
                'dloo3', \Doctrine\DBAL\Types\Type::getType('decimal'), array('precision' => 10, 'scale' => 6)
492
            ),
493
            array()
494
        );
495
        $tableDiff->changedColumns['dloo4'] = new \Doctrine\DBAL\Schema\ColumnDiff(
496
            'dloo4', new \Doctrine\DBAL\Schema\Column(
497
                'dloo4', \Doctrine\DBAL\Types\Type::getType('decimal'), array('precision' => 16, 'scale' => 8)
498
            ),
499
            array('precision', 'scale')
500
        );
501
502
        $sql = $this->_platform->getAlterTableSQL($tableDiff);
503
504
        $expectedSql = array(
505
            'ALTER TABLE mytable ALTER dloo1 TYPE NUMERIC(16, 6)',
506
            'ALTER TABLE mytable ALTER dloo2 TYPE NUMERIC(10, 4)',
507
            'ALTER TABLE mytable ALTER dloo4 TYPE NUMERIC(16, 8)',
508
        );
509
510
        $this->assertEquals($expectedSql, $sql);
511
    }
512
513
    /**
514
     * @group DBAL-365
515
     */
516
    public function testDroppingConstraintsBeforeColumns()
517
    {
518
        $newTable = new Table('mytable');
519
        $newTable->addColumn('id', 'integer');
520
        $newTable->setPrimaryKey(array('id'));
521
522
        $oldTable = clone $newTable;
523
        $oldTable->addColumn('parent_id', 'integer');
524
        $oldTable->addUnnamedForeignKeyConstraint('mytable', array('parent_id'), array('id'));
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\DBAL\Schema\Tab...dForeignKeyConstraint() has been deprecated with message: Use {@link addForeignKeyConstraint}

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
525
526
        $comparator = new \Doctrine\DBAL\Schema\Comparator();
527
        $tableDiff = $comparator->diffTable($oldTable, $newTable);
528
529
        $sql = $this->_platform->getAlterTableSQL($tableDiff);
0 ignored issues
show
Security Bug introduced by
It seems like $tableDiff defined by $comparator->diffTable($oldTable, $newTable) on line 527 can also be of type false; however, Doctrine\DBAL\Platforms\...orm::getAlterTableSQL() does only seem to accept object<Doctrine\DBAL\Schema\TableDiff>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
530
531
        $expectedSql = array(
532
            'ALTER TABLE mytable DROP CONSTRAINT FK_6B2BD609727ACA70',
533
            'DROP INDEX IDX_6B2BD609727ACA70',
534
            'ALTER TABLE mytable DROP parent_id',
535
        );
536
537
        $this->assertEquals($expectedSql, $sql);
538
    }
539
540
    /**
541
     * @group DBAL-563
542
     */
543
    public function testUsesSequenceEmulatedIdentityColumns()
544
    {
545
        $this->assertTrue($this->_platform->usesSequenceEmulatedIdentityColumns());
546
    }
547
548
    /**
549
     * @group DBAL-563
550
     */
551
    public function testReturnsIdentitySequenceName()
552
    {
553
        $this->assertSame('mytable_mycolumn_seq', $this->_platform->getIdentitySequenceName('mytable', 'mycolumn'));
554
    }
555
556
    /**
557
     * @dataProvider dataCreateSequenceWithCache
558
     * @group DBAL-139
559
     */
560
    public function testCreateSequenceWithCache($cacheSize, $expectedSql)
561
    {
562
        $sequence = new \Doctrine\DBAL\Schema\Sequence('foo', 1, 1, $cacheSize);
563
        $this->assertContains($expectedSql, $this->_platform->getCreateSequenceSQL($sequence));
564
    }
565
566
    public function dataCreateSequenceWithCache()
567
    {
568
        return array(
569
            array(3, 'CACHE 3')
570
        );
571
    }
572
573
    protected function getBinaryDefaultLength()
574
    {
575
        return 0;
576
    }
577
578
    protected function getBinaryMaxLength()
579
    {
580
        return 0;
581
    }
582
583 View Code Duplication
    public function testReturnsBinaryTypeDeclarationSQL()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
584
    {
585
        $this->assertSame('BYTEA', $this->_platform->getBinaryTypeDeclarationSQL(array()));
586
        $this->assertSame('BYTEA', $this->_platform->getBinaryTypeDeclarationSQL(array('length' => 0)));
587
        $this->assertSame('BYTEA', $this->_platform->getBinaryTypeDeclarationSQL(array('length' => 9999999)));
588
589
        $this->assertSame('BYTEA', $this->_platform->getBinaryTypeDeclarationSQL(array('fixed' => true)));
590
        $this->assertSame('BYTEA', $this->_platform->getBinaryTypeDeclarationSQL(array('fixed' => true, 'length' => 0)));
591
        $this->assertSame('BYTEA', $this->_platform->getBinaryTypeDeclarationSQL(array('fixed' => true, 'length' => 9999999)));
592
    }
593
594
    public function testDoesNotPropagateUnnecessaryTableAlterationOnBinaryType()
595
    {
596
        $table1 = new Table('mytable');
597
        $table1->addColumn('column_varbinary', 'binary');
598
        $table1->addColumn('column_binary', 'binary', array('fixed' => true));
599
        $table1->addColumn('column_blob', 'blob');
600
601
        $table2 = new Table('mytable');
602
        $table2->addColumn('column_varbinary', 'binary', array('fixed' => true));
603
        $table2->addColumn('column_binary', 'binary');
604
        $table2->addColumn('column_blob', 'binary');
605
606
        $comparator = new Comparator();
607
608
        // VARBINARY -> BINARY
609
        // BINARY    -> VARBINARY
610
        // BLOB      -> VARBINARY
611
        $this->assertEmpty($this->_platform->getAlterTableSQL($comparator->diffTable($table1, $table2)));
0 ignored issues
show
Security Bug introduced by
It seems like $comparator->diffTable($table1, $table2) targeting Doctrine\DBAL\Schema\Comparator::diffTable() can also be of type false; however, Doctrine\DBAL\Platforms\...orm::getAlterTableSQL() does only seem to accept object<Doctrine\DBAL\Schema\TableDiff>, did you maybe forget to handle an error condition?
Loading history...
612
613
        $table2 = new Table('mytable');
614
        $table2->addColumn('column_varbinary', 'binary', array('length' => 42));
615
        $table2->addColumn('column_binary', 'blob');
616
        $table2->addColumn('column_blob', 'binary', array('length' => 11, 'fixed' => true));
617
618
        // VARBINARY -> VARBINARY with changed length
619
        // BINARY    -> BLOB
620
        // BLOB      -> BINARY
621
        $this->assertEmpty($this->_platform->getAlterTableSQL($comparator->diffTable($table1, $table2)));
0 ignored issues
show
Security Bug introduced by
It seems like $comparator->diffTable($table1, $table2) targeting Doctrine\DBAL\Schema\Comparator::diffTable() can also be of type false; however, Doctrine\DBAL\Platforms\...orm::getAlterTableSQL() does only seem to accept object<Doctrine\DBAL\Schema\TableDiff>, did you maybe forget to handle an error condition?
Loading history...
622
623
        $table2 = new Table('mytable');
624
        $table2->addColumn('column_varbinary', 'blob');
625
        $table2->addColumn('column_binary', 'binary', array('length' => 42, 'fixed' => true));
626
        $table2->addColumn('column_blob', 'blob');
627
628
        // VARBINARY -> BLOB
629
        // BINARY    -> BINARY with changed length
630
        // BLOB      -> BLOB
631
        $this->assertEmpty($this->_platform->getAlterTableSQL($comparator->diffTable($table1, $table2)));
0 ignored issues
show
Security Bug introduced by
It seems like $comparator->diffTable($table1, $table2) targeting Doctrine\DBAL\Schema\Comparator::diffTable() can also be of type false; however, Doctrine\DBAL\Platforms\...orm::getAlterTableSQL() does only seem to accept object<Doctrine\DBAL\Schema\TableDiff>, did you maybe forget to handle an error condition?
Loading history...
632
    }
633
634
    /**
635
     * @group DBAL-234
636
     */
637
    protected function getAlterTableRenameIndexSQL()
638
    {
639
        return array(
640
            'ALTER INDEX idx_foo RENAME TO idx_bar',
641
        );
642
    }
643
644
    /**
645
     * @group DBAL-234
646
     */
647
    protected function getQuotedAlterTableRenameIndexSQL()
648
    {
649
        return array(
650
            'ALTER INDEX "create" RENAME TO "select"',
651
            'ALTER INDEX "foo" RENAME TO "bar"',
652
        );
653
    }
654
655
    /**
656
     * PostgreSQL boolean strings provider
657
     * @return array
658
     */
659
    public function pgBooleanProvider()
660
    {
661
        return array(
662
            // Database value, prepared statement value, boolean integer value, boolean value.
663
            array(true, 'true', 1, true),
664
            array('t', 'true', 1, true),
665
            array('true', 'true', 1, true),
666
            array('y', 'true', 1, true),
667
            array('yes', 'true', 1, true),
668
            array('on', 'true', 1, true),
669
            array('1', 'true', 1, true),
670
671
            array(false, 'false', 0, false),
672
            array('f', 'false', 0, false),
673
            array('false', 'false', 0, false),
674
            array( 'n', 'false', 0, false),
675
            array('no', 'false', 0, false),
676
            array('off', 'false', 0, false),
677
            array('0', 'false', 0, false),
678
679
            array(null, 'NULL', null, null)
680
        );
681
    }
682
683
    /**
684
     * {@inheritdoc}
685
     */
686
    protected function getQuotedAlterTableRenameColumnSQL()
687
    {
688
        return array(
689
            'ALTER TABLE mytable RENAME COLUMN unquoted1 TO unquoted',
690
            'ALTER TABLE mytable RENAME COLUMN unquoted2 TO "where"',
691
            'ALTER TABLE mytable RENAME COLUMN unquoted3 TO "foo"',
692
            'ALTER TABLE mytable RENAME COLUMN "create" TO reserved_keyword',
693
            'ALTER TABLE mytable RENAME COLUMN "table" TO "from"',
694
            'ALTER TABLE mytable RENAME COLUMN "select" TO "bar"',
695
            'ALTER TABLE mytable RENAME COLUMN quoted1 TO quoted',
696
            'ALTER TABLE mytable RENAME COLUMN quoted2 TO "and"',
697
            'ALTER TABLE mytable RENAME COLUMN quoted3 TO "baz"',
698
        );
699
    }
700
701
    /**
702
     * {@inheritdoc}
703
     */
704
    protected function getQuotedAlterTableChangeColumnLengthSQL()
705
    {
706
        return array(
707
            'ALTER TABLE mytable ALTER unquoted1 TYPE VARCHAR(255)',
708
            'ALTER TABLE mytable ALTER unquoted2 TYPE VARCHAR(255)',
709
            'ALTER TABLE mytable ALTER unquoted3 TYPE VARCHAR(255)',
710
            'ALTER TABLE mytable ALTER "create" TYPE VARCHAR(255)',
711
            'ALTER TABLE mytable ALTER "table" TYPE VARCHAR(255)',
712
            'ALTER TABLE mytable ALTER "select" TYPE VARCHAR(255)',
713
        );
714
    }
715
716
    /**
717
     * @group DBAL-807
718
     */
719
    protected function getAlterTableRenameIndexInSchemaSQL()
720
    {
721
        return array(
722
            'ALTER INDEX myschema.idx_foo RENAME TO idx_bar',
723
        );
724
    }
725
726
    /**
727
     * @group DBAL-807
728
     */
729
    protected function getQuotedAlterTableRenameIndexInSchemaSQL()
730
    {
731
        return array(
732
            'ALTER INDEX "schema"."create" RENAME TO "select"',
733
            'ALTER INDEX "schema"."foo" RENAME TO "bar"',
734
        );
735
    }
736
737
    protected function getQuotesDropForeignKeySQL()
738
    {
739
        return 'ALTER TABLE "table" DROP CONSTRAINT "select"';
740
    }
741
742
    public function testGetNullCommentOnColumnSQL()
743
    {
744
        $this->assertEquals(
745
            "COMMENT ON COLUMN mytable.id IS NULL",
746
            $this->_platform->getCommentOnColumnSQL('mytable', 'id', null)
747
        );
748
    }
749
750
    /**
751
     * @group DBAL-423
752
     */
753
    public function testReturnsGuidTypeDeclarationSQL()
754
    {
755
        $this->assertSame('UUID', $this->_platform->getGuidTypeDeclarationSQL(array()));
756
    }
757
758
    /**
759
     * {@inheritdoc}
760
     */
761
    public function getAlterTableRenameColumnSQL()
762
    {
763
        return array(
764
            'ALTER TABLE foo RENAME COLUMN bar TO baz',
765
        );
766
    }
767
768
    /**
769
     * {@inheritdoc}
770
     */
771 View Code Duplication
    protected function getQuotesTableIdentifiersInAlterTableSQL()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
772
    {
773
        return array(
774
            'ALTER TABLE "foo" DROP CONSTRAINT fk1',
775
            'ALTER TABLE "foo" DROP CONSTRAINT fk2',
776
            'ALTER TABLE "foo" ADD bloo INT NOT NULL',
777
            'ALTER TABLE "foo" DROP baz',
778
            'ALTER TABLE "foo" ALTER bar DROP NOT NULL',
779
            'ALTER TABLE "foo" RENAME COLUMN id TO war',
780
            'ALTER TABLE "foo" RENAME TO "table"',
781
            'ALTER TABLE "table" ADD CONSTRAINT fk_add FOREIGN KEY (fk3) REFERENCES fk_table (id) NOT DEFERRABLE ' .
782
            'INITIALLY IMMEDIATE',
783
            'ALTER TABLE "table" ADD CONSTRAINT fk2 FOREIGN KEY (fk2) REFERENCES fk_table2 (id) NOT DEFERRABLE ' .
784
            'INITIALLY IMMEDIATE',
785
        );
786
    }
787
788
    /**
789
     * {@inheritdoc}
790
     */
791
    protected function getCommentOnColumnSQL()
792
    {
793
        return array(
794
            'COMMENT ON COLUMN foo.bar IS \'comment\'',
795
            'COMMENT ON COLUMN "Foo"."BAR" IS \'comment\'',
796
            'COMMENT ON COLUMN "select"."from" IS \'comment\'',
797
        );
798
    }
799
800
    /**
801
     * @group DBAL-1004
802
     */
803 View Code Duplication
    public function testAltersTableColumnCommentWithExplicitlyQuotedIdentifiers()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
804
    {
805
        $table1 = new Table('"foo"', array(new Column('"bar"', Type::getType('integer'))));
806
        $table2 = new Table('"foo"', array(new Column('"bar"', Type::getType('integer'), array('comment' => 'baz'))));
807
808
        $comparator = new Comparator();
809
810
        $tableDiff = $comparator->diffTable($table1, $table2);
811
812
        $this->assertInstanceOf('Doctrine\DBAL\Schema\TableDiff', $tableDiff);
813
        $this->assertSame(
814
            array(
815
                'COMMENT ON COLUMN "foo"."bar" IS \'baz\'',
816
            ),
817
            $this->_platform->getAlterTableSQL($tableDiff)
0 ignored issues
show
Security Bug introduced by
It seems like $tableDiff defined by $comparator->diffTable($table1, $table2) on line 810 can also be of type false; however, Doctrine\DBAL\Platforms\...orm::getAlterTableSQL() does only seem to accept object<Doctrine\DBAL\Schema\TableDiff>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
818
        );
819
    }
820
821
    /**
822
     * {@inheritdoc}
823
     */
824
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL()
825
    {
826
        return 'CONSTRAINT "select" UNIQUE (foo)';
827
    }
828
829
    /**
830
     * {@inheritdoc}
831
     */
832
    protected function getQuotesReservedKeywordInIndexDeclarationSQL()
833
    {
834
        return 'INDEX "select" (foo)';
835
    }
836
837
    /**
838
     * {@inheritdoc}
839
     */
840
    protected function getQuotesReservedKeywordInTruncateTableSQL()
841
    {
842
        return 'TRUNCATE "select"';
843
    }
844
845
    /**
846
     * {@inheritdoc}
847
     */
848
    protected function getAlterStringToFixedStringSQL()
849
    {
850
        return array(
851
            'ALTER TABLE mytable ALTER name TYPE CHAR(2)',
852
        );
853
    }
854
855
    /**
856
     * {@inheritdoc}
857
     */
858
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL()
859
    {
860
        return array(
861
            'ALTER INDEX idx_foo RENAME TO idx_foo_renamed',
862
        );
863
    }
864
865
    /**
866
     * @group DBAL-1142
867
     */
868
    public function testInitializesTsvectorTypeMapping()
869
    {
870
        $this->assertTrue($this->_platform->hasDoctrineTypeMappingFor('tsvector'));
871
        $this->assertEquals('text', $this->_platform->getDoctrineTypeMapping('tsvector'));
872
    }
873
874
    /**
875
     * @group DBAL-1220
876
     */
877
    public function testReturnsDisallowDatabaseConnectionsSQL()
878
    {
879
        $this->assertSame(
880
            "UPDATE pg_database SET datallowconn = 'false' WHERE datname = 'foo'",
881
            $this->_platform->getDisallowDatabaseConnectionsSQL('foo')
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Doctrine\DBAL\Platforms\AbstractPlatform as the method getDisallowDatabaseConnectionsSQL() does only exist in the following sub-classes of Doctrine\DBAL\Platforms\AbstractPlatform: Doctrine\DBAL\Platforms\PostgreSQL91Platform, Doctrine\DBAL\Platforms\PostgreSQL92Platform, Doctrine\DBAL\Platforms\PostgreSQL94Platform, Doctrine\DBAL\Platforms\PostgreSqlPlatform. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
882
        );
883
    }
884
885
    /**
886
     * @group DBAL-1220
887
     */
888
    public function testReturnsCloseActiveDatabaseConnectionsSQL()
889
    {
890
        $this->assertSame(
891
            "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = 'foo'",
892
            $this->_platform->getCloseActiveDatabaseConnectionsSQL('foo')
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Doctrine\DBAL\Platforms\AbstractPlatform as the method getCloseActiveDatabaseConnectionsSQL() does only exist in the following sub-classes of Doctrine\DBAL\Platforms\AbstractPlatform: Doctrine\DBAL\Platforms\PostgreSQL91Platform, Doctrine\DBAL\Platforms\PostgreSQL92Platform, Doctrine\DBAL\Platforms\PostgreSQL94Platform, Doctrine\DBAL\Platforms\PostgreSqlPlatform. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
893
        );
894
    }
895
896
    /**
897
     * @group DBAL-2436
898
     */
899
    public function testQuotesTableNameInListTableForeignKeysSQL()
900
    {
901
        $this->assertContains("'Foo''Bar\\\\'", $this->_platform->getListTableForeignKeysSQL("Foo'Bar\\"), '', true);
902
    }
903
904
    /**
905
     * @group DBAL-2436
906
     */
907
    public function testQuotesSchemaNameInListTableForeignKeysSQL()
908
    {
909
        $this->assertContains(
910
            "'Foo''Bar\\\\'",
911
            $this->_platform->getListTableForeignKeysSQL("Foo'Bar\\.baz_table"),
912
            '',
913
            true
914
        );
915
    }
916
917
    /**
918
     * @group DBAL-2436
919
     */
920
    public function testQuotesTableNameInListTableConstraintsSQL()
921
    {
922
        $this->assertContains("'Foo''Bar\\\\'", $this->_platform->getListTableConstraintsSQL("Foo'Bar\\"), '', true);
923
    }
924
925
    /**
926
     * @group DBAL-2436
927
     */
928
    public function testQuotesTableNameInListTableIndexesSQL()
929
    {
930
        $this->assertContains("'Foo''Bar\\\\'", $this->_platform->getListTableIndexesSQL("Foo'Bar\\"), '', true);
931
    }
932
933
    /**
934
     * @group DBAL-2436
935
     */
936
    public function testQuotesSchemaNameInListTableIndexesSQL()
937
    {
938
        $this->assertContains(
939
            "'Foo''Bar\\\\'",
940
            $this->_platform->getListTableIndexesSQL("Foo'Bar\\.baz_table"),
941
            '',
942
            true
943
        );
944
    }
945
946
    /**
947
     * @group DBAL-2436
948
     */
949
    public function testQuotesTableNameInListTableColumnsSQL()
950
    {
951
        $this->assertContains("'Foo''Bar\\\\'", $this->_platform->getListTableColumnsSQL("Foo'Bar\\"), '', true);
952
    }
953
954
    /**
955
     * @group DBAL-2436
956
     */
957
    public function testQuotesSchemaNameInListTableColumnsSQL()
958
    {
959
        $this->assertContains(
960
            "'Foo''Bar\\\\'",
961
            $this->_platform->getListTableColumnsSQL("Foo'Bar\\.baz_table"),
962
            '',
963
            true
964
        );
965
    }
966
967
    /**
968
     * @group DBAL-2436
969
     */
970
    public function testQuotesDatabaseNameInCloseActiveDatabaseConnectionsSQL()
971
    {
972
        $this->assertContains(
973
            "'Foo''Bar\\\\'",
974
            $this->_platform->getCloseActiveDatabaseConnectionsSQL("Foo'Bar\\"),
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Doctrine\DBAL\Platforms\AbstractPlatform as the method getCloseActiveDatabaseConnectionsSQL() does only exist in the following sub-classes of Doctrine\DBAL\Platforms\AbstractPlatform: Doctrine\DBAL\Platforms\PostgreSQL91Platform, Doctrine\DBAL\Platforms\PostgreSQL92Platform, Doctrine\DBAL\Platforms\PostgreSQL94Platform, Doctrine\DBAL\Platforms\PostgreSqlPlatform. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
975
            '',
976
            true
977
        );
978
    }
979
}
980