Completed
Pull Request — master (#1351)
by José
03:47 queued 01:44
created

MysqlAdapter::getIndexSqlDefinition()   D

Complexity

Conditions 9
Paths 16

Size

Total Lines 38
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 38
ccs 18
cts 18
cp 1
rs 4.909
c 0
b 0
f 0
cc 9
eloc 24
nc 16
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
30
namespace Phinx\Db\Adapter;
31
32
use Phinx\Db\Table\Column;
33
use Phinx\Db\Table\ForeignKey;
34
use Phinx\Db\Table\Index;
35
use Phinx\Db\Table\Table;
36
use Phinx\Db\Util\AlterInstructions;
37
use Phinx\Util\Literal;
38
39
/**
40
 * Phinx MySQL Adapter.
41
 *
42
 * @author Rob Morgan <[email protected]>
43
 */
44
class MysqlAdapter extends PdoAdapter implements AdapterInterface
45
{
46
47
    protected $signedColumnTypes = [
48
        'integer'    => true,
49
        'biginteger' => true,
50
        'float'      => true,
51
        'decimal'    => true,
52
        'boolean'    => true
53
    ];
54
55
    const TEXT_TINY = 255;
56
    const TEXT_SMALL = 255; /* deprecated, alias of TEXT_TINY */
57
    const TEXT_REGULAR = 65535;
58
    const TEXT_MEDIUM = 16777215;
59
    const TEXT_LONG = 4294967295;
60
61
    // According to https://dev.mysql.com/doc/refman/5.0/en/blob.html BLOB sizes are the same as TEXT
62
    const BLOB_TINY = 255;
63
    const BLOB_SMALL = 255; /* deprecated, alias of BLOB_TINY */
64
    const BLOB_REGULAR = 65535;
65
    const BLOB_MEDIUM = 16777215;
66
    const BLOB_LONG = 4294967295;
67
68
    const INT_TINY = 255;
69
    const INT_SMALL = 65535;
70 80
    const INT_MEDIUM = 16777215;
71
    const INT_REGULAR = 4294967295;
72 80
    const INT_BIG = 18446744073709551615;
73 80
74
    const BIT = 64;
75
76
    const TYPE_YEAR = 'year';
77
78
    /**
79 80
     * {@inheritdoc}
80 80
     */
81
    public function connect()
82 80
    {
83
        if ($this->connection === null) {
84 80
            if (! class_exists('PDO') || ! in_array('mysql', \PDO::getAvailableDrivers(), true)) {
85
                // @codeCoverageIgnoreStart
86
                throw new \RuntimeException('You need to enable the PDO_Mysql extension for Phinx to run properly.');
87
                // @codeCoverageIgnoreEnd
88
            }
89 80
90 80
            $db      = null;
91 80
            $options = $this->getOptions();
92 80
93
            $dsn = 'mysql:';
94
95 80
            if (! empty($options['unix_socket'])) {
96
                // use socket connection
97
                $dsn .= 'unix_socket=' . $options['unix_socket'];
98 80
            } else {
99
                // use network connection
100
                $dsn .= 'host=' . $options['host'];
101
                if (! empty($options['port'])) {
102 80
                    $dsn .= ';port=' . $options['port'];
103
                }
104
            }
105
106 80
            $dsn .= ';dbname=' . $options['name'];
107 80
108
            // charset support
109
            if (! empty($options['charset'])) {
110 80
                $dsn .= ';charset=' . $options['charset'];
111
            }
112
113 80
            $driverOptions = [ \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION ];
114 80
115 1
            // support arbitrary \PDO::MYSQL_ATTR_* driver options and pass them to PDO
116 1
            // http://php.net/manual/en/ref.pdo-mysql.php#pdo-mysql.constants
117 1 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...
118 1
                if (strpos($key, 'mysql_attr_') === 0) {
119
                    $driverOptions[constant('\PDO::' . strtoupper($key))] = $option;
120
                }
121 80
            }
122 80
123 80
            try {
124
                $db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions);
125
            } catch (\PDOException $exception) {
126
                throw new \InvalidArgumentException(sprintf(
127
                    'There was a problem connecting to the database: %s',
128 81
                    $exception->getMessage()
129
                ));
130 81
            }
131 81
132
            $this->setConnection($db);
133
        }
134
    }
135
136 6
    /**
137
     * {@inheritdoc}
138 6
     */
139
    public function disconnect()
140
    {
141
        $this->connection = null;
142
    }
143
144 6
    /**
145
     * {@inheritdoc}
146 6
     */
147 6
    public function hasTransactions()
148
    {
149
        return true;
150
    }
151
152 6
    /**
153
     * {@inheritdoc}
154 6
     */
155 6
    public function beginTransaction()
156
    {
157
        $this->execute('START TRANSACTION');
158
    }
159
160 1
    /**
161
     * {@inheritdoc}
162 1
     */
163 1
    public function commitTransaction()
