Completed
Pull Request — master (#1436)
by
unknown
23:53 queued 05:42
created

SQLiteAdapter::getPhinxType()   F

Complexity

Conditions 18
Paths 433

Size

Total Lines 67

Duplication

Lines 15
Ratio 22.39 %

Code Coverage

Tests 32
CRAP Score 18.0658

Importance

Changes 0
Metric Value
dl 15
loc 67
ccs 32
cts 34
cp 0.9412
rs 1.4875
c 0
b 0
f 0
cc 18
nc 433
nop 1
crap 18.0658

How to fix   Long Method    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 Cake\Database\Connection;
32
use Cake\Database\Driver\Sqlite as SqliteDriver;
33
use Phinx\Db\Table\Column;
34
use Phinx\Db\Table\ForeignKey;
35
use Phinx\Db\Table\Index;
36
use Phinx\Db\Table\Table;
37
use Phinx\Db\Util\AlterInstructions;
38
use Phinx\Util\Literal;
39
40
/**
41
 * Phinx SQLite Adapter.
42
 *
43
 * @author Rob Morgan <[email protected]>
44
 * @author Richard McIntyre <[email protected]>
45
 */
46
class SQLiteAdapter extends PdoAdapter implements AdapterInterface
47
{
48
    protected $definitionsWithLimits = [
49
        'CHARACTER',
50
        'VARCHAR',
51
        'VARYING CHARACTER',
52
        'NCHAR',
53
        'NATIVE CHARACTER',
54
        'NVARCHAR'
55
    ];
56 42
57
    protected $suffix = '.sqlite3';
58 42
59 42
    /**
60
     * {@inheritdoc}
61
     */
62
    public function connect()
63
    {
64
        if ($this->connection === null) {
65 42
            if (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers(), true)) {
66 42
                // @codeCoverageIgnoreStart
67
                throw new \RuntimeException('You need to enable the PDO_SQLITE extension for Phinx to run properly.');
68
                // @codeCoverageIgnoreEnd
69 42
            }
70
71
            $db = null;
72 42
            $options = $this->getOptions();
73 42
74 42
            // if port is specified use it, otherwise use the MySQL default
75 42
            if (isset($options['memory'])) {
76
                $dsn = 'sqlite::memory:';
77
            } else {
78
                $dsn = 'sqlite:' . $options['name'] . $this->suffix;
79 42
            }
80 42
81
            try {
82
                $db = new \PDO($dsn);
83
            } catch (\PDOException $exception) {
84
                throw new \InvalidArgumentException(sprintf(
85
                    'There was a problem connecting to the database: %s',
86
                    $exception->getMessage()
87 42
                ));
88 42
            }
89 42
90
            $db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
91
            $this->setConnection($db);
92
        }
93
    }
94 48
95
    /**
96 48
     * {@inheritdoc}
97 48
     */
98
    public function setOptions(array $options)
99
    {
100
        parent::setOptions($options);
101
102
        if (isset($options['suffix'])) {
103
            $this->suffix = $options['suffix'];
104
        }
105
        //don't "fix" the file extension if it is blank, some people
106
        //might want a SQLITE db file with absolutely no extension.
107
        if (strlen($this->suffix) && substr($this->suffix, 0, 1) !== '.') {
108
            $this->suffix = '.' . $this->suffix;
109
        }
110 1
111
        return $this;
112 1
    }
113 1
114
    /**
115
     * {@inheritdoc}
116
     */
117
    public function disconnect()
118
    {
119
        $this->connection = null;
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125
    public function hasTransactions()
126
    {
127
        return true;
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function beginTransaction()
134 43
    {
135
        $this->getConnection()->beginTransaction();
136 43
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141
    public function commitTransaction()
142 44
    {
143
        $this->getConnection()->commit();
144 44
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function rollbackTransaction()
150 42
    {
151
        $this->getConnection()->rollBack();
152 42
    }
153 42
154 42
    /**
155 12
     * {@inheritdoc}
156 42
     */
157
    public function quoteTableName($tableName)
158 42
    {
159
        return str_replace('.', '`.`', $this->quoteColumnName($tableName));
160
    }
161
162
    /**
163
     * {@inheritdoc}
164 42
     */
165
    public function quoteColumnName($columnName)
166
    {
167 42
        return '`' . str_replace('`', '``', $columnName) . '`';
168 42
    }
169 42
170 35
    /**
171 35
     * {@inheritdoc}
172 35
     */
173 35
    public function hasTable($tableName)
174
    {
175 35
        $tables = [];
176 42
        $rows = $this->fetchAll(sprintf('SELECT name FROM sqlite_master WHERE type=\'table\' AND name=\'%s\'', $tableName));
177
        foreach ($rows as $row) {
178 1
            $tables[] = strtolower($row[0]);
179 1
        }
180 1
181 1
        return in_array(strtolower($tableName), $tables);
182
    }
183 1
184 1
    /**
185
     * {@inheritdoc}
186
     */
187 42
    public function createTable(Table $table, array $columns = [], array $indexes = [])
188 42
    {
189 42
        // Add the default primary key
190 42
        $options = $table->getOptions();
191 42
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
192
            $column = new Column();
193
            $column->setName('id')
194 42
                   ->setType('integer')
195 42
                   ->setIdentity(true);
196 42
197 42
            array_unshift($columns, $column);
198 42
        } elseif (isset($options['id']) && is_string($options['id'])) {
199 42
            // Handle id => "field_name" to support AUTO_INCREMENT
200
            $column = new Column();
201
            $column->setName($options['id'])
202 1
                   ->setType('integer')
203 1
                   ->setIdentity(true);
204 1
205
            array_unshift($columns, $column);
206 1
        }
207 1
208 1
        $sql = 'CREATE TABLE ';
209 1
        $sql .= $this->quoteTableName($table->getName()) . ' (';
210 1
        foreach ($columns as $column) {
211 1
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
212 42
213 42
            if (isset($options['primary_key']) && $column->getIdentity()) {
214 37
                //remove column from the primary key array as it is already defined as an autoincrement
215
                //primary id
216
                $identityColumnIndex = array_search($column->getName(), $options['primary_key']);
217
                if ($identityColumnIndex !== false) {
218 42
                    unset($options['primary_key'][$identityColumnIndex]);
219 42
220 1
                    if (empty($options['primary_key'])) {
221 1
                        //The last primary key has been removed
222 1
                        unset($options['primary_key']);
223 1
                    }
224
                }
225 42
            }
226
        }
227 42
228
        // set the primary key(s)
229 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...
230 6
            $sql = rtrim($sql);
231 42
            $sql .= ' PRIMARY KEY (';
232 42
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
233
                $sql .= $this->quoteColumnName($options['primary_key']);
234
            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
235
                $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
236
            }
237 1
            $sql .= ')';
238
        } else {
239 1
            $sql = substr(rtrim($sql), 0, -1); // no primary keys
240 1
        }
241
242
        $sql = rtrim($sql) . ');';
243
        // execute the sql
244
        $this->execute($sql);
245 1
246
        foreach ($indexes as $index) {
247 1
            $this->addIndex($table, $index);
248 1
        }
249
    }
250
251
    /**
252
     * {@inheritdoc}
253 1
     */
254
    protected function getChangePrimaryKeyInstructions(Table $table, $newColumns)
255 1
    {
256 1
        $instructions = new AlterInstructions();
257 1
258 1
        // Drop the existing primary key
259
        $primaryKey = $this->getPrimaryKey($table->getName());
260 1
        if (!empty($primaryKey)) {
261 1
            $instructions->merge(
262
                $this->getDropPrimaryKeyInstructions($table, $primaryKey)
263
            );
264
        }
265
266 1
        // Add the primary key(s)
267
        if (!empty($newColumns)) {
268 1
            if (!is_string($newColumns)) {
269 1
                throw new \InvalidArgumentException(sprintf(
270
                    "Invalid value for primary key: %s",
271 1
                    json_encode($newColumns)
272 1
                ));
273 1
            }
274 1
275 1
            $instructions->merge(
276 1
                $this->getAddPrimaryKeyInstructions($table, $newColumns)
277
            );
278 1
        }
279 1
280 1
        return $instructions;
281
    }
282 1
283 1
    /**
284 1
     * {@inheritdoc}
285
     */
286 1
    protected function getChangeCommentInstructions(Table $table, $newComment)
287 1
    {
288
        throw new \BadMethodCallException('SQLite does not have table comments');
289 1
    }
290
291
    /**
292
     * {@inheritdoc}
293
     */
294 View Code Duplication
    protected function getRenameTableInstructions($tableName, $newTableName)
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...
295 8
    {
296
        $sql = sprintf(
297 8
            'ALTER TABLE %s RENAME TO %s',
298 8
            $this->quoteTableName($tableName),
299 8
            $this->quoteTableName($newTableName)
300 7
        );
301
302 8
        return new AlterInstructions([], [$sql]);
303
    }
304 8
305
    /**
306
     * {@inheritdoc}
307
     */
308
    protected function getDropTableInstructions($tableName)
309
    {
310 4
        $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName));
311
312 4
        return new AlterInstructions([], [$sql]);
313 4
    }
