Completed
Pull Request — master (#1352)
by José
10:12 queued 07:45
created

MysqlAdapter::getIndexSqlDefinition()   D

Complexity

Conditions 10
Paths 24

Size

Total Lines 41
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 10

Importance

Changes 0
Metric Value
dl 0
loc 41
ccs 25
cts 25
cp 1
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 26
nc 24
nop 1
crap 10

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 * Copyright (c) 2015 Rob Morgan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated * documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 *
26
 * @package    Phinx
27
 * @subpackage Phinx\Db\Adapter
28
 */
29
namespace Phinx\Db\Adapter;
30
31
use Phinx\Db\Table\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 View Code Duplication
    public function hasTable($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...
189
    {
190
        $options = $this->getOptions();
191 82
192 82
        $exists = $this->fetchRow(sprintf(
193
            "SELECT TABLE_NAME
194 82
            FROM INFORMATION_SCHEMA.TABLES
195
            WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'",
196 82
            $options['name'],
197
            $tableName
198
        ));
199
200
        return !empty($exists);
201
    }
202 82
203
    /**
204
     * {@inheritdoc}
205
     */
206 82
    public function createTable(Table $table, array $columns = [], array $indexes = [])
207
    {
208 82
        // This method is based on the MySQL docs here: http://dev.mysql.com/doc/refman/5.1/en/create-index.html
209 82
        $defaultOptions = [
210
            'engine' => 'InnoDB',
211
            'collation' => 'utf8_general_ci'
212 82
        ];
213 82
214 68
        $options = array_merge(
215 68
            $defaultOptions,
216 68
            array_intersect_key($this->getOptions(), $defaultOptions),
217 68
            $table->getOptions()
218 68
        );
219
220 68
        // Add the default primary key
221 68
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
222 82
            $column = new Column();
223
            $column->setName('id')
224 2
                   ->setType('integer')
225 2
                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
226 2
                   ->setIdentity(true);
227 2
228
            array_unshift($columns, $column);
229 2
            $options['primary_key'] = 'id';
230 2
        } elseif (isset($options['id']) && is_string($options['id'])) {
231 2
            // Handle id => "field_name" to support AUTO_INCREMENT
232
            $column = new Column();
233
            $column->setName($options['id'])
234
                   ->setType('integer')
235
                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
236 82
                   ->setIdentity(true);
237 82
238 82
            array_unshift($columns, $column);
239 82
            $options['primary_key'] = $options['id'];
240
        }
241
242 82
        // TODO - process table options like collation etc
243 82
244 82
        // process table engine (default to InnoDB)
245 82
        $optionsStr = 'ENGINE = InnoDB';
246 82
        if (isset($options['engine'])) {
247
            $optionsStr = sprintf('ENGINE = %s', $options['engine']);
248
        }
249 82
250 2
        // process table collation
251 2
        if (isset($options['collation'])) {
252
            $charset = explode('_', $options['collation']);
253 82
            $optionsStr .= sprintf(' CHARACTER SET %s', $charset[0]);
254 82
            $optionsStr .= sprintf(' COLLATE %s', $options['collation']);
255 82
        }
256 82
257 82
        // set the table comment
258
        if (isset($options['comment'])) {
259
            $optionsStr .= sprintf(" COMMENT=%s ", $this->getConnection()->quote($options['comment']));
260 82
        }
261 82
262 82
        $sql = 'CREATE TABLE ';
263 82
        $sql .= $this->quoteTableName($table->getName()) . ' (';
264 81
        foreach ($columns as $column) {
265 82
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
266
        }
267
268 2
        // set the primary key(s)
269 2 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...
270 2
            $sql = rtrim($sql);
271 2
            $sql .= ' PRIMARY KEY (';
272 2
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
273 2
                $sql .= $this->quoteColumnName($options['primary_key']);
274 2
            } 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...
275 2
                $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
276 2
            }
277 2
            $sql .= ')';
278 82
        } else {
279 82
            $sql = substr(rtrim($sql), 0, -1); // no primary keys
280 1
        }
281
282
        // set the indexes
283
        foreach ($indexes as $index) {
284 82
            $sql .= ', ' . $this->getIndexSqlDefinition($index);
285 82
        }
