Completed
Push — master ( 05902f...9e6322 )
by mark
01:41 queued 11s
created

PdoAdapter::quoteString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
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
     *
580
     * @return string
581
     */
582
    protected function getDefaultValueDefinition($default, $columnType = null)
583
    {
584
        if (is_string($default) && $default !== 'CURRENT_TIMESTAMP') {
585
            $default = $this->getConnection()->quote($default);
586
        } elseif (is_bool($default)) {
587
            $default = $this->castToBool($default);
588
        } elseif ($default !== null && $columnType === static::PHINX_TYPE_BOOLEAN) {
589
            $default = $this->castToBool((bool)$default);
590
        }
591
592
        return isset($default) ? " DEFAULT $default" : '';
593
    }
594
595
    /**
596
     * Executes all the ALTER TABLE instructions passed for the given table
597
     *
598
     * @param string $tableName The table name to use in the ALTER statement
599
     * @param \Phinx\Db\Util\AlterInstructions $instructions The object containing the alter sequence
600
     *
601
     * @return void
602
     */
603
    protected function executeAlterSteps($tableName, AlterInstructions $instructions)
604
    {
605
        $alter = sprintf('ALTER TABLE %s %%s', $this->quoteTableName($tableName));
606
        $instructions->execute($alter, [$this, 'execute']);
607
    }
608
609
    /**
610
     * @inheritDoc
611
     */
612
    public function addColumn(Table $table, Column $column)
613
    {
614
        $instructions = $this->getAddColumnInstructions($table, $column);
615
        $this->executeAlterSteps($table->getName(), $instructions);
616
    }
617
618
    /**
619
     * Returns the instructions to add the specified column to a database table.
620
     *
621
     * @param \Phinx\Db\Table\Table $table Table
622
     * @param \Phinx\Db\Table\Column $column Column
623
     *
624
     * @return \Phinx\Db\Util\AlterInstructions
625
     */
626
    abstract protected function getAddColumnInstructions(Table $table, Column $column);
627
628
    /**
629
     * @inheritdoc
630
     */
631
    public function renameColumn($tableName, $columnName, $newColumnName)
632
    {
633
        $instructions = $this->getRenameColumnInstructions($tableName, $columnName, $newColumnName);
634
        $this->executeAlterSteps($tableName, $instructions);
635
    }
636
637
    /**
638
     * Returns the instructions to rename the specified column.
639
     *
640
     * @param string $tableName Table name
641
     * @param string $columnName Column Name
642
     * @param string $newColumnName New Column Name
643
     *
644
     * @return \Phinx\Db\Util\AlterInstructions
645
     */
646
    abstract protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName);
647
648
    /**
649
     * @inheritdoc
650
     */
651
    public function changeColumn($tableName, $columnName, Column $newColumn)
652
    {
653
        $instructions = $this->getChangeColumnInstructions($tableName, $columnName, $newColumn);
654
        $this->executeAlterSteps($tableName, $instructions);
655
    }
656
657
    /**
658
     * Returns the instructions to change a table column type.
659
     *
660
     * @param string $tableName Table name
661
     * @param string $columnName Column Name
662
     * @param \Phinx\Db\Table\Column $newColumn New Column
663
     *
664
     * @return \Phinx\Db\Util\AlterInstructions
665
     */
666
    abstract protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn);
667
668
    /**
669
     * @inheritdoc
670
     */
671
    public function dropColumn($tableName, $columnName)
672
    {
673
        $instructions = $this->getDropColumnInstructions($tableName, $columnName);
674
        $this->executeAlterSteps($tableName, $instructions);
675
    }
676
677
    /**
678
     * Returns the instructions to drop the specified column.
679
     *
680
     * @param string $tableName Table name
681
     * @param string $columnName Column Name
682
     *
683
     * @return \Phinx\Db\Util\AlterInstructions
684
     */
685
    abstract protected function getDropColumnInstructions($tableName, $columnName);
686
687
    /**
688
     * @inheritdoc
689
     */
690
    public function addIndex(Table $table, Index $index)
691
    {
692
        $instructions = $this->getAddIndexInstructions($table, $index);
693
        $this->executeAlterSteps($table->getName(), $instructions);
694
    }
695
696
    /**
697
     * Returns the instructions to add the specified index to a database table.
698
     *
699
     * @param \Phinx\Db\Table\Table $table Table
700
     * @param \Phinx\Db\Table\Index $index Index
701
     *
702
     * @return \Phinx\Db\Util\AlterInstructions
703
     */
704
    abstract protected function getAddIndexInstructions(Table $table, Index $index);
705
706
    /**
707
     * @inheritdoc
708
     */
