Completed
Pull Request — master (#1352)
by
unknown
02:23
created

MysqlAdapter::getIndexSqlDefinition()   D

Complexity

Conditions 9
Paths 24

Size

Total Lines 38
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 38
ccs 22
cts 22
cp 1
rs 4.909
c 0
b 0
f 0
cc 9
eloc 24
nc 24
nop 1
crap 9
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('SHOW FULL COLUMNS FROM %s', $this->quoteTableName($tableName)));
402 18
403 18
        foreach ($rows as $row) {
404
            if (strcasecmp($row['Field'], $columnName) === 0) {
405 18
                $null = ($row['Null'] == 'NO') ? 'NOT NULL' : 'NULL';
406 2
                $comment = isset($row['Comment']) ? ' COMMENT ' . '\'' . addslashes($row['Comment']) . '\'' : '';
407 2
                $extra = ' ' . strtoupper($row['Extra']);
408
                if (!is_null($row['Default'])) {
409 18
                    $extra .= $this->getDefaultValueDefinition($row['Default']);
410 18
                }
411
                $definition = $row['Type'] . ' ' . $null . $extra . $comment;
412
413
                $alter = sprintf(
414
                    'CHANGE COLUMN %s %s %s',
415 7
                    $this->quoteColumnName($columnName),
416
                    $this->quoteColumnName($newColumnName),
417 7
                    $definition
418 7
                );
419 7
420 5
                return new AlterInstructions([$alter]);
421 5
            }
422 5
        }
423 1
424 1
        throw new \InvalidArgumentException(sprintf(
425 5
            'The specified column doesn\'t exist: ' .
426
            $columnName
427 5
        ));
428 5
    }
429 5
430 5
    /**
431 5
     * {@inheritdoc}
432 5
     */
433
    protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn)
434 5
    {
435 5
        $after = $newColumn->getAfter() ? ' AFTER ' . $this->quoteColumnName($newColumn->getAfter()) : '';
436 5
        $alter = sprintf(
437
            'CHANGE %s %s %s%s',
438 6
            $this->quoteColumnName($columnName),
439
            $this->quoteColumnName($newColumn->getName()),
440 2
            $this->getColumnSqlDefinition($newColumn),
441
            $after
442
        );
443 2
444
        return new AlterInstructions([$alter]);
445
    }
446
447
    /**
448
     * {@inheritdoc}
449 5
     */
450
    protected function getDropColumnInstructions($tableName, $columnName)
451 5
    {
452 5
        $alter = sprintf('DROP COLUMN %s', $this->quoteColumnName($columnName));
453 5
454 5
        return new AlterInstructions([$alter]);
455 5
    }
456 5
457 5
    /**
458 5
     * Get an array of indexes from a particular table.
459
     *
460 5
     * @param string $tableName Table Name
461 5
     * @return array
462 5
     */
463
    protected function getIndexes($tableName)
464
    {
465
        $indexes = [];
466
        $rows = $this->fetchAll(sprintf('SHOW INDEXES FROM %s', $this->quoteTableName($tableName)));
467 5
        foreach ($rows as $row) {
468
            if (!isset($indexes[$row['Key_name']])) {
469 5
                $indexes[$row['Key_name']] = ['columns' => []];
470 5
            }
471 5
            $indexes[$row['Key_name']]['columns'][] = strtolower($row['Column_name']);
472 5
        }
473 5
474 5
        return $indexes;
475 5
    }
476 5
477
    /**
478
     * {@inheritdoc}
479
     */
480 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...
481
    {
482
        if (is_string($columns)) {
483
            $columns = [$columns]; // str to array
484 19
        }
485
486 19
        $columns = array_map('strtolower', $columns);
487 19
        $indexes = $this->getIndexes($tableName);
488 19
489 18
        foreach ($indexes as $index) {
490 18
            if ($columns == $index['columns']) {
491 18
                return true;
492 18
            }
493 19
        }
494 19
495
        return false;
496
    }
497
498
    /**
499
     * {@inheritdoc}
500 14
     */
501 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...
502 14
    {
503 6
        $indexes = $this->getIndexes($tableName);
504 6
505
        foreach ($indexes as $name => $index) {
506 14
            if ($name === $indexName) {
507 14
                return true;
508
            }
509 14
        }
510 14
511 12
        return false;
512
    }
