Completed
Pull Request — master (#1292)
by
unknown
03:00
created

MysqlAdapter   D

Complexity

Total Complexity 200

Size/Duplication

Total Lines 1091
Duplicated Lines 17.05 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 97.64%

Importance

Changes 0
Metric Value
wmc 200
lcom 1
cbo 6
dl 186
loc 1091
ccs 620
cts 635
cp 0.9764
rs 4.4131
c 0
b 0
f 0

40 Methods

Rating   Name   Duplication   Size   Complexity  
A hasIndex() 0 17 4
A hasIndexByName() 12 12 3
B hasForeignKey() 20 22 6
A getIndexes() 12 13 3
C connect() 5 54 10
A disconnect() 0 4 1
A hasTransactions() 0 4 1
A beginTransaction() 0 4 1
A commitTransaction() 0 4 1
A rollbackTransaction() 0 4 1
A quoteTableName() 0 4 1
A quoteColumnName() 0 4 1
A hasTable() 0 19 3
A queryTableExists() 0 10 1
F createTable() 12 87 16
A getRenameTableInstructions() 10 10 1
A getDropTableInstructions() 0 6 1
B getColumns() 0 28 4
A hasColumn() 11 11 3
A getAddColumnInstructions() 0 14 2
B getRenameColumnInstructions() 0 28 5
A getChangeColumnInstructions() 0 13 2
A getDropColumnInstructions() 0 6 1
A getAddIndexInstructions() 0 9 1
B getDropIndexByColumnsInstructions() 8 23 4
A getDropIndexByNameInstructions() 8 19 3
B getForeignKeys() 0 31 4
A getAddForeignKeyInstructions() 9 9 1
A getDropForeignKeyInstructions() 0 9 1
B getDropForeignKeyByColumnsInstructions() 0 32 4
D getSqlType() 34 108 45
F getPhinxType() 13 110 29
A createDatabase() 5 10 3
A hasDatabase() 0 17 3
A dropDatabase() 0 4 1
F getColumnSqlDefinition() 3 33 15
B getIndexSqlDefinition() 0 26 5
B getForeignKeySqlDefinition() 24 25 6
A describeTable() 0 16 1
A getColumnTypes() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

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

Common duplication problems, and corresponding solutions are:

Complex Class

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

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

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

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

1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 * Copyright (c) 2015 Rob Morgan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated * documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 *
26
 * @package    Phinx
27
 * @subpackage Phinx\Db\Adapter
28
 */
29
namespace Phinx\Db\Adapter;
30
31
use Phinx\Db\Table\Column;
32
use Phinx\Db\Table\ForeignKey;
33
use Phinx\Db\Table\Index;
34
use Phinx\Db\Table\Table;
35
use Phinx\Db\Util\AlterInstructions;
36
use Phinx\Util\Literal;
37
38
/**
39
 * Phinx MySQL Adapter.
40
 *
41
 * @author Rob Morgan <[email protected]>
42
 */
43
class MysqlAdapter extends PdoAdapter implements AdapterInterface
44
{
45
46
    protected $signedColumnTypes = ['integer' => true, 'biginteger' => true, 'float' => true, 'decimal' => true, 'boolean' => true];
47
48
    const TEXT_TINY = 255;
49
    const TEXT_SMALL = 255; /* deprecated, alias of TEXT_TINY */
50
    const TEXT_REGULAR = 65535;
51
    const TEXT_MEDIUM = 16777215;
52
    const TEXT_LONG = 4294967295;
53
54
    // According to https://dev.mysql.com/doc/refman/5.0/en/blob.html BLOB sizes are the same as TEXT
55
    const BLOB_TINY = 255;
56
    const BLOB_SMALL = 255; /* deprecated, alias of BLOB_TINY */
57
    const BLOB_REGULAR = 65535;
58
    const BLOB_MEDIUM = 16777215;
59
    const BLOB_LONG = 4294967295;
60
61
    const INT_TINY = 255;
62
    const INT_SMALL = 65535;
63
    const INT_MEDIUM = 16777215;
64
    const INT_REGULAR = 4294967295;
65
    const INT_BIG = 18446744073709551615;
66
67
    const BIT = 64;
68
69
    const TYPE_YEAR = 'year';
70 80
71
    /**
72 80
     * {@inheritdoc}
73 80
     */
74
    public function connect()
75
    {
76
        if ($this->connection === null) {
77
            if (!class_exists('PDO') || !in_array('mysql', \PDO::getAvailableDrivers(), true)) {
78
                // @codeCoverageIgnoreStart
79 80
                throw new \RuntimeException('You need to enable the PDO_Mysql extension for Phinx to run properly.');
80 80
                // @codeCoverageIgnoreEnd
81
            }
82 80
83
            $db = null;
84 80
            $options = $this->getOptions();
85
86
            $dsn = 'mysql:';
87
88
            if (!empty($options['unix_socket'])) {
89 80
                // use socket connection
90 80
                $dsn .= 'unix_socket=' . $options['unix_socket'];
91 80
            } else {
92 80
                // use network connection
93
                $dsn .= 'host=' . $options['host'];
94
                if (!empty($options['port'])) {
95 80
                    $dsn .= ';port=' . $options['port'];
96
                }
97
            }
98 80
99
            $dsn .= ';dbname=' . $options['name'];
100
101
            // charset support
102 80
            if (!empty($options['charset'])) {
103
                $dsn .= ';charset=' . $options['charset'];
104
            }
105
106 80
            $driverOptions = [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION];
107 80
108
            // support arbitrary \PDO::MYSQL_ATTR_* driver options and pass them to PDO
109
            // http://php.net/manual/en/ref.pdo-mysql.php#pdo-mysql.constants
110 80 View Code Duplication
            foreach ($options as $key => $option) {
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...
111
                if (strpos($key, 'mysql_attr_') === 0) {
112
                    $driverOptions[constant('\PDO::' . strtoupper($key))] = $option;
113 80
                }
114 80
            }
115 1
116 1
            try {
117 1
                $db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions);
118 1
            } catch (\PDOException $exception) {
119
                throw new \InvalidArgumentException(sprintf(
120
                    'There was a problem connecting to the database: %s',
121 80
                    $exception->getMessage()
122 80
                ));
123 80
            }
124
125
            $this->setConnection($db);
126
        }
127
    }
128 81
129
    /**
130 81
     * {@inheritdoc}
131 81
     */
132
    public function disconnect()
133
    {
134
        $this->connection = null;
135
    }
136 6
137
    /**
138 6
     * {@inheritdoc}
139
     */
140
    public function hasTransactions()
141
    {
142
        return true;
143
    }
144 6
145
    /**
146 6
     * {@inheritdoc}
147 6
     */
148
    public function beginTransaction()
149
    {
150
        $this->execute('START TRANSACTION');
151
    }
152 6
153
    /**
154 6
     * {@inheritdoc}
155 6
     */
156
    public function commitTransaction()
157
    {
158
        $this->execute('COMMIT');
159
    }
160 1
161
    /**
162 1
     * {@inheritdoc}
163 1
     */
164
    public function rollbackTransaction()
165
    {
166
        $this->execute('ROLLBACK');
167
    }
168 112
169
    /**
170 112
     * {@inheritdoc}
171
     */
172
    public function quoteTableName($tableName)
173
    {
174
        return str_replace('.', '`.`', $this->quoteColumnName($tableName));
175
    }
176 112
177
    /**
178 112
     * {@inheritdoc}
179
     */
180
    public function quoteColumnName($columnName)
181
    {
182
        return '`' . str_replace('`', '``', $columnName) . '`';
183
    }
184 82
185
    /**
186 82
     * {@inheritdoc}
187
     */
188 82
    public function hasTable($tableName)
189
    {
190
        if (strpos($tableName, '.') !== false) {
191 82
            list($schema, $table) = explode('.', $tableName);
192 82
193
            $exists = $this->queryTableExists($schema, $table);
194 82
195
            // Only break here on success, because it is possible for table names to contain a dot.
196 82
            if (!empty($exists)) {
197
                return true;
198
            }
199
        }
200
201
        $options = $this->getOptions();
202 82
203
        $exists = $this->queryTableExists($options['name'], $tableName);
204
205
        return !empty($exists);
206 82
    }
207
208 82
    /**
209 82
     * @param string $schema The table schema
210
     * @param string $tableName The table name
211
     *
212 82
     * @return array|mixed
213 82
     */
214 68
    private function queryTableExists($schema, $tableName)
215 68
    {
216 68
        return $this->fetchRow(sprintf(
217 68
            "SELECT TABLE_NAME
218 68
            FROM INFORMATION_SCHEMA.TABLES
219
            WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'",
220 68
            $schema,
221 68
            $tableName
222 82
        ));
223
    }
224 2
225 2
    /**
226 2
     * {@inheritdoc}
227 2
     */
228
    public function createTable(Table $table, array $columns = [], array $indexes = [])
229 2
    {
230 2
        // This method is based on the MySQL docs here: http://dev.mysql.com/doc/refman/5.1/en/create-index.html
231 2
        $defaultOptions = [
232
            'engine' => 'InnoDB',
233
            'collation' => 'utf8_general_ci'
234
        ];
235
236 82
        $options = array_merge(
237 82
            $defaultOptions,
238 82
            array_intersect_key($this->getOptions(), $defaultOptions),
239 82
            $table->getOptions()
240
        );
241
242 82
        // Add the default primary key
243 82
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
244 82
            $column = new Column();
245 82
            $column->setName('id')
246 82
                   ->setType('integer')
247
                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
248
                   ->setIdentity(true);
249 82
250 2
            array_unshift($columns, $column);
251 2
            $options['primary_key'] = 'id';
252
        } elseif (isset($options['id']) && is_string($options['id'])) {
253 82
            // Handle id => "field_name" to support AUTO_INCREMENT
254 82
            $column = new Column();
255 82
            $column->setName($options['id'])
256 82
                   ->setType('integer')
257 82
                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
258
                   ->setIdentity(true);
259
260 82
            array_unshift($columns, $column);
261 82
            $options['primary_key'] = $options['id'];
262 82
        }
263 82
264 81
        // TODO - process table options like collation etc
265 82
266
        // process table engine (default to InnoDB)
267
        $optionsStr = 'ENGINE = InnoDB';
268 2
        if (isset($options['engine'])) {
269 2
            $optionsStr = sprintf('ENGINE = %s', $options['engine']);
270 2
        }
271 2
272 2
        // process table collation
273 2
        if (isset($options['collation'])) {
274 2
            $charset = explode('_', $options['collation']);
275 2
            $optionsStr .= sprintf(' CHARACTER SET %s', $charset[0]);
276 2
            $optionsStr .= sprintf(' COLLATE %s', $options['collation']);
277 2
        }
278 82
279 82
        // set the table comment
280 1
        if (isset($options['comment'])) {
281
            $optionsStr .= sprintf(" COMMENT=%s ", $this->getConnection()->quote($options['comment']));
282
        }
283
284 82
        $sql = 'CREATE TABLE ';
285 82
        $sql .= $this->quoteTableName($table->getName()) . ' (';
286 10
        foreach ($columns as $column) {
287 82
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
288
        }
289
290 82
        // set the primary key(s)
291 82 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...
292 2
            $sql = rtrim($sql);
293 82
            $sql .= ' PRIMARY KEY (';
294
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
295 82
                $sql .= $this->quoteColumnName($options['primary_key']);
296 82
            } 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...
