Completed
Pull Request — master (#1560)
by Richard
01:52
created

PdoAdapter::setBreakpoint()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 * Copyright (c) 2015 Rob Morgan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated * documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 *
26
 * @package    Phinx
27
 * @subpackage Phinx\Db\Adapter
28
 */
29
namespace Phinx\Db\Adapter;
30
31
use BadMethodCallException;
32
use Phinx\Db\Action\AddColumn;
33
use Phinx\Db\Action\AddForeignKey;
34
use Phinx\Db\Action\AddIndex;
35
use Phinx\Db\Action\ChangeColumn;
36
use Phinx\Db\Action\ChangeComment;
37
use Phinx\Db\Action\ChangePrimaryKey;
38
use Phinx\Db\Action\DropForeignKey;
39
use Phinx\Db\Action\DropIndex;
40
use Phinx\Db\Action\DropTable;
41
use Phinx\Db\Action\RemoveColumn;
42
use Phinx\Db\Action\RenameColumn;
43
use Phinx\Db\Action\RenameTable;
44
use Phinx\Db\Table\Column;
45
use Phinx\Db\Table\ForeignKey;
46
use Phinx\Db\Table\Index;
47
use Phinx\Db\Table\Table;
48
use Phinx\Db\Util\AlterInstructions;
49
use Phinx\Migration\MigrationInterface;
50
use Symfony\Component\Console\Output\OutputInterface;
51 287
52
/**
53 287
 * Phinx PDO Adapter.
54
 *
55 287
 * @author Rob Morgan <[email protected]>
56 3
 */
57 3
abstract class PdoAdapter extends AbstractAdapter implements DirectActionInterface
58
{
59 287
    /**
60
     * @var \PDO|null
61
     */
62
    protected $connection;
63
64
    /**
65
     * Writes a message to stdout if verbose output is on
66
     *
67
     * @param string $message The message to show
68 193
     * @return void
69
     */
70 193
    protected function verboseLog($message)
71
    {
72
        if (!$this->isDryRunEnabled() &&
73 193
             $this->getOutput()->getVerbosity() < OutputInterface::VERBOSITY_VERY_VERBOSE) {
74 191
            return;
75 191
        }
76 74
77 74
        $this->getOutput()->writeln($message);
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function setOptions(array $options)
84
    {
85
        parent::setOptions($options);
86 74
87
        if (isset($options['connection'])) {
88
            $this->setConnection($options['connection']);
89
        }
90
91
        return $this;
92
    }
93 193
94
    /**
95
     * Sets the database connection.
96
     *
97
     * @param \PDO $connection Connection
98
     * @return \Phinx\Db\Adapter\AdapterInterface
99
     */
100
    public function setConnection(\PDO $connection)
101 191
    {
102
        $this->connection = $connection;
103 191
104 189
        // Create the schema table if it doesn't already exist
105 189
        if (!$this->hasSchemaTable()) {
106 191
            $this->createSchemaTable();
107
        } else {
108
            $table = new \Phinx\Db\Table($this->getSchemaTableName(), [], $this);
109
            if (!$table->hasColumn('migration_name')) {
110
                $table
111
                    ->addColumn(
112 1
                        'migration_name',
113
                        'string',
114 1
                        ['limit' => 100, 'after' => 'version', 'default' => null, 'null' => true]
115
                    )
116
                    ->save();
117
            }
118
            if (!$table->hasColumn('breakpoint')) {
119
                $table
120
                    ->addColumn('breakpoint', 'boolean', ['default' => false])
121
                    ->save();
122
            }
123
        }
124
125
        return $this;
126 218
    }
127
128 218
    /**
129 3
     * Gets the database connection
130 3
     *
131
     * @return \PDO
132
     */
133 217
    public function getConnection()
134
    {
135
        if ($this->connection === null) {
136
            $this->connect();
137
        }
138
139
        return $this->connection;
140
    }
141
142 220
    /**
143
     * {@inheritdoc}
144 220
     */
145
    public function connect()
146
    {
147
    }
148
149
    /**
150 151
     * {@inheritdoc}
151
     */
152 151
    public function disconnect()
153 151
    {
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159 213
    public function execute($sql)
160
    {
161 213
        $this->verboseLog($sql);
162 213
163 213
        if ($this->isDryRunEnabled()) {
164 208
            return 0;
165 208
        }
166 213
167
        return $this->getConnection()->exec($sql);
168
    }
169
170
    /**
171
     * Returns the Cake\Database connection object using the same underlying
172 1
     * PDO object as this connection.
173
     *
174 1
     * @return \Cake\Database\Connection
175 1
     */
176 1
    abstract public function getDecoratedConnection();
177 1
178
    /**
179 1
     * {@inheritdoc}
180 1
     */
181 1
    public function getQueryBuilder()
182
    {
183 1
        return $this->getDecoratedConnection()->newQuery();
184 1
    }
185 1
186
    /**
187
     * Executes a query and returns PDOStatement.
188
     *
189
     * @param string $sql SQL
190 11
     * @return \PDOStatement
191
     */
192 11
    public function query($sql)
193 11
    {
194 11
        return $this->getConnection()->query($sql);
195 11
    }
196
197 11
    /**
198 11
     * {@inheritdoc}
199 11
     */
200
    public function fetchRow($sql)
201 11
    {
202 11
        $result = $this->query($sql);
203 11
204 11
        return $result->fetch();
205 11
    }
206 11
207
    /**
208 11
     * {@inheritdoc}
209 11
     */
210
    public function fetchAll($sql)
211 11
    {
212 11
        $rows = [];
213 11
        $result = $this->query($sql);
214
        while ($row = $result->fetch()) {
215 11
            $rows[] = $row;
216 11
        }
217 11
218
        return $rows;
219
    }
220
221
    /**
222 5
     * {@inheritdoc}
223
     */
224 5
    public function insert(Table $table, $row)
225
    {
226 5
        $sql = sprintf(
227
            'INSERT INTO %s ',
228
            $this->quoteTableName($table->getName())
229
        );
230
        $columns = array_keys($row);
231
        $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $columns)) . ')';
232 8
233
        foreach ($row as $column => $value) {
234 8
            if (is_bool($value)) {
235
                $row[$column] = $this->castToBool($value);
236 8
            }
237 8
        }
238 6
239 6
        if ($this->isDryRunEnabled()) {
240 2
            $sql .= ' VALUES (' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ');';
241 1
            $this->output->writeln($sql);
242 1
        } else {
243 1
            $sql .= ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
244 1
            $stmt = $this->getConnection()->prepare($sql);
245 8
            $stmt->execute(array_values($row));
246
        }
247 7
    }
248 7
249 7
    /**
250 7
     * Quotes a database value.
251
     *
252 7
     * @param mixed $value  The value to quote
253
     * @return mixed
254
     */
255
    private function quoteValue($value)
256
    {
257
        if (is_numeric($value)) {
258 5
            return $value;
259
        }
260 5
261
        if ($value === null) {
262 5
            return 'null';
263 5
        }
264 5
265 5
        return $this->getConnection()->quote($value);
266 5
    }
267 5
268 5
    /**
269 5
     * Quotes a database string.
270 5
     *
271 5
     * @param string $value  The string to quote
272 5
     * @return string
273 5
     */
274 5
    protected function quoteString($value)
275 5
    {
276
        return $this->getConnection()->quote($value);
277 5
    }
278 5
279
    /**
280 3
     * {@inheritdoc}
281 3
     */
282 3
    public function bulkinsert(Table $table, $rows)
283 3
    {
284 3
        $sql = sprintf(
285 3
            'INSERT INTO %s ',
286
            $this->quoteTableName($table->getName())
287 3
        );
288
        $current = current($rows);
289
        $keys = array_keys($current);
290 5
        $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $keys)) . ') VALUES ';
291
292
        if ($this->isDryRunEnabled()) {
293
            $values = array_map(function ($row) {
294
                return '(' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ')';
295
            }, $rows);
296 1
            $sql .= implode(', ', $values) . ';';
297
            $this->output->writeln($sql);
298 1
        } else {
299 1
            $count_keys = count($keys);
300 1
            $query = '(' . implode(', ', array_fill(0, $count_keys, '?')) . ')';
301 1
            $count_vars = count($rows);
302 1
            $queries = array_fill(0, $count_vars, $query);
303 1
            $sql .= implode(',', $queries);
304 1
            $stmt = $this->getConnection()->prepare($sql);
305 1
            $vals = [];
306 1
307 1
            foreach ($rows as $row) {
308 1
                foreach ($row as $v) {
309 1
                    if (is_bool($v)) {
310
                        $vals[] = $this->castToBool($v);
311 1
                    } else {
312
                        $vals[] = $v;
313
                    }
314
                }
315
            }
316
317 1
            $stmt->execute($vals);
318
        }
319 1
    }
