Completed
Pull Request — master (#1175)
by
unknown
01:55
created

SQLiteAdapter::getColumnSqlDefinition()   F

Complexity

Conditions 15
Paths 312

Size

Total Lines 37
Code Lines 24

Duplication

Lines 6
Ratio 16.22 %

Code Coverage

Tests 23
CRAP Score 15.1152

Importance

Changes 0
Metric Value
dl 6
loc 37
ccs 23
cts 25
cp 0.92
rs 3.7313
c 0
b 0
f 0
cc 15
eloc 24
nc 312
nop 1
crap 15.1152

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 Phinx\Db\Table;
32
use Phinx\Db\Table\Column;
33
use Phinx\Db\Table\Index;
34
use Phinx\Db\Table\ForeignKey;
35
36
/**
37
 * Phinx SQLite Adapter.
38
 *
39
 * @author Rob Morgan <[email protected]>
40
 * @author Richard McIntyre <[email protected]>
41
 */
42
class SQLiteAdapter extends PdoAdapter implements AdapterInterface
43
{
44
    protected $definitionsWithLimits = [
45
        'CHARACTER',
46
        'VARCHAR',
47
        'VARYING CHARACTER',
48
        'NCHAR',
49
        'NATIVE CHARACTER',
50
        'NVARCHAR'
51
    ];
52
53
    /**
54
     * {@inheritdoc}
55
     */
56 42
    public function connect()
57
    {
58 42
        if ($this->connection === null) {
59 42
            if (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers(), true)) {
60
                // @codeCoverageIgnoreStart
61
                throw new \RuntimeException('You need to enable the PDO_SQLITE extension for Phinx to run properly.');
62
                // @codeCoverageIgnoreEnd
63
            }
64
65 42
            $db = null;
66 42
            $options = $this->getOptions();
67
68
            // if port is specified use it, otherwise use the MySQL default
69 42
            if (isset($options['memory'])) {
70
                $dsn = 'sqlite::memory:';
71
            } else {
72 42
                $dsn = 'sqlite:' . $options['name'];
73 42
                if (file_exists($options['name'] . '.sqlite3')) {
74 42
                    $dsn = 'sqlite:' . $options['name'] . '.sqlite3';
75 42
                }
76
            }
77
78
            try {
79 42
                $db = new \PDO($dsn);
80 42
            } catch (\PDOException $exception) {
81
                throw new \InvalidArgumentException(sprintf(
82
                    'There was a problem connecting to the database: %s',
83
                    $exception->getMessage()
84
                ));
85
            }
86
87 42
            $this->setConnection($db);
88 42
        }
89 42
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94 48
    public function disconnect()
95
    {
96 48
        $this->connection = null;
97 48
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function hasTransactions()
103
    {
104
        return true;
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110 1
    public function beginTransaction()
111
    {
112 1
        $this->execute('BEGIN TRANSACTION');
113 1
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118
    public function commitTransaction()
119
    {
120
        $this->execute('COMMIT');
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126
    public function rollbackTransaction()
127
    {
128
        $this->execute('ROLLBACK');
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134 43
    public function quoteTableName($tableName)
135
    {
136 43
        return str_replace('.', '`.`', $this->quoteColumnName($tableName));
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142 44
    public function quoteColumnName($columnName)
143
    {
144 44
        return '`' . str_replace('`', '``', $columnName) . '`';
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150 42
    public function hasTable($tableName)
151
    {
152 42
        $tables = [];
153 42
        $rows = $this->fetchAll(sprintf('SELECT name FROM sqlite_master WHERE type=\'table\' AND name=\'%s\'', $tableName));
154 42
        foreach ($rows as $row) {
155 12
            $tables[] = strtolower($row[0]);
156 42
        }
157
158 42
        return in_array(strtolower($tableName), $tables);
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164 42
    public function createTable(Table $table)
165
    {
166
        // Add the default primary key
167 42
        $columns = $table->getPendingColumns();
168 42
        $options = $table->getOptions();
169 42
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
170 35
            $column = new Column();
171 35
            $column->setName('id')
172 35
                   ->setType('integer')
173 35
                   ->setIdentity(true);
174
175 35
            array_unshift($columns, $column);
176 42
        } elseif (isset($options['id']) && is_string($options['id'])) {
177
            // Handle id => "field_name" to support AUTO_INCREMENT
178 1
            $column = new Column();
179 1
            $column->setName($options['id'])
180 1
                   ->setType('integer')
181 1
                   ->setIdentity(true);
182
183 1
            array_unshift($columns, $column);
184 1
        }
185
186
187 42
        $sql = 'CREATE TABLE ';
188 42
        $sql .= $this->quoteTableName($table->getName()) . ' (';
189 42 View Code Duplication
        foreach ($columns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
190 42
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
191 42
        }
192
193
        // set the primary key(s)
194 42 View Code Duplication
        if (isset($options['primary_key'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
195 42
            $sql = rtrim($sql);
196 42
            $sql .= ' PRIMARY KEY (';
197 42
            if (is_string($options['primary_key'])) {       // handle primary_key => 'id'
198 42
                $sql .= $this->quoteColumnName($options['primary_key']);
199 42
            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
200
                // PHP 5.4 will allow access of $this, so we can call quoteColumnName() directly in the anonymous function,
201
                // but for now just hard-code the adapter quotes
202 1
                $sql .= implode(
203 1
                    ',',
204 1
                    array_map(
205
                        function ($v) {
206 1
                            return '`' . $v . '`';
207 1
                        },
208 1
                        $options['primary_key']
209 1
                    )
210 1
                );
211 1
            }
212 42
            $sql .= ')';
213 42
        } else {
214 37
            $sql = substr(rtrim($sql), 0, -1);              // no primary keys
215
        }
216
217
        // set the foreign keys
218 42
        $foreignKeys = $table->getForeignKeys();
219 42
        if (!empty($foreignKeys)) {
220 1
            foreach ($foreignKeys as $foreignKey) {
221 1
                $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey);
222 1
            }
223 1
        }
224
225 42
        $sql = rtrim($sql) . ');';
226
        // execute the sql
227 42
        $this->execute($sql);
228
229 42
        foreach ($table->getIndexes() as $index) {
230 6
            $this->addIndex($table, $index);
231 42
        }
232 42
    }
233
234
    /**
235
     * {@inheritdoc}
236
     */
237 1
    public function renameTable($tableName, $newTableName)
238
    {
239 1
        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($tableName), $this->quoteTableName($newTableName)));
240 1
    }
241
242
    /**
243
     * {@inheritdoc}
244
     */
245 1
    public function dropTable($tableName)
246
    {
247 1
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
248 1
    }
249
250
    /**
251
     * {@inheritdoc}
252
     */
253 1
    public function truncateTable($tableName)
254
    {
255 1
        $sql = sprintf(
256 1
            'DELETE FROM %s',
257 1
            $this->quoteTableName($tableName)
258 1
        );
259
260 1
        $this->execute($sql);
261 1
    }
262
263
    /**
264
     * {@inheritdoc}
265
     */
266 1
    public function getColumns($tableName)
267
    {
268 1
        $columns = [];
269 1
        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
270
271 1
        foreach ($rows as $columnInfo) {
272 1
            $column = new Column();
273 1
            $type = strtolower($columnInfo['type']);
274 1
            $column->setName($columnInfo['name'])
275 1
                   ->setNull($columnInfo['notnull'] !== '1')
276 1
                   ->setDefault($columnInfo['dflt_value']);
277
278 1
            $phinxType = $this->getPhinxType($type);
279 1
            $column->setType($phinxType['name'])
280 1
                   ->setLimit($phinxType['limit']);
281
282 1
            if ($columnInfo['pk'] == 1) {
283 1
                $column->setIdentity(true);
284 1
            }
285
286 1
            $columns[] = $column;
287 1
        }
288
289 1
        return $columns;
290
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295 8 View Code Duplication
    public function hasColumn($tableName, $columnName)
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...
296
    {
297 8
        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
298 8
        foreach ($rows as $column) {
299 8
            if (strcasecmp($column['name'], $columnName) === 0) {
300 7
                return true;
301
            }
302 8
        }
303
304 8
        return false;
305
    }
306
307
    /**
308
     * {@inheritdoc}
309
     */
310 4 View Code Duplication
    public function addColumn(Table $table, Column $column)
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...
311
    {
312 4
        $sql = sprintf(
313 4
            'ALTER TABLE %s ADD COLUMN %s %s',
314 4
            $this->quoteTableName($table->getName()),
315 4
            $this->quoteColumnName($column->getName()),
316 4
            $this->getColumnSqlDefinition($column)
317 4
        );
318
319 4
        $this->execute($sql);
320 4
    }
321
322
    /**
323
     * {@inheritdoc}
324
     */
325 2
    public function renameColumn($tableName, $columnName, $newColumnName)
326
    {
327 2
        $tmpTableName = 'tmp_' . $tableName;
328
329 2
        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');
330
331 2
        $sql = '';
332 2
        foreach ($rows as $table) {
333 2
            if ($table['tbl_name'] === $tableName) {
334 2
                $sql = $table['sql'];
335 2
            }
336 2
        }
337
338 2
        $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
339 2
        $selectColumns = [];
340 2
        $writeColumns = [];
341 2 View Code Duplication
        foreach ($columns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
342 2
            $selectName = $column['name'];
343 2
            $writeName = ($selectName == $columnName)? $newColumnName : $selectName;
344 2
            $selectColumns[] = $this->quoteColumnName($selectName);
345 2
            $writeColumns[] = $this->quoteColumnName($writeName);
346 2
        }
347
348 2 View Code Duplication
        if (!in_array($this->quoteColumnName($columnName), $selectColumns)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
349 1
            throw new \InvalidArgumentException(sprintf(
350
                'The specified column doesn\'t exist: ' . $columnName
351 1
            ));
352
        }
353
354 1
        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName));
355
356 1
        $sql = str_replace(
357 1
            $this->quoteColumnName($columnName),
358 1
            $this->quoteColumnName($newColumnName),
359
            $sql
360 1
        );
361 1
        $this->execute($sql);
362
363
364 1
        $sql = sprintf(
365 1
            'INSERT INTO %s(%s) SELECT %s FROM %s',
366 1
            $tableName,
367 1
            implode(', ', $writeColumns),
368 1
            implode(', ', $selectColumns),
369
            $tmpTableName
370 1
        );
371
372 1
        $this->execute($sql);
373
374 1
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
375 1
    }
376
377
    /**
378
     * {@inheritdoc}
379
     */
380 6
    public function changeColumn($tableName, $columnName, Column $newColumn)
381
    {
382
383
        // TODO: DRY this up....
384 6
        $tmpTableName = 'tmp_' . $tableName;
385
386 6
        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');
387
388 6
        $sql = '';
389 6
        foreach ($rows as $table) {
390 6
            if ($table['tbl_name'] === $tableName) {
391 6
                $sql = $table['sql'];
392 6
            }
393 6
        }
394
395 6
        $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
396 6
        $selectColumns = [];
397 6
        $writeColumns = [];
398 6 View Code Duplication
        foreach ($columns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
399 6
            $selectName = $column['name'];
400 6
            $writeName = ($selectName === $columnName)? $newColumn->getName() : $selectName;
401 6
            $selectColumns[] = $this->quoteColumnName($selectName);
402 6
            $writeColumns[] = $this->quoteColumnName($writeName);
403 6
        }
404
405 6 View Code Duplication
        if (!in_array($this->quoteColumnName($columnName), $selectColumns)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
406
            throw new \InvalidArgumentException(sprintf(
407
                'The specified column doesn\'t exist: ' . $columnName
408
            ));
409
        }
410
411 6
        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName));
412
413 6
        $sql = preg_replace(
414 6
            sprintf("/%s(?:\/\*.*?\*\/|\([^)]+\)|'[^']*?'|[^,])+([,)])/", $this->quoteColumnName($columnName)),
415 6
            sprintf('%s %s$1', $this->quoteColumnName($newColumn->getName()), $this->getColumnSqlDefinition($newColumn)),
416 6
            $sql,
417
            1
418 6
        );
419
420 6
        $this->execute($sql);
421
422 6
        $sql = sprintf(
423 6
            'INSERT INTO %s(%s) SELECT %s FROM %s',
424 6
            $tableName,
425 6
            implode(', ', $writeColumns),
426 6
            implode(', ', $selectColumns),
427
            $tmpTableName
428 6
        );
429
430 6
        $this->execute($sql);
431 6
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
432 6
    }
433
434
    /**
435
     * {@inheritdoc}
436
     */
437 2
    public function dropColumn($tableName, $columnName)
438
    {
439
        // TODO: DRY this up....
440 2
        $tmpTableName = 'tmp_' . $tableName;
441
442 2
        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');
443
444 2
        $sql = '';
445 2
        foreach ($rows as $table) {
446 2
            if ($table['tbl_name'] === $tableName) {
447 2
                $sql = $table['sql'];
448 2
            }
449 2
        }
450
451 2
        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
452 2
        $columns = [];
453 2
        $columnType = null;
454 2
        foreach ($rows as $row) {
455 2
            if ($row['name'] !== $columnName) {
456 2
                $columns[] = $row['name'];
457 2
            } else {
458 2
                $found = true;
459 2
                $columnType = $row['type'];
460
            }
461 2
        }
462
463 2
        if (!isset($found)) {
464
            throw new \InvalidArgumentException(sprintf(
465
                'The specified column doesn\'t exist: ' . $columnName
466
            ));
467
        }
468
469 2
        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName));
470
471 2
        $sql = preg_replace(
472 2
            sprintf("/%s\s%s.*(,\s(?!')|\)$)/U", preg_quote($this->quoteColumnName($columnName)), preg_quote($columnType)),
473 2
            "",
474
            $sql
475 2
        );
476
477 2
        if (substr($sql, -2) === ', ') {
478 2
            $sql = substr($sql, 0, -2) . ')';
479 2
        }
480
481 2
        $this->execute($sql);
482
483 2
        $sql = sprintf(
484 2
            'INSERT INTO %s(%s) SELECT %s FROM %s',
485 2
            $tableName,
486 2
            implode(', ', $columns),
487 2
            implode(', ', $columns),
488
            $tmpTableName
489 2
        );
490
491 2
        $this->execute($sql);
492 2
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
493 2
    }
494
495
    /**
496
     * Get an array of indexes from a particular table.
497
     *
498
     * @param string $tableName Table Name
499
     * @return array
500
     */
501 9
    protected function getIndexes($tableName)
502
    {
503 9
        $indexes = [];
504 9
        $rows = $this->fetchAll(sprintf('pragma index_list(%s)', $tableName));
505
506 9
        foreach ($rows as $row) {
507 9
            $indexData = $this->fetchAll(sprintf('pragma index_info(%s)', $row['name']));
508 9
            if (!isset($indexes[$tableName])) {
509 9
                $indexes[$tableName] = ['index' => $row['name'], 'columns' => []];
510 9
            }
511 9
            foreach ($indexData as $indexItem) {
512 9
                $indexes[$tableName]['columns'][] = strtolower($indexItem['name']);
513 9
            }
514 9
        }
515 9
        return $indexes;
516
    }
517
518
    /**
519
     * {@inheritdoc}
520
     */
521 9 View Code Duplication
    public function hasIndex($tableName, $columns)
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...
522
    {
523 9
        if (is_string($columns)) {
524 4
            $columns = [$columns]; // str to array
525 4
        }
526
527 9
        $columns = array_map('strtolower', $columns);
528 9
        $indexes = $this->getIndexes($tableName);
529
530 9
        foreach ($indexes as $index) {
531 9
            $a = array_diff($columns, $index['columns']);
532 9
            if (empty($a)) {
533 9
                return true;
534
            }
535 8
        }
536
537 8
        return false;
538
    }
539
540
    /**
541
     * {@inheritdoc}
542
     */
543 1 View Code Duplication
    public function hasIndexByName($tableName, $indexName)
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...
544
    {
545 1
        $indexes = $this->getIndexes($tableName);
546
547 1
        foreach ($indexes as $index) {
548 1
            if ($indexName === $index['index']) {
549 1
                return true;
550
            }
551
        }
552
553
        return false;
554
    }
555
556
    /**
557
     * {@inheritdoc}
558
     */
559 8
    public function addIndex(Table $table, Index $index)
560
    {
561 8
        $indexColumnArray = [];
562 8
        foreach ($index->getColumns() as $column) {
563 8
            $indexColumnArray []= sprintf('`%s` ASC', $column);
564 8
        }
565 8
        $indexColumns = implode(',', $indexColumnArray);
566 8
        $this->execute(
567 8
            sprintf(
568 8
                'CREATE %s ON %s (%s)',
569 8
                $this->getIndexSqlDefinition($table, $index),
570 8
                $this->quoteTableName($table->getName()),
571
                $indexColumns
572 8
            )
573 8
        );
574 8
    }
575
576
    /**
577
     * {@inheritdoc}
578
     */
579 1
    public function dropIndex($tableName, $columns)
580
    {
581 1
        if (is_string($columns)) {
582 1
            $columns = [$columns]; // str to array
583 1
        }
584
585 1
        $indexes = $this->getIndexes($tableName);
586 1
        $columns = array_map('strtolower', $columns);
587
588 1
        foreach ($indexes as $index) {
589 1
            $a = array_diff($columns, $index['columns']);
590 1
            if (empty($a)) {
591 1
                $this->execute(
592 1
                    sprintf(
593 1
                        'DROP INDEX %s',
594 1
                        $this->quoteColumnName($index['index'])
595 1
                    )
596 1
                );
597 1
                return;
598
            }
599
        }
600
    }
601
602
    /**
603
     * {@inheritdoc}
604
     */
605 1 View Code Duplication
    public function dropIndexByName($tableName, $indexName)
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...
606
    {
607 1
        $indexes = $this->getIndexes($tableName);
608
609 1
        foreach ($indexes as $index) {
610 1
            if ($indexName === $index['index']) {
611 1
                $this->execute(
612 1
                    sprintf(
613 1
                        'DROP INDEX %s',
614 1
                        $this->quoteColumnName($indexName)
615 1
                    )
616 1
                );
617 1
                return;
618
            }
619
        }
620
    }
621
622
    /**
623
     * {@inheritdoc}
624
     */
625 5
    public function hasForeignKey($tableName, $columns, $constraint = null)
626
    {
627 5
        if (is_string($columns)) {
628
            $columns = [$columns]; // str to array
629
        }
630 5
        $foreignKeys = $this->getForeignKeys($tableName);
631
632 5
        $a = array_diff($columns, $foreignKeys);
633 5
        if (empty($a)) {
634 5
            return true;
635
        }
636 1
        return false;
637
    }
638
639
    /**
640
     * Get an array of foreign keys from a particular table.
641
     *
642
     * @param string $tableName Table Name
643
     * @return array
644
     */
645 5
    protected function getForeignKeys($tableName)
646
    {
647 5
        $foreignKeys = [];
648 5
        $rows = $this->fetchAll(
649
            "SELECT sql, tbl_name
650
              FROM (
651
                    SELECT sql sql, type type, tbl_name tbl_name, name name
652
                      FROM sqlite_master
653
                     UNION ALL
654
                    SELECT sql, type, tbl_name, name
655
                      FROM sqlite_temp_master
656
                   )
657
             WHERE type != 'meta'
658
               AND sql NOTNULL
659
               AND name NOT LIKE 'sqlite_%'
660
             ORDER BY substr(type, 2, 1), name"
661 5
        );
662
663 5
        foreach ($rows as $row) {
664 5
            if ($row['tbl_name'] === $tableName) {
665 5
                if (strpos($row['sql'], 'REFERENCES') !== false) {
666 5
                    preg_match_all("/\(`([^`]*)`\) REFERENCES/", $row['sql'], $matches);
667 5
                    foreach ($matches[1] as $match) {
668 5
                        $foreignKeys[] = $match;
669 5
                    }
670 5
                }
671 5
            }
672 5
        }
673 5
        return $foreignKeys;
674
    }
675
676
    /**
677
     * {@inheritdoc}
678
     */
679 4
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
680
    {
681
        // TODO: DRY this up....
682 4
        $this->execute('pragma foreign_keys = ON');
683
684 4
        $tmpTableName = 'tmp_' . $table->getName();
685 4
        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');
686
687 4
        $sql = '';
688 4
        foreach ($rows as $row) {
689 4
            if ($row['tbl_name'] === $table->getName()) {
690 4
                $sql = $row['sql'];
691 4
            }
692 4
        }
693
694 4
        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($table->getName())));
695 4
        $columns = [];
696 4
        foreach ($rows as $column) {
697 4
            $columns[] = $this->quoteColumnName($column['name']);
698 4
        }
699
700 4
        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($table->getName()), $tmpTableName));