513 13
514
    /**
515 11
     * {@inheritdoc}
516
     */
517
    protected function getAddIndexInstructions(Table $table, Index $index)
518
    {
519
        $alter = sprintf(
520
            'ADD %s',
521 1
            $this->getIndexSqlDefinition($index)
522
        );
523 1
524
        return new AlterInstructions([$alter]);
525 1
    }
526 1
527 1
    /**
528
     * {@inheritdoc}
529 1
     */
530
    protected function getDropIndexByColumnsInstructions($tableName, $columns)
531
    {
532
        if (is_string($columns)) {
533
            $columns = [$columns]; // str to array
534
        }
535
536
        $indexes = $this->getIndexes($tableName);
537 4
        $columns = array_map('strtolower', $columns);
538
539 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...
540 4
            if ($columns == $index['columns']) {
541 4
                return new AlterInstructions([sprintf(
542 4
                    'DROP INDEX %s',
543 4
                    $this->quoteColumnName($indexName)
544 4
                )]);
545 4
            }
546 4
        }
547
548
        throw new \InvalidArgumentException(sprintf(
549
            "The specified index on columns '%s' does not exist",
550
            implode(',', $columns)
551 3
        ));
552
    }
553 3
554 2
    /**
555 2
     * {@inheritdoc}
556
     */
557 3
    protected function getDropIndexByNameInstructions($tableName, $indexName)
558 3
    {
559
560 3
        $indexes = $this->getIndexes($tableName);
561 3
562 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...
563 3
            if ($name === $indexName) {
564 3
                return new AlterInstructions([sprintf(
565 3
                    'DROP INDEX %s',
566 3
                    $this->quoteColumnName($indexName)
567 3
                )]);
568 3
            }
569 3
        }
570
571 3
        throw new \InvalidArgumentException(sprintf(
572 1
            "The specified index name '%s' does not exist",
573
            $indexName
574
        ));
575
    }
576
577 2
    /**
578
     * {@inheritdoc}
579 2
     */
580 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...
581 2
    {
582
        if (is_string($columns)) {
583 2
            $columns = [$columns]; // str to array
584 2
        }
585 2
        $foreignKeys = $this->getForeignKeys($tableName);
586 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...
587 2
            if (isset($foreignKeys[$constraint])) {
588 2
                return !empty($foreignKeys[$constraint]);
589 2
            }
590 2
591 2
            return false;
592
        } else {
593 2
            foreach ($foreignKeys as $key) {
594
                if ($columns == $key['columns']) {
595
                    return true;
596
                }
597
            }
598
599 21
            return false;
600
        }
601 21
    }
602 5
603 5
    /**
604 21
     * Get an array of foreign keys from a particular table.
605 21
     *
606 6
     * @param string $tableName Table Name
607 4
     * @return array
608
     */
609 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...
610
    {
611 15
        $foreignKeys = [];
612 12
        $rows = $this->fetchAll(sprintf(
613 10
            "SELECT
614
              CONSTRAINT_NAME,
615 11
              TABLE_NAME,
616 11
              COLUMN_NAME,
617
              REFERENCED_TABLE_NAME,
618
              REFERENCED_COLUMN_NAME
619
            FROM information_schema.KEY_COLUMN_USAGE
620
            WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
621
              AND REFERENCED_TABLE_NAME IS NOT NULL
622
              AND TABLE_NAME = '%s'
623
            ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
624
            $tableName
625
        ));
626 22
        foreach ($rows as $row) {
627
            $foreignKeys[$row['CONSTRAINT_NAME']]['table'] = $row['TABLE_NAME'];
628 22
            $foreignKeys[$row['CONSTRAINT_NAME']]['columns'][] = $row['COLUMN_NAME'];
629 22
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_table'] = $row['REFERENCED_TABLE_NAME'];
630
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_columns'][] = $row['REFERENCED_COLUMN_NAME'];
631
        }
632
633
        return $foreignKeys;
634
    }
635
636
    /**
637
     * {@inheritdoc}
638
     */
639 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...
640 22
    {
641
        $alter = sprintf(
642 22
            'ADD %s',
643 22
            $this->getForeignKeySqlDefinition($foreignKey)
644 19
        );
645 19
646 19
        return new AlterInstructions([$alter]);
647 19
    }
648 22
649 22
    /**
650
     * {@inheritdoc}
651
     */