286 10
287 82
        $sql .= ') ' . $optionsStr;
288
        $sql = rtrim($sql) . ';';
289
290 82
        // execute the sql
291 82
        $this->execute($sql);
292 2
    }
293 82
294
    /**
295 82
     * {@inheritdoc}
296 82
     */
297 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...
298
    {
299 82
        $sql = sprintf(
300 82
            'RENAME TABLE %s TO %s',
301
            $this->quoteTableName($tableName),
302
            $this->quoteTableName($newTableName)
303
        );
304
305 5
        return new AlterInstructions([], [$sql]);
306
    }
307 5
308 5
    /**
309
     * {@inheritdoc}
310
     */
311
    protected function getDropTableInstructions($tableName)
312
    {
313 5
        $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName));
314
315 5
        return new AlterInstructions([], [$sql]);
316 5
    }
317
318
    /**
319
     * {@inheritdoc}
320
     */
321 1
    public function truncateTable($tableName)
322
    {
323 1
        $sql = sprintf(
324 1
            'TRUNCATE TABLE %s',
325 1
            $this->quoteTableName($tableName)
326 1
        );
327
328 1
        $this->execute($sql);
329 1
    }
330
331
    /**
332
     * {@inheritdoc}
333
     */
334 12
    public function getColumns($tableName)
335
    {
336 12
        $columns = [];
337 12
        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
338 12
        foreach ($rows as $columnInfo) {
339 12
            $phinxType = $this->getPhinxType($columnInfo['Type']);
340
341 12
            $column = new Column();
342 12
            $column->setName($columnInfo['Field'])
343 12
                   ->setNull($columnInfo['Null'] !== 'NO')
344 12
                   ->setDefault($columnInfo['Default'])
345 12
                   ->setType($phinxType['name'])
346 12
                   ->setSigned(strpos($columnInfo['Type'], 'unsigned') === false)
347
                   ->setLimit($phinxType['limit']);
348 12
349 12
            if ($columnInfo['Extra'] === 'auto_increment') {
350 12
                $column->setIdentity(true);
351
            }
352 12
353 3
            if (isset($phinxType['values'])) {
354 3
                $column->setValues($phinxType['values']);
355
            }
356 12
357 12
            $columns[] = $column;
358
        }
359 12
360
        return $columns;
361
    }
362
363
    /**
364
     * {@inheritdoc}
365 79
     */
366 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...
367 79
    {
368 79
        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
369 79
        foreach ($rows as $column) {
370 77
            if (strcasecmp($column['Field'], $columnName) === 0) {
371
                return true;
372 77
            }
373
        }
374 21
375
        return false;
376
    }
377
378
    /**
379
     * {@inheritdoc}
380
     */
381
    protected function getAddColumnInstructions(Table $table, Column $column)
382
    {
383 95
        $alter = sprintf(
384
            'ADD %s %s',
385 95
            $this->quoteColumnName($column->getName()),
386 10
            $this->getColumnSqlDefinition($column)
387 95
        );
388 79
389 79
        if ($column->getAfter()) {
390 95
            $alter .= ' AFTER ' . $this->quoteColumnName($column->getAfter());
391
        }
392
393
        return new AlterInstructions([$alter]);
394
    }
395
396 18
    /**
397
     * {@inheritdoc}
398 18
     */
399 18
    protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName)
400 18
    {
401 18
        $rows = $this->fetchAll(sprintf('DESCRIBE %s', $this->quoteTableName($tableName)));
402 18
        foreach ($rows as $row) {
403 18
            if (strcasecmp($row['Field'], $columnName) === 0) {
404
                $null = ($row['Null'] == 'NO') ? 'NOT NULL' : 'NULL';
405 18
                $extra = ' ' . strtoupper($row['Extra']);
406 2
                if (!is_null($row['Default'])) {
407 2
                    $extra .= $this->getDefaultValueDefinition($row['Default']);
408
                }
409 18
                $definition = $row['Type'] . ' ' . $null . $extra;
410 18
411
                $alter = sprintf(
412
                    'CHANGE COLUMN %s %s %s',
413
                    $this->quoteColumnName($columnName),
414
                    $this->quoteColumnName($newColumnName),
415 7
                    $definition
416
                );
417 7
418 7
                return new AlterInstructions([$alter]);
419 7
            }
420 5
        }
421 5
422 5
        throw new \InvalidArgumentException(sprintf(
423 1
            'The specified column doesn\'t exist: ' .
424 1
            $columnName
425 5
        ));
426
    }