320 1
321 1
    /**
322 1
     * {@inheritdoc}
323 1
     */
324 1
    public function getVersions()
325 1
    {
326 1
        $rows = $this->getVersionLog();
327 1
328
        return array_keys($rows);
329
    }
330
331
    /**
332
     * {@inheritdoc}
333
     */
334
    public function getVersionLog()
335
    {
336
        $result = [];
337
338
        switch ($this->options['version_order']) {
339
            case \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME:
340
                $orderBy = 'version ASC';
341
                break;
342
            case \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME:
343
                $orderBy = 'start_time ASC, version ASC';
344
                break;
345
            default:
346
                throw new \RuntimeException('Invalid version_order configuration option');
347
        }
348
349 208
        $rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY %s', $this->getSchemaTableName(), $orderBy));
350
        foreach ($rows as $version) {
351
            $result[$version['version']] = $version;
352 208
        }
353 208
354 208
        return $result;
355 208
    }
356 208
357 208
    /**
358 208
     * {@inheritdoc}
359 208
     */
360 208
    public function migrated(MigrationInterface $migration, $direction, $startTime, $endTime)
361 208
    {
362 208
        if (strcasecmp($direction, MigrationInterface::UP) === 0) {
363 208
            // up
364 208
            $sql = sprintf(
365 208
                "INSERT INTO %s (%s, %s, %s, %s, %s) VALUES ('%s', '%s', '%s', '%s', %s);",
366 208
                $this->quoteTableName($this->getSchemaTableName()),
367 208
                $this->quoteColumnName('version'),
368
                $this->quoteColumnName('migration_name'),
369 208
                $this->quoteColumnName('start_time'),
370 208
                $this->quoteColumnName('end_time'),
371 208
                $this->quoteColumnName('breakpoint'),
372 208
                $migration->getVersion(),
373 208
                substr($migration->getName(), 0, 100),
374
                $startTime,
375
                $endTime,
376
                $this->castToBool(false)
377
            );
378
379 121
            $this->execute($sql);
380
        } else {
381 121
            // down
382
            $sql = sprintf(
383
                "DELETE FROM %s WHERE %s = '%s'",
384
                $this->quoteTableName($this->getSchemaTableName()),
385
                $this->quoteColumnName('version'),
386
                $migration->getVersion()
387
            );
388
389
            $this->execute($sql);
390
        }
391
392
        return $this;
393
    }