314 4
315 4
    /**
316 4
     * {@inheritdoc}
317 4
     */
318
    public function truncateTable($tableName)
319 4
    {
320 4
        $sql = sprintf(
321
            'DELETE FROM %s',
322
            $this->quoteTableName($tableName)
323
        );
324
325 2
        $this->execute($sql);
326
    }
327 2
328
    /**
329 2
     * {@inheritdoc}
330
     */
331 2
    public function getColumns($tableName)
332 2
    {
333 2
        $columns = [];
334 2
        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
335 2
336 2
        foreach ($rows as $columnInfo) {
337
            $column = new Column();
338 2
            $type = strtolower($columnInfo['type']);
339 2
            $column->setName($columnInfo['name'])
340 2
                   ->setNull($columnInfo['notnull'] !== '1')
341 2
                   ->setDefault($columnInfo['dflt_value']);
342 2
343 2
            $phinxType = $this->getPhinxType($type);
344 2
            $column->setType($phinxType['name'])
345 2
                   ->setLimit($phinxType['limit']);
346 2
347
            if ($columnInfo['pk'] == 1) {
348 2
                $column->setIdentity(true);
349 1
            }
350
351 1
            $columns[] = $column;
352
        }
353
354 1
        return $columns;
355
    }
356 1
357 1
    /**
358 1
     * {@inheritdoc}
359
     */
360 1 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...
361 1
    {
362
        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
363
        foreach ($rows as $column) {
364 1
            if (strcasecmp($column['name'], $columnName) === 0) {
365 1
                return true;
366 1
            }
367 1
        }
368 1
369
        return false;
370 1
    }
371
372 1
    /**
373
     * {@inheritdoc}
374 1
     */