427 5
428 5
    /**
429 5
     * {@inheritdoc}
430 5
     */
431 5
    protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn)
432 5
    {
433
        $after = $newColumn->getAfter() ? ' AFTER ' . $this->quoteColumnName($newColumn->getAfter()) : '';
434 5
        $alter = sprintf(
435 5
            'CHANGE %s %s %s%s',
436 5
            $this->quoteColumnName($columnName),
437
            $this->quoteColumnName($newColumn->getName()),
438 6
            $this->getColumnSqlDefinition($newColumn),
439
            $after
440 2
        );
441
442
        return new AlterInstructions([$alter]);
443 2
    }
444
445
    /**
446
     * {@inheritdoc}
447
     */
448
    protected function getDropColumnInstructions($tableName, $columnName)
449 5
    {
450
        $alter = sprintf('DROP COLUMN %s', $this->quoteColumnName($columnName));
451 5
452 5
        return new AlterInstructions([$alter]);
453 5
    }
454 5
455 5
    /**
456 5
     * Get an array of indexes from a particular table.
457 5
     *
458 5
     * @param string $tableName Table Name
459
     * @return array
460 5
     */
461 5
    protected function getIndexes($tableName)
462 5
    {
463
        $indexes = [];
464
        $rows = $this->fetchAll(sprintf('SHOW INDEXES FROM %s', $this->quoteTableName($tableName)));
465
        foreach ($rows as $row) {
466
            if (!isset($indexes[$row['Key_name']])) {
467 5
                $indexes[$row['Key_name']] = ['columns' => []];
468
            }
469 5
            $indexes[$row['Key_name']]['columns'][] = strtolower($row['Column_name']);
470 5
        }
471 5
472 5
        return $indexes;
473 5
    }
474 5
475 5
    /**
476 5
     * {@inheritdoc}
477
     */
478 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...
479
    {
480
        if (is_string($columns)) {
481
            $columns = [$columns]; // str to array
482
        }
483
484 19
        $columns = array_map('strtolower', $columns);
485
        $indexes = $this->getIndexes($tableName);
486 19
487 19
        foreach ($indexes as $index) {
488 19
            if ($columns == $index['columns']) {
489 18
                return true;
490 18
            }
491 18
        }
492 18
493 19
        return false;
494 19
    }
495
496
    /**
497
     * {@inheritdoc}
498
     */
499 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...
500 14
    {
501
        $indexes = $this->getIndexes($tableName);
502 14
503 6
        foreach ($indexes as $name => $index) {
504 6
            if ($name === $indexName) {
505
                return true;
506 14
            }
507 14
        }
508
509 14
        return false;
510 14
    }
511 12
512
    /**
513 13
     * {@inheritdoc}
514
     */
515 11
    protected function getAddIndexInstructions(Table $table, Index $index)
516
    {
517
        $alter = sprintf(
518
            'ADD %s',
519
            $this->getIndexSqlDefinition($index)
520
        );
521 1
522
        return new AlterInstructions([$alter]);
523 1
    }
524
525 1
    /**
526 1
     * {@inheritdoc}
527 1
     */
528
    protected function getDropIndexByColumnsInstructions($tableName, $columns)
529 1
    {
530
        if (is_string($columns)) {
531
            $columns = [$columns]; // str to array
532
        }
533
534
        $indexes = $this->getIndexes($tableName);
535
        $columns = array_map('strtolower', $columns);
536
537 4 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...
538
            if ($columns == $index['columns']) {
539 4
                return new AlterInstructions([sprintf(
540 4
                    'DROP INDEX %s',
541 4
                    $this->quoteColumnName($indexName)
542 4
                )]);
543 4
            }
544 4
        }
545 4
546 4
        throw new \InvalidArgumentException(sprintf(
547
            "The specified index on columns '%s' does not exist",
548
            implode(',', $columns)
549
        ));
