Completed
Pull Request — master (#1627)
by Alexandr
01:24
created

PdoAdapter::createPdoConnection()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2.5

Importance

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