Completed
Pull Request — master (#1864)
by
unknown
01:21
created

PdoAdapter::dropIndexByName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 2
1
<?php
2
3
/**
4
 * MIT License
5
 * For full license information, please view the LICENSE file that was distributed with this source code.
6
 */
7
8
namespace Phinx\Db\Adapter;
9
10
use BadMethodCallException;
11
use InvalidArgumentException;
12
use PDO;
13
use PDOException;
14
use Phinx\Config\Config;
15
use Phinx\Db\Action\AddColumn;
16
use Phinx\Db\Action\AddForeignKey;
17
use Phinx\Db\Action\AddIndex;
18
use Phinx\Db\Action\ChangeColumn;
19
use Phinx\Db\Action\ChangeComment;
20
use Phinx\Db\Action\ChangePrimaryKey;
21
use Phinx\Db\Action\DropForeignKey;
22
use Phinx\Db\Action\DropIndex;
23
use Phinx\Db\Action\DropTable;
24
use Phinx\Db\Action\RemoveColumn;
25
use Phinx\Db\Action\RenameColumn;
26
use Phinx\Db\Action\RenameTable;
27
use Phinx\Db\Table as DbTable;
28
use Phinx\Db\Table\Column;
29
use Phinx\Db\Table\ForeignKey;
30
use Phinx\Db\Table\Index;
31
use Phinx\Db\Table\Table;
32
use Phinx\Db\Util\AlterInstructions;
33
use Phinx\Migration\MigrationInterface;
34
use RuntimeException;
35
use Symfony\Component\Console\Output\OutputInterface;
36
37
/**
38
 * Phinx PDO Adapter.
39
 *
40
 * @author Rob Morgan <[email protected]>
41
 */
42
abstract class PdoAdapter extends AbstractAdapter implements DirectActionInterface
43
{
44
    /**
45
     * @var \PDO|null
46
     */
47
    protected $connection;
48
49
    /**
50
     * Writes a message to stdout if verbose output is on
51 287
     *
52
     * @param string $message The message to show
53 287
     *
54
     * @return void
55 287
     */
56 3
    protected function verboseLog($message)
57 3
    {
58
        if (
59 287
            !$this->isDryRunEnabled() &&
60
             $this->getOutput()->getVerbosity() < OutputInterface::VERBOSITY_VERY_VERBOSE
61
        ) {
62
            return;
63
        }
64
65
        $this->getOutput()->writeln($message);
66
    }
67
68 193
    /**
69
     * Create PDO connection
70 193
     *
71
     * @param string $dsn Connection string
72
     * @param string|null $username Database username
73 193
     * @param string|null $password Database password
74 191
     * @param array $options Connection options
75 191
     * @return \PDO
76 74
     */
77 74
    protected function createPdoConnection($dsn, $username = null, $password = null, array $options = [])
78
    {
79
        try {
80
            $db = new PDO($dsn, $username, $password, $options);
81
            $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
82
        } catch (PDOException $e) {
83
            throw new InvalidArgumentException(sprintf(
84
                'There was a problem connecting to the database: %s',
85
                $e->getMessage()
86 74
            ), $e->getCode(), $e);
87
        }
88
89
        return $db;
90
    }
91
92
    /**
93 193
     * @inheritDoc
94
     */
95
    public function setOptions(array $options)
96
    {
97
        parent::setOptions($options);
98
99
        if (isset($options['connection'])) {
100
            $this->setConnection($options['connection']);
101 191
        }
102
103 191
        return $this;
104 189
    }
105 189
106 191
    /**
107
     * Sets the database connection.
108
     *
109
     * @param \PDO $connection Connection
110
     *
111
     * @return \Phinx\Db\Adapter\AdapterInterface
112 1
     */
113
    public function setConnection(PDO $connection)
114 1
    {
115
        $this->connection = $connection;
116
117
        // Create the schema table if it doesn't already exist
118
        if (!$this->hasSchemaTable()) {
119
            $this->createSchemaTable();
120
        } else {
121
            $table = new DbTable($this->getSchemaTableName(), [], $this);
122
            if (!$table->hasColumn('migration_name')) {
123
                $table
124
                    ->addColumn(
125
                        'migration_name',
126 218
                        'string',
127
                        ['limit' => 100, 'after' => 'version', 'default' => null, 'null' => true]
128 218
                    )
129 3
                    ->save();
130 3
            }
131
            if (!$table->hasColumn('breakpoint')) {
132
                $table
133 217
                    ->addColumn('breakpoint', 'boolean', ['default' => false])
134
                    ->save();
135
            }
136
        }
137
138
        return $this;
139
    }
140
141
    /**
142 220
     * Gets the database connection
143
     *
144 220
     * @return \PDO
145
     */
146
    public function getConnection()
147
    {
148
        if ($this->connection === null) {
149
            $this->connect();
150 151
        }
151
152 151
        return $this->connection;
153 151
    }
154
155
    /**
156
     * @inheritDoc
157
     */
158
    public function connect()
159 213
    {
160
    }
161 213
162 213
    /**
163 213
     * @inheritDoc
164 208
     */
165 208
    public function disconnect()
166 213
    {
167
    }
168
169
    /**
170
     * @inheritDoc
171
     */
172 1
    public function execute($sql)
173
    {
174 1
        $sql = rtrim($sql, "; \t\n\r\0\x0B") . ';';
175 1
        $this->verboseLog($sql);
176 1
177 1
        if ($this->isDryRunEnabled()) {
178
            return 0;
179 1
        }
180 1
181 1
        return $this->getConnection()->exec($sql);
182
    }
183 1
184 1
    /**
185 1
     * Returns the Cake\Database connection object using the same underlying
186
     * PDO object as this connection.
187
     *
188
     * @return \Cake\Database\Connection
189
     */
190 11
    abstract public function getDecoratedConnection();
191
192 11
    /**
193 11
     * @inheritDoc
194 11
     */
195 11
    public function getQueryBuilder()
196
    {
197 11
        return $this->getDecoratedConnection()->newQuery();
198 11
    }
199 11
200
    /**
201 11
     * Executes a query and returns PDOStatement.
202 11
     *
203 11
     * @param string $sql SQL
204 11
     *
205 11
     * @return \PDOStatement
206 11
     */
207
    public function query($sql)
208 11
    {
209 11
        return $this->getConnection()->query($sql);
210
    }
211 11
212 11
    /**
213 11
     * @inheritDoc
214
     */
215 11
    public function fetchRow($sql)
216 11
    {
217 11
        return $this->query($sql)->fetch();
218
    }
219
220
    /**
221
     * @inheritDoc
222 5
     */
223
    public function fetchAll($sql)
224 5
    {
225
        return $this->query($sql)->fetchAll();
226 5
    }
227
228
    /**
229
     * @inheritDoc
230
     */
231
    public function insert(Table $table, $row)
232 8
    {
233
        $sql = sprintf(
234 8
            'INSERT INTO %s ',
235
            $this->quoteTableName($table->getName())
236 8
        );
237 8
        $columns = array_keys($row);
238 6
        $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $columns)) . ')';
239 6
240 2
        foreach ($row as $column => $value) {
241 1
            if (is_bool($value)) {
242 1
                $row[$column] = $this->castToBool($value);
243 1
            }
244 1
        }
245 8
246
        if ($this->isDryRunEnabled()) {
247 7
            $sql .= ' VALUES (' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ');';
248 7
            $this->output->writeln($sql);
249 7
        } else {
250 7
            $sql .= ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
251
            $stmt = $this->getConnection()->prepare($sql);
252 7
            $stmt->execute(array_values($row));
253
        }
254
    }