550
    }
551 3
552
    /**
553 3
     * {@inheritdoc}
554 2
     */
555 2
    protected function getDropIndexByNameInstructions($tableName, $indexName)
556
    {
557 3
558 3
        $indexes = $this->getIndexes($tableName);
559
560 3 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...
561 3
            if ($name === $indexName) {
562 3
                return new AlterInstructions([sprintf(
563 3
                    'DROP INDEX %s',
564 3
                    $this->quoteColumnName($indexName)
565 3
                )]);
566 3
            }
567 3
        }
568 3
569 3
        throw new \InvalidArgumentException(sprintf(
570
            "The specified index name '%s' does not exist",
571 3
            $indexName
572 1
        ));
573
    }
574
575
    /**
576
     * {@inheritdoc}
577 2
     */
578 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...
579 2
    {
580
        if (is_string($columns)) {
581 2
            $columns = [$columns]; // str to array
582
        }
583 2
        $foreignKeys = $this->getForeignKeys($tableName);
584 2
        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...
585 2
            if (isset($foreignKeys[$constraint])) {
586 2
                return !empty($foreignKeys[$constraint]);
587 2
            }
588 2
589 2
            return false;
590 2
        } else {
591 2
            foreach ($foreignKeys as $key) {
592
                if ($columns == $key['columns']) {
593 2
                    return true;
594
                }
595
            }
596
597
            return false;
598
        }
599 21
    }
600
601 21
    /**
602 5
     * Get an array of foreign keys from a particular table.
603 5
     *
604 21
     * @param string $tableName Table Name
605 21
     * @return array
606 6
     */