701
702 4
        $sql = substr($sql, 0, -1) . ',' . $this->getForeignKeySqlDefinition($foreignKey) . ')';
703 4
        $this->execute($sql);
704
705 4
        $sql = sprintf(
706 4
            'INSERT INTO %s(%s) SELECT %s FROM %s',
707 4
            $this->quoteTableName($table->getName()),
708 4
            implode(', ', $columns),
709 4
            implode(', ', $columns),
710 4
            $this->quoteTableName($tmpTableName)
711 4
        );
712
713 4
        $this->execute($sql);
714 4
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
715 4
    }
716
717
    /**
718
     * {@inheritdoc}
719
     */
720 1
    public function dropForeignKey($tableName, $columns, $constraint = null)
721
    {
722
        // TODO: DRY this up....
723 1
        if (is_string($columns)) {
724
            $columns = [$columns]; // str to array
725
        }
726
727 1
        $tmpTableName = 'tmp_' . $tableName;
728
729 1
        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');
730
731 1
        $sql = '';
732 1
        foreach ($rows as $table) {
733 1
            if ($table['tbl_name'] === $tableName) {
734 1
                $sql = $table['sql'];
735 1
            }
736 1
        }
737
738 1
        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
739 1
        $replaceColumns = [];
740 1
        foreach ($rows as $row) {
741 1
            if (!in_array($row['name'], $columns)) {
742 1
                $replaceColumns[] = $row['name'];
743 1
            } else {
744 1
                $found = true;
745
            }
746 1
        }
747
748 1
        if (!isset($found)) {
749
            throw new \InvalidArgumentException(sprintf(
750
                'The specified column doesn\'t exist: '
751
            ));
752
        }
753
754 1
        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($tableName), $tmpTableName));