255
256
    /**
257
     * Quotes a database value.
258 5
     *
259
     * @param mixed $value The value to quote
260 5
     *
261
     * @return mixed
262 5
     */
263 5
    protected function quoteValue($value)
264 5
    {
265 5
        if (is_numeric($value)) {
266 5
            return $value;
267 5
        }
268 5
269 5
        if ($value === null) {
270 5
            return 'null';
271 5
        }
272 5
273 5
        return $this->getConnection()->quote($value);
274 5
    }
275 5
276
    /**
277 5
     * Quotes a database string.
278 5
     *
279
     * @param string $value The string to quote
280 3
     *
281 3
     * @return string
282 3
     */
283 3
    protected function quoteString($value)
284 3
    {
285 3
        return $this->getConnection()->quote($value);
286
    }
287 3
288
    /**
289
     * @inheritDoc
290 5
     */
291
    public function bulkinsert(Table $table, $rows)
292
    {
293
        $sql = sprintf(
294
            'INSERT INTO %s ',
295
            $this->quoteTableName($table->getName())
296 1
        );
297
        $current = current($rows);
298 1
        $keys = array_keys($current);
299 1
        $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $keys)) . ') VALUES ';
300 1
301 1
        if ($this->isDryRunEnabled()) {
302 1
            $values = array_map(function ($row) {
303 1
                return '(' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ')';
304 1
            }, $rows);
305 1
            $sql .= implode(', ', $values) . ';';
306 1
            $this->output->writeln($sql);
307 1
        } else {
308 1
            $count_keys = count($keys);
309 1
            $query = '(' . implode(', ', array_fill(0, $count_keys, '?')) . ')';
310
            $count_vars = count($rows);
311 1
            $queries = array_fill(0, $count_vars, $query);
312
            $sql .= implode(',', $queries);
313
            $stmt = $this->getConnection()->prepare($sql);
314
            $vals = [];
315
316
            foreach ($rows as $row) {
317 1
                foreach ($row as $v) {
318
                    if (is_bool($v)) {
319 1
                        $vals[] = $this->castToBool($v);
320 1
                    } else {
321 1
                        $vals[] = $v;
322 1
                    }
323 1
                }
324 1
            }
325 1
326 1
            $stmt->execute($vals);
327 1
        }