297
                $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
298
            }
299 82
            $sql .= ')';
300 82
        } else {
301
            $sql = substr(rtrim($sql), 0, -1); // no primary keys
302
        }
303
304
        // set the indexes
305 5
        foreach ($indexes as $index) {
306
            $sql .= ', ' . $this->getIndexSqlDefinition($index);
307 5
        }
308 5
309
        $sql .= ') ' . $optionsStr;
310
        $sql = rtrim($sql) . ';';
311
312
        // execute the sql
313 5
        $this->execute($sql);
314
    }
315 5
316 5
    /**
317
     * {@inheritdoc}
318
     */
319 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...
320
    {
321 1
        $sql = sprintf(
322
            'RENAME TABLE %s TO %s',
323 1
            $this->quoteTableName($tableName),
324 1
            $this->quoteTableName($newTableName)
325 1
        );
326 1
327
        return new AlterInstructions([], [$sql]);
328 1
    }
329 1
330
    /**
331
     * {@inheritdoc}
332
     */
333
    protected function getDropTableInstructions($tableName)
334 12
    {
335
        $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName));
336 12
337 12
        return new AlterInstructions([], [$sql]);
338 12
    }
339 12
340
    /**
341 12
     * {@inheritdoc}
342 12
     */
343 12
    public function truncateTable($tableName)