755
756 1
        foreach ($columns as $columnName) {
757 1
            $search = sprintf(
758 1
                "/,[^,]*\(%s(?:,`?(.*)`?)?\) REFERENCES[^,]*\([^\)]*\)[^,)]*/",
759 1
                $this->quoteColumnName($columnName)
760 1
            );
761 1
            $sql = preg_replace($search, '', $sql, 1);
762 1
        }
763
764 1
        $this->execute($sql);
765
766 1
        $sql = sprintf(
767 1
            'INSERT INTO %s(%s) SELECT %s FROM %s',
768 1
            $tableName,
769 1
            implode(', ', $columns),
770 1
            implode(', ', $columns),
771
            $tmpTableName
772 1
        );
773
774 1
        $this->execute($sql);
775 1
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
776 1
    }
777
778
    /**
779
     * {@inheritdoc}
780
     */
781
    public function insert(Table $table, $row)
782
    {
783
        $sql = sprintf(
784
            "INSERT INTO %s ",
785
            $this->quoteTableName($table->getName())
786
        );
787
788
        $columns = array_keys($row);
789
        $sql .= "(". implode(', ', array_map([$this, 'quoteColumnName'], $columns)) . ")";
790
        $sql .= " VALUES ";
791
792
        $sql .= "(" . implode(', ', array_map(function ($value) {
793
            if (is_numeric($value)) {
794
                return $value;
795
            }
796
797
            if ($value === null) {
798
                return 'null';
799
            }
800
801
                return $this->getConnection()->quote($value);
802
        }, $row)) . ")";
803
804
        $this->execute($sql);
805
    }