375 1 View Code Duplication
    protected function getAddColumnInstructions(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...
376
    {
377
        $alter = sprintf(
378
            'ALTER TABLE %s ADD COLUMN %s %s',
379
            $this->quoteTableName($table->getName()),
380 6
            $this->quoteColumnName($column->getName()),
381
            $this->getColumnSqlDefinition($column)
382
        );
383
384 6
        return new AlterInstructions([], [$alter]);
385
    }
386 6
387
    /**
388 6
     * Returns the original CREATE statement for the give table
389 6
     *
390 6
     * @param string $tableName The table name to get the create statement for
391 6
     * @return string
392 6
     */
393 6
    protected function getDeclaringSql($tableName)
394
    {
395 6
        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');
396 6
397 6
        $sql = '';
398 6
        foreach ($rows as $table) {
399 6
            if ($table['tbl_name'] === $tableName) {
400 6
                $sql = $table['sql'];
401 6
            }
402 6
        }
403 6
404
        return $sql;
405 6
    }
406
407
    /**
408
     * Copies all the data from a tmp table to another table
409
     *
410
     * @param string $tableName The table name to copy the data to
411 6
     * @param string $tmpTableName The tmp table name where the data is stored
412
     * @param string[] $writeColumns The list of columns in the target table
413 6
     * @param string[] $selectColumns The list of columns in the tmp table
414 6
     * @return void
415 6
     */
416 6
    protected function copyDataToNewTable($tableName, $tmpTableName, $writeColumns, $selectColumns)
417
    {
418 6
        $sql = sprintf(
419
            'INSERT INTO %s(%s) SELECT %s FROM %s',
420 6
            $this->quoteTableName($tableName),
421
            implode(', ', $writeColumns),
422 6
            implode(', ', $selectColumns),
423 6
            $this->quoteTableName($tmpTableName)
424 6
        );
425 6
        $this->execute($sql);
426 6
    }
427
428 6
    /**
429
     * Modifies the passed instructions to copy all data from the tmp table into
430 6
     * the provided table and then drops the tmp table.
431 6
     *
432 6
     * @param AlterInstructions $instructions The instructions to modify
433
     * @param string $tableName The table name to copy the data to
434
     * @return AlterInstructions
435
     */
436
    protected function copyAndDropTmpTable($instructions, $tableName)
437 2
    {
438
        $instructions->addPostStep(function ($state) use ($tableName) {
439
            $this->copyDataToNewTable(
440 2
                $tableName,
441
                $state['tmpTableName'],
442 2
                $state['writeColumns'],
443
                $state['selectColumns']
444 2
            );
445 2
446 2
            $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($state['tmpTableName'])));
447 2
448 2
            return $state;
449 2
        });
450
451 2
        return $instructions;
452 2
    }
453 2
454 2
    /**
455 2
     * Returns the columns and type to use when copying a table to another in the process
456 2
     * of altering a table
457 2
     *
458 2
     * @param string $tableName The table to modify
459 2
     * @param string $columnName The column name that is about to change
460
     * @param string|false $newColumnName Optionally the new name for the column
461 2
     * @return AlterInstructions
462
     */
463 2
    protected function calculateNewTableColumns($tableName, $columnName, $newColumnName)
464
    {
465
        $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
466
        $selectColumns = [];
467
        $writeColumns = [];
468
        $columnType = null;
469 2
        $found = false;
470
471 2
        foreach ($columns as $column) {
472 2
            $selectName = $column['name'];
473 2
            $writeName = $selectName;
474
475 2
            if ($selectName == $columnName) {
476
                $writeName = $newColumnName;
477 2
                $found = true;
478 2
                $columnType = $column['type'];
479 2
                $selectName = $newColumnName === false ? $newColumnName : $selectName;
480
            }
481 2
482
            $selectColumns[] = $selectName;
483 2
            $writeColumns[] = $writeName;
484 2
        }
485 2
486 2
        $selectColumns = array_filter($selectColumns, 'strlen');
487 2
        $writeColumns = array_filter($writeColumns, 'strlen');
488
        $selectColumns = array_map([$this, 'quoteColumnName'], $selectColumns);
489 2
        $writeColumns = array_map([$this, 'quoteColumnName'], $writeColumns);
490
491 2
        if (!$found) {
492 2
            throw new \InvalidArgumentException(sprintf(
493 2
                'The specified column doesn\'t exist: ' . $columnName
494
            ));
495
        }
496
497
        return compact('writeColumns', 'selectColumns', 'columnType');
498
    }
499
500
    /**
501 9
     * Returns the initial instructions to alter a table using the
502
     * rename-alter-copy strategy
503 9
     *
504 9
     * @param string $tableName The table to modify
505
     * @return AlterInstructions
506 9
     */
507 9
    protected function beginAlterByCopyTable($tableName)
508 9
    {
509 9
        $instructions = new AlterInstructions();
510 9
        $instructions->addPostStep(function ($state) use ($tableName) {
511 9
            $createSQL = $this->getDeclaringSql($tableName);
512 9
513 9
            $tmpTableName = 'tmp_' . $tableName;
514 9
            $this->execute(
515 9
                sprintf(
516
                    'ALTER TABLE %s RENAME TO %s',
517
                    $this->quoteTableName($tableName),
518
                    $this->quoteTableName($tmpTableName)
519
                )
520
            );
521 9
522
            return compact('createSQL', 'tmpTableName') + $state;
523 9
        });
524 4
525 4
        return $instructions;
526
    }
527 9
528 9
    /**
529
     * {@inheritdoc}
530 9
     */
531 9
    protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName)