164
    {
165
        $this->execute('COMMIT');
166
    }
167
168 112
    /**
169
     * {@inheritdoc}
170 112
     */
171
    public function rollbackTransaction()
172
    {
173
        $this->execute('ROLLBACK');
174
    }
175
176 112
    /**
177
     * {@inheritdoc}
178 112
     */
179
    public function quoteTableName($tableName)
180
    {
181
        return str_replace('.', '`.`', $this->quoteColumnName($tableName));
182
    }
183
184 82
    /**
185
     * {@inheritdoc}
186 82
     */
187
    public function quoteColumnName($columnName)
188 82
    {
189
        return '`' . str_replace('`', '``', $columnName) . '`';
190
    }
191 82
192 82
    /**
193
     * {@inheritdoc}
194 82
     */
195 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...
196 82
    {
197
        $options = $this->getOptions();
198
199
        $exists = $this->fetchRow(sprintf(
200
            "SELECT TABLE_NAME
201
            FROM INFORMATION_SCHEMA.TABLES
202 82
            WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'",
203
            $options['name'],
204
            $tableName
205
        ));
206 82
207
        return ! empty($exists);
208 82
    }
209 82
210
    /**
211
     * {@inheritdoc}
212 82
     */
213 82
    public function createTable(Table $table, array $columns = [], array $indexes = [])
214 68
    {
215 68
        // This method is based on the MySQL docs here: http://dev.mysql.com/doc/refman/5.1/en/create-index.html
216 68
        $defaultOptions = [
217 68
            'engine'    => 'InnoDB',
218 68
            'collation' => 'utf8_general_ci'
219
        ];
220 68
221 68
        $options = array_merge(
222 82
            $defaultOptions,
223
            array_intersect_key($this->getOptions(), $defaultOptions),
224 2
            $table->getOptions()
225 2
        );
226 2
227 2
        // Add the default primary key
228
        if (! isset($options['id']) || ( isset($options['id']) && $options['id'] === true )) {
229 2
            $column = new Column();
230 2
            $column->setName('id')
231 2
                   ->setType('integer')
232
                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
233
                   ->setIdentity(true);
234
235
            array_unshift($columns, $column);
236 82
            $options['primary_key'] = 'id';
237 82
        } elseif (isset($options['id']) && is_string($options['id'])) {
238 82
            // Handle id => "field_name" to support AUTO_INCREMENT
239 82
            $column = new Column();
240
            $column->setName($options['id'])
241
                   ->setType('integer')
242 82
                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
243 82
                   ->setIdentity(true);
244 82
245 82
            array_unshift($columns, $column);
246 82
            $options['primary_key'] = $options['id'];
247
        }
248
249 82
        // TODO - process table options like collation etc
250 2
251 2
        // process table engine (default to InnoDB)
252
        $optionsStr = 'ENGINE = InnoDB';
253 82
        if (isset($options['engine'])) {
254 82
            $optionsStr = sprintf('ENGINE = %s', $options['engine']);
255 82
        }
256 82
257 82
        // process table collation
258
        if (isset($options['collation'])) {
259
            $charset    = explode('_', $options['collation']);
260 82
            $optionsStr .= sprintf(' CHARACTER SET %s', $charset[0]);
261 82
            $optionsStr .= sprintf(' COLLATE %s', $options['collation']);
262 82
        }
263 82
264 81
        // set the table comment
265 82
        if (isset($options['comment'])) {
266
            $optionsStr .= sprintf(" COMMENT=%s ", $this->getConnection()->quote($options['comment']));
267
        }
268 2
269 2
        $sql = 'CREATE TABLE ';
270 2
        $sql .= $this->quoteTableName($table->getName()) . ' (';
271 2
        foreach ($columns as $column) {
272 2
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
273 2
        }
274 2
275 2
        // set the primary key(s)
276 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...
277 2
            $sql = rtrim($sql);
278 82
            $sql .= ' PRIMARY KEY (';
279 82
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
280 1
                $sql .= $this->quoteColumnName($options['primary_key']);
281
            } 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...
282
                $sql .= implode(',', array_map([ $this, 'quoteColumnName' ], $options['primary_key']));
283
            }
284 82
            $sql .= ')';
285 82
        } else {
286 10
            $sql = substr(rtrim($sql), 0, - 1); // no primary keys
287 82
        }
288
289
        // set the indexes
290 82
        foreach ($indexes as $index) {
291 82
            $sql .= ', ' . $this->getIndexSqlDefinition($index);
292 2
        }
293 82
294
        $sql .= ') ' . $optionsStr;
295 82
        $sql = rtrim($sql) . ';';
296 82
297
        // execute the sql
298
        $this->execute($sql);
299 82
    }
300 82
301
    /**
302
     * {@inheritdoc}
303
     */
304 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...
305 5
    {
306
        $sql = sprintf(
307 5
            'RENAME TABLE %s TO %s',
308 5
            $this->quoteTableName($tableName),
309
            $this->quoteTableName($newTableName)
310
        );
311
312
        return new AlterInstructions([], [ $sql ]);
313 5
    }