607 4 View Code Duplication
    protected function getForeignKeys($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...
608
    {
609 4
        $foreignKeys = [];
610
        $rows = $this->fetchAll(sprintf(
611 15
            "SELECT
612 12
              CONSTRAINT_NAME,
613 10
              TABLE_NAME,
614
              COLUMN_NAME,
615 11
              REFERENCED_TABLE_NAME,
616 11
              REFERENCED_COLUMN_NAME
617
            FROM information_schema.KEY_COLUMN_USAGE
618
            WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
619
              AND REFERENCED_TABLE_NAME IS NOT NULL
620
              AND TABLE_NAME = '%s'
621
            ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
622
            $tableName
623
        ));
624
        foreach ($rows as $row) {
625
            $foreignKeys[$row['CONSTRAINT_NAME']]['table'] = $row['TABLE_NAME'];
626 22
            $foreignKeys[$row['CONSTRAINT_NAME']]['columns'][] = $row['COLUMN_NAME'];
627
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_table'] = $row['REFERENCED_TABLE_NAME'];
628 22
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_columns'][] = $row['REFERENCED_COLUMN_NAME'];
629 22
        }
630
631
        return $foreignKeys;
632
    }
633
634
    /**
635
     * {@inheritdoc}
636
     */
637 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...
638
    {
639
        $alter = sprintf(
640 22
            'ADD %s',
641
            $this->getForeignKeySqlDefinition($foreignKey)
642 22
        );
643 22
644 19
        return new AlterInstructions([$alter]);
645 19
    }
646 19
647 19
    /**
648 22
     * {@inheritdoc}
649 22
     */
650
    protected function getDropForeignKeyInstructions($tableName, $constraint)
651
    {
652
        $alter = sprintf(
653
            'DROP FOREIGN KEY %s',
654
            $constraint
655 15
        );
656
657 15
        return new AlterInstructions([$alter]);
658 15
    }
659 15
660 15
    /**
661 15
     * {@inheritdoc}
662 15
     */
663 15
    protected function getDropForeignKeyByColumnsInstructions($tableName, $columns)
664 15
    {
665
        $instructions = new AlterInstructions();
666
667 View Code Duplication
        foreach ($columns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
668
            $rows = $this->fetchAll(sprintf(
669 8
                "SELECT
670
                    CONSTRAINT_NAME
671 8
                  FROM information_schema.KEY_COLUMN_USAGE
672 3
                  WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
673 3
                    AND REFERENCED_TABLE_NAME IS NOT NULL
674
                    AND TABLE_NAME = '%s'
675
                    AND COLUMN_NAME = '%s'
676 8
                  ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
677 8
                $tableName,
678 8
                $column
679 8
            ));
680 8
681
            foreach ($rows as $row) {
682 8
                $instructions->merge($this->getDropForeignKeyInstructions($tableName, $row['CONSTRAINT_NAME']));
683 8
            }
684 8
        }
685
686 7
        if (empty($instructions->getAlterParts())) {
687 7
            throw new \InvalidArgumentException(sprintf(
688
                "Not foreign key on columns '%s' exist",
689
                implode(',', $columns)
690
            ));
691
        }
692
693
        return $instructions;
694
    }
695 7
696 7
    /**
697
     * {@inheritdoc}
698 7
     */
699 7
    public function getSqlType($type, $limit = null)
700 7
    {
701 7
        switch ($type) {
702 7
            case static::PHINX_TYPE_FLOAT:
703
            case static::PHINX_TYPE_DECIMAL:
704 7
            case static::PHINX_TYPE_DATE:
705
            case static::PHINX_TYPE_ENUM:
706
            case static::PHINX_TYPE_SET:
707
            case static::PHINX_TYPE_JSON:
708
            // Geospatial database types
709 96
            case static::PHINX_TYPE_GEOMETRY:
710
            case static::PHINX_TYPE_POINT:
711
            case static::PHINX_TYPE_LINESTRING:
712 96
            case static::PHINX_TYPE_POLYGON:
713 87
                return ['name' => $type];
714
            case static::PHINX_TYPE_DATETIME:
715 96
            case static::PHINX_TYPE_TIMESTAMP:
716 4
            case static::PHINX_TYPE_TIME:
717
                return ['name' => $type, 'limit' => $limit];
718 96
            case static::PHINX_TYPE_STRING:
719 9
                return ['name' => 'varchar', 'limit' => $limit ?: 255];
720
            case static::PHINX_TYPE_CHAR:
721
                return ['name' => 'char', 'limit' => $limit ?: 255];
722 6 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...
723 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...
724 6
                    $sizes = [
725 6
                        // Order matters! Size must always be tested from longest to shortest!
726 6
                        'longtext' => static::TEXT_LONG,
727 6
                        'mediumtext' => static::TEXT_MEDIUM,
728 6
                        'text' => static::TEXT_REGULAR,
729 6
                        'tinytext' => static::TEXT_SMALL,
730
                    ];
731 5
                    foreach ($sizes as $name => $length) {
732
                        if ($limit >= $length) {
733 5
                            return ['name' => $name];
734
                        }
735 95
                    }
736 5
                }
737
738 95
                return ['name' => 'text'];
739 3
            case static::PHINX_TYPE_BINARY:
740
                return ['name' => 'binary', 'limit' => $limit ?: 255];
741 95
            case static::PHINX_TYPE_VARBINARY:
742 1
                return ['name' => 'varbinary', 'limit' => $limit ?: 255];
743 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...
744
                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...
745 1
                    $sizes = [
746 1
                        // Order matters! Size must always be tested from longest to shortest!
747 1
                        'longblob' => static::BLOB_LONG,
748 1
                        'mediumblob' => static::BLOB_MEDIUM,
749 1
                        'blob' => static::BLOB_REGULAR,
750 1
                        'tinyblob' => static::BLOB_SMALL,
751 1
                    ];
752 1
                    foreach ($sizes as $name => $length) {
753
                        if ($limit >= $length) {
754 1
                            return ['name' => $name];
755
                        }
756 1
                    }
757
                }
758 95
759 82
                return ['name' => 'blob'];
760
            case static::PHINX_TYPE_BIT:
761
                return ['name' => 'bit', 'limit' => $limit ?: 64];
762 6
            case static::PHINX_TYPE_INTEGER:
763 6
                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...
764 6
                    $sizes = [
765 6
                        // Order matters! Size must always be tested from longest to shortest!
766 6
                        'bigint' => static::INT_BIG,
767 6
                        'int' => static::INT_REGULAR,
768
                        'mediumint' => static::INT_MEDIUM,
769 6
                        'smallint' => static::INT_SMALL,
770 6
                        'tinyint' => static::INT_TINY,
771 6
                    ];
772 6
                    $limits = [
773 6
                        'int' => 11,
774 6
                        'bigint' => 20,
775 6
                    ];
776 2
                    foreach ($sizes as $name => $length) {
777 2
                        if ($limit >= $length) {
778 6
                            $def = ['name' => $name];
779
                            if (isset($limits[$name])) {
780 5
                                $def['limit'] = $limits[$name];
781 82
                            }
782 76
783 76
                            return $def;
784 82
                        }
785
                    }
786 86
                } 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...