532 9
    {
533 9
        $instructions = $this->beginAlterByCopyTable($tableName);
534 View Code Duplication
        $instructions->addPostStep(function ($state) use ($columnName, $newColumnName) {
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...
535 8
            $newState = $this->calculateNewTableColumns($state['tmpTableName'], $columnName, $newColumnName);
536
537 8
            return $newState + $state;
538
        });
539
540
        $instructions->addPostStep(function ($state) use ($columnName, $newColumnName) {
541
            $sql = str_replace(
542
                $this->quoteColumnName($columnName),
543 1
                $this->quoteColumnName($newColumnName),
544
                $state['createSQL']
545 1
            );
546
            $this->execute($sql);
547 1
548 1
            return $state;
549 1
        });
550
551
        return $this->copyAndDropTmpTable($instructions, $tableName);
552
    }
553
554
    /**
555
     * {@inheritdoc}
556
     */
557
    protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn)
558
    {
559 8
        $instructions = $this->beginAlterByCopyTable($tableName);
560
561 8
        $newColumnName = $newColumn->getName();
562 8 View Code Duplication
        $instructions->addPostStep(function ($state) use ($columnName, $newColumnName) {
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...
563 8
            $newState = $this->calculateNewTableColumns($state['tmpTableName'], $columnName, $newColumnName);
564 8
565 8
            return $newState + $state;
566 8
        });
567 8
568 8
        $instructions->addPostStep(function ($state) use ($columnName, $newColumn) {
569 8
            $sql = preg_replace(
570 8
                sprintf("/%s(?:\/\*.*?\*\/|\([^)]+\)|'[^']*?'|[^,])+([,)])/", $this->quoteColumnName($columnName)),
571
                sprintf('%s %s$1', $this->quoteColumnName($newColumn->getName()), $this->getColumnSqlDefinition($newColumn)),
572 8
                $state['createSQL'],
573 8
                1
574 8
            );
575
            $this->execute($sql);
576
577
            return $state;
578
        });
579 1
580
        return $this->copyAndDropTmpTable($instructions, $tableName);
581 1
    }
582 1
583 1
    /**
584
     * {@inheritdoc}
585 1
     */
586 1
    protected function getDropColumnInstructions($tableName, $columnName)
587
    {
588 1
        $instructions = $this->beginAlterByCopyTable($tableName);
589 1
590 1 View Code Duplication
        $instructions->addPostStep(function ($state) use ($columnName) {
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...
591 1
            $newState = $this->calculateNewTableColumns($state['tmpTableName'], $columnName, false);
592 1
593 1
            return $newState + $state;
594 1
        });
595 1
596 1
        $instructions->addPostStep(function ($state) use ($columnName) {
597 1
            $sql = preg_replace(
598
                sprintf("/%s\s%s.*(,\s(?!')|\)$)/U", preg_quote($this->quoteColumnName($columnName)), preg_quote($state['columnType'])),
599
                "",
600
                $state['createSQL']
601
            );
602
603
            if (substr($sql, -2) === ', ') {
604
                $sql = substr($sql, 0, -2) . ')';
605 1
            }
606
607 1
            $this->execute($sql);
608
609 1
            return $state;
610 1
        });
611 1
612 1
        return $this->copyAndDropTmpTable($instructions, $tableName);
613 1
    }
614 1
615 1
    /**
616 1
     * Get an array of indexes from a particular table.
617 1
     *
618
     * @param string $tableName Table Name
619
     * @return array
620
     */
621
    protected function getIndexes($tableName)
622
    {
623
        $indexes = [];
624
        $rows = $this->fetchAll(sprintf('pragma index_list(%s)', $tableName));
625 5
626
        foreach ($rows as $row) {
627 5
            $indexData = $this->fetchAll(sprintf('pragma index_info(%s)', $row['name']));
628
            if (!isset($indexes[$tableName])) {
629
                $indexes[$tableName] = ['index' => $row['name'], 'columns' => []];
630 5
            }
631
            foreach ($indexData as $indexItem) {
632 5
                $indexes[$tableName]['columns'][] = strtolower($indexItem['name']);
633 5
            }
634 5
        }
635
636 1
        return $indexes;
637
    }
638
639
    /**
640
     * {@inheritdoc}
641
     */
642 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...
643
    {
644
        if (is_string($columns)) {
645 5
            $columns = [$columns]; // str to array
646
        }
647 5
648 5
        $columns = array_map('strtolower', $columns);
649
        $indexes = $this->getIndexes($tableName);
650
651
        foreach ($indexes as $index) {
652
            $a = array_diff($columns, $index['columns']);
653
            if (empty($a)) {
654
                return true;
655
            }
656
        }
657
658
        return false;
659
    }
660
661 5
    /**
662
     * {@inheritdoc}
663 5
     */
664 5 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...
665 5
    {
666 5
        $indexes = $this->getIndexes($tableName);
667 5
668 5
        foreach ($indexes as $index) {
669 5
            if ($indexName === $index['index']) {
670 5
                return true;
671 5
            }
672 5
        }
673 5
674
        return false;
675
    }
676
677
    /**
678
     * {@inheritdoc}
679 4
     */
680
    protected function getAddIndexInstructions(Table $table, Index $index)
681
    {
682 4
        $indexColumnArray = [];
683
        foreach ($index->getColumns() as $column) {
684 4
            $indexColumnArray[] = sprintf('`%s` ASC', $column);
685 4
        }
686
        $indexColumns = implode(',', $indexColumnArray);
687 4
        $sql = sprintf(
688 4
            'CREATE %s ON %s (%s)',
689 4
            $this->getIndexSqlDefinition($table, $index),
690 4
            $this->quoteTableName($table->getName()),
691 4
            $indexColumns
692 4
        );
693
694 4
        return new AlterInstructions([], [$sql]);
695 4
    }