314
315 5
    /**
316 5
     * {@inheritdoc}
317
     */
318
    protected function getDropTableInstructions($tableName)
319
    {
320
        $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName));
321 1
322
        return new AlterInstructions([], [ $sql ]);
323 1
    }
324 1
325 1
    /**
326 1
     * {@inheritdoc}
327
     */
328 1
    public function truncateTable($tableName)
329 1
    {
330
        $sql = sprintf(
331
            'TRUNCATE TABLE %s',
332
            $this->quoteTableName($tableName)
333
        );
334 12
335
        $this->execute($sql);
336 12
    }
337 12
338 12
    /**
339 12
     * {@inheritdoc}
340
     */
341 12
    public function getColumns($tableName)
342 12
    {
343 12
        $columns = [];
344 12
        $rows    = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
345 12
        foreach ($rows as $columnInfo) {
346 12
            $phinxType = $this->getPhinxType($columnInfo['Type']);
347
348 12
            $column = new Column();
349 12
            $column->setName($columnInfo['Field'])
350 12
                   ->setNull($columnInfo['Null'] !== 'NO')
351
                   ->setDefault($columnInfo['Default'])
352 12
                   ->setType($phinxType['name'])
353 3
                   ->setSigned(strpos($columnInfo['Type'], 'unsigned') === false)
354 3
                   ->setLimit($phinxType['limit']);
355
356 12
            if ($columnInfo['Extra'] === 'auto_increment') {
357 12
                $column->setIdentity(true);
358
            }
359 12
360
            if (isset($phinxType['values'])) {
361
                $column->setValues($phinxType['values']);
362
            }
363
364
            $columns[] = $column;
365 79
        }
366
367 79
        return $columns;
368 79
    }
369 79
370 77
    /**
371
     * {@inheritdoc}
372 77
     */
373 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...
374 21
    {
375
        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
376
        foreach ($rows as $column) {
377
            if (strcasecmp($column['Field'], $columnName) === 0) {
378
                return true;
379
            }
380
        }
381
382
        return false;
383 95
    }
384
385 95
    /**
386 10
     * {@inheritdoc}
387 95
     */
388 79
    protected function getAddColumnInstructions(Table $table, Column $column)
389 79
    {
390 95
        $alter = sprintf(
391
            'ADD %s %s',
392
            $this->quoteColumnName($column->getName()),
393
            $this->getColumnSqlDefinition($column)
394
        );
395
396 18
        if ($column->getAfter()) {
397
            $alter .= ' AFTER ' . $this->quoteColumnName($column->getAfter());
398 18
        }
399 18
400 18
        return new AlterInstructions([ $alter ]);
401 18
    }
402 18
403 18
    /**
404
     * {@inheritdoc}
405 18
     */
406 2
    protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName)
407 2
    {
408
        $rows = $this->fetchAll(sprintf('DESCRIBE %s', $this->quoteTableName($tableName)));
409 18
        foreach ($rows as $row) {
410 18
            if (strcasecmp($row['Field'], $columnName) === 0) {
411
                $null  = ( $row['Null'] == 'NO' ) ? 'NOT NULL' : 'NULL';
412
                $extra = ' ' . strtoupper($row['Extra']);
413
                if (! is_null($row['Default'])) {
414
                    $extra .= $this->getDefaultValueDefinition($row['Default']);
415 7
                }
416
                $definition = $row['Type'] . ' ' . $null . $extra;
417 7
418 7
                $alter = sprintf(
419 7
                    'CHANGE COLUMN %s %s %s',
420 5
                    $this->quoteColumnName($columnName),
421 5
                    $this->quoteColumnName($newColumnName),
422 5
                    $definition
423 1
                );
424 1
425 5
                return new AlterInstructions([ $alter ]);
426
            }
427 5
        }
428 5
429 5
        throw new \InvalidArgumentException(sprintf(
430 5
            'The specified column doesn\'t exist: ' .
431 5
            $columnName
432 5
        ));
433
    }
434 5
435 5
    /**
436 5
     * {@inheritdoc}
437
     */
438 6
    protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn)
439
    {
440 2
        $after = $newColumn->getAfter() ? ' AFTER ' . $this->quoteColumnName($newColumn->getAfter()) : '';
441
        $alter = sprintf(
442
            'CHANGE %s %s %s%s',
443 2
            $this->quoteColumnName($columnName),
444
            $this->quoteColumnName($newColumn->getName()),
445
            $this->getColumnSqlDefinition($newColumn),
446
            $after
447
        );
448
449 5
        return new AlterInstructions([ $alter ]);
450
    }
451 5
452 5
    /**
453 5
     * {@inheritdoc}
454 5
     */
455 5
    protected function getDropColumnInstructions($tableName, $columnName)