709
    public function dropIndex($tableName, $columns)
710
    {
711
        $instructions = $this->getDropIndexByColumnsInstructions($tableName, $columns);
712
        $this->executeAlterSteps($tableName, $instructions);
713
    }
714
715
    /**
716
     * Returns the instructions to drop the specified index from a database table.
717
     *
718
     * @param string $tableName The name of of the table where the index is
719
     * @param mixed $columns Column(s)
720
     *
721
     * @return \Phinx\Db\Util\AlterInstructions
722
     */
723
    abstract protected function getDropIndexByColumnsInstructions($tableName, $columns);
724
725
    /**
726
     * @inheritdoc
727
     */
728
    public function dropIndexByName($tableName, $indexName)
729
    {
730
        $instructions = $this->getDropIndexByNameInstructions($tableName, $indexName);
731
        $this->executeAlterSteps($tableName, $instructions);
732
    }
733
734
    /**
735
     * Returns the instructions to drop the index specified by name from a database table.
736
     *
737
     * @param string $tableName The table name whe the index is
738
     * @param string $indexName The name of the index
739
     *
740
     * @return \Phinx\Db\Util\AlterInstructions
741
     */
742
    abstract protected function getDropIndexByNameInstructions($tableName, $indexName);
743
744
    /**
745
     * @inheritdoc
746
     */
747
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
748
    {
749
        $instructions = $this->getAddForeignKeyInstructions($table, $foreignKey);
750
        $this->executeAlterSteps($table->getName(), $instructions);
751
    }
752
753
    /**
754
     * Returns the instructions to adds the specified foreign key to a database table.
755
     *
756
     * @param \Phinx\Db\Table\Table $table The table to add the constraint to
757
     * @param \Phinx\Db\Table\ForeignKey $foreignKey The foreign key to add
758
     * @return \Phinx\Db\Util\AlterInstructions
759
     */
760
    abstract protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey);
761
762
    /**
763
     * @inheritDoc
764
     */
765
    public function dropForeignKey($tableName, $columns, $constraint = null)
766
    {
767
        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...
768
            $instructions = $this->getDropForeignKeyInstructions($tableName, $constraint);
769
        } else {
770
            $instructions = $this->getDropForeignKeyByColumnsInstructions($tableName, $columns);
771
        }
772
773
        $this->executeAlterSteps($tableName, $instructions);
774
    }
775
776
    /**
777
     * Returns the instructions to drop the specified foreign key from a database table.
778
     *
779
     * @param string $tableName The table where the foreign key constraint is
780
     * @param string $constraint Constraint name
781
     *
782
     * @return \Phinx\Db\Util\AlterInstructions
783
     */
784
    abstract protected function getDropForeignKeyInstructions($tableName, $constraint);
785
786
    /**
787
     * Returns the instructions to drop the specified foreign key from a database table.
788
     *
789
     * @param string $tableName The table where the foreign key constraint is
790
     * @param string[] $columns The list of column names
791
     *
792
     * @return \Phinx\Db\Util\AlterInstructions
793
     */
794
    abstract protected function getDropForeignKeyByColumnsInstructions($tableName, $columns);
795
796
    /**
797
     * @inheritdoc
798
     */
799
    public function dropTable($tableName)
800
    {
801
        $instructions = $this->getDropTableInstructions($tableName);
802
        $this->executeAlterSteps($tableName, $instructions);
803
    }
804
805
    /**
806
     * Returns the instructions to drop the specified database table.
807
     *
808
     * @param string $tableName Table name
809
     *
810
     * @return \Phinx\Db\Util\AlterInstructions
811
     */
812
    abstract protected function getDropTableInstructions($tableName);
813
814
    /**
815
     * @inheritdoc
816
     */
817
    public function renameTable($tableName, $newTableName)
818
    {
819
        $instructions = $this->getRenameTableInstructions($tableName, $newTableName);
820
        $this->executeAlterSteps($tableName, $instructions);
821
    }
822
823
    /**
824
     * Returns the instructions to rename the specified database table.
825
     *
826
     * @param string $tableName Table name
827
     * @param string $newTableName New Name
828
     *
829
     * @return \Phinx\Db\Util\AlterInstructions
830
     */
831
    abstract protected function getRenameTableInstructions($tableName, $newTableName);
832
833
    /**
834
     * @inheritdoc
835
     */
836
    public function changePrimaryKey(Table $table, $newColumns)
837
    {
838
        $instructions = $this->getChangePrimaryKeyInstructions($table, $newColumns);
839
        $this->executeAlterSteps($table->getName(), $instructions);
840
    }