394
395
    /**
396
     * {@inheritdoc}
397
     */
398
    public function toggleBreakpoint(MigrationInterface $migration)
399
    {
400
        $this->query(
401
            sprintf(
402
                'UPDATE %1$s SET %2$s = CASE %2$s WHEN %3$s THEN %4$s ELSE %3$s END, %7$s = %7$s WHERE %5$s = \'%6$s\';',
403
                $this->getSchemaTableName(),
404
                $this->quoteColumnName('breakpoint'),
405
                $this->castToBool(true),
406
                $this->castToBool(false),
407
                $this->quoteColumnName('version'),
408
                $migration->getVersion(),
409
                $this->quoteColumnName('start_time')
410
            )
411
        );
412
413
        return $this;
414
    }
415
416
    /**
417
     * {@inheritdoc}
418
     */
419
    public function resetAllBreakpoints()
420
    {
421
        return $this->execute(
422
            sprintf(
423
                'UPDATE %1$s SET %2$s = %3$s, %4$s = %4$s WHERE %2$s <> %3$s;',
424
                $this->getSchemaTableName(),
425
                $this->quoteColumnName('breakpoint'),
426
                $this->castToBool(false),
427
                $this->quoteColumnName('start_time')
428
            )
429
        );
430
    }
431
432
    /**
433
     * {@inheritdoc}
434
     */
