Completed
Pull Request — master (#1357)
by José
01:56
created

MysqlAdapter::getSqlType()   D

Complexity

Conditions 45
Paths 40

Size

Total Lines 108
Code Lines 81

Duplication

Lines 34
Ratio 31.48 %

Code Coverage

Tests 74
CRAP Score 45.0368

Importance

Changes 0
Metric Value
dl 34
loc 108
c 0
b 0
f 0
ccs 74
cts 76
cp 0.9737
rs 4.1818
cc 45
eloc 81
nc 40
nop 2
crap 45.0368

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 * Copyright (c) 2015 Rob Morgan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated * documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 *
26
 * @package    Phinx
27
 * @subpackage Phinx\Db\Adapter
28
 */
29
namespace Phinx\Db\Adapter;
30
31
use Cake\Database\Connection;
32
use Cake\Database\Driver\Mysql as MysqlDriver;
33
use Phinx\Db\Table\Column;
34
use Phinx\Db\Table\ForeignKey;
35
use Phinx\Db\Table\Index;
36
use Phinx\Db\Table\Table;
37
use Phinx\Db\Util\AlterInstructions;
38
use Phinx\Util\Literal;
39
40
/**
41
 * Phinx MySQL Adapter.
42
 *
43
 * @author Rob Morgan <[email protected]>
44
 */
45
class MysqlAdapter extends PdoAdapter implements AdapterInterface
46
{
47
48
    protected $signedColumnTypes = ['integer' => true, 'biginteger' => true, 'float' => true, 'decimal' => true, 'boolean' => true];
49
50
    const TEXT_TINY = 255;
51
    const TEXT_SMALL = 255; /* deprecated, alias of TEXT_TINY */
52
    const TEXT_REGULAR = 65535;
53
    const TEXT_MEDIUM = 16777215;
54
    const TEXT_LONG = 4294967295;
55
56
    // According to https://dev.mysql.com/doc/refman/5.0/en/blob.html BLOB sizes are the same as TEXT
57
    const BLOB_TINY = 255;
58
    const BLOB_SMALL = 255; /* deprecated, alias of BLOB_TINY */
59
    const BLOB_REGULAR = 65535;
60
    const BLOB_MEDIUM = 16777215;
61
    const BLOB_LONG = 4294967295;
62
63
    const INT_TINY = 255;
64
    const INT_SMALL = 65535;
65
    const INT_MEDIUM = 16777215;
66
    const INT_REGULAR = 4294967295;
67
    const INT_BIG = 18446744073709551615;
68
69
    const BIT = 64;
70 80
71
    const TYPE_YEAR = 'year';
72 80
73 80
    /**
74
     * {@inheritdoc}
75
     */
76
    public function connect()
77
    {
78
        if ($this->connection === null) {
79 80
            if (!class_exists('PDO') || !in_array('mysql', \PDO::getAvailableDrivers(), true)) {
80 80
                // @codeCoverageIgnoreStart
81
                throw new \RuntimeException('You need to enable the PDO_Mysql extension for Phinx to run properly.');
82 80
                // @codeCoverageIgnoreEnd
83
            }
84 80
85
            $db = null;
86
            $options = $this->getOptions();
87
88
            $dsn = 'mysql:';
89 80
90 80
            if (!empty($options['unix_socket'])) {
91 80
                // use socket connection
92 80
                $dsn .= 'unix_socket=' . $options['unix_socket'];
93
            } else {
94
                // use network connection
95 80
                $dsn .= 'host=' . $options['host'];
96
                if (!empty($options['port'])) {
97
                    $dsn .= ';port=' . $options['port'];
98 80
                }
99
            }
100
101
            $dsn .= ';dbname=' . $options['name'];
102 80
103
            // charset support
104
            if (!empty($options['charset'])) {
105
                $dsn .= ';charset=' . $options['charset'];
106 80
            }
107 80
108
            $driverOptions = [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION];
109
110 80
            // support arbitrary \PDO::MYSQL_ATTR_* driver options and pass them to PDO
111
            // http://php.net/manual/en/ref.pdo-mysql.php#pdo-mysql.constants
112 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...
113 80
                if (strpos($key, 'mysql_attr_') === 0) {
114 80
                    $driverOptions[constant('\PDO::' . strtoupper($key))] = $option;
115 1
                }
116 1
            }
117 1
118 1
            try {
119
                $db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions);
120
            } catch (\PDOException $exception) {
121 80
                throw new \InvalidArgumentException(sprintf(
122 80
                    'There was a problem connecting to the database: %s',
123 80
                    $exception->getMessage()
124
                ));
125
            }
126
127
            $this->setConnection($db);
128 81
        }
129
    }