344 12
    {
345 12
        $sql = sprintf(
346 12
            'TRUNCATE TABLE %s',
347
            $this->quoteTableName($tableName)
348 12
        );
349 12
350 12
        $this->execute($sql);
351
    }
352 12
353 3
    /**
354 3
     * {@inheritdoc}
355
     */
356 12
    public function getColumns($tableName)
357 12
    {
358
        $columns = [];
359 12
        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
360
        foreach ($rows as $columnInfo) {
361
            $phinxType = $this->getPhinxType($columnInfo['Type']);
362
363
            $column = new Column();
364
            $column->setName($columnInfo['Field'])
365 79
                   ->setNull($columnInfo['Null'] !== 'NO')
366
                   ->setDefault($columnInfo['Default'])
367 79
                   ->setType($phinxType['name'])
368 79
                   ->setSigned(strpos($columnInfo['Type'], 'unsigned') === false)
369 79
                   ->setLimit($phinxType['limit']);
370 77
371
            if ($columnInfo['Extra'] === 'auto_increment') {
372 77
                $column->setIdentity(true);
373
            }
374 21
375
            if (isset($phinxType['values'])) {
376
                $column->setValues($phinxType['values']);
377
            }
378
379
            $columns[] = $column;
380
        }
381
382
        return $columns;
383 95
    }
384
385 95
    /**
386 10
     * {@inheritdoc}
387 95
     */
388 79 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...
389 79
    {
390 95
        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
391
        foreach ($rows as $column) {
392
            if (strcasecmp($column['Field'], $columnName) === 0) {
393
                return true;
394
            }
395
        }
396 18
397
        return false;
398 18
    }
399 18
400 18
    /**
401 18
     * {@inheritdoc}
402 18
     */
403 18
    protected function getAddColumnInstructions(Table $table, Column $column)
404
    {
405 18
        $alter = sprintf(
406 2
            'ADD %s %s',
407 2
            $this->quoteColumnName($column->getName()),
408
            $this->getColumnSqlDefinition($column)
409 18
        );
410 18
411
        if ($column->getAfter()) {
412
            $alter .= ' AFTER ' . $this->quoteColumnName($column->getAfter());
413
        }
414
415 7
        return new AlterInstructions([$alter]);
416
    }
417 7
418 7
    /**
419 7
     * {@inheritdoc}
420 5
     */
421 5
    protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName)