806
807
    /**
808
     * {@inheritdoc}
809
     */
810 43
    public function getSqlType($type, $limit = null)
811
    {
812
        switch ($type) {
813 43
            case static::PHINX_TYPE_STRING:
814 42
                return ['name' => 'varchar', 'limit' => 255];
815
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
816 43
            case static::PHINX_TYPE_CHAR:
817
                return ['name' => 'char', 'limit' => 255];
818
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
819 43
            case static::PHINX_TYPE_TEXT:
820 1
                return ['name' => 'text'];
821
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
822 43
            case static::PHINX_TYPE_INTEGER:
823 38
                return ['name' => 'integer'];
824
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
825 43
            case static::PHINX_TYPE_BIG_INTEGER:
826 42
                return ['name' => 'bigint'];
827
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
828 43
            case static::PHINX_TYPE_FLOAT:
829 2
                return ['name' => 'float'];
830
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
831 43
            case static::PHINX_TYPE_DECIMAL:
832 1
                return ['name' => 'decimal'];
833
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
834 43
            case static::PHINX_TYPE_DATETIME:
835 1
                return ['name' => 'datetime'];
836
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
837 43
            case static::PHINX_TYPE_TIMESTAMP:
838 42
                return ['name' => 'datetime'];
839
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
840 43
            case static::PHINX_TYPE_TIME:
841 1
                return ['name' => 'time'];
842
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
843 43
            case static::PHINX_TYPE_DATE:
844 1
                return ['name' => 'date'];
845
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
846 43
            case static::PHINX_TYPE_BLOB:
847 43
            case static::PHINX_TYPE_BINARY:
848 1
                return ['name' => 'blob'];
849
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
850 43
            case static::PHINX_TYPE_BOOLEAN:
851 42
                return ['name' => 'boolean'];
852
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
853 5
            case static::PHINX_TYPE_UUID:
854
                return ['name' => 'char', 'limit' => 36];
855 5
            case static::PHINX_TYPE_ENUM:
856 4
                return ['name' => 'enum'];
857
            // Geospatial database types
858
            // No specific data types exist in SQLite, instead all geospatial
859
            // functionality is handled in the client. See also: SpatiaLite.
860 1
            case static::PHINX_TYPE_GEOMETRY:
861 1
            case static::PHINX_TYPE_POLYGON:
862
                return ['name' => 'text'];
863
                return;
0 ignored issues
show
Unused Code introduced by
return; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
864 1
            case static::PHINX_TYPE_LINESTRING:
865
                return ['name' => 'varchar', 'limit' => 255];
866
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
867 1
            case static::PHINX_TYPE_POINT:
868
                return ['name' => 'float'];
869 1
            default:
870 1
                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
871 1
        }
872
    }