130 81
131 81
    /**
132
     * {@inheritdoc}
133
     */
134
    public function disconnect()
135
    {
136 6
        $this->connection = null;
137
    }
138 6
139
    /**
140
     * {@inheritdoc}
141
     */
142
    public function hasTransactions()
143
    {
144 6
        return true;
145
    }
146 6
147 6
    /**
148
     * {@inheritdoc}
149
     */
150
    public function beginTransaction()
151
    {
152 6
        $this->execute('START TRANSACTION');
153
    }
154 6
155 6
    /**
156
     * {@inheritdoc}
157
     */
158
    public function commitTransaction()
159
    {
160 1
        $this->execute('COMMIT');
161
    }
162 1
163 1
    /**
164
     * {@inheritdoc}
165
     */
166
    public function rollbackTransaction()
167
    {
168 112
        $this->execute('ROLLBACK');
169
    }
170 112
171
    /**
172
     * {@inheritdoc}
173
     */
174
    public function quoteTableName($tableName)
175
    {
176 112
        return str_replace('.', '`.`', $this->quoteColumnName($tableName));
177
    }
178 112
179
    /**
180
     * {@inheritdoc}
181
     */
182
    public function quoteColumnName($columnName)
183
    {
184 82
        return '`' . str_replace('`', '``', $columnName) . '`';
185
    }
186 82
187
    /**
188 82
     * {@inheritdoc}
189
     */
190 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...
191 82
    {
192 82
        $options = $this->getOptions();
193
194 82
        $exists = $this->fetchRow(sprintf(
195
            "SELECT TABLE_NAME
196 82
            FROM INFORMATION_SCHEMA.TABLES
197
            WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'",
198
            $options['name'],
199
            $tableName
200
        ));
201
202 82
        return !empty($exists);
203
    }
204
205
    /**
206 82
     * {@inheritdoc}
207
     */
208 82
    public function createTable(Table $table, array $columns = [], array $indexes = [])
209 82
    {
210
        // This method is based on the MySQL docs here: http://dev.mysql.com/doc/refman/5.1/en/create-index.html
211
        $defaultOptions = [
212 82
            'engine' => 'InnoDB',
213 82
            'collation' => 'utf8_general_ci'
214 68
        ];
215 68
216 68
        $options = array_merge(
217 68
            $defaultOptions,
218 68
            array_intersect_key($this->getOptions(), $defaultOptions),
219
            $table->getOptions()
220 68
        );
221 68
222 82
        // Add the default primary key
223
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
224 2
            $column = new Column();
225 2
            $column->setName('id')
226 2
                   ->setType('integer')
227 2
                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
228
                   ->setIdentity(true);
229 2
230 2
            array_unshift($columns, $column);
231 2
            $options['primary_key'] = 'id';
232
        } elseif (isset($options['id']) && is_string($options['id'])) {
233
            // Handle id => "field_name" to support AUTO_INCREMENT
234
            $column = new Column();
235
            $column->setName($options['id'])
236 82
                   ->setType('integer')
237 82
                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
238 82
                   ->setIdentity(true);
239 82
240
            array_unshift($columns, $column);
241
            $options['primary_key'] = $options['id'];
242 82
        }
243 82
244 82
        // TODO - process table options like collation etc
245 82
246 82
        // process table engine (default to InnoDB)
247
        $optionsStr = 'ENGINE = InnoDB';
248
        if (isset($options['engine'])) {
249 82
            $optionsStr = sprintf('ENGINE = %s', $options['engine']);
250 2
        }