422 5
    {
423 1
        $rows = $this->fetchAll(sprintf('DESCRIBE %s', $this->quoteTableName($tableName)));
424 1
        foreach ($rows as $row) {
425 5
            if (strcasecmp($row['Field'], $columnName) === 0) {
426
                $null = ($row['Null'] == 'NO') ? 'NOT NULL' : 'NULL';
427 5
                $extra = ' ' . strtoupper($row['Extra']);
428 5
                if (!is_null($row['Default'])) {
429 5
                    $extra .= $this->getDefaultValueDefinition($row['Default']);
430 5
                }
431 5
                $definition = $row['Type'] . ' ' . $null . $extra;
432 5
433
                $alter = sprintf(
434 5
                    'CHANGE COLUMN %s %s %s',
435 5
                    $this->quoteColumnName($columnName),
436 5
                    $this->quoteColumnName($newColumnName),
437
                    $definition
438 6
                );
439
440 2
                return new AlterInstructions([$alter]);
441
            }
442
        }
443 2
444
        throw new \InvalidArgumentException(sprintf(
445
            'The specified column doesn\'t exist: ' .
446
            $columnName
447
        ));
448
    }
449 5
450
    /**
451 5
     * {@inheritdoc}
452 5
     */
453 5
    protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn)
454 5
    {
455 5
        $after = $newColumn->getAfter() ? ' AFTER ' . $this->quoteColumnName($newColumn->getAfter()) : '';
456 5
        $alter = sprintf(
457 5
            'CHANGE %s %s %s%s',
458 5
            $this->quoteColumnName($columnName),
459
            $this->quoteColumnName($newColumn->getName()),
460 5
            $this->getColumnSqlDefinition($newColumn),
461 5
            $after
462 5
        );
463
464
        return new AlterInstructions([$alter]);
465
    }
466
467 5
    /**
468
     * {@inheritdoc}
469 5
     */
470 5
    protected function getDropColumnInstructions($tableName, $columnName)
471 5
    {
472 5
        $alter = sprintf('DROP COLUMN %s', $this->quoteColumnName($columnName));
473 5
474 5
        return new AlterInstructions([$alter]);
475 5
    }
476 5
477
    /**
478
     * Get an array of indexes from a particular table.
479
     *
480
     * @param string $tableName Table Name
481
     * @return array
482
     */
483 View Code Duplication
    protected function getIndexes($tableName)
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...
484 19
    {
485
        $indexes = [];
486 19
        $rows = $this->fetchAll(sprintf('SHOW INDEXES FROM %s', $this->quoteTableName($tableName)));
487 19
        foreach ($rows as $row) {
488 19
            if (!isset($indexes[$row['Key_name']])) {
489 18
                $indexes[$row['Key_name']] = ['columns' => []];
490 18
            }
491 18
            $indexes[$row['Key_name']]['columns'][] = strtolower($row['Column_name']);
492 18
        }
493 19
494 19
        return $indexes;
495
    }
496
497
    /**
498
     * {@inheritdoc}
499
     */
500 14
    public function hasIndex($tableName, $columns)
501
    {
502 14
        if (is_string($columns)) {
503 6
            $columns = [$columns]; // str to array
504 6
        }
505
506 14
        $columns = array_map('strtolower', $columns);
507 14
        $indexes = $this->getIndexes($tableName);
508
509 14
        foreach ($indexes as $index) {
510 14
            if ($columns == $index['columns']) {
511 12
                return true;
512
            }
513 13
        }
514
515 11
        return false;
516
    }
517
518
    /**
519
     * {@inheritdoc}
520
     */
521 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...
522
    {
523 1
        $indexes = $this->getIndexes($tableName);
524
525 1
        foreach ($indexes as $name => $index) {
526 1
            if ($name === $indexName) {
527 1
                return true;
528
            }
529 1
        }
530
531
        return false;
532
    }
533
534
    /**
535
     * {@inheritdoc}
536
     */
537 4
    protected function getAddIndexInstructions(Table $table, Index $index)
538
    {
539 4
        $alter = sprintf(
540 4
            'ADD %s',
541 4
            $this->getIndexSqlDefinition($index)
542 4
        );
543 4
544 4
        return new AlterInstructions([$alter]);
545 4
    }
546 4
547
    /**
548
     * {@inheritdoc}
549
     */
550
    protected function getDropIndexByColumnsInstructions($tableName, $columns)
551 3
    {
552
        if (is_string($columns)) {
553 3
            $columns = [$columns]; // str to array
554 2
        }
555 2
556
        $indexes = $this->getIndexes($tableName);
557 3
        $columns = array_map('strtolower', $columns);
558 3
559 View Code Duplication
        foreach ($indexes as $indexName => $index) {
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...
560 3
            if ($columns == $index['columns']) {
561 3
                return new AlterInstructions([sprintf(
562 3
                    'DROP INDEX %s',
563 3
                    $this->quoteColumnName($indexName)
564 3
                )]);
565 3
            }
566 3
        }
567 3
568 3
        throw new \InvalidArgumentException(sprintf(
569 3
            "The specified index on columns '%s' does not exist",
570
            implode(',', $columns)
571 3
        ));
572 1
    }
573
574
    /**
575
     * {@inheritdoc}
576
     */
577 2
    protected function getDropIndexByNameInstructions($tableName, $indexName)