435
    public function setBreakpoint(MigrationInterface $migration)
436
    {
437
        return $this->markBreakpoint($migration, true);
438
    }
439
440
    /**
441
     * {@inheritdoc}
442
     */
443
    public function unsetBreakpoint(MigrationInterface $migration)
444
    {
445
        return $this->markBreakpoint($migration, false);
446
    }
447
448
    /**
449
     * Mark a migration breakpoint.
450
     *
451
     * @param \Phinx\Migration\MigrationInterface $migration The migration target for the breakpoint
452
     * @param bool $state The required state of the breakpoint
453
     *
454
     * @return \Phinx\Db\Adapter\AdapterInterface
455
     */
456
    protected function markBreakpoint(MigrationInterface $migration, $state)
457
    {
458
        $this->query(
459
            sprintf(
460
                'UPDATE %1$s SET %2$s = %3$s, %4$s = %4$s WHERE %5$s = \'%6$s\';',
461
                $this->getSchemaTableName(),
462
                $this->quoteColumnName('breakpoint'),
463
                $this->castToBool($state),
464
                $this->quoteColumnName('start_time'),
465
                $this->quoteColumnName('version'),
466
                $migration->getVersion()
467
            )
468
        );
469
470
        return $this;
471
    }
472
473
    /**
474
     * {@inheritdoc}
475
     */
476
    public function createSchema($schemaName = 'public')
477
    {
478
        throw new BadMethodCallException('Creating a schema is not supported');
479
    }
480
481
    /**
482
     * {@inheritdoc}
483
     */
484
    public function dropSchema($name)
485
    {
486
        throw new BadMethodCallException('Dropping a schema is not supported');
487
    }
488
489
    /**
490
     * {@inheritdoc}
491
     */
492
    public function getColumnTypes()
493
    {
494
        return [
495
            'string',
496
            'char',
497
            'text',
498
            'smallinteger',
499
            'integer',
500
            'biginteger',
501
            'bit',
502
            'float',
503
            'decimal',
504
            'double',
505
            'datetime',
506
            'timestamp',
507
            'time',
508
            'date',
509
            'blob',
510
            'binary',
511
            'varbinary',
512
            'boolean',
513
            'uuid',
514
            // Geospatial data types
515
            'geometry',
516
            'point',
517
            'linestring',
518
            'polygon',
519
        ];
520
    }
521
522
    /**
523
     * {@inheritdoc}
524
     */
525
    public function castToBool($value)
526
    {
527
        return (bool)$value ? 1 : 0;
528
    }
529
530
    /**
531
     * Retrieve a database connection attribute
532
     * @see http://php.net/manual/en/pdo.getattribute.php
533
     *
534
     * @param int $attribute One of the PDO::ATTR_* constants
535
     * @return mixed
536
     */
537
    public function getAttribute($attribute)
538
    {
539
        return $this->connection->getAttribute($attribute);
540
    }
541
542
    /**
543
     * Get the definition for a `DEFAULT` statement.
544
     *
545
     * @param  mixed $default Default value
546
     * @param string $columnType column type added
547
     * @return string
548
     */
549 View Code Duplication
    protected function getDefaultValueDefinition($default, $columnType = null)
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...
550
    {
551
        if (is_string($default) && 'CURRENT_TIMESTAMP' !== $default) {
552
            $default = $this->getConnection()->quote($default);
553
        } elseif (is_bool($default)) {
554
            $default = $this->castToBool($default);
555
        } elseif ($default !== null && $columnType === static::PHINX_TYPE_BOOLEAN) {
556
            $default = $this->castToBool((bool)$default);
557
        }
558
559
        return isset($default) ? " DEFAULT $default" : '';
560
    }
561
562
    /**
563
     * Executes all the ALTER TABLE instructions passed for the given table
564
     *
565
     * @param string $tableName The table name to use in the ALTER statement
566
     * @param AlterInstructions $instructions The object containing the alter sequence
567
     * @return void
568
     */
569
    protected function executeAlterSteps($tableName, AlterInstructions $instructions)
570
    {
571
        $alter = sprintf('ALTER TABLE %s %%s', $this->quoteTableName($tableName));
572
        $instructions->execute($alter, [$this, 'execute']);
573
    }