251 2
252
        // process table collation
253 82
        if (isset($options['collation'])) {
254 82
            $charset = explode('_', $options['collation']);
255 82
            $optionsStr .= sprintf(' CHARACTER SET %s', $charset[0]);
256 82
            $optionsStr .= sprintf(' COLLATE %s', $options['collation']);
257 82
        }
258
259
        // set the table comment
260 82
        if (isset($options['comment'])) {
261 82
            $optionsStr .= sprintf(" COMMENT=%s ", $this->getConnection()->quote($options['comment']));
262 82
        }
263 82
264 81
        $sql = 'CREATE TABLE ';
265 82
        $sql .= $this->quoteTableName($table->getName()) . ' (';
266
        foreach ($columns as $column) {
267
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
268 2
        }
269 2
270 2
        // set the primary key(s)
271 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...
272 2
            $sql = rtrim($sql);
273 2
            $sql .= ' PRIMARY KEY (';
274 2
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
275 2
                $sql .= $this->quoteColumnName($options['primary_key']);
276 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...
277 2
                $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
278 82
            }
279 82
            $sql .= ')';
280 1
        } else {
281
            $sql = substr(rtrim($sql), 0, -1); // no primary keys
282
        }
283
284 82
        // set the indexes
285 82
        foreach ($indexes as $index) {
286 10
            $sql .= ', ' . $this->getIndexSqlDefinition($index);
287 82
        }
288
289
        $sql .= ') ' . $optionsStr;
290 82
        $sql = rtrim($sql) . ';';
291 82
292 2
        // execute the sql
293 82
        $this->execute($sql);
294
    }
295 82
296 82
    /**
297
     * {@inheritdoc}
298
     */
299 82 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...
300 82
    {
301
        $sql = sprintf(
302
            'RENAME TABLE %s TO %s',
303
            $this->quoteTableName($tableName),
304
            $this->quoteTableName($newTableName)
305 5
        );
306
307 5
        return new AlterInstructions([], [$sql]);
308 5
    }
309
310
    /**
311
     * {@inheritdoc}
312
     */
313 5
    protected function getDropTableInstructions($tableName)
314
    {
315 5
        $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName));
316 5
317
        return new AlterInstructions([], [$sql]);
318
    }
319
320
    /**
321 1
     * {@inheritdoc}
322
     */
323 1
    public function truncateTable($tableName)
324 1
    {
325 1
        $sql = sprintf(
326 1
            'TRUNCATE TABLE %s',
327
            $this->quoteTableName($tableName)
328 1
        );
329 1
330
        $this->execute($sql);
331
    }
332
333
    /**
334 12
     * {@inheritdoc}
335
     */
336 12
    public function getColumns($tableName)
337 12
    {
338 12
        $columns = [];
339 12
        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
340
        foreach ($rows as $columnInfo) {
341 12
            $phinxType = $this->getPhinxType($columnInfo['Type']);
342 12
343 12
            $column = new Column();
344 12
            $column->setName($columnInfo['Field'])
345 12
                   ->setNull($columnInfo['Null'] !== 'NO')
346 12
                   ->setDefault($columnInfo['Default'])
347
                   ->setType($phinxType['name'])
348 12
                   ->setSigned(strpos($columnInfo['Type'], 'unsigned') === false)
349 12
                   ->setLimit($phinxType['limit']);
350 12
351
            if ($columnInfo['Extra'] === 'auto_increment') {
352 12
                $column->setIdentity(true);
353 3
            }
354 3
355
            if (isset($phinxType['values'])) {
356 12
                $column->setValues($phinxType['values']);
357 12
            }
358
359 12
            $columns[] = $column;
360
        }
361
362
        return $columns;
363
    }
364
365 79
    /**
366
     * {@inheritdoc}
367 79
     */
368 79 View Code Duplication
    public function hasColumn($tableName, $columnName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

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

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

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

Loading history...
1141
        }
1142
1143
        return new Connection(['driver' => $driver] + $options);
1144
    }
1145
}
1146