578
    {
579 2
580
        $indexes = $this->getIndexes($tableName);
581 2
582 View Code Duplication
        foreach ($indexes as $name => $index) {
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...
583 2
            if ($name === $indexName) {
584 2
                return new AlterInstructions([sprintf(
585 2
                    'DROP INDEX %s',
586 2
                    $this->quoteColumnName($indexName)
587 2
                )]);
588 2
            }
589 2
        }
590 2
591 2
        throw new \InvalidArgumentException(sprintf(
592
            "The specified index name '%s' does not exist",
593 2
            $indexName
594
        ));
595
    }
596
597
    /**
598
     * {@inheritdoc}
599 21
     */
600 View Code Duplication
    public function hasForeignKey($tableName, $columns, $constraint = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
601 21
    {
602 5
        if (is_string($columns)) {
603 5
            $columns = [$columns]; // str to array
604 21
        }
605 21
        $foreignKeys = $this->getForeignKeys($tableName);
606 6
        if ($constraint) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $constraint of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
607 4
            if (isset($foreignKeys[$constraint])) {
608
                return !empty($foreignKeys[$constraint]);
609 4
            }
610
611 15
            return false;
612 12
        } else {
613 10
            foreach ($foreignKeys as $key) {
614
                if ($columns == $key['columns']) {
615 11
                    return true;
616 11
                }
617
            }
618
619
            return false;
620
        }
621
    }
622
623
    /**
624
     * Get an array of foreign keys from a particular table.
625
     *
626 22
     * @param string $tableName Table Name
627
     * @return array
628 22
     */
629 22
    protected function getForeignKeys($tableName)
630
    {
631
        if (strpos($tableName, '.') !== false) {
632
            list($schema, $tableName) = explode('.', $tableName);
633
        }
634
635
        $foreignKeys = [];
636
        $rows = $this->fetchAll(sprintf(
637
            "SELECT
638
              CONSTRAINT_NAME,
639
              CONCAT(TABLE_SCHEMA, '.', TABLE_NAME) AS TABLE_NAME,
640 22
              COLUMN_NAME,
641
              CONCAT(REFERENCED_TABLE_SCHEMA, '.', REFERENCED_TABLE_NAME) AS REFERENCED_TABLE_NAME,
642 22
              REFERENCED_COLUMN_NAME
643 22
            FROM information_schema.KEY_COLUMN_USAGE
644 19
            WHERE REFERENCED_TABLE_NAME IS NOT NULL
645 19
              AND TABLE_SCHEMA = %s
646 19
              AND TABLE_NAME = '%s'
647 19
            ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
648 22
            empty($schema) ? 'DATABASE()' : "'$schema'",
649 22
            $tableName
650
        ));
651
        foreach ($rows as $row) {
652
            $foreignKeys[$row['CONSTRAINT_NAME']]['table'] = $row['TABLE_NAME'];
653
            $foreignKeys[$row['CONSTRAINT_NAME']]['columns'][] = $row['COLUMN_NAME'];
654
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_table'] = $row['REFERENCED_TABLE_NAME'];
655 15
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_columns'][] = $row['REFERENCED_COLUMN_NAME'];
656
        }
657 15
658 15
        return $foreignKeys;
659 15
    }
660 15
661 15
    /**
662 15
     * {@inheritdoc}
663 15
     */