456 5
    {
457 5
        $alter = sprintf('DROP COLUMN %s', $this->quoteColumnName($columnName));
458 5
459
        return new AlterInstructions([ $alter ]);
460 5
    }
461 5
462 5
    /**
463
     * Get an array of indexes from a particular table.
464
     *
465
     * @param string $tableName Table Name
466
     *
467 5
     * @return array
468
     */
469 5 View Code Duplication
    protected function getIndexes($tableName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
470 5
    {
471 5
        $indexes = [];
472 5
        $rows    = $this->fetchAll(sprintf('SHOW INDEXES FROM %s', $this->quoteTableName($tableName)));
473 5
        foreach ($rows as $row) {
474 5
            if (! isset($indexes[$row['Key_name']])) {
475 5
                $indexes[$row['Key_name']] = [ 'columns' => [] ];
476 5
            }
477
            $indexes[$row['Key_name']]['columns'][] = strtolower($row['Column_name']);
478
        }
479
480
        return $indexes;
481
    }
482
483
    /**
484 19
     * {@inheritdoc}
485
     */
486 19
    public function hasIndex($tableName, $columns)
487 19
    {
488 19
        if (is_string($columns)) {
489 18
            $columns = [ $columns ]; // str to array
490 18
        }
491 18
492 18
        $columns = array_map('strtolower', $columns);
493 19
        $indexes = $this->getIndexes($tableName);
494 19
495
        foreach ($indexes as $index) {
496
            if ($columns == $index['columns']) {
497
                return true;
498
            }
499
        }
500 14
501
        return false;
502 14
    }
503 6
504 6
    /**
505
     * {@inheritdoc}
506 14
     */
507 14 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...
508
    {
509 14
        $indexes = $this->getIndexes($tableName);
510 14
511 12
        foreach ($indexes as $name => $index) {
512
            if ($name === $indexName) {
513 13
                return true;
514
            }
515 11
        }
516
517
        return false;
518
    }
519
520
    /**
521 1
     * {@inheritdoc}
522
     */
523 1
    protected function getAddIndexInstructions(Table $table, Index $index)
524
    {
525 1
        $alter = sprintf(
526 1
            'ADD %s',
527 1
            $this->getIndexSqlDefinition($index)
528
        );
529 1
530
        return new AlterInstructions([ $alter ]);
531
    }
532
533
    /**
534
     * {@inheritdoc}
535
     */
536
    protected function getDropIndexByColumnsInstructions($tableName, $columns)
537 4
    {
538
        if (is_string($columns)) {
539 4
            $columns = [ $columns ]; // str to array
540 4
        }
541 4
542 4
        $indexes = $this->getIndexes($tableName);
543 4
        $columns = array_map('strtolower', $columns);
544 4
545 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...
546 4
            if ($columns == $index['columns']) {
547
                return new AlterInstructions([
548
                    sprintf(
549
                        'DROP INDEX %s',
550
                        $this->quoteColumnName($indexName)
551 3
                    )
552
                ]);
553 3
            }
554 2
        }
555 2
556
        throw new \InvalidArgumentException(sprintf(
557 3
            "The specified index on columns '%s' does not exist",
558 3
            implode(',', $columns)
559
        ));
560 3
    }
561 3
562 3
    /**
563 3
     * {@inheritdoc}
564 3
     */
565 3
    protected function getDropIndexByNameInstructions($tableName, $indexName)
566 3
    {
567 3
568 3
        $indexes = $this->getIndexes($tableName);
569 3
570 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...
571 3
            if ($name === $indexName) {
572 1
                return new AlterInstructions([
573
                    sprintf(
574
                        'DROP INDEX %s',
575
                        $this->quoteColumnName($indexName)
576
                    )
577 2
                ]);
578
            }
579 2
        }
580
581 2
        throw new \InvalidArgumentException(sprintf(
582
            "The specified index name '%s' does not exist",
583 2
            $indexName
584 2
        ));
585 2
    }
586 2
587 2
    /**
588 2
     * {@inheritdoc}
589 2
     */
590 2 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...
591 2
    {
592
        if (is_string($columns)) {
593 2
            $columns = [ $columns ]; // str to array
594
        }
595
        $foreignKeys = $this->getForeignKeys($tableName);
596
        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...
597
            if (isset($foreignKeys[$constraint])) {
598
                return ! empty($foreignKeys[$constraint]);
599 21
            }
600
601 21
            return false;
602 5
        } else {
603 5
            foreach ($foreignKeys as $key) {
604 21
                if ($columns == $key['columns']) {
605 21
                    return true;
606 6
                }
607 4
            }
608
609 4
            return false;
610
        }
611 15
    }
612 12
613 10
    /**
614
     * Get an array of foreign keys from a particular table.
615 11
     *
616 11
     * @param string $tableName Table Name
617
     *
618
     * @return array
619
     */