328
    }
329
330
    /**
331
     * @inheritDoc
332
     */
333
    public function getVersions()
334
    {
335
        $rows = $this->getVersionLog();
336
337
        return array_keys($rows);
338
    }
339
340
    /**
341
     * {@inheritDoc}
342
     *
343
     * @throws \RuntimeException
344
     */
345
    public function getVersionLog()
346
    {
347
        $result = [];
348
349 208
        switch ($this->options['version_order']) {
350
            case Config::VERSION_ORDER_CREATION_TIME:
351
                $orderBy = 'version ASC';
352 208
                break;
353 208
            case Config::VERSION_ORDER_EXECUTION_TIME:
354 208
                $orderBy = 'start_time ASC, version ASC';
355 208
                break;
356 208
            default:
357 208
                throw new RuntimeException('Invalid version_order configuration option');
358 208
        }
359 208
360 208
        // This will throw an exception if doing a --dry-run without any migrations as phinxlog
361 208
        // does not exist, so in that case, we can just expect to trivially return empty set
362 208
        try {
363 208
            $rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY %s', $this->quoteTableName($this->getSchemaTableName()), $orderBy));
364 208
        } catch (PDOException $e) {
365 208
            if (!$this->isDryRunEnabled()) {
366 208
                throw $e;
367 208
            }
368
            $rows = [];
369 208
        }
370 208
371 208
        foreach ($rows as $version) {
372 208
            $result[$version['version']] = $version;
373 208
        }
374
375
        return $result;
376
    }
377
378
    /**
379 121
     * @inheritDoc
380
     */
381 121
    public function migrated(MigrationInterface $migration, $direction, $startTime, $endTime)