574
575
    /**
576
     * {@inheritdoc}
577
     */
578
    public function addColumn(Table $table, Column $column)
579
    {
580
        $instructions = $this->getAddColumnInstructions($table, $column);
581
        $this->executeAlterSteps($table->getName(), $instructions);
582
    }
583
584
    /**
585
     * Returns the instructions to add the specified column to a database table.
586
     *
587
     * @param \Phinx\Db\Table\Table $table Table
588
     * @param \Phinx\Db\Table\Column $column Column
589
     * @return AlterInstructions
590
     */
591
    abstract protected function getAddColumnInstructions(Table $table, Column $column);
592
593
    /**
594
     * {@inheritdoc}
595
     */
596
    public function renameColumn($tableName, $columnName, $newColumnName)
597
    {
598
        $instructions = $this->getRenameColumnInstructions($tableName, $columnName, $newColumnName);
599
        $this->executeAlterSteps($tableName, $instructions);
600
    }
601
602
    /**
603
     * Returns the instructions to rename the specified column.
604
     *
605
     * @param string $tableName Table Name
606
     * @param string $columnName Column Name
607
     * @param string $newColumnName New Column Name
608
     * @return AlterInstructions:w
0 ignored issues
show
Documentation introduced by
The doc-type AlterInstructions:w could not be parsed: Unknown type name "AlterInstructions:w" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
609
     *
610
     */
611
    abstract protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName);
612
613
    /**
614
     * {@inheritdoc}
615
     */
616
    public function changeColumn($tableName, $columnName, Column $newColumn)
617
    {
618
        $instructions = $this->getChangeColumnInstructions($tableName, $columnName, $newColumn);
619
        $this->executeAlterSteps($tableName, $instructions);
620
    }
621
622
    /**
623
     * Returns the instructions to change a table column type.
624
     *
625
     * @param string $tableName  Table Name
626
     * @param string $columnName Column Name
627
     * @param \Phinx\Db\Table\Column $newColumn  New Column
628
     * @return AlterInstructions
629
     */
630
    abstract protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn);
631
632
    /**
633
     * {@inheritdoc}
634
     */
635
    public function dropColumn($tableName, $columnName)
636
    {
637
        $instructions = $this->getDropColumnInstructions($tableName, $columnName);
638
        $this->executeAlterSteps($tableName, $instructions);
639
    }
640
641
    /**
642
     * Returns the instructions to drop the specified column.
643
     *
644
     * @param string $tableName Table Name
645
     * @param string $columnName Column Name
646
     * @return AlterInstructions
647
     */
648
    abstract protected function getDropColumnInstructions($tableName, $columnName);
649
650
    /**
651
     * {@inheritdoc}
652
     */
653
    public function addIndex(Table $table, Index $index)
654
    {
655
        $instructions = $this->getAddIndexInstructions($table, $index);
656
        $this->executeAlterSteps($table->getName(), $instructions);
657
    }
658
659
    /**
660
     * Returns the instructions to add the specified index to a database table.
661
     *
662
     * @param \Phinx\Db\Table\Table $table Table
663
     * @param \Phinx\Db\Table\Index $index Index
664
     * @return AlterInstructions
665
     */
666
    abstract protected function getAddIndexInstructions(Table $table, Index $index);
667
668
    /**
669
     * {@inheritdoc}
670
     */
671
    public function dropIndex($tableName, $columns)
672
    {
673
        $instructions = $this->getDropIndexByColumnsInstructions($tableName, $columns);
674
        $this->executeAlterSteps($tableName, $instructions);
675
    }
676
677
    /**
678
     * Returns the instructions to drop the specified index from a database table.
679
     *
680
     * @param string $tableName The name of of the table where the index is
681
     * @param mixed $columns Column(s)
682
     * @return AlterInstructions
683
     */
684
    abstract protected function getDropIndexByColumnsInstructions($tableName, $columns);
685
686
    /**
687
     * {@inheritdoc}
688
     */
689
    public function dropIndexByName($tableName, $indexName)
690
    {
691
        $instructions = $this->getDropIndexByNameInstructions($tableName, $indexName);
692
        $this->executeAlterSteps($tableName, $instructions);
693
    }