620 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...
621
    {
622
        $foreignKeys = [];
623
        $rows        = $this->fetchAll(sprintf(
624
            "SELECT
625
              CONSTRAINT_NAME,
626 22
              TABLE_NAME,
627
              COLUMN_NAME,
628 22
              REFERENCED_TABLE_NAME,
629 22
              REFERENCED_COLUMN_NAME
630
            FROM information_schema.KEY_COLUMN_USAGE
631
            WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
632
              AND REFERENCED_TABLE_NAME IS NOT NULL
633
              AND TABLE_NAME = '%s'
634
            ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
635
            $tableName
636
        ));
637
        foreach ($rows as $row) {
638
            $foreignKeys[$row['CONSTRAINT_NAME']]['table']                = $row['TABLE_NAME'];
639
            $foreignKeys[$row['CONSTRAINT_NAME']]['columns'][]            = $row['COLUMN_NAME'];
640 22
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_table']     = $row['REFERENCED_TABLE_NAME'];
641
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_columns'][] = $row['REFERENCED_COLUMN_NAME'];
642 22
        }
643 22
644 19
        return $foreignKeys;
645 19
    }
646 19
647 19
    /**
648 22
     * {@inheritdoc}
649 22
     */
650 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...
651
    {
652
        $alter = sprintf(
653
            'ADD %s',
654
            $this->getForeignKeySqlDefinition($foreignKey)
655 15
        );
656
657 15
        return new AlterInstructions([ $alter ]);
658 15
    }
659 15
660 15
    /**
661 15
     * {@inheritdoc}
662 15
     */
663 15
    protected function getDropForeignKeyInstructions($tableName, $constraint)
664 15
    {
665
        $alter = sprintf(
666
            'DROP FOREIGN KEY %s',
667
            $constraint
668
        );
669 8
670
        return new AlterInstructions([ $alter ]);
671 8
    }
672 3
673 3
    /**
674
     * {@inheritdoc}
675
     */
676 8
    protected function getDropForeignKeyByColumnsInstructions($tableName, $columns)
677 8
    {
678 8
        $instructions = new AlterInstructions();
679 8
680 8
        foreach ($columns as $column) {
681
            $rows = $this->fetchAll(sprintf(
682 8
                "SELECT
683 8
                    CONSTRAINT_NAME
684 8
                  FROM information_schema.KEY_COLUMN_USAGE
685
                  WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
686 7
                    AND REFERENCED_TABLE_NAME IS NOT NULL
687 7
                    AND TABLE_NAME = '%s'
688
                    AND COLUMN_NAME = '%s'
689
                  ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
690
                $tableName,
691
                $column
692
            ));
693
694
            foreach ($rows as $row) {
695 7
                $instructions->merge($this->getDropForeignKeyInstructions($tableName, $row['CONSTRAINT_NAME']));
696 7
            }
697
        }
698 7
699 7
        if (empty($instructions->getAlterParts())) {
700 7
            throw new \InvalidArgumentException(sprintf(
701 7
                "Not foreign key on columns '%s' exist",
702 7
                implode(',', $columns)
703
            ));
704 7
        }
705
706
        return $instructions;
707
    }
708
709 96
    /**
710
     * {@inheritdoc}
711
     */
712 96
    public function getSqlType($type, $limit = null)
713 87
    {
714
        switch ($type) {
715 96
            case static::PHINX_TYPE_FLOAT:
716 4
            case static::PHINX_TYPE_DECIMAL:
717
            case static::PHINX_TYPE_DATE:
718 96
            case static::PHINX_TYPE_ENUM:
719 9
            case static::PHINX_TYPE_SET:
720
            case static::PHINX_TYPE_JSON:
721
                // Geospatial database types
722 6
            case static::PHINX_TYPE_GEOMETRY:
723 6
            case static::PHINX_TYPE_POINT:
724 6
            case static::PHINX_TYPE_LINESTRING:
725 6
            case static::PHINX_TYPE_POLYGON:
726 6
                return [ 'name' => $type ];
727 6
            case static::PHINX_TYPE_DATETIME:
728 6
            case static::PHINX_TYPE_TIMESTAMP:
729 6
            case static::PHINX_TYPE_TIME:
730
                return [ 'name' => $type, 'limit' => $limit ];
731 5
            case static::PHINX_TYPE_STRING:
732
                return [ 'name' => 'varchar', 'limit' => $limit ?: 255 ];
733 5
            case static::PHINX_TYPE_CHAR:
734
                return [ 'name' => 'char', 'limit' => $limit ?: 255 ];
735 95 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...
736 5
                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...
737
                    $sizes = [
738 95
                        // Order matters! Size must always be tested from longest to shortest!
739 3
                        'longtext'   => static::TEXT_LONG,
740
                        'mediumtext' => static::TEXT_MEDIUM,
741 95
                        'text'       => static::TEXT_REGULAR,
742 1
                        'tinytext'   => static::TEXT_SMALL,
743
                    ];
744
                    foreach ($sizes as $name => $length) {
745 1
                        if ($limit >= $length) {
746 1
                            return [ 'name' => $name ];
747 1
                        }
748 1
                    }
749 1
                }
750 1
751 1
                return [ 'name' => 'text' ];
752 1
            case static::PHINX_TYPE_BINARY:
753
                return [ 'name' => 'binary', 'limit' => $limit ?: 255 ];
754 1
            case static::PHINX_TYPE_VARBINARY:
755
                return [ 'name' => 'varbinary', 'limit' => $limit ?: 255 ];
756 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...
757
                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...
758 95
                    $sizes = [
759 82
                        // Order matters! Size must always be tested from longest to shortest!
760
                        'longblob'   => static::BLOB_LONG,
761
                        'mediumblob' => static::BLOB_MEDIUM,
762 6
                        'blob'       => static::BLOB_REGULAR,
763 6
                        'tinyblob'   => static::BLOB_SMALL,
764 6
                    ];
765 6
                    foreach ($sizes as $name => $length) {
766 6
                        if ($limit >= $length) {
767 6
                            return [ 'name' => $name ];
768
                        }
769 6
                    }
770 6
                }