382
    {
383
        if (strcasecmp($direction, MigrationInterface::UP) === 0) {
384
            // up
385
            $sql = sprintf(
386
                "INSERT INTO %s (%s, %s, %s, %s, %s) VALUES ('%s', '%s', '%s', '%s', %s);",
387
                $this->quoteTableName($this->getSchemaTableName()),
388
                $this->quoteColumnName('version'),
389
                $this->quoteColumnName('migration_name'),
390
                $this->quoteColumnName('start_time'),
391
                $this->quoteColumnName('end_time'),
392
                $this->quoteColumnName('breakpoint'),
393
                $migration->getVersion(),
394
                substr($migration->getName(), 0, 100),
395
                $startTime,
396
                $endTime,
397
                $this->castToBool(false)
398
            );
399
400
            $this->execute($sql);
401
        } else {
402
            // down
403
            $sql = sprintf(
404
                "DELETE FROM %s WHERE %s = '%s'",
405
                $this->quoteTableName($this->getSchemaTableName()),
406
                $this->quoteColumnName('version'),
407
                $migration->getVersion()
408
            );
409
410
            $this->execute($sql);
411
        }
412
413
        return $this;
414
    }
415
416
    /**
417
     * @inheritDoc
418
     */
419
    public function toggleBreakpoint(MigrationInterface $migration)
420
    {
421
        $this->query(
422
            sprintf(
423
                '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\';',
424
                $this->quoteTableName($this->getSchemaTableName()),
425
                $this->quoteColumnName('breakpoint'),
426
                $this->castToBool(true),
427
                $this->castToBool(false),
428
                $this->quoteColumnName('version'),
429
                $migration->getVersion(),
430
                $this->quoteColumnName('start_time')
431
            )
432
        );
433
434
        return $this;
435
    }
436
437
    /**
438
     * @inheritDoc
439
     */
440
    public function resetAllBreakpoints()
441
    {
442
        return $this->execute(
443
            sprintf(
444
                'UPDATE %1$s SET %2$s = %3$s, %4$s = %4$s WHERE %2$s <> %3$s;',
445
                $this->quoteTableName($this->getSchemaTableName()),
446
                $this->quoteColumnName('breakpoint'),
447
                $this->castToBool(false),
448
                $this->quoteColumnName('start_time')
449
            )
450
        );
451
    }
452
453
    /**
454
     * @inheritDoc
455
     */
456
    public function setBreakpoint(MigrationInterface $migration)
457
    {
458
        return $this->markBreakpoint($migration, true);
459
    }
460
461
    /**
462
     * @inheritDoc
463
     */
464
    public function unsetBreakpoint(MigrationInterface $migration)
465
    {
466
        return $this->markBreakpoint($migration, false);
467
    }
468
469
    /**
470
     * Mark a migration breakpoint.
471
     *
472
     * @param \Phinx\Migration\MigrationInterface $migration The migration target for the breakpoint
473
     * @param bool $state The required state of the breakpoint
474
     *
475
     * @return \Phinx\Db\Adapter\AdapterInterface
476
     */
477
    protected function markBreakpoint(MigrationInterface $migration, $state)
478
    {
479
        $this->query(
480
            sprintf(
481
                'UPDATE %1$s SET %2$s = %3$s, %4$s = %4$s WHERE %5$s = \'%6$s\';',
482
                $this->quoteTableName($this->getSchemaTableName()),
483
                $this->quoteColumnName('breakpoint'),
484
                $this->castToBool($state),
485
                $this->quoteColumnName('start_time'),
486
                $this->quoteColumnName('version'),
487
                $migration->getVersion()
488
            )
489
        );
490
491
        return $this;
492
    }
493
494
    /**
495
     * {@inheritDoc}
496
     *
497
     * @throws \BadMethodCallException
498
     *
499
     * @return void
500
     */
501
    public function createSchema($schemaName = 'public')
502
    {
503
        throw new BadMethodCallException('Creating a schema is not supported');
504
    }
505
506
    /**
507
     * {@inheritDoc}
508
     *
509
     * @throws \BadMethodCallException
510
     *
511
     * @return void
512
     */
513
    public function dropSchema($name)
514
    {
515
        throw new BadMethodCallException('Dropping a schema is not supported');
516
    }
517
518
    /**
519
     * @inheritDoc
520
     */
521
    public function getColumnTypes()