873
874
    /**
875
     * Returns Phinx type by SQL type
876
     *
877
     * @param string $sqlTypeDef SQL type
878
     * @returns string Phinx type
879
     */
880 3
    public function getPhinxType($sqlTypeDef)
881
    {
882 3
        if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*$/', $sqlTypeDef, $matches)) {
883 1
            throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported');
884
        } else {
885 2
            $limit = null;
886 2
            $precision = null;
887 2
            $type = $matches[1];
888 2 View Code Duplication
            if (count($matches) > 2) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
889 1
                $limit = $matches[3] ? $matches[3] : null;
890 1
            }
891 2
            if (count($matches) > 4) {
892
                $precision = $matches[5];
893
            }
894 2
            switch ($matches[1]) {
895 2
                case 'varchar':
896 1
                    $type = static::PHINX_TYPE_STRING;
897 1
                    if ($limit === 255) {
898
                        $limit = null;
899
                    }
900 1
                    break;
901 2 View Code Duplication
                case 'char':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
902
                    $type = static::PHINX_TYPE_CHAR;
903
                    if ($limit === 255) {
904
                        $limit = null;
905
                    }
906
                    if ($limit === 36) {
907
                        $type = static::PHINX_TYPE_UUID;
908
                    }
909
                    break;
910 2
                case 'int':
911
                    $type = static::PHINX_TYPE_INTEGER;
912
                    if ($limit === 11) {
913
                        $limit = null;
914
                    }
915
                    break;
916 2
                case 'bigint':
917 1
                    if ($limit === 11) {
918
                        $limit = null;
919
                    }
920 1
                    $type = static::PHINX_TYPE_BIG_INTEGER;
921 1
                    break;
922 2
                case 'blob':
923 1
                    $type = static::PHINX_TYPE_BINARY;
924 1
                    break;
925 2
            }