694
695
    /**
696
     * Returns the instructions to drop the index specified by name from a database table.
697
     *
698
     * @param string $tableName The table name whe the index is
699
     * @param string $indexName The name of the index
700
     * @return AlterInstructions
701
     */
702
    abstract protected function getDropIndexByNameInstructions($tableName, $indexName);
703
704
    /**
705
     * {@inheritdoc}
706
     */
707
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
708
    {
709
        $instructions = $this->getAddForeignKeyInstructions($table, $foreignKey);
710
        $this->executeAlterSteps($table->getName(), $instructions);
711
    }
712
713
    /**
714
     * Returns the instructions to adds the specified foreign key to a database table.
715
     *
716
     * @param \Phinx\Db\Table\Table $table The table to add the constraint to
717
     * @param \Phinx\Db\Table\ForeignKey $foreignKey The foreign key to add
718
     * @return AlterInstructions
719
     */
720
    abstract protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey);
721
722
    /**
723
     * {@inheritdoc}
724
     */
725
    public function dropForeignKey($tableName, $columns, $constraint = null)
726
    {
727
        if ($constraint) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $constraint of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
728
            $instructions = $this->getDropForeignKeyInstructions($tableName, $constraint);
729
        } else {
730
            $instructions = $this->getDropForeignKeyByColumnsInstructions($tableName, $columns);
731
        }
732
733
        $this->executeAlterSteps($tableName, $instructions);
734
    }
735
736
    /**
737
     * Returns the instructions to drop the specified foreign key from a database table.
738
     *
739
     * @param string   $tableName The table where the foreign key constraint is
740
     * @param string   $constraint Constraint name
741
     * @return AlterInstructions
742
     */
743
    abstract protected function getDropForeignKeyInstructions($tableName, $constraint);
744
745
    /**
746
     * Returns the instructions to drop the specified foreign key from a database table.
747
     *
748
     * @param string $tableName The table where the foreign key constraint is
749
     * @param array $columns The list of column names
750
     * @return AlterInstructions
751
     */
752
    abstract protected function getDropForeignKeyByColumnsInstructions($tableName, $columns);
753
754
    /**
755
     * {@inheritdoc}
756
     */
757
    public function dropTable($tableName)
758
    {
759
        $instructions = $this->getDropTableInstructions($tableName);
760
        $this->executeAlterSteps($tableName, $instructions);
761
    }
762
763
    /**
764
     * Returns the instructions to drop the specified database table.
765
     *
766
     * @param string $tableName Table Name
767
     * @return AlterInstructions
768
     */
769
    abstract protected function getDropTableInstructions($tableName);
770
771
    /**
772
     * {@inheritdoc}
773
     */
774
    public function renameTable($tableName, $newTableName)
775
    {
776
        $instructions = $this->getRenameTableInstructions($tableName, $newTableName);
777
        $this->executeAlterSteps($tableName, $instructions);
778
    }
779
780
    /**
781
     * Returns the instructions to rename the specified database table.
782
     *
783
     * @param string $tableName Table Name
784
     * @param string $newTableName New Name
785
     * @return AlterInstructions
786
     */
787
    abstract protected function getRenameTableInstructions($tableName, $newTableName);
788
789
    /**
790
     * {@inheritdoc}
791
     */
792
    public function changePrimaryKey(Table $table, $newColumns)
793
    {
794
        $instructions = $this->getChangePrimaryKeyInstructions($table, $newColumns);
795
        $this->executeAlterSteps($table->getName(), $instructions);
796
    }
797
798
    /**
799
     * Returns the instructions to change the primary key for the specified database table.
800
     *
801
     * @param Table $table Table
802
     * @param string|array|null $newColumns Column name(s) to belong to the primary key, or null to drop the key
803
     * @return AlterInstructions
804
     */
805
    abstract protected function getChangePrimaryKeyInstructions(Table $table, $newColumns);
806
807
    /**
808
     * {@inheritdoc}
809
     */
810
    public function changeComment(Table $table, $newComment)
811
    {
812
        $instructions = $this->getChangeCommentInstructions($table, $newComment);
813
        $this->executeAlterSteps($table->getName(), $instructions);
814
    }
815
816
    /**
817
     * Returns the instruction to change the comment for the specified database table.
818
     *
819
     * @param Table $table Table
820
     * @param string|null $newComment New comment string, or null to drop the comment
821
     * @return AlterInstructions
822
     */