522
    {
523
        return [
524
            'string',
525
            'char',
526
            'text',
527
            'tinyinteger',
528
            'smallinteger',
529
            'integer',
530
            'biginteger',
531
            'bit',
532
            'float',
533
            'decimal',
534
            'double',
535
            'datetime',
536
            'timestamp',
537
            'time',
538
            'date',
539
            'blob',
540
            'binary',
541
            'varbinary',
542
            'boolean',
543
            'uuid',
544
            // Geospatial data types
545
            'geometry',
546
            'point',
547
            'linestring',
548
            'polygon',
549
        ];
550
    }
551
552
    /**
553
     * @inheritDoc
554
     */
555
    public function castToBool($value)
556
    {
557
        return (bool)$value ? 1 : 0;
558
    }
559
560
    /**
561
     * Retrieve a database connection attribute
562
     *
563
     * @see http://php.net/manual/en/pdo.getattribute.php
564
     *
565
     * @param int $attribute One of the PDO::ATTR_* constants
566
     *
567
     * @return mixed
568
     */
569
    public function getAttribute($attribute)
570
    {
571
        return $this->connection->getAttribute($attribute);
572
    }
573
574
    /**
575
     * Get the definition for a `DEFAULT` statement.
576
     *
577
     * @param mixed $default Default value
578
     * @param string|null $columnType column type added
579
     * @param int|null $precision datetime/timestamp column default value limit/precision
580
     *
581
     * @return string
582
     */
583
    protected function getDefaultValueDefinition($default, $columnType = null, $precision = null)
584
    {
585
        if (is_string($default)) {
586
            if (str_starts_with($default, 'CURRENT_TIMESTAMP')) {
587
                if ($precision !== null) {
588
                    $default .= '(' . $precision . ')';
589
                }
590
            } else {
591
                $default = $this->getConnection()->quote($default);
592
            }
593
        } elseif (is_bool($default)) {
594
            $default = $this->castToBool($default);
595
        } elseif ($default !== null && $columnType === static::PHINX_TYPE_BOOLEAN) {
596
            $default = $this->castToBool((bool)$default);
597
        }
598
599
        return isset($default) ? " DEFAULT $default" : '';
600
    }
601
602
    /**
603
     * Executes all the ALTER TABLE instructions passed for the given table
604
     *
605
     * @param string $tableName The table name to use in the ALTER statement
606
     * @param \Phinx\Db\Util\AlterInstructions $instructions The object containing the alter sequence
607
     *
608
     * @return void
609
     */
610
    protected function executeAlterSteps($tableName, AlterInstructions $instructions)
611
    {
612
        $alter = sprintf('ALTER TABLE %s %%s', $this->quoteTableName($tableName));
613
        $instructions->execute($alter, [$this, 'execute']);
614
    }
615
616
    /**
617
     * @inheritDoc
618
     */
619
    public function addColumn(Table $table, Column $column)
620
    {
621
        $instructions = $this->getAddColumnInstructions($table, $column);
622
        $this->executeAlterSteps($table->getName(), $instructions);
623
    }
624
625
    /**
626
     * Returns the instructions to add the specified column to a database table.
627
     *
628
     * @param \Phinx\Db\Table\Table $table Table
629
     * @param \Phinx\Db\Table\Column $column Column
630
     *
631
     * @return \Phinx\Db\Util\AlterInstructions
632
     */
633
    abstract protected function getAddColumnInstructions(Table $table, Column $column);
634
635
    /**
636
     * @inheritdoc
637
     */
638
    public function renameColumn($tableName, $columnName, $newColumnName)
639
    {
640
        $instructions = $this->getRenameColumnInstructions($tableName, $columnName, $newColumnName);
641
        $this->executeAlterSteps($tableName, $instructions);
642
    }
643
644
    /**
645
     * Returns the instructions to rename the specified column.
646
     *
647
     * @param string $tableName Table name
648
     * @param string $columnName Column Name
649
     * @param string $newColumnName New Column Name
650
     *
651
     * @return \Phinx\Db\Util\AlterInstructions
652
     */