771 6
772 6
                return [ 'name' => 'blob' ];
773 6
            case static::PHINX_TYPE_BIT:
774 6
                return [ 'name' => 'bit', 'limit' => $limit ?: 64 ];
775 6
            case static::PHINX_TYPE_INTEGER:
776 2
                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...
777 2
                    $sizes  = [
778 6
                        // Order matters! Size must always be tested from longest to shortest!
779
                        'bigint'    => static::INT_BIG,
780 5
                        'int'       => static::INT_REGULAR,
781 82
                        'mediumint' => static::INT_MEDIUM,
782 76
                        'smallint'  => static::INT_SMALL,
783 76
                        'tinyint'   => static::INT_TINY,
784 82
                    ];
785
                    $limits = [
786 86
                        'int'    => 11,
787 82
                        'bigint' => 20,
788
                    ];
789 86
                    foreach ($sizes as $name => $length) {
790 7
                        if ($limit >= $length) {
791
                            $def = [ 'name' => $name ];
792 84
                            if (isset($limits[$name])) {
793 5
                                $def['limit'] = $limits[$name];
794
                            }
795 83
796 7
                            return $def;
797
                        }
798 83
                    }
799 80
                } 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...
800
                    $limit = 11;
801 83
                }
802 4
803
                return [ 'name' => 'int', 'limit' => $limit ];
804 83
            case static::PHINX_TYPE_BIG_INTEGER:
805 4
                return [ 'name' => 'bigint', 'limit' => 20 ];
806
            case static::PHINX_TYPE_BOOLEAN:
807 83
                return [ 'name' => 'tinyint', 'limit' => 1 ];
808 80
            case static::PHINX_TYPE_UUID:
809
                return [ 'name' => 'char', 'limit' => 36 ];
810 10
            case static::TYPE_YEAR:
811 2
                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...
812
                    $limit = 4;
813 10
                }
814 10
815 10
                return [ 'name' => 'year', 'limit' => $limit ];
816 10
            default:
817 5
                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
818 8
        }
819 5
    }
820
821 6
    /**
822 4
     * Returns Phinx type by SQL type
823
     *
824 2
     * @param string $sqlTypeDef
825
     *
826
     * @throws \RuntimeException
827
     * @internal param string $sqlType SQL type
828
     * @returns string Phinx type
829
     */
830 2
    public function getPhinxType($sqlTypeDef)