823
    abstract protected function getChangeCommentInstructions(Table $table, $newComment);
824
825
    /**
826
     * {@inheritdoc}
827
     */
828
    public function executeActions(Table $table, array $actions)
829
    {
830
        $instructions = new AlterInstructions();
831
832
        foreach ($actions as $action) {
833
            switch (true) {
834
                case ($action instanceof AddColumn):
835
                    $instructions->merge($this->getAddColumnInstructions($table, $action->getColumn()));
836
                    break;
837
838
                case ($action instanceof AddIndex):
839
                    $instructions->merge($this->getAddIndexInstructions($table, $action->getIndex()));
840
                    break;
841
842
                case ($action instanceof AddForeignKey):
843
                    $instructions->merge($this->getAddForeignKeyInstructions($table, $action->getForeignKey()));
844
                    break;
845
846
                case ($action instanceof ChangeColumn):
847
                    $instructions->merge($this->getChangeColumnInstructions(
848
                        $table->getName(),
849
                        $action->getColumnName(),
850
                        $action->getColumn()
851
                    ));
852
                    break;
853
854
                case ($action instanceof DropForeignKey && !$action->getForeignKey()->getConstraint()):
855
                    $instructions->merge($this->getDropForeignKeyByColumnsInstructions(
856
                        $table->getName(),
857
                        $action->getForeignKey()->getColumns()
858
                    ));
859
                    break;
860
861
                case ($action instanceof DropForeignKey && $action->getForeignKey()->getConstraint()):
862
                    $instructions->merge($this->getDropForeignKeyInstructions(
863
                        $table->getName(),
864
                        $action->getForeignKey()->getConstraint()
0 ignored issues
show
Bug introduced by
It seems like $action->getForeignKey()->getConstraint() targeting Phinx\Db\Table\ForeignKey::getConstraint() can also be of type boolean; however, Phinx\Db\Adapter\PdoAdap...oreignKeyInstructions() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
865
                    ));
866
                    break;
867
868
                case ($action instanceof DropIndex && $action->getIndex()->getName() !== null):
869
                    $instructions->merge($this->getDropIndexByNameInstructions(
870
                        $table->getName(),
871
                        $action->getIndex()->getName()
872
                    ));
873
                    break;
874
875
                case ($action instanceof DropIndex && $action->getIndex()->getName() == null):
876
                    $instructions->merge($this->getDropIndexByColumnsInstructions(
877
                        $table->getName(),
878
                        $action->getIndex()->getColumns()
879
                    ));
880
                    break;
881
882
                case ($action instanceof DropTable):
883
                    $instructions->merge($this->getDropTableInstructions(
884
                        $table->getName()
885
                    ));
886
                    break;
887
888
                case ($action instanceof RemoveColumn):
889
                    $instructions->merge($this->getDropColumnInstructions(
890
                        $table->getName(),
891
                        $action->getColumn()->getName()
892
                    ));
893
                    break;
894
895
                case ($action instanceof RenameColumn):
896
                    $instructions->merge($this->getRenameColumnInstructions(
897
                        $table->getName(),
898
                        $action->getColumn()->getName(),
899
                        $action->getNewName()
900
                    ));
901
                    break;
902
903
                case ($action instanceof RenameTable):
904
                    $instructions->merge($this->getRenameTableInstructions(
905
                        $table->getName(),
906
                        $action->getNewName()
907
                    ));
908
                    break;
909
910
                case ($action instanceof ChangePrimaryKey):
911
                    $instructions->merge($this->getChangePrimaryKeyInstructions(
912
                        $table,
913
                        $action->getNewColumns()
914
                    ));
915
                    break;
916
917
                case ($action instanceof ChangeComment):
918
                    $instructions->merge($this->getChangeCommentInstructions(
919
                        $table,
920
                        $action->getNewComment()
921
                    ));
922
                    break;
923
924
                default:
925
                    throw new \InvalidArgumentException(
926
                        sprintf("Don't know how to execute action: '%s'", get_class($action))
927
                    );
928
            }
929
        }
930
931
        $this->executeAlterSteps($table->getName(), $instructions);
932
    }
933
}
934