653
    abstract protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName);
654
655
    /**
656
     * @inheritdoc
657
     */
658
    public function changeColumn($tableName, $columnName, Column $newColumn)
659
    {
660
        $instructions = $this->getChangeColumnInstructions($tableName, $columnName, $newColumn);
661
        $this->executeAlterSteps($tableName, $instructions);
662
    }
663
664
    /**
665
     * Returns the instructions to change a table column type.
666
     *
667
     * @param string $tableName Table name
668
     * @param string $columnName Column Name
669
     * @param \Phinx\Db\Table\Column $newColumn New Column
670
     *
671
     * @return \Phinx\Db\Util\AlterInstructions
672
     */
673
    abstract protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn);
674
675
    /**
676
     * @inheritdoc
677
     */
678
    public function dropColumn($tableName, $columnName)
679
    {
680
        $instructions = $this->getDropColumnInstructions($tableName, $columnName);
681
        $this->executeAlterSteps($tableName, $instructions);
682
    }
683
684
    /**
685
     * Returns the instructions to drop the specified column.
686
     *
687
     * @param string $tableName Table name
688
     * @param string $columnName Column Name
689
     *
690
     * @return \Phinx\Db\Util\AlterInstructions
691
     */
692
    abstract protected function getDropColumnInstructions($tableName, $columnName);
693
694
    /**
695
     * @inheritdoc
696
     */
697
    public function addIndex(Table $table, Index $index)
698
    {
699
        $instructions = $this->getAddIndexInstructions($table, $index);
700
        $this->executeAlterSteps($table->getName(), $instructions);
701
    }
702
703
    /**
704
     * Returns the instructions to add the specified index to a database table.
705
     *
706
     * @param \Phinx\Db\Table\Table $table Table
707
     * @param \Phinx\Db\Table\Index $index Index
708
     *
709
     * @return \Phinx\Db\Util\AlterInstructions
710
     */
711
    abstract protected function getAddIndexInstructions(Table $table, Index $index);
712
713
    /**
714
     * @inheritdoc
715
     */
716
    public function dropIndex($tableName, $columns)
717
    {
718
        $instructions = $this->getDropIndexByColumnsInstructions($tableName, $columns);
719
        $this->executeAlterSteps($tableName, $instructions);
720
    }
721
722
    /**
723
     * Returns the instructions to drop the specified index from a database table.
724
     *
725
     * @param string $tableName The name of of the table where the index is
726
     * @param mixed $columns Column(s)
727
     *
728
     * @return \Phinx\Db\Util\AlterInstructions
729
     */
730
    abstract protected function getDropIndexByColumnsInstructions($tableName, $columns);
731
732
    /**
733
     * @inheritdoc
734
     */
735
    public function dropIndexByName($tableName, $indexName)
736
    {
737
        $instructions = $this->getDropIndexByNameInstructions($tableName, $indexName);
738
        $this->executeAlterSteps($tableName, $instructions);
739
    }
740
741
    /**
742
     * Returns the instructions to drop the index specified by name from a database table.
743
     *
744
     * @param string $tableName The table name whe the index is
745
     * @param string $indexName The name of the index
746
     *
747
     * @return \Phinx\Db\Util\AlterInstructions
748
     */
749
    abstract protected function getDropIndexByNameInstructions($tableName, $indexName);
750
751
    /**
752
     * @inheritdoc
753
     */
754
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
755
    {
756
        $instructions = $this->getAddForeignKeyInstructions($table, $foreignKey);
757
        $this->executeAlterSteps($table->getName(), $instructions);
758
    }
759
760
    /**
761
     * Returns the instructions to adds the specified foreign key to a database table.
762
     *
763
     * @param \Phinx\Db\Table\Table $table The table to add the constraint to
764
     * @param \Phinx\Db\Table\ForeignKey $foreignKey The foreign key to add
765
     * @return \Phinx\Db\Util\AlterInstructions
766
     */