926 2 View Code Duplication
            if ($type === 'tinyint') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
927
                if ($matches[3] === 1) {
928
                    $type = static::PHINX_TYPE_BOOLEAN;
929
                    $limit = null;
930
                }
931
            }
932
933 2
            $this->getSqlType($type);
934
935
            return [
936 1
                'name' => $type,
937 1
                'limit' => $limit,
938
                'precision' => $precision
939 1
            ];
940
        }
941
    }
942
943
    /**
944
     * {@inheritdoc}
945
     */
946 48
    public function createDatabase($name, $options = [])
947
    {
948 48
        touch($name . '.sqlite3');
949 48
    }
950
951
    /**
952
     * {@inheritdoc}
953
     */
954 2
    public function hasDatabase($name)
955
    {
956 2
        return is_file($name . '.sqlite3');
957
    }
958
959
    /**
960
     * {@inheritdoc}
961
     */
962 48
    public function dropDatabase($name)
963
    {
964 48
        if (file_exists($name . '.sqlite3')) {
965 47
            unlink($name . '.sqlite3');
966 47
        }
967 48
    }
968
969
    /**
970
     * Gets the SQLite Column Definition for a Column object.
971
     *
972
     * @param Column $column Column
973
     * @return string
974
     */
975 42
    protected function getColumnSqlDefinition(Column $column)