652
    protected function getDropForeignKeyInstructions($tableName, $constraint)
653
    {
654
        $alter = sprintf(
655 15
            'DROP FOREIGN KEY %s',
656
            $constraint
657 15
        );
658 15
659 15
        return new AlterInstructions([$alter]);
660 15
    }
661 15
662 15
    /**
663 15
     * {@inheritdoc}
664 15
     */
665
    protected function getDropForeignKeyByColumnsInstructions($tableName, $columns)
666
    {
667
        $instructions = new AlterInstructions();
668
669 8 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...
670
            $rows = $this->fetchAll(sprintf(
671 8
                "SELECT
672 3
                    CONSTRAINT_NAME
673 3
                  FROM information_schema.KEY_COLUMN_USAGE
674
                  WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
675
                    AND REFERENCED_TABLE_NAME IS NOT NULL
676 8
                    AND TABLE_NAME = '%s'
677 8
                    AND COLUMN_NAME = '%s'
678 8
                  ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
679 8
                $tableName,
680 8
                $column
681
            ));
682 8
683 8
            foreach ($rows as $row) {
684 8
                $instructions->merge($this->getDropForeignKeyInstructions($tableName, $row['CONSTRAINT_NAME']));
685
            }
686 7
        }
687 7
688
        if (empty($instructions->getAlterParts())) {
689
            throw new \InvalidArgumentException(sprintf(
690
                "Not foreign key on columns '%s' exist",
691
                implode(',', $columns)
692
            ));
693
        }
694
695 7
        return $instructions;
696 7
    }
697
698 7
    /**
699 7
     * {@inheritdoc}
700 7
     */
701 7
    public function getSqlType($type, $limit = null)
702 7
    {
703
        switch ($type) {
704 7
            case static::PHINX_TYPE_FLOAT:
705
            case static::PHINX_TYPE_DECIMAL:
706
            case static::PHINX_TYPE_DATE:
707
            case static::PHINX_TYPE_ENUM:
708
            case static::PHINX_TYPE_SET:
709 96
            case static::PHINX_TYPE_JSON:
710
            // Geospatial database types
711
            case static::PHINX_TYPE_GEOMETRY:
712 96
            case static::PHINX_TYPE_POINT:
713 87
            case static::PHINX_TYPE_LINESTRING:
714
            case static::PHINX_TYPE_POLYGON:
715 96
                return ['name' => $type];
716 4
            case static::PHINX_TYPE_DATETIME:
717
            case static::PHINX_TYPE_TIMESTAMP:
718 96
            case static::PHINX_TYPE_TIME:
719 9
                return ['name' => $type, 'limit' => $limit];
720
            case static::PHINX_TYPE_STRING:
721
                return ['name' => 'varchar', 'limit' => $limit ?: 255];
722 6
            case static::PHINX_TYPE_CHAR:
723 6
                return ['name' => 'char', 'limit' => $limit ?: 255];
724 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...
725 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...
726 6
                    $sizes = [
727 6
                        // Order matters! Size must always be tested from longest to shortest!
728 6
                        'longtext' => static::TEXT_LONG,
729 6
                        'mediumtext' => static::TEXT_MEDIUM,
730
                        'text' => static::TEXT_REGULAR,
731 5
                        'tinytext' => static::TEXT_SMALL,
732
                    ];
733 5
                    foreach ($sizes as $name => $length) {
734
                        if ($limit >= $length) {
735 95
                            return ['name' => $name];
736 5
                        }
737
                    }
738 95
                }
739 3
740
                return ['name' => 'text'];
741 95
            case static::PHINX_TYPE_BINARY:
742 1
                return ['name' => 'binary', 'limit' => $limit ?: 255];
743
            case static::PHINX_TYPE_VARBINARY:
744
                return ['name' => 'varbinary', 'limit' => $limit ?: 255];
745 1 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...
746 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...
747 1
                    $sizes = [
748 1
                        // Order matters! Size must always be tested from longest to shortest!
749 1
                        'longblob' => static::BLOB_LONG,
750 1
                        'mediumblob' => static::BLOB_MEDIUM,
751 1
                        'blob' => static::BLOB_REGULAR,
752 1
                        'tinyblob' => static::BLOB_SMALL,
753
                    ];
754 1
                    foreach ($sizes as $name => $length) {
755
                        if ($limit >= $length) {
756 1
                            return ['name' => $name];
757
                        }
758 95
                    }
759 82
                }