767
    abstract protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey);
768
769
    /**
770
     * @inheritDoc
771
     */
772
    public function dropForeignKey($tableName, $columns, $constraint = null)
773
    {
774
        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...
775
            $instructions = $this->getDropForeignKeyInstructions($tableName, $constraint);
776
        } else {
777
            $instructions = $this->getDropForeignKeyByColumnsInstructions($tableName, $columns);
778
        }
779
780
        $this->executeAlterSteps($tableName, $instructions);
781
    }
782
783
    /**
784
     * Returns the instructions to drop the specified foreign key from a database table.
785
     *
786
     * @param string $tableName The table where the foreign key constraint is
787
     * @param string $constraint Constraint name
788
     *
789
     * @return \Phinx\Db\Util\AlterInstructions
790
     */
791
    abstract protected function getDropForeignKeyInstructions($tableName, $constraint);
792
793
    /**
794
     * Returns the instructions to drop the specified foreign key from a database table.
795
     *
796
     * @param string $tableName The table where the foreign key constraint is
797
     * @param string[] $columns The list of column names
798
     *
799
     * @return \Phinx\Db\Util\AlterInstructions
800
     */
801
    abstract protected function getDropForeignKeyByColumnsInstructions($tableName, $columns);
802
803
    /**
804
     * @inheritdoc
805
     */
806
    public function dropTable($tableName)
807
    {
808
        $instructions = $this->getDropTableInstructions($tableName);
809
        $this->executeAlterSteps($tableName, $instructions);
810
    }
811
812
    /**
813
     * Returns the instructions to drop the specified database table.
814
     *
815
     * @param string $tableName Table name
816
     *
817
     * @return \Phinx\Db\Util\AlterInstructions
818
     */
819
    abstract protected function getDropTableInstructions($tableName);
820
821
    /**
822
     * @inheritdoc
823
     */
824
    public function renameTable($tableName, $newTableName)
825
    {
826
        $instructions = $this->getRenameTableInstructions($tableName, $newTableName);
827
        $this->executeAlterSteps($tableName, $instructions);
828
    }
829
830
    /**
831
     * Returns the instructions to rename the specified database table.
832
     *
833
     * @param string $tableName Table name
834
     * @param string $newTableName New Name
835
     *
836
     * @return \Phinx\Db\Util\AlterInstructions
837
     */
838
    abstract protected function getRenameTableInstructions($tableName, $newTableName);
839
840
    /**
841
     * @inheritdoc
842
     */
843
    public function changePrimaryKey(Table $table, $newColumns)
844
    {
845
        $instructions = $this->getChangePrimaryKeyInstructions($table, $newColumns);
846
        $this->executeAlterSteps($table->getName(), $instructions);
847
    }
848
849
    /**
850
     * Returns the instructions to change the primary key for the specified database table.
851
     *
852
     * @param \Phinx\Db\Table\Table $table Table
853
     * @param string|string[]|null $newColumns Column name(s) to belong to the primary key, or null to drop the key
854
     *
855
     * @return \Phinx\Db\Util\AlterInstructions
856
     */
857
    abstract protected function getChangePrimaryKeyInstructions(Table $table, $newColumns);
858
859
    /**
860
     * @inheritdoc
861
     */
862
    public function changeComment(Table $table, $newComment)
863
    {
864
        $instructions = $this->getChangeCommentInstructions($table, $newComment);
865
        $this->executeAlterSteps($table->getName(), $instructions);
866
    }
867
868
    /**
869
     * Returns the instruction to change the comment for the specified database table.
870
     *
871
     * @param \Phinx\Db\Table\Table $table Table
872
     * @param string|null $newComment New comment string, or null to drop the comment
873
     *
874
     * @return \Phinx\Db\Util\AlterInstructions
875
     */
876
    abstract protected function getChangeCommentInstructions(Table $table, $newComment);
877
878
    /**
879
     * {@inheritDoc}
880
     *
881
     * @throws \InvalidArgumentException
882
     *
883
     * @return void
884
     */