976
    {
977 42
        $isCustomColumn = $column instanceof Table\CustomColumn;
978 8
        if ($isCustomColumn) {
979 42
            $def = $column->getType();
980 42
        } else {
981 42
            $sqlType = $this->getSqlType($column->getType());
982 42
            $def = '';
983
            $def .= strtoupper($sqlType['name']);
984 View Code Duplication
            if ($column->getPrecision() && $column->getScale()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
985
                $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
986
            }
987
            $limitable = in_array(strtoupper($sqlType['name']), $this->definitionsWithLimits);
988
            if (($column->getLimit() || isset($sqlType['limit'])) && $limitable) {
989
                $def .= '(' . ($column->getLimit() ? $column->getLimit() : $sqlType['limit']) . ')';
990
            }
991 42 View Code Duplication
            if (($values = $column->getValues()) && is_array($values)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
992
                $def .= " CHECK({$column->getName()} IN ('" . implode("', '", $values) . "'))";
993 42
            }
994 42
        }
995 42
996 42
        $default = $column->getDefault();
997
998
        $def .= ($column->isNull() || is_null($default)) ? ' NULL' : ' NOT NULL';
999 42
        $def .= $this->getDefaultValueDefinition($default);
1000 42
        if (!$isCustomColumn) {
1001 42
            $def .= $column->isIdentity() ? ' PRIMARY KEY AUTOINCREMENT' : '';
1002 42
        }
1003 42
1004 4
        if ($column->getUpdate()) {
1005 4
            $def .= ' ON UPDATE ' . $column->getUpdate();
1006
        }
1007 42
1008
        $def .= $this->getCommentDefinition($column);
1009 42
1010 42
        return $def;
1011 42
    }