831
    {
832
        $matches = [];
833 2
        if (! preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*(.+)*$/', $sqlTypeDef, $matches)) {
834 2
            throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported');
835 2
        } else {
836
            $limit     = null;
837
            $precision = null;
838
            $type      = $matches[1];
839
            if (count($matches) > 2) {
840
                $limit = $matches[3] ? (int)$matches[3] : null;
841
            }
842
            if (count($matches) > 4) {
843
                $precision = (int)$matches[5];
844
            }
845 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...
846 17
                $type  = static::PHINX_TYPE_BOOLEAN;
847
                $limit = null;
848 17
            }
849 17
            switch ($type) {
850 1
                case 'varchar':
851
                    $type = static::PHINX_TYPE_STRING;
852 16
                    if ($limit === 255) {
853 16
                        $limit = null;
854 16
                    }
855 16
                    break;
856 14 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...
857 14
                    $type = static::PHINX_TYPE_CHAR;
858 16
                    if ($limit === 255) {
859 4
                        $limit = null;
860 4
                    }
861 16
                    if ($limit === 36) {
862 3
                        $type = static::PHINX_TYPE_UUID;
863 3
                    }
864 3
                    break;
865
                case 'tinyint':
866 16
                    $type  = static::PHINX_TYPE_INTEGER;
867 6
                    $limit = static::INT_TINY;
868 6
                    break;
869 3
                case 'smallint':
870 3
                    $type  = static::PHINX_TYPE_INTEGER;
871 6
                    $limit = static::INT_SMALL;
872 16
                    break;
873 5
                case 'mediumint':
874 5
                    $type  = static::PHINX_TYPE_INTEGER;
875 1
                    $limit = static::INT_MEDIUM;
876 1
                    break;
877 5
                case 'int':
878 2
                    $type = static::PHINX_TYPE_INTEGER;
879 2
                    if ($limit === 11) {
880 5
                        $limit = null;
881 16
                    }
882 2
                    break;
883 2
                case 'bigint':
884 2
                    if ($limit === 20) {
885 16
                        $limit = null;
886 2
                    }
887 2
                    $type = static::PHINX_TYPE_BIG_INTEGER;
888 2
                    break;
889 16
                case 'bit':
890 2
                    $type = static::PHINX_TYPE_BIT;
891 2
                    if ($limit === 64) {
892 2
                        $limit = null;
893 16
                    }
894 15
                    break;
895 15
                case 'blob':
896 12
                    $type = static::PHINX_TYPE_BINARY;
897 12
                    break;
898 15
                case 'tinyblob':
899 11
                    $type  = static::PHINX_TYPE_BINARY;
900 6
                    $limit = static::BLOB_TINY;
901 4
                    break;
902 4
                case 'mediumblob':
903 6
                    $type  = static::PHINX_TYPE_BINARY;
904 6
                    $limit = static::BLOB_MEDIUM;
905 10
                    break;
906 2
                case 'longblob':
907 2
                    $type  = static::PHINX_TYPE_BINARY;
908 10
                    $limit = static::BLOB_LONG;
909 1
                    break;
910 1
                case 'tinytext':
911 1
                    $type  = static::PHINX_TYPE_TEXT;
912 10
                    $limit = static::TEXT_TINY;
913 1
                    break;
914 1
                case 'mediumtext':
915 1
                    $type  = static::PHINX_TYPE_TEXT;
916 10
                    $limit = static::TEXT_MEDIUM;
917 1
                    break;
918 1
                case 'longtext':
919 1
                    $type  = static::PHINX_TYPE_TEXT;
920 10
                    $limit = static::TEXT_LONG;
921 2
                    break;
922 2
            }
923 2
924 9
            // Call this to check if parsed type is supported.
925 2
            $this->getSqlType($type, $limit);
926 2
927 2
            $phinxType = [
928 8
                'name'      => $type,
929 2
                'limit'     => $limit,
930 2
                'precision' => $precision
931 2
            ];
932
933
            if (static::PHINX_TYPE_ENUM == $type) {
934
                $phinxType['values'] = explode("','", trim($matches[6], "()'"));
935 16
            }
936
937
            return $phinxType;
938 15
        }
939 15
    }
940
941 15
    /**
942
     * {@inheritdoc}
943 15
     */
944 3
    public function createDatabase($name, $options = [])
945 3
    {
946
        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
947 15
948 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...
949
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s` COLLATE `%s`', $name, $charset, $options['collation']));
950
        } else {
951
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s`', $name, $charset));
952
        }
953
    }
954 83
955
    /**
956 83
     * {@inheritdoc}
957
     */
958 83
    public function hasDatabase($name)
959 1
    {
960 1
        $rows = $this->fetchAll(
961 82
            sprintf(
962
                'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = \'%s\'',
963 83
                $name
964
            )
965
        );
966
967
        foreach ($rows as $row) {
968 4
            if (! empty($row)) {
969
                return true;
970 4
            }
971 4
        }
972 4
973
        return false;
974 4
    }
975 4
976
    /**
977 4
     * {@inheritdoc}
978 3
     */
979 3
    public function dropDatabase($name)
980
    {
981 3
        $this->execute(sprintf('DROP DATABASE IF EXISTS `%s`', $name));
982
    }
983 3
984
    /**
985
     * Gets the MySQL Column Definition for a Column object.
986
     *
987
     * @param \Phinx\Db\Table\Column $column Column
988
     *
989 81
     * @return string
990
     */
991 81
    protected function getColumnSqlDefinition(Column $column)
992 81
    {
993
        if ($column->getType() instanceof Literal) {
994
            $def = (string)$column->getType();
995
        } else {
996
            $sqlType = $this->getSqlType($column->getType(), $column->getLimit());
997
            $def     = strtoupper($sqlType['name']);
998
        }
999
        if ($column->getPrecision() && $column->getScale()) {
1000 89
            $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
1001
        } elseif (isset($sqlType['limit'])) {
1002 89
            $def .= '(' . $sqlType['limit'] . ')';
1003
        }
1004 89 View Code Duplication
        if (( $values = $column->getValues() ) && is_array($values)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1005 89
            $def .= "('" . implode("', '", $values) . "')";
1006 89
        }
1007 2
        $def .= $column->getEncoding() ? ' CHARACTER SET ' . $column->getEncoding() : '';
1008 89
        $def .= $column->getCollation() ? ' COLLATE ' . $column->getCollation() : '';
1009 86
        $def .= ( ! $column->isSigned() && isset($this->signedColumnTypes[$column->getType()]) ) ? ' unsigned' : '';
1010 86
        $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...
1011 89
        $def .= $column->isIdentity() ? ' AUTO_INCREMENT' : '';
1012 5
        $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...
1013 5
1014 89
        if ($column->getComment()) {
1015 89
            $def .= ' COMMENT ' . $this->getConnection()->quote($column->getComment());
1016 89
        }
1017 89
1018 89
        if ($column->getUpdate()) {
1019 89
            $def .= ' ON UPDATE ' . $column->getUpdate();
1020
        }
1021 89
1022 2
        return $def;
1023 2
    }