787 82
                    $limit = 11;
788
                }
789 86
790 7
                return ['name' => 'int', 'limit' => $limit];
791
            case static::PHINX_TYPE_BIG_INTEGER:
792 84
                return ['name' => 'bigint', 'limit' => 20];
793 5
            case static::PHINX_TYPE_BOOLEAN:
794
                return ['name' => 'tinyint', 'limit' => 1];
795 83
            case static::PHINX_TYPE_UUID:
796 7
                return ['name' => 'char', 'limit' => 36];
797
            case static::TYPE_YEAR:
798 83
                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...
799 80
                    $limit = 4;
800
                }
801 83
802 4
                return ['name' => 'year', 'limit' => $limit];
803
            default:
804 83
                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
805 4
        }
806
    }
807 83
808 80
    /**
809
     * Returns Phinx type by SQL type
810 10
     *
811 2
     * @param string $sqlTypeDef
812
     * @throws \RuntimeException
813 10
     * @internal param string $sqlType SQL type
814 10
     * @returns string Phinx type
815 10
     */
816 10
    public function getPhinxType($sqlTypeDef)
817 5
    {
818 8
        $matches = [];
819 5
        if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*(.+)*$/', $sqlTypeDef, $matches)) {
820
            throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported');
821 6
        } else {
822 4
            $limit = null;
823
            $precision = null;
824 2
            $type = $matches[1];
825
            if (count($matches) > 2) {
826
                $limit = $matches[3] ? (int)$matches[3] : null;
827
            }
828
            if (count($matches) > 4) {
829
                $precision = (int)$matches[5];
830 2
            }
831 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...
832
                $type = static::PHINX_TYPE_BOOLEAN;
833 2
                $limit = null;
834 2
            }
835 2
            switch ($type) {
836
                case 'varchar':
837
                    $type = static::PHINX_TYPE_STRING;
838
                    if ($limit === 255) {
839
                        $limit = null;
840
                    }
841
                    break;
842 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...
843
                    $type = static::PHINX_TYPE_CHAR;
844
                    if ($limit === 255) {
845
                        $limit = null;
846 17
                    }
847
                    if ($limit === 36) {
848 17
                        $type = static::PHINX_TYPE_UUID;
849 17
                    }
850 1
                    break;
851
                case 'tinyint':
852 16
                    $type = static::PHINX_TYPE_INTEGER;
853 16
                    $limit = static::INT_TINY;
854 16
                    break;
855 16
                case 'smallint':
856 14
                    $type = static::PHINX_TYPE_INTEGER;
857 14
                    $limit = static::INT_SMALL;
858 16
                    break;
859 4
                case 'mediumint':
860 4
                    $type = static::PHINX_TYPE_INTEGER;
861 16
                    $limit = static::INT_MEDIUM;
862 3
                    break;
863 3
                case 'int':
864 3
                    $type = static::PHINX_TYPE_INTEGER;
865
                    if ($limit === 11) {
866 16
                        $limit = null;
867 6
                    }
868 6
                    break;
869 3
                case 'bigint':
870 3
                    if ($limit === 20) {
871 6
                        $limit = null;
872 16
                    }
873 5
                    $type = static::PHINX_TYPE_BIG_INTEGER;
874 5
                    break;
875 1
                case 'bit':
876 1
                    $type = static::PHINX_TYPE_BIT;
877 5
                    if ($limit === 64) {
878 2
                        $limit = null;
879 2
                    }
880 5
                    break;
881 16
                case 'blob':
882 2
                    $type = static::PHINX_TYPE_BINARY;
883 2
                    break;
884 2
                case 'tinyblob':
885 16
                    $type = static::PHINX_TYPE_BINARY;
886 2
                    $limit = static::BLOB_TINY;
887 2
                    break;
888 2
                case 'mediumblob':
889 16
                    $type = static::PHINX_TYPE_BINARY;
890 2
                    $limit = static::BLOB_MEDIUM;
891 2
                    break;
892 2
                case 'longblob':
893 16
                    $type = static::PHINX_TYPE_BINARY;
894 15
                    $limit = static::BLOB_LONG;
895 15
                    break;
896 12
                case 'tinytext':
897 12
                    $type = static::PHINX_TYPE_TEXT;
898 15
                    $limit = static::TEXT_TINY;
899 11
                    break;
900 6
                case 'mediumtext':
901 4
                    $type = static::PHINX_TYPE_TEXT;
902 4
                    $limit = static::TEXT_MEDIUM;
903 6
                    break;
904 6
                case 'longtext':
905 10
                    $type = static::PHINX_TYPE_TEXT;
906 2
                    $limit = static::TEXT_LONG;
907 2
                    break;
908 10
            }