760
761
                return ['name' => 'blob'];
762 6
            case static::PHINX_TYPE_BIT:
763 6
                return ['name' => 'bit', 'limit' => $limit ?: 64];
764 6
            case static::PHINX_TYPE_INTEGER:
765 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...
766 6
                    $sizes = [
767 6
                        // Order matters! Size must always be tested from longest to shortest!
768
                        'bigint' => static::INT_BIG,
769 6
                        'int' => static::INT_REGULAR,
770 6
                        'mediumint' => static::INT_MEDIUM,
771 6
                        'smallint' => static::INT_SMALL,
772 6
                        'tinyint' => static::INT_TINY,
773 6
                    ];
774 6
                    $limits = [
775 6
                        'int' => 11,
776 2
                        'bigint' => 20,
777 2
                    ];
778 6
                    foreach ($sizes as $name => $length) {
779
                        if ($limit >= $length) {
780 5
                            $def = ['name' => $name];
781 82
                            if (isset($limits[$name])) {
782 76
                                $def['limit'] = $limits[$name];
783 76
                            }
784 82
785
                            return $def;
786 86
                        }
787 82
                    }
788
                } 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...
789 86
                    $limit = 11;
790 7
                }
791
792 84
                return ['name' => 'int', 'limit' => $limit];
793 5
            case static::PHINX_TYPE_BIG_INTEGER:
794
                return ['name' => 'bigint', 'limit' => 20];
795 83
            case static::PHINX_TYPE_BOOLEAN:
796 7
                return ['name' => 'tinyint', 'limit' => 1];
797
            case static::PHINX_TYPE_UUID:
798 83
                return ['name' => 'char', 'limit' => 36];
799 80
            case static::TYPE_YEAR:
800
                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...
801 83
                    $limit = 4;
802 4
                }
803
804 83
                return ['name' => 'year', 'limit' => $limit];
805 4
            default:
806
                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
807 83
        }
808 80
    }
809
810 10
    /**
811 2
     * Returns Phinx type by SQL type
812
     *
813 10
     * @param string $sqlTypeDef
814 10
     * @throws \RuntimeException
815 10
     * @internal param string $sqlType SQL type
816 10
     * @returns string Phinx type
817 5
     */
818 8
    public function getPhinxType($sqlTypeDef)
819 5
    {
820
        $matches = [];
821 6
        if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*(.+)*$/', $sqlTypeDef, $matches)) {
822 4
            throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported');
823
        } else {
824 2
            $limit = null;
825
            $precision = null;
826
            $type = $matches[1];
827
            if (count($matches) > 2) {
828
                $limit = $matches[3] ? (int)$matches[3] : null;
829
            }
830 2
            if (count($matches) > 4) {
831
                $precision = (int)$matches[5];
832
            }
833 2 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...
834 2
                $type = static::PHINX_TYPE_BOOLEAN;
835 2
                $limit = null;
836
            }