885
    public function executeActions(Table $table, array $actions)
886
    {
887
        $instructions = new AlterInstructions();
888
889
        foreach ($actions as $action) {
890
            switch (true) {
891
                case ($action instanceof AddColumn):
892
                    $instructions->merge($this->getAddColumnInstructions($table, $action->getColumn()));
893
                    break;
894
895
                case ($action instanceof AddIndex):
896
                    $instructions->merge($this->getAddIndexInstructions($table, $action->getIndex()));
897
                    break;
898
899
                case ($action instanceof AddForeignKey):
900
                    $instructions->merge($this->getAddForeignKeyInstructions($table, $action->getForeignKey()));
901
                    break;
902
903
                case ($action instanceof ChangeColumn):
904
                    $instructions->merge($this->getChangeColumnInstructions(
905
                        $table->getName(),
906
                        $action->getColumnName(),
907
                        $action->getColumn()
908
                    ));
909
                    break;
910
911
                case ($action instanceof DropForeignKey && !$action->getForeignKey()->getConstraint()):
0 ignored issues
show
Bug Best Practice introduced by
The expression $action->getForeignKey()->getConstraint() of type string|null is loosely compared to false; 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...
912
                    $instructions->merge($this->getDropForeignKeyByColumnsInstructions(
913
                        $table->getName(),
914
                        $action->getForeignKey()->getColumns()
915
                    ));
916
                    break;
917
918
                case ($action instanceof DropForeignKey && $action->getForeignKey()->getConstraint()):
0 ignored issues
show
Bug Best Practice introduced by
The expression $action->getForeignKey()->getConstraint() 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...
919
                    $instructions->merge($this->getDropForeignKeyInstructions(
920
                        $table->getName(),
921
                        $action->getForeignKey()->getConstraint()
922
                    ));
923
                    break;
924
925
                case ($action instanceof DropIndex && $action->getIndex()->getName() !== null):
926
                    $instructions->merge($this->getDropIndexByNameInstructions(
927
                        $table->getName(),
928
                        $action->getIndex()->getName()
929
                    ));
930
                    break;
931
932
                case ($action instanceof DropIndex && $action->getIndex()->getName() == null):
933
                    $instructions->merge($this->getDropIndexByColumnsInstructions(
934
                        $table->getName(),
935
                        $action->getIndex()->getColumns()
936
                    ));
937
                    break;
938
939
                case ($action instanceof DropTable):
940
                    $instructions->merge($this->getDropTableInstructions(
941
                        $table->getName()
942
                    ));
943
                    break;
944
945
                case ($action instanceof RemoveColumn):
946
                    $instructions->merge($this->getDropColumnInstructions(
947
                        $table->getName(),
948
                        $action->getColumn()->getName()
949
                    ));
950
                    break;
951
952
                case ($action instanceof RenameColumn):
953
                    $instructions->merge($this->getRenameColumnInstructions(
954
                        $table->getName(),
955
                        $action->getColumn()->getName(),
956
                        $action->getNewName()
957
                    ));
958
                    break;
959
960
                case ($action instanceof RenameTable):
961
                    $instructions->merge($this->getRenameTableInstructions(
962
                        $table->getName(),
963
                        $action->getNewName()
964
                    ));
965
                    break;
966
967
                case ($action instanceof ChangePrimaryKey):
968
                    $instructions->merge($this->getChangePrimaryKeyInstructions(
969
                        $table,
970
                        $action->getNewColumns()
971
                    ));
972
                    break;
973
974
                case ($action instanceof ChangeComment):
975
                    $instructions->merge($this->getChangeCommentInstructions(
976
                        $table,
977
                        $action->getNewComment()
978
                    ));
979
                    break;
980
981
                default:
982
                    throw new InvalidArgumentException(
983
                        sprintf("Don't know how to execute action: '%s'", get_class($action))
984
                    );
985
            }
986
        }
987
988
        $this->executeAlterSteps($table->getName(), $instructions);
989
    }
990
}
991