909 1
910 1
            // Call this to check if parsed type is supported.
911 1
            $this->getSqlType($type, $limit);
912 10
913 1
            $phinxType = [
914 1
                'name' => $type,
915 1
                'limit' => $limit,
916 10
                'precision' => $precision
917 1
            ];
918 1
919 1
            if (static::PHINX_TYPE_ENUM == $type) {
920 10
                $phinxType['values'] = explode("','", trim($matches[6], "()'"));
921 2
            }
922 2
923 2
            return $phinxType;
924 9
        }
925 2
    }
926 2
927 2
    /**
928 8
     * {@inheritdoc}
929 2
     */
930 2
    public function createDatabase($name, $options = [])
931 2
    {
932
        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
933
934 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...
935 16
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s` COLLATE `%s`', $name, $charset, $options['collation']));
936
        } else {
937
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s`', $name, $charset));
938 15
        }
939 15
    }
940
941 15
    /**
942
     * {@inheritdoc}
943 15
     */
944 3
    public function hasDatabase($name)
945 3
    {
946
        $rows = $this->fetchAll(
947 15
            sprintf(
948
                'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = \'%s\'',
949
                $name
950
            )
951
        );
952
953
        foreach ($rows as $row) {
954 83
            if (!empty($row)) {
955
                return true;
956 83
            }
957
        }
958 83
959 1
        return false;
960 1
    }
961 82
962
    /**
963 83
     * {@inheritdoc}
964
     */
965
    public function dropDatabase($name)
966
    {
967
        $this->execute(sprintf('DROP DATABASE IF EXISTS `%s`', $name));
968 4
    }
969
970 4
    /**
971 4
     * Gets the MySQL Column Definition for a Column object.
972 4
     *
973
     * @param \Phinx\Db\Table\Column $column Column
974 4
     * @return string
975 4
     */
976
    protected function getColumnSqlDefinition(Column $column)
977 4
    {
978 3
        if ($column->getType() instanceof Literal) {
979 3
            $def = (string)$column->getType();
980
        } else {
981 3
            $sqlType = $this->getSqlType($column->getType(), $column->getLimit());
982
            $def = strtoupper($sqlType['name']);
983 3
        }
984
        if ($column->getPrecision() && $column->getScale()) {
985
            $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
986
        } elseif (isset($sqlType['limit'])) {
987
            $def .= '(' . $sqlType['limit'] . ')';
988
        }
989 81 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...
990
            $def .= "('" . implode("', '", $values) . "')";
991 81
        }
992 81
        $def .= $column->getEncoding() ? ' CHARACTER SET ' . $column->getEncoding() : '';
993
        $def .= $column->getCollation() ? ' COLLATE ' . $column->getCollation() : '';
994
        $def .= (!$column->isSigned() && isset($this->signedColumnTypes[$column->getType()])) ? ' unsigned' : '';
995
        $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...
996
        $def .= $column->isIdentity() ? ' AUTO_INCREMENT' : '';
997
        $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...
998
999
        if ($column->getComment()) {
1000 89
            $def .= ' COMMENT ' . $this->getConnection()->quote($column->getComment());
1001
        }