837
            switch ($type) {
838
                case 'varchar':
839
                    $type = static::PHINX_TYPE_STRING;
840
                    if ($limit === 255) {
841
                        $limit = null;
842
                    }
843
                    break;
844 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...
845
                    $type = static::PHINX_TYPE_CHAR;
846 17
                    if ($limit === 255) {
847
                        $limit = null;
848 17
                    }
849 17
                    if ($limit === 36) {
850 1
                        $type = static::PHINX_TYPE_UUID;
851
                    }
852 16
                    break;
853 16
                case 'tinyint':
854 16
                    $type = static::PHINX_TYPE_INTEGER;
855 16
                    $limit = static::INT_TINY;
856 14
                    break;
857 14
                case 'smallint':
858 16
                    $type = static::PHINX_TYPE_INTEGER;
859 4
                    $limit = static::INT_SMALL;
860 4
                    break;
861 16
                case 'mediumint':
862 3
                    $type = static::PHINX_TYPE_INTEGER;
863 3
                    $limit = static::INT_MEDIUM;
864 3
                    break;
865
                case 'int':
866 16
                    $type = static::PHINX_TYPE_INTEGER;
867 6
                    if ($limit === 11) {
868 6
                        $limit = null;
869 3
                    }
870 3
                    break;
871 6
                case 'bigint':
872 16
                    if ($limit === 20) {
873 5
                        $limit = null;
874 5
                    }
875 1
                    $type = static::PHINX_TYPE_BIG_INTEGER;
876 1
                    break;
877 5
                case 'bit':
878 2
                    $type = static::PHINX_TYPE_BIT;
879 2
                    if ($limit === 64) {
880 5
                        $limit = null;
881 16
                    }
882 2
                    break;
883 2
                case 'blob':
884 2
                    $type = static::PHINX_TYPE_BINARY;
885 16
                    break;
886 2
                case 'tinyblob':
887 2
                    $type = static::PHINX_TYPE_BINARY;
888 2
                    $limit = static::BLOB_TINY;
889 16
                    break;
890 2
                case 'mediumblob':
891 2
                    $type = static::PHINX_TYPE_BINARY;
892 2
                    $limit = static::BLOB_MEDIUM;
893 16
                    break;
894 15
                case 'longblob':
895 15
                    $type = static::PHINX_TYPE_BINARY;
896 12
                    $limit = static::BLOB_LONG;
897 12
                    break;
898 15
                case 'tinytext':
899 11
                    $type = static::PHINX_TYPE_TEXT;
900 6
                    $limit = static::TEXT_TINY;
901 4
                    break;
902 4
                case 'mediumtext':
903 6
                    $type = static::PHINX_TYPE_TEXT;
904 6
                    $limit = static::TEXT_MEDIUM;
905 10
                    break;
906 2
                case 'longtext':
907 2
                    $type = static::PHINX_TYPE_TEXT;
908 10
                    $limit = static::TEXT_LONG;
909 1
                    break;
910 1
            }
911 1
912 10
            // Call this to check if parsed type is supported.
913 1
            $this->getSqlType($type, $limit);
914 1
915 1
            $phinxType = [
916 10
                'name' => $type,
917 1
                'limit' => $limit,
918 1
                'precision' => $precision
919 1
            ];
920 10
921 2
            if (static::PHINX_TYPE_ENUM == $type) {
922 2
                $phinxType['values'] = explode("','", trim($matches[6], "()'"));
923 2
            }
924 9
925 2
            return $phinxType;
926 2
        }
927 2
    }
928 8
929 2
    /**
930 2
     * {@inheritdoc}
931 2
     */
932
    public function createDatabase($name, $options = [])
933
    {
934
        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
935 16
936 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...
937
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s` COLLATE `%s`', $name, $charset, $options['collation']));
938 15
        } else {
939 15
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s`', $name, $charset));
940
        }
941 15
    }
942
943 15
    /**
944 3
     * {@inheritdoc}
945 3
     */
946
    public function hasDatabase($name)
947 15
    {
948
        $rows = $this->fetchAll(
949
            sprintf(
950
                'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = \'%s\'',
951
                $name
952
            )
953
        );
954 83
955
        foreach ($rows as $row) {
956 83
            if (!empty($row)) {
957
                return true;
958 83
            }
959 1
        }
960 1
961 82
        return false;
962
    }
963 83
964
    /**
965
     * {@inheritdoc}
966
     */
967
    public function dropDatabase($name)
968 4
    {
969
        $this->execute(sprintf('DROP DATABASE IF EXISTS `%s`', $name));
970 4
    }
971 4
972 4
    /**
973
     * Gets the MySQL Column Definition for a Column object.
974 4
     *
975 4
     * @param \Phinx\Db\Table\Column $column Column
976
     * @return string
977 4
     */
978 3
    protected function getColumnSqlDefinition(Column $column)
979 3
    {
980
        if ($column->getType() instanceof Literal) {
981 3
            $def = (string)$column->getType();
982
        } else {
983 3
            $sqlType = $this->getSqlType($column->getType(), $column->getLimit());
984
            $def = strtoupper($sqlType['name']);
985
        }
986
        if ($column->getPrecision() && $column->getScale()) {
987
            $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
988
        } elseif (isset($sqlType['limit'])) {
989 81
            $def .= '(' . $sqlType['limit'] . ')';
990
        }
991 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...
992 81
            $def .= "('" . implode("', '", $values) . "')";
993
        }
994
        $def .= $column->getEncoding() ? ' CHARACTER SET ' . $column->getEncoding() : '';
995
        $def .= $column->getCollation() ? ' COLLATE ' . $column->getCollation() : '';