664 15 View Code Duplication
    protected function getAddForeignKeyInstructions(Table $table, 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...
665
    {
666
        $alter = sprintf(
667
            'ADD %s',
668
            $this->getForeignKeySqlDefinition($foreignKey)
669 8
        );
670
671 8
        return new AlterInstructions([$alter]);
672 3
    }
673 3
674
    /**
675
     * {@inheritdoc}
676 8
     */
677 8
    protected function getDropForeignKeyInstructions($tableName, $constraint)
678 8
    {
679 8
        $alter = sprintf(
680 8
            'DROP FOREIGN KEY %s',
681
            $constraint
682 8
        );
683 8
684 8
        return new AlterInstructions([$alter]);
685
    }
686 7
687 7
    /**
688
     * {@inheritdoc}
689
     */
690
    protected function getDropForeignKeyByColumnsInstructions($tableName, $columns)
691
    {
692
        $instructions = new AlterInstructions();
693
694
        foreach ($columns as $column) {
695 7
            $rows = $this->fetchAll(sprintf(
696 7
                "SELECT
697
                    CONSTRAINT_NAME
698 7
                  FROM information_schema.KEY_COLUMN_USAGE
699 7
                  WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
700 7
                    AND REFERENCED_TABLE_NAME IS NOT NULL
701 7
                    AND TABLE_NAME = '%s'
702 7
                    AND COLUMN_NAME = '%s'
703
                  ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
704 7
                $tableName,
705
                $column
706
            ));
707
708
            foreach ($rows as $row) {
709 96
                $instructions->merge($this->getDropForeignKeyInstructions($tableName, $row['CONSTRAINT_NAME']));
710
            }
711
        }
712 96
713 87
        if (empty($instructions->getAlterParts())) {
714
            throw new \InvalidArgumentException(sprintf(
715 96
                "Not foreign key on columns '%s' exist",
716 4
                implode(',', $columns)
717
            ));
718 96
        }
719 9
720
        return $instructions;
721
    }
722 6
723 6
    /**
724 6
     * {@inheritdoc}
725 6
     */
726 6
    public function getSqlType($type, $limit = null)
727 6
    {
728 6
        switch ($type) {
729 6
            case static::PHINX_TYPE_FLOAT:
730
            case static::PHINX_TYPE_DECIMAL:
731 5
            case static::PHINX_TYPE_DATE:
732
            case static::PHINX_TYPE_ENUM:
733 5
            case static::PHINX_TYPE_SET:
734
            case static::PHINX_TYPE_JSON:
735 95
            // Geospatial database types
736 5
            case static::PHINX_TYPE_GEOMETRY:
737
            case static::PHINX_TYPE_POINT:
738 95
            case static::PHINX_TYPE_LINESTRING:
739 3
            case static::PHINX_TYPE_POLYGON:
740
                return ['name' => $type];
741 95
            case static::PHINX_TYPE_DATETIME:
742 1
            case static::PHINX_TYPE_TIMESTAMP:
743
            case static::PHINX_TYPE_TIME:
744
                return ['name' => $type, 'limit' => $limit];
745 1
            case static::PHINX_TYPE_STRING:
746 1
                return ['name' => 'varchar', 'limit' => $limit ?: 255];
747 1
            case static::PHINX_TYPE_CHAR:
748 1
                return ['name' => 'char', 'limit' => $limit ?: 255];
749 1 View Code Duplication
            case static::PHINX_TYPE_TEXT:
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...
750 1
                if ($limit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
751 1
                    $sizes = [
752 1
                        // Order matters! Size must always be tested from longest to shortest!
753
                        'longtext' => static::TEXT_LONG,
754 1
                        'mediumtext' => static::TEXT_MEDIUM,
755
                        'text' => static::TEXT_REGULAR,
756 1
                        'tinytext' => static::TEXT_SMALL,
757
                    ];
758 95
                    foreach ($sizes as $name => $length) {
759 82
                        if ($limit >= $length) {
760
                            return ['name' => $name];
761
                        }
762 6
                    }
763 6
                }
764 6
765 6
                return ['name' => 'text'];
766 6
            case static::PHINX_TYPE_BINARY:
767 6
                return ['name' => 'binary', 'limit' => $limit ?: 255];
768
            case static::PHINX_TYPE_VARBINARY:
769 6
                return ['name' => 'varbinary', 'limit' => $limit ?: 255];
770 6 View Code Duplication
            case static::PHINX_TYPE_BLOB:
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...
771 6
                if ($limit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
772 6
                    $sizes = [
773 6
                        // Order matters! Size must always be tested from longest to shortest!
774 6
                        'longblob' => static::BLOB_LONG,
775 6
                        'mediumblob' => static::BLOB_MEDIUM,
776 2
                        'blob' => static::BLOB_REGULAR,
777 2
                        'tinyblob' => static::BLOB_SMALL,
778 6
                    ];
779
                    foreach ($sizes as $name => $length) {
780 5
                        if ($limit >= $length) {
781 82
                            return ['name' => $name];
782 76
                        }
783 76
                    }
784 82
                }
785
786 86
                return ['name' => 'blob'];
787 82
            case static::PHINX_TYPE_BIT:
788
                return ['name' => 'bit', 'limit' => $limit ?: 64];
789 86
            case static::PHINX_TYPE_INTEGER:
790 7
                if ($limit && $limit >= static::INT_TINY) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
791
                    $sizes = [
792 84
                        // Order matters! Size must always be tested from longest to shortest!
793 5
                        'bigint' => static::INT_BIG,
794
                        'int' => static::INT_REGULAR,
795 83
                        'mediumint' => static::INT_MEDIUM,
796 7
                        'smallint' => static::INT_SMALL,
797
                        'tinyint' => static::INT_TINY,
798 83
                    ];
799 80
                    $limits = [
800
                        'int' => 11,
801 83
                        'bigint' => 20,
802 4
                    ];
803
                    foreach ($sizes as $name => $length) {
804 83
                        if ($limit >= $length) {
805 4
                            $def = ['name' => $name];
806
                            if (isset($limits[$name])) {
807 83
                                $def['limit'] = $limits[$name];
808 80
                            }
809
810 10
                            return $def;
811 2
                        }
812
                    }
813 10
                } elseif (!$limit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
814 10
                    $limit = 11;
815 10
                }
816 10
817 5
                return ['name' => 'int', 'limit' => $limit];
818 8
            case static::PHINX_TYPE_BIG_INTEGER:
819 5
                return ['name' => 'bigint', 'limit' => 20];
820
            case static::PHINX_TYPE_BOOLEAN:
821 6
                return ['name' => 'tinyint', 'limit' => 1];
822 4
            case static::PHINX_TYPE_UUID:
823
                return ['name' => 'char', 'limit' => 36];
824 2
            case static::TYPE_YEAR:
825
                if (!$limit || in_array($limit, [2, 4])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
826
                    $limit = 4;
827
                }
828
829
                return ['name' => 'year', 'limit' => $limit];
830 2
            default:
831
                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
832
        }
833 2
    }
834 2
835 2
    /**
836
     * Returns Phinx type by SQL type
837
     *
838
     * @param string $sqlTypeDef
839
     * @throws \RuntimeException
840
     * @internal param string $sqlType SQL type
841
     * @returns string Phinx type
842
     */
843
    public function getPhinxType($sqlTypeDef)