1002 89
1003
        if ($column->getUpdate()) {
1004 89
            $def .= ' ON UPDATE ' . $column->getUpdate();
1005 89
        }
1006 89
1007 2
        return $def;
1008 89
    }
1009 86
1010 86
    /**
1011 89
     * Gets the MySQL Index Definition for an Index object.
1012 5
     *
1013 5
     * @param \Phinx\Db\Table\Index $index Index
1014 89
     * @return string
1015 89
     */
1016 89
    protected function getIndexSqlDefinition(Index $index)
1017 89
    {
1018 89
        $def = '';
1019 89
        $limit = '';
1020
1021 89
        if ($index->getType() == Index::UNIQUE) {
1022 2
            $def .= ' UNIQUE';
1023 2
        }
1024
1025 89
        if ($index->getType() == Index::FULLTEXT) {
1026 1
            $def .= ' FULLTEXT';
1027 1
        }
1028
1029 89
        $def .= ' KEY';
1030
1031
        if (is_string($index->getName())) {
1032
            $def .= ' `' . $index->getName() . '`';
1033
        }
1034
1035
        if (! is_array($index->getLimit())) {
1036
            if ($index->getLimit()) {
1037
                $limit = '(' . $index->getLimit() . ')';
1038 16
            }
1039
            $def .= ' (`' . implode('`,`', $index->getColumns()) . '`' . $limit . ')';
1040 16
        } else {
1041 16
            $columns = $index->getColumns();
1042 16
            $limits = $index->getLimit();
1043 2
            $def .= ' (';
1044 2
            $columns_iter = new \CachingIterator(new \ArrayIterator($columns));
1045
            foreach ($columns_iter as $column) {
1046 16
                $limit = ! isset($limits[$column]) || $limits[$column] <= 0 ? '' : '(' . $limits[$column] . ')';
1047 5
                $def .= '`' . $column . '`' . $limit;
1048 5
                if ($columns_iter->hasNext()) {
1049
                    $def .= ',';
1050 16
                }
1051 1
            }
1052 1
            $def .= ' )';
1053
        }
1054 16
1055
        return $def;
1056 16
    }
1057 5
1058 5
    /**
1059
     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
1060 16
     *
1061
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
1062 16
     * @return string
1063
     */
1064 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...
1065
    {
1066
        $def = '';
1067
        if ($foreignKey->getConstraint()) {
1068
            $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...
1069
        }
1070
        $columnNames = [];
1071 17
        foreach ($foreignKey->getColumns() as $column) {
1072
            $columnNames[] = $this->quoteColumnName($column);
1073 17
        }
1074 17
        $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
1075 5
        $refColumnNames = [];
1076 5
        foreach ($foreignKey->getReferencedColumns() as $column) {
1077 17
            $refColumnNames[] = $this->quoteColumnName($column);
1078 17
        }
1079 17
        $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
1080 17
        if ($foreignKey->getOnDelete()) {
1081 17
            $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
1082 17
        }
1083 17
        if ($foreignKey->getOnUpdate()) {
1084 17
            $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
1085 17
        }
1086 17
1087 17
        return $def;
1088 2
    }
1089 2
1090 17
    /**
1091 2
     * Describes a database table. This is a MySQL adapter specific method.
1092 2
     *
1093 17
     * @param string $tableName Table name
1094
     * @return array
1095
     */
1096 View Code Duplication
    public function describeTable($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...
1097
    {
1098
        $options = $this->getOptions();
1099
1100
        // mysql specific
1101
        $sql = sprintf(
1102 2
            "SELECT *
1103
             FROM information_schema.tables
1104 2
             WHERE table_schema = '%s'
1105
             AND table_name = '%s'",
1106
            $options['name'],
1107 2
            $tableName
1108
        );
1109
1110
        return $this->fetchRow($sql);
1111 2
    }
1112 2
1113
    /**
1114 2
     * Returns MySQL column types (inherited and MySQL specified).
1115
     * @return array
1116 2
     */
1117
    public function getColumnTypes()
1118
    {
1119
        return array_merge(parent::getColumnTypes(), ['enum', 'set', 'year', 'json']);
1120
    }
1121
}
1122