996
        $def .= (!$column->isSigned() && isset($this->signedColumnTypes[$column->getType()])) ? ' unsigned' : '';
997
        $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...
998
        $def .= $column->isIdentity() ? ' AUTO_INCREMENT' : '';
999
        $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...
1000 89
1001
        if ($column->getComment()) {
1002 89
            $def .= ' COMMENT ' . $this->getConnection()->quote($column->getComment());
1003
        }
1004 89
1005 89
        if ($column->getUpdate()) {
1006 89
            $def .= ' ON UPDATE ' . $column->getUpdate();
1007 2
        }
1008 89
1009 86
        return $def;
1010 86
    }
1011 89
1012 5
    /**
1013 5
     * Gets the MySQL Index Definition for an Index object.
1014 89
     *
1015 89
     * @param \Phinx\Db\Table\Index $index Index
1016 89
     * @return string
1017 89
     */
1018 89
    protected function getIndexSqlDefinition(Index $index)
1019 89
    {
1020
        $def = '';
1021 89
        $limit = '';
1022 2
1023 2
        if ($index->getType() == Index::UNIQUE) {
1024
            $def .= ' UNIQUE';
1025 89
        }
1026 1
1027 1
        if ($index->getType() == Index::FULLTEXT) {
1028
            $def .= ' FULLTEXT';
1029 89
        }
1030
1031
        $def .= ' KEY';
1032
1033
        if (is_string($index->getName())) {
1034
            $def .= ' `' . $index->getName() . '`';
1035
        }
1036
1037
        if (!is_array($index->getLimit())) {
1038 16
            if ($index->getLimit()) {
1039
                $limit = '(' . $index->getLimit() . ')';
1040 16
            }
1041 16
            $def .= ' (`' . implode('`,`', $index->getColumns()) . '`' . $limit . ')';
1042 16
        } else {
1043 2
            $columns = $index->getColumns();
1044 2
            $limits = $index->getLimit();
1045
            $def .= ' (';
1046 16
            foreach ($columns as $column) {
1047 5
                $limit = !isset($limits[$column]) || $limits[$column] <= 0 ? '' : '(' . $limits[$column] . ')';
1048 5
                $def .= '`' . $column . '`' . $limit.', ';
1049
            }
1050 16
            $def = rtrim($def,', ');
1051 1
            $def .= ' )';
1052 1
        }
1053
1054 16
        return $def;
1055
    }
1056 16
1057 5
    /**
1058 5
     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
1059
     *
1060 16
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
1061
     * @return string
1062 16
     */
1063 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...
1064
    {
1065
        $def = '';
1066
        if ($foreignKey->getConstraint()) {
1067
            $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...
1068
        }
1069
        $columnNames = [];
1070
        foreach ($foreignKey->getColumns() as $column) {
1071 17
            $columnNames[] = $this->quoteColumnName($column);
1072
        }
1073 17
        $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
1074 17
        $refColumnNames = [];
1075 5
        foreach ($foreignKey->getReferencedColumns() as $column) {
1076 5
            $refColumnNames[] = $this->quoteColumnName($column);
1077 17
        }
1078 17
        $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
1079 17
        if ($foreignKey->getOnDelete()) {
1080 17
            $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
1081 17
        }
1082 17
        if ($foreignKey->getOnUpdate()) {
1083 17
            $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
1084 17
        }
1085 17
1086 17
        return $def;
1087 17
    }
1088 2
1089 2
    /**
1090 17
     * Describes a database table. This is a MySQL adapter specific method.
1091 2
     *
1092 2
     * @param string $tableName Table name
1093 17
     * @return array
1094
     */
1095 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...
1096
    {
1097
        $options = $this->getOptions();
1098
1099
        // mysql specific
1100
        $sql = sprintf(
1101
            "SELECT *
1102 2
             FROM information_schema.tables
1103
             WHERE table_schema = '%s'
1104 2
             AND table_name = '%s'",
1105
            $options['name'],
1106
            $tableName
1107 2
        );
1108
1109
        return $this->fetchRow($sql);
1110
    }
1111 2
1112 2
    /**
1113
     * Returns MySQL column types (inherited and MySQL specified).
1114 2
     * @return array
1115
     */
1116 2
    public function getColumnTypes()
1117
    {
1118
        return array_merge(parent::getColumnTypes(), ['enum', 'set', 'year', 'json']);
1119
    }
1120
}
1121