696 4
697 4
    /**
698 4
     * {@inheritdoc}
699
     */
700 4
    protected function getDropIndexByColumnsInstructions($tableName, $columns)
701
    {
702 4
        if (is_string($columns)) {
703 4
            $columns = [$columns]; // str to array
704
        }
705 4
706 4
        $indexes = $this->getIndexes($tableName);
707 4
        $columns = array_map('strtolower', $columns);
708 4
        $instructions = new AlterInstructions();
709 4
710 4
        foreach ($indexes as $index) {
711 4
            $a = array_diff($columns, $index['columns']);
712
            if (empty($a)) {
713 4
                $instructions->addPostStep(sprintf(
714 4
                    'DROP INDEX %s',
715 4
                    $this->quoteColumnName($index['index'])
716
                ));
717
            }
718
        }
719
720 1
        return $instructions;
721
    }
722
723 1
    /**
724
     * {@inheritdoc}
725
     */
726
    protected function getDropIndexByNameInstructions($tableName, $indexName)
727 1
    {
728
        $indexes = $this->getIndexes($tableName);
729 1
        $instructions = new AlterInstructions();
730
731 1
        foreach ($indexes as $index) {
732 1
            if ($indexName === $index['index']) {
733 1
                $instructions->addPostStep(sprintf(
734 1
                    'DROP INDEX %s',
735 1
                    $this->quoteColumnName($indexName)
736 1
                ));
737
            }
738 1
        }
739 1
740 1
        return $instructions;
741 1
    }
742 1
743 1
    /**
744 1
     * {@inheritdoc}
745
     */
746 1
    public function hasPrimaryKey($tableName, $columns, $constraint = null)
747
    {
748 1
        $primaryKey = $this->getPrimaryKey($tableName);
749
750
        if (empty($primaryKey)) {
751
            return false;
752
        }
753
754 1
        if (is_string($columns)) {
755
            $columns = [$columns]; // str to array
756 1
        }
757 1
        $missingColumns = array_diff($columns, [$primaryKey]);
758 1
759 1
        return empty($missingColumns);
760 1
    }
761 1
762 1
    /**
763
     * Get the primary key from a particular table.
764 1
     *
765
     * @param string $tableName Table Name
766 1
     * @return string|null
767 1
     */
768 1
    protected function getPrimaryKey($tableName)
769 1
    {
770 1
        $rows = $this->fetchAll(
771
            "SELECT sql, tbl_name
772 1
              FROM (
773
                    SELECT sql sql, type type, tbl_name tbl_name, name name
774 1
                      FROM sqlite_master
775 1
                     UNION ALL
776 1
                    SELECT sql, type, tbl_name, name
777
                      FROM sqlite_temp_master
778
                   )
779
             WHERE type != 'meta'
780
               AND sql NOTNULL
781
               AND name NOT LIKE 'sqlite_%'
782
             ORDER BY substr(type, 2, 1), name"
783
        );
784
785
        foreach ($rows as $row) {
786
            if ($row['tbl_name'] === $tableName) {
787
                if (strpos($row['sql'], 'PRIMARY KEY') !== false) {
788
                    preg_match_all("/PRIMARY KEY\s*\(`([^`]*)`\)/", $row['sql'], $matches);
789
                    foreach ($matches[1] as $match) {
790
                        if (!empty($match)) {
791
                            return $match;
792
                        }
793
                    }
794
                    preg_match_all("/`([^`]+)`[\w\s]+PRIMARY KEY/", $row['sql'], $matches);
795
                    foreach ($matches[1] as $match) {
796
                        if (!empty($match)) {
797
                            return $match;
798
                        }
799
                    }
800
                }
801
            }
802
        }
803
804
        return null;
805
    }
806
807
    /**
808
     * {@inheritdoc}
809
     */
810 43
    public function hasForeignKey($tableName, $columns, $constraint = null)
811
    {
812
        if (is_string($columns)) {
813 43
            $columns = [$columns]; // str to array
814 42
        }
815
        $foreignKeys = $this->getForeignKeys($tableName);
816 43
817
        return !array_diff($columns, $foreignKeys);
818
    }
819 43
820 1
    /**
821
     * Get an array of foreign keys from a particular table.
822 43
     *
823 38
     * @param string $tableName Table Name
824
     * @return array
825 43
     */
826 42
    protected function getForeignKeys($tableName)
827
    {
828 43
        $foreignKeys = [];
829 2
        $rows = $this->fetchAll(
830
            "SELECT sql, tbl_name
831 43
              FROM (
832 1
                    SELECT sql sql, type type, tbl_name tbl_name, name name
833
                      FROM sqlite_master
834 43
                     UNION ALL
835 1
                    SELECT sql, type, tbl_name, name
836
                      FROM sqlite_temp_master
837 43
                   )
838 42
             WHERE type != 'meta'
839
               AND sql NOTNULL
840 43
               AND name NOT LIKE 'sqlite_%'
841 1
             ORDER BY substr(type, 2, 1), name"
842
        );
843 43
844 1
        foreach ($rows as $row) {
845
            if ($row['tbl_name'] === $tableName) {
846 43
                if (strpos($row['sql'], 'REFERENCES') !== false) {
847 43
                    preg_match_all("/\(`([^`]*)`\) REFERENCES/", $row['sql'], $matches);
848 1
                    foreach ($matches[1] as $match) {
849
                        $foreignKeys[] = $match;
850 43
                    }
851 42
                }
852
            }
853 5
        }
854
855 5
        return $foreignKeys;
856 4
    }
