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

PdoAdapter   F

Complexity

Total Complexity 95

Size/Duplication

Total Lines 903
Duplicated Lines 1.33 %

Coupling/Cohesion

Components 1
Dependencies 21

Test Coverage

Coverage 91.01%

Importance

Changes 0
Metric Value
wmc 95
lcom 1
cbo 21
dl 12
loc 903
ccs 162
cts 178
cp 0.9101
rs 1.697
c 0
b 0
f 0

56 Methods

Rating   Name   Duplication   Size   Complexity  
A verboseLog() 0 9 3
A createPdoConnection() 0 15 2
A setOptions() 0 10 2
A setConnection() 0 27 4
A getConnection() 0 8 2
A connect() 0 3 1
A disconnect() 0 3 1
A execute() 0 11 2
getDecoratedConnection() 0 1 ?
A getQueryBuilder() 0 4 1
A query() 0 4 1
A fetchRow() 0 6 1
A fetchAll() 0 10 2
A insert() 0 24 4
A quoteValue() 0 12 3
A quoteString() 0 4 1
A bulkinsert() 0 38 5
A getVersions() 0 6 1
A migrated() 0 34 2
A setBreakpoint() 0 4 1
A unsetBreakpoint() 0 4 1
A markBreakpoint() 0 16 1
A createSchema() 0 4 1
A dropSchema() 0 4 1
A getColumnTypes() 0 29 1
A getAttribute() 0 4 1
B getDefaultValueDefinition() 12 12 7
A executeAlterSteps() 0 5 1
A addColumn() 0 5 1
getAddColumnInstructions() 0 1 ?
A renameColumn() 0 5 1
getRenameColumnInstructions() 0 1 ?
A changeColumn() 0 5 1
getChangeColumnInstructions() 0 1 ?
A dropColumn() 0 5 1
getDropColumnInstructions() 0 1 ?
A addIndex() 0 5 1
getAddIndexInstructions() 0 1 ?
A dropIndex() 0 5 1
getDropIndexByColumnsInstructions() 0 1 ?
A dropIndexByName() 0 5 1
getDropIndexByNameInstructions() 0 1 ?
A addForeignKey() 0 5 1
getAddForeignKeyInstructions() 0 1 ?
A dropForeignKey() 0 10 2
getDropForeignKeyInstructions() 0 1 ?
getDropForeignKeyByColumnsInstructions() 0 1 ?
A dropTable() 0 5 1
getDropTableInstructions() 0 1 ?
A renameTable() 0 5 1
getRenameTableInstructions() 0 1 ?
A changePrimaryKey() 0 5 1
getChangePrimaryKeyInstructions() 0 1 ?
A changeComment() 0 5 1
getChangeCommentInstructions() 0 1 ?
F executeActions() 0 105 20

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PdoAdapter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PdoAdapter, and based on these observations, apply Extract Interface, too.

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