1024
1025 89
    /**
1026 1
     * Gets the MySQL Index Definition for an Index object.
1027 1
     *
1028
     * @param \Phinx\Db\Table\Index $index Index
1029 89
     *
1030
     * @return string
1031
     */
1032
    protected function getIndexSqlDefinition(Index $index)
1033
    {
1034
        $def   = '';
1035
        $limit = '';
0 ignored issues
show
Unused Code introduced by
$limit is not used, you could remove the assignment.

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

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

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

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

Loading history...
1036
1037
        if ($index->getType() == Index::UNIQUE) {
1038 16
            $def .= ' UNIQUE';
1039
        }
1040 16
1041 16
        if ($index->getType() == Index::FULLTEXT) {
1042 16
            $def .= ' FULLTEXT';
1043 2
        }
1044 2
1045
        $def .= ' KEY';
1046 16
1047 5
        if (is_string($index->getName())) {
1048 5
            $def .= ' `' . $index->getName() . '`';
1049
        }
1050 16
1051 1
        if (! is_array($index->getLimit())) {
1052 1
            $limit = '(' . $index->getLimit() . ')';
1053
            $def   .= ' (`' . implode('`,`', $index->getColumns()) . '`' . $limit . ')';
1054 16
        } else {
1055
            $columns = $index->getColumns();
1056 16
            $limits  = $index->getLimit();
1057 5
            $def     .= ' (';
1058 5
            for ($i = 0; $i < count($columns); $i ++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1059
                $limit = ! isset($limits[$i]) || $limits[$i] <= 0 ? '' : '(' . $limits[$i] . ')';
1060 16
                $def   .= '`' . $columns[$i] . '`' . $limit;
1061
                if ($i + 1 < count($columns)) {
1062 16
                    $def .= ',';
1063
                }
1064
            }
1065
            $def .= ' )';
1066
        }
1067
1068
        return $def;
1069
    }
1070
1071 17
    /**
1072
     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
1073 17
     *
1074 17
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
1075 5
     *
1076 5
     * @return string
1077 17
     */
1078 17 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...
1079 17
    {
1080 17
        $def = '';
1081 17
        if ($foreignKey->getConstraint()) {
1082 17
            $def .= ' CONSTRAINT ' . $this->quoteColumnName($foreignKey->getConstraint());
0 ignored issues
show
Bug introduced by
It seems like $foreignKey->getConstraint() targeting Phinx\Db\Table\ForeignKey::getConstraint() can also be of type boolean; however, Phinx\Db\Adapter\MysqlAdapter::quoteColumnName() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1083 17
        }
1084 17
        $columnNames = [];
1085 17
        foreach ($foreignKey->getColumns() as $column) {
1086 17
            $columnNames[] = $this->quoteColumnName($column);
1087 17
        }
1088 2
        $def            .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
1089 2
        $refColumnNames = [];
1090 17
        foreach ($foreignKey->getReferencedColumns() as $column) {
1091 2
            $refColumnNames[] = $this->quoteColumnName($column);
1092 2
        }
1093 17
        $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
1094
        if ($foreignKey->getOnDelete()) {
1095
            $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
1096
        }
1097
        if ($foreignKey->getOnUpdate()) {
1098
            $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
1099
        }
1100
1101
        return $def;
1102 2
    }
1103
1104 2
    /**
1105
     * Describes a database table. This is a MySQL adapter specific method.
1106
     *
1107 2
     * @param string $tableName Table name
1108
     *
1109
     * @return array
1110
     */
1111 2 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...
1112 2
    {
1113
        $options = $this->getOptions();
1114 2
1115
        // mysql specific
1116 2
        $sql = sprintf(
1117
            "SELECT *
1118
             FROM information_schema.tables
1119
             WHERE table_schema = '%s'
1120
             AND table_name = '%s'",
1121
            $options['name'],
1122
            $tableName
1123 85
        );
1124
1125 85
        return $this->fetchRow($sql);
1126
    }
1127
1128
    /**
1129
     * Returns MySQL column types (inherited and MySQL specified).
1130
     * @return array
1131
     */
1132
    public function getColumnTypes()
1133
    {
1134
        return array_merge(parent::getColumnTypes(), [ 'enum', 'set', 'year', 'json' ]);
1135
    }
1136
}
1137