841
842
    /**
843
     * Returns the instructions to change the primary key for the specified database table.
844
     *
845
     * @param \Phinx\Db\Table\Table $table Table
846
     * @param string|string[]|null $newColumns Column name(s) to belong to the primary key, or null to drop the key
847
     *
848
     * @return \Phinx\Db\Util\AlterInstructions
849
     */
850
    abstract protected function getChangePrimaryKeyInstructions(Table $table, $newColumns);
851
852
    /**
853
     * @inheritdoc
854
     */
855
    public function changeComment(Table $table, $newComment)
856
    {
857
        $instructions = $this->getChangeCommentInstructions($table, $newComment);
858
        $this->executeAlterSteps($table->getName(), $instructions);
859
    }
860
861
    /**
862
     * Returns the instruction to change the comment for the specified database table.
863
     *
864
     * @param \Phinx\Db\Table\Table $table Table
865
     * @param string|null $newComment New comment string, or null to drop the comment
866
     *
867
     * @return \Phinx\Db\Util\AlterInstructions
868
     */
869
    abstract protected function getChangeCommentInstructions(Table $table, $newComment);
870
871
    /**
872
     * {@inheritDoc}
873
     *
874
     * @throws \InvalidArgumentException
875
     *
876
     * @return void
877
     */
878
    public function executeActions(Table $table, array $actions)
879
    {
880
        $instructions = new AlterInstructions();
881
882
        foreach ($actions as $action) {
883
            switch (true) {
884
                case ($action instanceof AddColumn):
885
                    $instructions->merge($this->getAddColumnInstructions($table, $action->getColumn()));
886
                    break;
887
888
                case ($action instanceof AddIndex):
889
                    $instructions->merge($this->getAddIndexInstructions($table, $action->getIndex()));
890
                    break;
891
892
                case ($action instanceof AddForeignKey):
893
                    $instructions->merge($this->getAddForeignKeyInstructions($table, $action->getForeignKey()));
894
                    break;
895
896
                case ($action instanceof ChangeColumn):
897
                    $instructions->merge($this->getChangeColumnInstructions(
898
                        $table->getName(),
899
                        $action->getColumnName(),
900
                        $action->getColumn()
901
                    ));
902
                    break;
903
904
                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...
905
                    $instructions->merge($this->getDropForeignKeyByColumnsInstructions(
906
                        $table->getName(),
907
                        $action->getForeignKey()->getColumns()
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 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...
912
                    $instructions->merge($this->getDropForeignKeyInstructions(
913
                        $table->getName(),
914
                        $action->getForeignKey()->getConstraint()
915
                    ));
916
                    break;
917
918
                case ($action instanceof DropIndex && $action->getIndex()->getName() !== null):
919
                    $instructions->merge($this->getDropIndexByNameInstructions(
920
                        $table->getName(),
921
                        $action->getIndex()->getName()
922
                    ));
923
                    break;
924
925
                case ($action instanceof DropIndex && $action->getIndex()->getName() == null):
926
                    $instructions->merge($this->getDropIndexByColumnsInstructions(
927
                        $table->getName(),
928
                        $action->getIndex()->getColumns()
929
                    ));
930
                    break;
931
932
                case ($action instanceof DropTable):
933
                    $instructions->merge($this->getDropTableInstructions(
934
                        $table->getName()
935
                    ));
936
                    break;
937
938
                case ($action instanceof RemoveColumn):
939
                    $instructions->merge($this->getDropColumnInstructions(
940
                        $table->getName(),
941
                        $action->getColumn()->getName()
942
                    ));
943
                    break;
944
945
                case ($action instanceof RenameColumn):
946
                    $instructions->merge($this->getRenameColumnInstructions(
947
                        $table->getName(),
948
                        $action->getColumn()->getName(),
949
                        $action->getNewName()
950
                    ));
951
                    break;
952
953
                case ($action instanceof RenameTable):
954
                    $instructions->merge($this->getRenameTableInstructions(
955
                        $table->getName(),
956
                        $action->getNewName()
957
                    ));
958
                    break;
959
960
                case ($action instanceof ChangePrimaryKey):
961
                    $instructions->merge($this->getChangePrimaryKeyInstructions(
962
                        $table,
963
                        $action->getNewColumns()
964
                    ));
965
                    break;
966
967
                case ($action instanceof ChangeComment):
968
                    $instructions->merge($this->getChangeCommentInstructions(
969
                        $table,
970
                        $action->getNewComment()
971
                    ));
972
                    break;
973
974
                default:
975
                    throw new InvalidArgumentException(
976
                        sprintf("Don't know how to execute action: '%s'", get_class($action))
977
                    );
978
            }
979
        }
980
981
        $this->executeAlterSteps($table->getName(), $instructions);
982
    }
983
}
984