857
858
    /**
859
     * @param Table $table The Table
860 1
     * @param string $column Column Name
861 1
     * @return AlterInstructions
862
     */
863
    protected function getAddPrimaryKeyInstructions(Table $table, $column)
864 1
    {
865
        $instructions = $this->beginAlterByCopyTable($table->getName());
866
867 1
        $tableName = $table->getName();
868 View Code Duplication
        $instructions->addPostStep(function ($state) use ($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...
869 1
            $sql = preg_replace("/(`$column`)\s+\w+\s+((NOT )?NULL)/", '$1 INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT', $state['createSQL'], 1);
870 1
            $this->execute($sql);
871 1
872
            return $state;
873
        });
874
875 View Code Duplication
        $instructions->addPostStep(function ($state) {
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...
876
            $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($state['tmpTableName'])));
877
            $names = array_map([$this, 'quoteColumnName'], array_column($columns, 'name'));
878
            $selectColumns = $writeColumns = $names;
879
880 3
            return compact('selectColumns', 'writeColumns') + $state;
881
        });
882 3
883 1
        return $this->copyAndDropTmpTable($instructions, $tableName);
884
    }
885 2
886 2
    /**
887 2
     * @param Table $table Table
888 2
     * @param string $column Column Name
889 1
     * @return AlterInstructions
890 1
     */
891 2
    protected function getDropPrimaryKeyInstructions($table, $column)
892
    {
893
        $instructions = $this->beginAlterByCopyTable($table->getName());
894 2
895 2
        $instructions->addPostStep(function ($state) use ($column) {
896 1
            $newState = $this->calculateNewTableColumns($state['tmpTableName'], $column, $column);
897 1
898
            return $newState + $state;
899
        });
900 1
901 2 View Code Duplication
        $instructions->addPostStep(function ($state) {
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
            $search = "/(,?\s*PRIMARY KEY\s*\([^\)]*\)|\s+PRIMARY KEY(\s+AUTOINCREMENT)?)/";
903
            $sql = preg_replace($search, '', $state['createSQL'], 1);
904
905
            if ($sql) {
906
                $this->execute($sql);
907
            }
908
909
            return $state;
910 2
        });
911
912
        return $this->copyAndDropTmpTable($instructions, $table->getName());
913
    }
914
915
    /**
916 2
     * {@inheritdoc}
917 1
     */
918
    protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey)
919
    {
920 1
        $instructions = $this->beginAlterByCopyTable($table->getName());
921 1
922 2
        $tableName = $table->getName();
923 1
        $instructions->addPostStep(function ($state) use ($foreignKey) {
924 1
            $this->execute('pragma foreign_keys = ON');
925 2
            $sql = substr($state['createSQL'], 0, -1) . ',' . $this->getForeignKeySqlDefinition($foreignKey) . ')';
926 2
            $this->execute($sql);
927
928
            return $state;
929
        });
930
931 View Code Duplication
        $instructions->addPostStep(function ($state) {
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...
932
            $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($state['tmpTableName'])));
933 2
            $names = array_map([$this, 'quoteColumnName'], array_column($columns, 'name'));
934
            $selectColumns = $writeColumns = $names;
935
936 1
            return compact('selectColumns', 'writeColumns') + $state;
937 1
        });
938
939 1
        return $this->copyAndDropTmpTable($instructions, $tableName);
940
    }
941
942
    /**
943
     * {@inheritdoc}
944
     */
945
    protected function getDropForeignKeyInstructions($tableName, $constraint)
946 48
    {
947
        throw new \BadMethodCallException('SQLite does not have named foreign keys');
948 48
    }
949 48
950
    /**
951
     * {@inheritdoc}
952
     */
953
    protected function getDropForeignKeyByColumnsInstructions($tableName, $columns)
954 2
    {
955
        $instructions = $this->beginAlterByCopyTable($tableName);
956 2
957
        $instructions->addPostStep(function ($state) use ($columns) {
958
            $newState = $this->calculateNewTableColumns($state['tmpTableName'], $columns[0], $columns[0]);
959
960
            $selectColumns = $newState['selectColumns'];
961
            $columns = array_map([$this, 'quoteColumnName'], $columns);
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $columns, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
962 48
            $diff = array_diff($columns, $selectColumns);
963
964 48
            if (!empty($diff)) {
965 47
                throw new \InvalidArgumentException(sprintf(
966 47
                    'The specified columns don\'t exist: ' . implode(', ', $diff)
967 48
                ));
968
            }
969
970
            return $newState + $state;
971
        });
972
973
        $instructions->addPostStep(function ($state) use ($columns) {
974
            $sql = '';
975 42
976
            foreach ($columns as $columnName) {
977 42
                $search = sprintf(
978 8
                    "/,[^,]*\(%s(?:,`?(.*)`?)?\) REFERENCES[^,]*\([^\)]*\)[^,)]*/",
979 42
                    $this->quoteColumnName($columnName)
980 42
                );
981 42
                $sql = preg_replace($search, '', $state['createSQL'], 1);
982 42
            }
983
984
            if ($sql) {
985
                $this->execute($sql);
986
            }
987
988
            return $state;
989
        });
990
991 42
        return $this->copyAndDropTmpTable($instructions, $tableName);
992
    }
993 42
994 42
    /**
995 42
     * {@inheritdoc}
996 42
     */
997
    public function getSqlType($type, $limit = null)