844
    {
845
        $matches = [];
846 17
        if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*(.+)*$/', $sqlTypeDef, $matches)) {
847
            throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported');
848 17
        } else {
849 17
            $limit = null;
850 1
            $precision = null;
851
            $type = $matches[1];
852 16
            if (count($matches) > 2) {
853 16
                $limit = $matches[3] ? (int)$matches[3] : null;
854 16
            }
855 16
            if (count($matches) > 4) {
856 14
                $precision = (int)$matches[5];
857 14
            }
858 16 View Code Duplication
            if ($type === 'tinyint' && $limit === 1) {
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...
859 4
                $type = static::PHINX_TYPE_BOOLEAN;
860 4
                $limit = null;
861 16
            }
862 3
            switch ($type) {
863 3
                case 'varchar':
864 3
                    $type = static::PHINX_TYPE_STRING;
865
                    if ($limit === 255) {
866 16
                        $limit = null;
867 6
                    }
868 6
                    break;
869 3 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...
870 3
                    $type = static::PHINX_TYPE_CHAR;
871 6
                    if ($limit === 255) {
872 16
                        $limit = null;
873 5
                    }
874 5
                    if ($limit === 36) {
875 1
                        $type = static::PHINX_TYPE_UUID;
876 1
                    }
877 5
                    break;
878 2
                case 'tinyint':
879 2
                    $type = static::PHINX_TYPE_INTEGER;
880 5
                    $limit = static::INT_TINY;
881 16
                    break;
882 2
                case 'smallint':
883 2
                    $type = static::PHINX_TYPE_INTEGER;
884 2
                    $limit = static::INT_SMALL;
885 16
                    break;
886 2
                case 'mediumint':
887 2
                    $type = static::PHINX_TYPE_INTEGER;
888 2
                    $limit = static::INT_MEDIUM;
889 16
                    break;
890 2
                case 'int':
891 2
                    $type = static::PHINX_TYPE_INTEGER;
892 2
                    if ($limit === 11) {
893 16
                        $limit = null;
894 15
                    }
895 15
                    break;
896 12
                case 'bigint':
897 12
                    if ($limit === 20) {
898 15
                        $limit = null;
899 11
                    }
900 6
                    $type = static::PHINX_TYPE_BIG_INTEGER;
901 4
                    break;
902 4
                case 'bit':
903 6
                    $type = static::PHINX_TYPE_BIT;
904 6
                    if ($limit === 64) {
905 10
                        $limit = null;
906 2
                    }
907 2
                    break;
908 10
                case 'blob':
909 1
                    $type = static::PHINX_TYPE_BINARY;
910 1
                    break;
911 1
                case 'tinyblob':
912 10
                    $type = static::PHINX_TYPE_BINARY;
913 1
                    $limit = static::BLOB_TINY;
914 1
                    break;
915 1
                case 'mediumblob':
916 10
                    $type = static::PHINX_TYPE_BINARY;
917 1
                    $limit = static::BLOB_MEDIUM;
918 1
                    break;
919 1
                case 'longblob':
920 10
                    $type = static::PHINX_TYPE_BINARY;
921 2
                    $limit = static::BLOB_LONG;
922 2
                    break;
923 2
                case 'tinytext':
924 9
                    $type = static::PHINX_TYPE_TEXT;
925 2
                    $limit = static::TEXT_TINY;
926 2
                    break;
927 2
                case 'mediumtext':
928 8
                    $type = static::PHINX_TYPE_TEXT;
929 2
                    $limit = static::TEXT_MEDIUM;
930 2
                    break;
931 2
                case 'longtext':
932
                    $type = static::PHINX_TYPE_TEXT;
933
                    $limit = static::TEXT_LONG;
934
                    break;
935 16
            }
936
937
            // Call this to check if parsed type is supported.
938 15
            $this->getSqlType($type, $limit);
939 15
940
            $phinxType = [
941 15
                'name' => $type,
942
                'limit' => $limit,
943 15
                'precision' => $precision
944 3
            ];
945 3
946
            if (static::PHINX_TYPE_ENUM == $type) {
947 15
                $phinxType['values'] = explode("','", trim($matches[6], "()'"));
948
            }
949
950
            return $phinxType;
951
        }
952
    }
953
954 83
    /**
955
     * {@inheritdoc}
956 83
     */
957
    public function createDatabase($name, $options = [])
958 83
    {
959 1
        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
960 1
961 82 View Code Duplication
        if (isset($options['collation'])) {
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...
962
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s` COLLATE `%s`', $name, $charset, $options['collation']));
963 83
        } else {
964
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s`', $name, $charset));
965
        }
966
    }
967
968 4
    /**
969
     * {@inheritdoc}
970 4
     */
971 4
    public function hasDatabase($name)
972 4
    {
973
        $rows = $this->fetchAll(
974 4
            sprintf(
975 4
                'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = \'%s\'',
976
                $name
977 4
            )
978 3
        );
979 3
980
        foreach ($rows as $row) {
981 3
            if (!empty($row)) {
982
                return true;
983 3
            }
984
        }
985
986
        return false;
987
    }
988
989 81
    /**
990
     * {@inheritdoc}
991 81
     */
992 81
    public function dropDatabase($name)