1012
1013 42
    /**
1014
     * Gets the comment Definition for a Column object.
1015
     *
1016
     * @param Column $column Column
1017 42
     * @return string
1018
     */
1019 42
    protected function getCommentDefinition(Column $column)
1020
    {
1021
        if ($column->getComment()) {
1022
            return ' /* ' . $column->getComment() . ' */ ';
1023
        }
1024
        return '';
1025
    }
1026
1027
    /**
1028 42
     * Gets the SQLite Index Definition for an Index object.
1029
     *
1030 42
     * @param Index $index Index
1031 2
     * @return string
1032
     */
1033 42
    protected function getIndexSqlDefinition(Table $table, Index $index)
1034
    {
1035
        if ($index->getType() === Index::UNIQUE) {
1036
            $def = 'UNIQUE INDEX';
1037
        } else {
1038
            $def = 'INDEX';
1039
        }
1040
        if (is_string($index->getName())) {
1041
            $indexName = $index->getName();
1042 8
        } else {
1043
            $indexName = $table->getName() . '_';
1044 8
            foreach ($index->getColumns() as $column) {
1045 2
                $indexName .= $column . '_';
1046 2
            }
1047 6
            $indexName .= 'index';
1048
        }
1049 8
        $def .= ' `' . $indexName . '`';
1050 3
        return $def;
1051 3
    }
1052 6
1053 6
    /**
1054 6
     * {@inheritdoc}
1055 6
     */
1056 6
    public function getColumnTypes()
1057
    {
1058 8
        return array_merge(parent::getColumnTypes(), ['enum']);
1059 8
    }
1060
1061
    /**
1062
     * Gets the SQLite Foreign Key Definition for an ForeignKey object.
1063
     *
1064
     * @param ForeignKey $foreignKey
1065 47
     * @return string
1066
     */
1067 47 View Code Duplication
    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey)
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...
1068
    {
1069
        $def = '';
1070
        if ($foreignKey->getConstraint()) {
1071
            $def .= ' CONSTRAINT ' . $this->quoteColumnName($foreignKey->getConstraint());
0 ignored issues
show
Bug introduced by
It seems like $foreignKey->getConstraint() targeting Phinx\Db\Table\ForeignKey::getConstraint() can also be of type boolean; however, Phinx\Db\Adapter\SQLiteAdapter::quoteColumnName() 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...
1072
        } else {
1073
            $columnNames = [];
1074
            foreach ($foreignKey->getColumns() as $column) {
1075
                $columnNames[] = $this->quoteColumnName($column);
1076 5
            }
1077
            $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
1078 5
            $refColumnNames = [];
1079 5
            foreach ($foreignKey->getReferencedColumns() as $column) {
1080
                $refColumnNames[] = $this->quoteColumnName($column);
1081
            }
1082 5
            $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
1083 5
            if ($foreignKey->getOnDelete()) {
1084 5
                $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
1085 5
            }
1086 5
            if ($foreignKey->getOnUpdate()) {
1087 5
                $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
1088 5
            }
1089 5
        }
1090 5
        return $def;
1091 5
    }
1092
}
1093