998
    {
999 42
        switch ($type) {
1000 42
            case static::PHINX_TYPE_TEXT:
1001 42
            case static::PHINX_TYPE_INTEGER:
1002 42
            case static::PHINX_TYPE_FLOAT:
1003 42
            case static::PHINX_TYPE_DECIMAL:
1004 4
            case static::PHINX_TYPE_DATETIME:
1005 4
            case static::PHINX_TYPE_TIME:
1006
            case static::PHINX_TYPE_DATE:
1007 42
            case static::PHINX_TYPE_BLOB:
1008
            case static::PHINX_TYPE_BOOLEAN:
1009 42
            case static::PHINX_TYPE_ENUM:
1010 42
                return ['name' => $type];
1011 42
            case static::PHINX_TYPE_STRING:
1012
                return ['name' => 'varchar', 'limit' => 255];
1013 42
            case static::PHINX_TYPE_CHAR:
1014
                return ['name' => 'char', 'limit' => 255];
1015
            case static::PHINX_TYPE_BIG_INTEGER:
1016
                return ['name' => 'bigint'];
1017 42
            case static::PHINX_TYPE_TIMESTAMP:
1018
                return ['name' => 'datetime'];
1019 42
            case static::PHINX_TYPE_BINARY:
1020
                return ['name' => 'blob'];
1021
            case static::PHINX_TYPE_UUID:
1022
                return ['name' => 'char', 'limit' => 36];
1023
            case static::PHINX_TYPE_JSON:
1024
            case static::PHINX_TYPE_JSONB:
1025
                return ['name' => 'text'];
1026
            // Geospatial database types
1027
            // No specific data types exist in SQLite, instead all geospatial
1028 42
            // functionality is handled in the client. See also: SpatiaLite.
1029
            case static::PHINX_TYPE_GEOMETRY:
1030 42
            case static::PHINX_TYPE_POLYGON:
1031 2
                return ['name' => 'text'];
1032
            case static::PHINX_TYPE_LINESTRING:
1033 42
                return ['name' => 'varchar', 'limit' => 255];
1034
            case static::PHINX_TYPE_POINT:
1035
                return ['name' => 'float'];
1036
            default:
1037
                throw new UnsupportedColumnTypeException('Column type ' . $type . ' is not supported by SQLite.');
1038
        }
1039
    }
1040
1041
    /**
1042 8
     * Returns Phinx type by SQL type
1043
     *
1044 8
     * @param string $sqlTypeDef SQL type
1045 2
     * @throws UnsupportedColumnTypeException
1046 2
     * @return array Phinx type
1047 6
     */
1048
    public function getPhinxType($sqlTypeDef)
1049 8
    {
1050 3
        if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*$/', $sqlTypeDef, $matches)) {
1051 3
            throw new UnsupportedColumnTypeException('Column type ' . $sqlTypeDef . ' is not supported by SQLite.');
1052 6
        } else {
1053 6
            $limit = null;
1054 6
            $precision = null;
1055 6
            $type = $matches[1];
1056 6
            if (count($matches) > 2) {
1057
                $limit = $matches[3] ?: null;
1058 8
            }
1059 8
            if (count($matches) > 4) {
1060
                $precision = $matches[5];
1061
            }
1062
            switch ($matches[1]) {
1063
                case 'varchar':
1064
                    $type = static::PHINX_TYPE_STRING;
1065 47
                    if ($limit === 255) {
1066
                        $limit = null;
1067 47
                    }
1068
                    break;
1069 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...
1070
                    $type = static::PHINX_TYPE_CHAR;
1071
                    if ($limit === 255) {
1072
                        $limit = null;
1073
                    }
1074
                    if ($limit === 36) {
1075
                        $type = static::PHINX_TYPE_UUID;
1076 5
                    }
1077
                    break;
1078 5
                case 'int':
1079 5
                    $type = static::PHINX_TYPE_INTEGER;
1080
                    if ($limit === 11) {
1081
                        $limit = null;
1082 5
                    }
1083 5
                    break;
1084 5
                case 'bigint':
1085 5
                    if ($limit === 11) {
1086 5
                        $limit = null;
1087 5
                    }
1088 5
                    $type = static::PHINX_TYPE_BIG_INTEGER;
1089 5
                    break;
1090 5
                case 'blob':
1091 5
                    $type = static::PHINX_TYPE_BINARY;
1092 5
                    break;
1093 1
            }
1094 1 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...
1095 5
                if ($matches[3] === 1) {
1096 1
                    $type = static::PHINX_TYPE_BOOLEAN;
1097 1
                    $limit = null;
1098
                }
1099 5
            }
1100
1101
            try {
1102
                // Call this to check if parsed type is supported.
1103
                $this->getSqlType($type);
1104
            } catch (UnsupportedColumnTypeException $e) {
1105
                $type = Literal::from($type);
1106
            }
1107
1108
            return [
1109
                'name' => $type,
1110
                'limit' => $limit,
1111
                'precision' => $precision
1112
            ];
1113
        }
1114
    }
1115
1116
    /**
1117
     * {@inheritdoc}
1118
     */
1119
    public function createDatabase($name, $options = [])
1120
    {
1121
        touch($name . $this->suffix);
1122
    }
1123
1124
    /**
1125
     * {@inheritdoc}
1126
     */
1127
    public function hasDatabase($name)
1128
    {
1129
        return is_file($name . $this->suffix);
1130
    }
1131
1132
    /**
1133
     * {@inheritdoc}
1134
     */
1135
    public function dropDatabase($name)
1136
    {
1137
        if (file_exists($name . '.sqlite3')) {
1138
            unlink($name . '.sqlite3');
1139
        }
1140
    }
1141
1142
    /**
1143
     * Gets the SQLite Column Definition for a Column object.
1144
     *
1145
     * @param \Phinx\Db\Table\Column $column Column
1146
     * @return string
1147
     */