993
    {
994
        $this->execute(sprintf('DROP DATABASE IF EXISTS `%s`', $name));
995
    }
996
997
    /**
998
     * Gets the MySQL Column Definition for a Column object.
999
     *
1000 89
     * @param \Phinx\Db\Table\Column $column Column
1001
     * @return string
1002 89
     */
1003
    protected function getColumnSqlDefinition(Column $column)
1004 89
    {
1005 89
        if ($column->getType() instanceof Literal) {
1006 89
            $def = (string)$column->getType();
1007 2
        } else {
1008 89
            $sqlType = $this->getSqlType($column->getType(), $column->getLimit());
1009 86
            $def = strtoupper($sqlType['name']);
1010 86
        }
1011 89
        if ($column->getPrecision() && $column->getScale()) {
1012 5
            $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
1013 5
        } elseif (isset($sqlType['limit'])) {
1014 89
            $def .= '(' . $sqlType['limit'] . ')';
1015 89
        }
1016 89 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...
1017 89
            $def .= "('" . implode("', '", $values) . "')";
1018 89
        }
1019 89
        $def .= $column->getEncoding() ? ' CHARACTER SET ' . $column->getEncoding() : '';
1020
        $def .= $column->getCollation() ? ' COLLATE ' . $column->getCollation() : '';
1021 89
        $def .= (!$column->isSigned() && isset($this->signedColumnTypes[$column->getType()])) ? ' unsigned' : '';
1022 2
        $def .= ($column->isNull() == false) ? ' NOT NULL' : ' NULL';
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1023 2
        $def .= $column->isIdentity() ? ' AUTO_INCREMENT' : '';
1024
        $def .= $this->getDefaultValueDefinition($column->getDefault(), $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...
1025 89
1026 1
        if ($column->getComment()) {
1027 1
            $def .= ' COMMENT ' . $this->getConnection()->quote($column->getComment());
1028
        }
1029 89
1030
        if ($column->getUpdate()) {
1031
            $def .= ' ON UPDATE ' . $column->getUpdate();
1032
        }
1033
1034
        return $def;
1035
    }
1036
1037
    /**
1038 16
     * Gets the MySQL Index Definition for an Index object.
1039
     *
1040 16
     * @param \Phinx\Db\Table\Index $index Index
1041 16
     * @return string
1042 16
     */
1043 2
    protected function getIndexSqlDefinition(Index $index)
1044 2
    {
1045
        $def = '';
1046 16
        $limit = '';
1047 5
        if ($index->getLimit()) {
1048 5
            $limit = '(' . $index->getLimit() . ')';
1049
        }
1050 16
1051 1
        if ($index->getType() == Index::UNIQUE) {
1052 1
            $def .= ' UNIQUE';
1053
        }
1054 16
1055
        if ($index->getType() == Index::FULLTEXT) {
1056 16
            $def .= ' FULLTEXT';
1057 5
        }
1058 5
1059
        $def .= ' KEY';
1060 16
1061
        if (is_string($index->getName())) {
1062 16
            $def .= ' `' . $index->getName() . '`';
1063
        }
1064
1065
        $def .= ' (`' . implode('`,`', $index->getColumns()) . '`' . $limit . ')';
1066
1067
        return $def;
1068
    }
1069
1070
    /**
1071 17
     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
1072
     *
1073 17
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
1074 17
     * @return string
1075 5
     */
1076 5 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...
1077 17
    {
1078 17
        $def = '';
1079 17
        if ($foreignKey->getConstraint()) {
1080 17
            $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\MysqlAdapter::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...
1081 17
        }
1082 17
        $columnNames = [];
1083 17
        foreach ($foreignKey->getColumns() as $column) {
1084 17
            $columnNames[] = $this->quoteColumnName($column);
1085 17
        }
1086 17
        $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
1087 17
        $refColumnNames = [];
1088 2
        foreach ($foreignKey->getReferencedColumns() as $column) {
1089 2
            $refColumnNames[] = $this->quoteColumnName($column);
1090 17
        }
1091 2
        $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
1092 2
        if ($foreignKey->getOnDelete()) {
1093 17
            $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
1094
        }
1095
        if ($foreignKey->getOnUpdate()) {
1096
            $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
1097
        }
1098
1099
        return $def;
1100
    }
1101
1102 2
    /**
1103
     * Describes a database table. This is a MySQL adapter specific method.
1104 2
     *
1105
     * @param string $tableName Table name
1106
     * @return array
1107 2
     */
1108
    public function describeTable($tableName)
1109
    {
1110
        $options = $this->getOptions();
1111 2
1112 2
        // mysql specific
1113
        $sql = sprintf(
1114 2
            "SELECT *
1115
             FROM information_schema.tables
1116 2
             WHERE table_schema = '%s'
1117
             AND table_name = '%s'",
1118
            $options['name'],
1119
            $tableName
1120
        );
1121
1122
        return $this->fetchRow($sql);
1123 85
    }
1124
1125 85
    /**
1126
     * Returns MySQL column types (inherited and MySQL specified).
1127
     * @return array
1128
     */
1129
    public function getColumnTypes()
1130
    {
1131
        return array_merge(parent::getColumnTypes(), ['enum', 'set', 'year', 'json']);
1132
    }
1133
}
1134