1148
    protected function getColumnSqlDefinition(Column $column)
1149
    {
1150
        $isLiteralType = $column->getType() instanceof Literal;
1151
        if ($isLiteralType) {
1152
            $def = (string)$column->getType();
1153
        } else {
1154
            $sqlType = $this->getSqlType($column->getType());
1155
            $def = strtoupper($sqlType['name']);
1156
1157
            $limitable = in_array(strtoupper($sqlType['name']), $this->definitionsWithLimits);
1158
            if (($column->getLimit() || isset($sqlType['limit'])) && $limitable) {
1159
                $def .= '(' . ($column->getLimit() ?: $sqlType['limit']) . ')';
1160
            }
1161
        }
1162 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...
1163
            $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
1164
        }
1165 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...
1166
            $def .= " CHECK({$column->getName()} IN ('" . implode("', '", $values) . "'))";
1167
        }
1168
1169
        $default = $column->getDefault();
1170
1171
        $def .= (!$column->isIdentity() && ($column->isNull() || is_null($default))) ? ' NULL' : ' NOT NULL';
1172
        $def .= $this->getDefaultValueDefinition($default, $column->getType());
0 ignored issues
show
Bug introduced by
It seems like $column->getType() targeting Phinx\Db\Table\Column::getType() can also be of type object<Phinx\Util\Literal>; however, Phinx\Db\Adapter\PdoAdap...efaultValueDefinition() does only seem to accept string|null, 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...
1173
        $def .= $column->isIdentity() ? ' PRIMARY KEY AUTOINCREMENT' : '';
1174
1175
        if ($column->getUpdate()) {
1176
            $def .= ' ON UPDATE ' . $column->getUpdate();
1177
        }
1178
1179
        $def .= $this->getCommentDefinition($column);
1180
1181
        return $def;
1182
    }
1183
1184
    /**
1185
     * Gets the comment Definition for a Column object.
1186
     *
1187
     * @param \Phinx\Db\Table\Column $column Column
1188
     * @return string
1189
     */
1190
    protected function getCommentDefinition(Column $column)
1191
    {
1192
        if ($column->getComment()) {
1193
            return ' /* ' . $column->getComment() . ' */ ';
1194
        }
1195
1196
        return '';
1197
    }
1198
1199
    /**
1200
     * Gets the SQLite Index Definition for an Index object.
1201
     *
1202
     * @param \Phinx\Db\Table\Table $table Table
1203
     * @param \Phinx\Db\Table\Index $index Index
1204
     * @return string
1205
     */
1206
    protected function getIndexSqlDefinition(Table $table, Index $index)
1207
    {
1208
        if ($index->getType() === Index::UNIQUE) {
1209
            $def = 'UNIQUE INDEX';
1210
        } else {
1211
            $def = 'INDEX';
1212
        }
1213
        if (is_string($index->getName())) {
1214
            $indexName = $index->getName();
1215
        } else {
1216
            $indexName = $table->getName() . '_';
1217
            foreach ($index->getColumns() as $column) {
1218
                $indexName .= $column . '_';
1219
            }
1220
            $indexName .= 'index';
1221
        }
1222
        $def .= ' `' . $indexName . '`';
1223
1224
        return $def;
1225
    }
1226
1227
    /**
1228
     * {@inheritdoc}
1229
     */
1230
    public function getColumnTypes()
1231
    {
1232
        return array_merge(parent::getColumnTypes(), ['enum', 'json', 'jsonb']);
1233
    }
1234
1235
    /**
1236
     * Gets the SQLite Foreign Key Definition for an ForeignKey object.
1237
     *
1238
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
1239
     * @return string
1240
     */
1241 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...
1242
    {
1243
        $def = '';
1244
        if ($foreignKey->getConstraint()) {
1245
            $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...
1246
        } else {
1247
            $columnNames = [];
1248
            foreach ($foreignKey->getColumns() as $column) {
1249
                $columnNames[] = $this->quoteColumnName($column);
1250
            }
1251
            $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
1252
            $refColumnNames = [];
1253
            foreach ($foreignKey->getReferencedColumns() as $column) {
1254
                $refColumnNames[] = $this->quoteColumnName($column);
1255
            }
1256
            $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
1257
            if ($foreignKey->getOnDelete()) {
1258
                $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
1259
            }
1260
            if ($foreignKey->getOnUpdate()) {
1261
                $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
1262
            }
1263
        }
1264
1265
        return $def;
1266
    }
1267
1268
    /**
1269
     * {@inheritDoc}
1270
     *
1271
     */
1272
    public function getDecoratedConnection()
1273
    {
1274
        $options = $this->getOptions();
1275
        $options['quoteIdentifiers'] = true;
1276
        $database = ':memory:';
0 ignored issues
show
Unused Code introduced by
$database is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1277
1278
        if (!empty($options['name'])) {
1279
            $options['database'] = $options['name'];
1280
1281
            if (file_exists($options['name'] . $this->suffix)) {
1282
                $options['database'] = $options['name'] . $this->suffix;
1283
            }
1284
        }
1285
1286
        $driver = new SqliteDriver($options);
1287
        if (method_exists($driver, 'setConnection')) {
1288
            $driver->setConnection($this->connection);
0 ignored issues
show
Bug introduced by
It seems like $this->connection can be null; however, setConnection() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1289
        } else {
1290
            $driver->connection($this->connection);
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Database\Driver::connection() has been deprecated with message: 3.6.0 Use getConnection()/setConnection() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1291
        }
1292
1293
        return new Connection(['driver' => $driver] + $options);
1294
    }
1295
}
1296