Completed
Pull Request — master (#1357)
by José
11:07
created

MysqlAdapter::getDecoratedConnection()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 14

Duplication

Lines 19
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 19
loc 19
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 14
nc 2
nop 0
crap 6
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
        // set the table row format
265 82
        if (isset($options['row_format'])) {
266
            $optionsStr .= sprintf(" ROW_FORMAT=%s ", $options['row_format']);
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('SHOW FULL COLUMNS FROM %s', $this->quoteTableName($tableName)));
409 18
410 18
        foreach ($rows as $row) {
411
            if (strcasecmp($row['Field'], $columnName) === 0) {
412
                $null = ($row['Null'] == 'NO') ? 'NOT NULL' : 'NULL';
413
                $comment = isset($row['Comment']) ? ' COMMENT ' . '\'' . addslashes($row['Comment']) . '\'' : '';
414
                $extra = ' ' . strtoupper($row['Extra']);
415 7
                if (!is_null($row['Default'])) {
416
                    $extra .= $this->getDefaultValueDefinition($row['Default']);
417 7
                }
418 7
                $definition = $row['Type'] . ' ' . $null . $extra . $comment;
419 7
420 5
                $alter = sprintf(
421 5
                    'CHANGE COLUMN %s %s %s',
422 5
                    $this->quoteColumnName($columnName),
423 1
                    $this->quoteColumnName($newColumnName),
424 1
                    $definition
425 5
                );
426
427 5
                return new AlterInstructions([$alter]);
428 5
            }
429 5
        }
430 5
431 5
        throw new \InvalidArgumentException(sprintf(
432 5
            'The specified column doesn\'t exist: ' .
433
            $columnName
434 5
        ));
435 5
    }
436 5
437
    /**
438 6
     * {@inheritdoc}
439
     */
440 2
    protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn)
441
    {
442
        $after = $newColumn->getAfter() ? ' AFTER ' . $this->quoteColumnName($newColumn->getAfter()) : '';
443 2
        $alter = sprintf(
444
            'CHANGE %s %s %s%s',
445
            $this->quoteColumnName($columnName),
446
            $this->quoteColumnName($newColumn->getName()),
447
            $this->getColumnSqlDefinition($newColumn),
448
            $after
449 5
        );
450
451 5
        return new AlterInstructions([$alter]);
452 5
    }
453 5
454 5
    /**
455 5
     * {@inheritdoc}
456 5
     */
457 5
    protected function getDropColumnInstructions($tableName, $columnName)
458 5
    {
459
        $alter = sprintf('DROP COLUMN %s', $this->quoteColumnName($columnName));
460 5
461 5
        return new AlterInstructions([$alter]);
462 5
    }
463
464
    /**
465
     * Get an array of indexes from a particular table.
466
     *
467 5
     * @param string $tableName Table Name
468
     * @return array
469 5
     */
470 5
    protected function getIndexes($tableName)
471 5
    {
472 5
        $indexes = [];
473 5
        $rows = $this->fetchAll(sprintf('SHOW INDEXES FROM %s', $this->quoteTableName($tableName)));
474 5 View Code Duplication
        foreach ($rows as $row) {
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...
475 5
            if (!isset($indexes[$row['Key_name']])) {
476 5
                $indexes[$row['Key_name']] = ['columns' => []];
477
            }
478
            $indexes[$row['Key_name']]['columns'][] = strtolower($row['Column_name']);
479
        }
480
481
        return $indexes;
482
    }
483
484 19
    /**
485
     * {@inheritdoc}
486 19
     */
487 19 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...
488 19
    {
489 18
        if (is_string($columns)) {
490 18
            $columns = [$columns]; // str to array
491 18
        }
492 18
493 19
        $columns = array_map('strtolower', $columns);
494 19
        $indexes = $this->getIndexes($tableName);
495
496
        foreach ($indexes as $index) {
497
            if ($columns == $index['columns']) {
498
                return true;
499
            }
500 14
        }
501
502 14
        return false;
503 6
    }
504 6
505
    /**
506 14
     * {@inheritdoc}
507 14
     */
508 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...
509 14
    {
510 14
        $indexes = $this->getIndexes($tableName);
511 12
512
        foreach ($indexes as $name => $index) {
513 13
            if ($name === $indexName) {
514
                return true;
515 11
            }
516
        }
517
518
        return false;
519
    }
520
521 1
    /**
522
     * {@inheritdoc}
523 1
     */
524
    protected function getAddIndexInstructions(Table $table, Index $index)
525 1
    {
526 1
        $alter = sprintf(
527 1
            'ADD %s',
528
            $this->getIndexSqlDefinition($index)
529 1
        );
530
531
        return new AlterInstructions([$alter]);
532
    }
533
534
    /**
535
     * {@inheritdoc}
536
     */
537 4
    protected function getDropIndexByColumnsInstructions($tableName, $columns)
538
    {
539 4
        if (is_string($columns)) {
540 4
            $columns = [$columns]; // str to array
541 4
        }
542 4
543 4
        $indexes = $this->getIndexes($tableName);
544 4
        $columns = array_map('strtolower', $columns);
545 4
546 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...
547
            if ($columns == $index['columns']) {
548
                return new AlterInstructions([sprintf(
549
                    'DROP INDEX %s',
550
                    $this->quoteColumnName($indexName)
551 3
                )]);
552
            }
553 3
        }
554 2
555 2
        throw new \InvalidArgumentException(sprintf(
556
            "The specified index on columns '%s' does not exist",
557 3
            implode(',', $columns)
558 3
        ));
559
    }
560 3
561 3
    /**
562 3
     * {@inheritdoc}
563 3
     */
564 3
    protected function getDropIndexByNameInstructions($tableName, $indexName)
565 3
    {
566 3
567 3
        $indexes = $this->getIndexes($tableName);
568 3
569 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...
570
            if ($name === $indexName) {
571 3
                return new AlterInstructions([sprintf(
572 1
                    'DROP INDEX %s',
573
                    $this->quoteColumnName($indexName)
574
                )]);
575
            }
576
        }
577 2
578
        throw new \InvalidArgumentException(sprintf(
579 2
            "The specified index name '%s' does not exist",
580
            $indexName
581 2
        ));
582
    }
583 2
584 2
    /**
585 2
     * {@inheritdoc}
586 2
     */
587 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...
588 2
    {
589 2
        if (is_string($columns)) {
590 2
            $columns = [$columns]; // str to array
591 2
        }
592
        $foreignKeys = $this->getForeignKeys($tableName);
593 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...
594
            if (isset($foreignKeys[$constraint])) {
595
                return !empty($foreignKeys[$constraint]);
596
            }
597
598
            return false;
599 21
        } else {
600
            foreach ($foreignKeys as $key) {
601 21
                if ($columns == $key['columns']) {
602 5
                    return true;
603 5
                }
604 21
            }
605 21
606 6
            return false;
607 4
        }
608
    }
609 4
610
    /**
611 15
     * Get an array of foreign keys from a particular table.
612 12
     *
613 10
     * @param string $tableName Table Name
614
     * @return array
615 11
     */
616 11 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...
617
    {
618
        $foreignKeys = [];
619
        $rows = $this->fetchAll(sprintf(
620
            "SELECT
621
              CONSTRAINT_NAME,
622
              TABLE_NAME,
623
              COLUMN_NAME,
624
              REFERENCED_TABLE_NAME,
625
              REFERENCED_COLUMN_NAME
626 22
            FROM information_schema.KEY_COLUMN_USAGE
627
            WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
628 22
              AND REFERENCED_TABLE_NAME IS NOT NULL
629 22
              AND TABLE_NAME = '%s'
630
            ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
631
            $tableName
632
        ));
633
        foreach ($rows as $row) {
634
            $foreignKeys[$row['CONSTRAINT_NAME']]['table'] = $row['TABLE_NAME'];
635
            $foreignKeys[$row['CONSTRAINT_NAME']]['columns'][] = $row['COLUMN_NAME'];
636
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_table'] = $row['REFERENCED_TABLE_NAME'];
637
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_columns'][] = $row['REFERENCED_COLUMN_NAME'];
638
        }
639
640 22
        return $foreignKeys;
641
    }
642 22
643 22
    /**
644 19
     * {@inheritdoc}
645 19
     */
646 19 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...
647 19
    {
648 22
        $alter = sprintf(
649 22
            'ADD %s',
650
            $this->getForeignKeySqlDefinition($foreignKey)
651
        );
652
653
        return new AlterInstructions([$alter]);
654
    }
655 15
656
    /**
657 15
     * {@inheritdoc}
658 15
     */
659 15
    protected function getDropForeignKeyInstructions($tableName, $constraint)
660 15
    {
661 15
        $alter = sprintf(
662 15
            'DROP FOREIGN KEY %s',
663 15
            $constraint
664 15
        );
665
666
        return new AlterInstructions([$alter]);
667
    }
668
669 8
    /**
670
     * {@inheritdoc}
671 8
     */
672 3
    protected function getDropForeignKeyByColumnsInstructions($tableName, $columns)
673 3
    {
674
        $instructions = new AlterInstructions();
675
676 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...
677 8
            $rows = $this->fetchAll(sprintf(
678 8
                "SELECT
679 8
                    CONSTRAINT_NAME
680 8
                  FROM information_schema.KEY_COLUMN_USAGE
681
                  WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
682 8
                    AND REFERENCED_TABLE_NAME IS NOT NULL
683 8
                    AND TABLE_NAME = '%s'
684 8
                    AND COLUMN_NAME = '%s'
685
                  ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
686 7
                $tableName,
687 7
                $column
688
            ));
689
690
            foreach ($rows as $row) {
691
                $instructions->merge($this->getDropForeignKeyInstructions($tableName, $row['CONSTRAINT_NAME']));
692
            }
693
        }
694
695 7
        if (empty($instructions->getAlterParts())) {
696 7
            throw new \InvalidArgumentException(sprintf(
697
                "Not foreign key on columns '%s' exist",
698 7
                implode(',', $columns)
699 7
            ));
700 7
        }
701 7
702 7
        return $instructions;
703
    }
704 7
705
    /**
706
     * {@inheritdoc}
707
     */
708
    public function getSqlType($type, $limit = null)
709 96
    {
710
        switch ($type) {
711
            case static::PHINX_TYPE_FLOAT:
712 96
            case static::PHINX_TYPE_DECIMAL:
713 87
            case static::PHINX_TYPE_DATE:
714
            case static::PHINX_TYPE_ENUM:
715 96
            case static::PHINX_TYPE_SET:
716 4
            case static::PHINX_TYPE_JSON:
717
            // Geospatial database types
718 96
            case static::PHINX_TYPE_GEOMETRY:
719 9
            case static::PHINX_TYPE_POINT:
720
            case static::PHINX_TYPE_LINESTRING:
721
            case static::PHINX_TYPE_POLYGON:
722 6
                return ['name' => $type];
723 6
            case static::PHINX_TYPE_DATETIME:
724 6
            case static::PHINX_TYPE_TIMESTAMP:
725 6
            case static::PHINX_TYPE_TIME:
726 6
                return ['name' => $type, 'limit' => $limit];
727 6
            case static::PHINX_TYPE_STRING:
728 6
                return ['name' => 'varchar', 'limit' => $limit ?: 255];
729 6
            case static::PHINX_TYPE_CHAR:
730
                return ['name' => 'char', 'limit' => $limit ?: 255];
731 5 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...
732
                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...
733 5
                    $sizes = [
734
                        // Order matters! Size must always be tested from longest to shortest!
735 95
                        'longtext' => static::TEXT_LONG,
736 5
                        'mediumtext' => static::TEXT_MEDIUM,
737
                        'text' => static::TEXT_REGULAR,
738 95
                        'tinytext' => static::TEXT_SMALL,
739 3
                    ];
740
                    foreach ($sizes as $name => $length) {
741 95
                        if ($limit >= $length) {
742 1
                            return ['name' => $name];
743
                        }
744
                    }
745 1
                }
746 1
747 1
                return ['name' => 'text'];
748 1
            case static::PHINX_TYPE_BINARY:
749 1
                return ['name' => 'binary', 'limit' => $limit ?: 255];
750 1
            case static::PHINX_TYPE_VARBINARY:
751 1
                return ['name' => 'varbinary', 'limit' => $limit ?: 255];
752 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...
753
                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...
754 1
                    $sizes = [
755
                        // Order matters! Size must always be tested from longest to shortest!
756 1
                        'longblob' => static::BLOB_LONG,
757
                        'mediumblob' => static::BLOB_MEDIUM,
758 95
                        'blob' => static::BLOB_REGULAR,
759 82
                        'tinyblob' => static::BLOB_SMALL,
760
                    ];
761
                    foreach ($sizes as $name => $length) {
762 6
                        if ($limit >= $length) {
763 6
                            return ['name' => $name];
764 6
                        }
765 6
                    }
766 6
                }
767 6
768
                return ['name' => 'blob'];
769 6
            case static::PHINX_TYPE_BIT:
770 6
                return ['name' => 'bit', 'limit' => $limit ?: 64];
771 6
            case static::PHINX_TYPE_INTEGER:
772 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...
773 6
                    $sizes = [
774 6
                        // Order matters! Size must always be tested from longest to shortest!
775 6
                        'bigint' => static::INT_BIG,
776 2
                        'int' => static::INT_REGULAR,
777 2
                        'mediumint' => static::INT_MEDIUM,
778 6
                        'smallint' => static::INT_SMALL,
779
                        'tinyint' => static::INT_TINY,
780 5
                    ];
781 82
                    $limits = [
782 76
                        'int' => 11,
783 76
                        'bigint' => 20,
784 82
                    ];
785
                    foreach ($sizes as $name => $length) {
786 86
                        if ($limit >= $length) {
787 82
                            $def = ['name' => $name];
788
                            if (isset($limits[$name])) {
789 86
                                $def['limit'] = $limits[$name];
790 7
                            }
791
792 84
                            return $def;
793 5
                        }
794
                    }
795 83
                } 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...
796 7
                    $limit = 11;
797
                }
798 83
799 80
                return ['name' => 'int', 'limit' => $limit];
800
            case static::PHINX_TYPE_BIG_INTEGER:
801 83
                return ['name' => 'bigint', 'limit' => 20];
802 4
            case static::PHINX_TYPE_BOOLEAN:
803
                return ['name' => 'tinyint', 'limit' => 1];
804 83
            case static::PHINX_TYPE_UUID:
805 4
                return ['name' => 'char', 'limit' => 36];
806
            case static::TYPE_YEAR:
807 83
                if (!$limit || in_array($limit, [2, 4])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
808 80
                    $limit = 4;
809
                }
810 10
811 2
                return ['name' => 'year', 'limit' => $limit];
812
            default:
813 10
                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
814 10
        }
815 10
    }
816 10
817 5
    /**
818 8
     * Returns Phinx type by SQL type
819 5
     *
820
     * @param string $sqlTypeDef
821 6
     * @throws \RuntimeException
822 4
     * @internal param string $sqlType SQL type
823
     * @returns string Phinx type
824 2
     */
825
    public function getPhinxType($sqlTypeDef)
826
    {
827
        $matches = [];
828
        if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*(.+)*$/', $sqlTypeDef, $matches)) {
829
            throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported');
830 2
        } else {
831
            $limit = null;
832
            $precision = null;
833 2
            $type = $matches[1];
834 2
            if (count($matches) > 2) {
835 2
                $limit = $matches[3] ? (int)$matches[3] : null;
836
            }
837
            if (count($matches) > 4) {
838
                $precision = (int)$matches[5];
839
            }
840 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...
841
                $type = static::PHINX_TYPE_BOOLEAN;
842
                $limit = null;
843
            }
844
            switch ($type) {
845
                case 'varchar':
846 17
                    $type = static::PHINX_TYPE_STRING;
847
                    if ($limit === 255) {
848 17
                        $limit = null;
849 17
                    }
850 1
                    break;
851 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...
852 16
                    $type = static::PHINX_TYPE_CHAR;
853 16
                    if ($limit === 255) {
854 16
                        $limit = null;
855 16
                    }
856 14
                    if ($limit === 36) {
857 14
                        $type = static::PHINX_TYPE_UUID;
858 16
                    }
859 4
                    break;
860 4
                case 'tinyint':
861 16
                    $type = static::PHINX_TYPE_INTEGER;
862 3
                    $limit = static::INT_TINY;
863 3
                    break;
864 3
                case 'smallint':
865
                    $type = static::PHINX_TYPE_INTEGER;
866 16
                    $limit = static::INT_SMALL;
867 6
                    break;
868 6
                case 'mediumint':
869 3
                    $type = static::PHINX_TYPE_INTEGER;
870 3
                    $limit = static::INT_MEDIUM;
871 6
                    break;
872 16
                case 'int':
873 5
                    $type = static::PHINX_TYPE_INTEGER;
874 5
                    if ($limit === 11) {
875 1
                        $limit = null;
876 1
                    }
877 5
                    break;
878 2
                case 'bigint':
879 2
                    if ($limit === 20) {
880 5
                        $limit = null;
881 16
                    }
882 2
                    $type = static::PHINX_TYPE_BIG_INTEGER;
883 2
                    break;
884 2
                case 'bit':
885 16
                    $type = static::PHINX_TYPE_BIT;
886 2
                    if ($limit === 64) {
887 2
                        $limit = null;
888 2
                    }
889 16
                    break;
890 2
                case 'blob':
891 2
                    $type = static::PHINX_TYPE_BINARY;
892 2
                    break;
893 16
                case 'tinyblob':
894 15
                    $type = static::PHINX_TYPE_BINARY;
895 15
                    $limit = static::BLOB_TINY;
896 12
                    break;
897 12
                case 'mediumblob':
898 15
                    $type = static::PHINX_TYPE_BINARY;
899 11
                    $limit = static::BLOB_MEDIUM;
900 6
                    break;
901 4
                case 'longblob':
902 4
                    $type = static::PHINX_TYPE_BINARY;
903 6
                    $limit = static::BLOB_LONG;
904 6
                    break;
905 10
                case 'tinytext':
906 2
                    $type = static::PHINX_TYPE_TEXT;
907 2
                    $limit = static::TEXT_TINY;
908 10
                    break;
909 1
                case 'mediumtext':
910 1
                    $type = static::PHINX_TYPE_TEXT;
911 1
                    $limit = static::TEXT_MEDIUM;
912 10
                    break;
913 1
                case 'longtext':
914 1
                    $type = static::PHINX_TYPE_TEXT;
915 1
                    $limit = static::TEXT_LONG;
916 10
                    break;
917 1
            }
918 1
919 1
            // Call this to check if parsed type is supported.
920 10
            $this->getSqlType($type, $limit);
921 2
922 2
            $phinxType = [
923 2
                'name' => $type,
924 9
                'limit' => $limit,
925 2
                'precision' => $precision
926 2
            ];
927 2
928 8
            if (static::PHINX_TYPE_ENUM == $type) {
929 2
                $phinxType['values'] = explode("','", trim($matches[6], "()'"));
930 2
            }
931 2
932
            return $phinxType;
933
        }
934
    }
935 16
936
    /**
937
     * {@inheritdoc}
938 15
     */
939 15
    public function createDatabase($name, $options = [])
940
    {
941 15
        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
942
943 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...
944 3
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s` COLLATE `%s`', $name, $charset, $options['collation']));
945 3
        } else {
946
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s`', $name, $charset));
947 15
        }
948
    }
949
950
    /**
951
     * {@inheritdoc}
952
     */
953
    public function hasDatabase($name)
954 83
    {
955
        $rows = $this->fetchAll(
956 83
            sprintf(
957
                'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = \'%s\'',
958 83
                $name
959 1
            )
960 1
        );
961 82
962
        foreach ($rows as $row) {
963 83
            if (!empty($row)) {
964
                return true;
965
            }
966
        }
967
968 4
        return false;
969
    }
970 4
971 4
    /**
972 4
     * {@inheritdoc}
973
     */
974 4
    public function dropDatabase($name)
975 4
    {
976
        $this->execute(sprintf('DROP DATABASE IF EXISTS `%s`', $name));
977 4
    }
978 3
979 3
    /**
980
     * Gets the MySQL Column Definition for a Column object.
981 3
     *
982
     * @param \Phinx\Db\Table\Column $column Column
983 3
     * @return string
984
     */
985
    protected function getColumnSqlDefinition(Column $column)
986
    {
987
        if ($column->getType() instanceof Literal) {
988
            $def = (string)$column->getType();
989 81
        } else {
990
            $sqlType = $this->getSqlType($column->getType(), $column->getLimit());
991 81
            $def = strtoupper($sqlType['name']);
992 81
        }
993
        if ($column->getPrecision() && $column->getScale()) {
994
            $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
995
        } elseif (isset($sqlType['limit'])) {
996
            $def .= '(' . $sqlType['limit'] . ')';
997
        }
998 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...
999
            $def .= "('" . implode("', '", $values) . "')";
1000 89
        }
1001
        $def .= $column->getEncoding() ? ' CHARACTER SET ' . $column->getEncoding() : '';
1002 89
        $def .= $column->getCollation() ? ' COLLATE ' . $column->getCollation() : '';
1003
        $def .= (!$column->isSigned() && isset($this->signedColumnTypes[$column->getType()])) ? ' unsigned' : '';
1004 89
        $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...
1005 89
        $def .= $column->isIdentity() ? ' AUTO_INCREMENT' : '';
1006 89
        $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...
1007 2
1008 89
        if ($column->getComment()) {
1009 86
            $def .= ' COMMENT ' . $this->getConnection()->quote($column->getComment());
1010 86
        }
1011 89
1012 5
        if ($column->getUpdate()) {
1013 5
            $def .= ' ON UPDATE ' . $column->getUpdate();
1014 89
        }
1015 89
1016 89
        return $def;
1017 89
    }
1018 89
1019 89
    /**
1020
     * Gets the MySQL Index Definition for an Index object.
1021 89
     *
1022 2
     * @param \Phinx\Db\Table\Index $index Index
1023 2
     * @return string
1024
     */
1025 89
    protected function getIndexSqlDefinition(Index $index)
1026 1
    {
1027 1
        $def = '';
1028
        $limit = '';
1029 89
1030
        if ($index->getType() == Index::UNIQUE) {
1031
            $def .= ' UNIQUE';
1032
        }
1033
1034
        if ($index->getType() == Index::FULLTEXT) {
1035
            $def .= ' FULLTEXT';
1036
        }
1037
1038 16
        $def .= ' KEY';
1039
1040 16
        if (is_string($index->getName())) {
1041 16
            $def .= ' `' . $index->getName() . '`';
1042 16
        }
1043 2
1044 2
        if (!is_array($index->getLimit())) {
1045
            if ($index->getLimit()) {
1046 16
                $limit = '(' . $index->getLimit() . ')';
1047 5
            }
1048 5
            $def .= ' (`' . implode('`,`', $index->getColumns()) . '`' . $limit . ')';
1049
        } else {
1050 16
            $columns = $index->getColumns();
1051 1
            $limits = $index->getLimit();
1052 1
            $def .= ' (';
1053
            foreach ($columns as $column) {
1054 16
                $limit = !isset($limits[$column]) || $limits[$column] <= 0 ? '' : '(' . $limits[$column] . ')';
1055
                $def .= '`' . $column . '`' . $limit . ', ';
1056 16
            }
1057 5
            $def = rtrim($def, ', ');
1058 5
            $def .= ' )';
1059
        }
1060 16
1061
        return $def;
1062 16
    }
1063
1064
    /**
1065
     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
1066
     *
1067
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
1068
     * @return string
1069
     */
1070 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...
1071 17
    {
1072
        $def = '';
1073 17
        if ($foreignKey->getConstraint()) {
1074 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...
1075 5
        }
1076 5
        $columnNames = [];
1077 17
        foreach ($foreignKey->getColumns() as $column) {
1078 17
            $columnNames[] = $this->quoteColumnName($column);
1079 17
        }
1080 17
        $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
1081 17
        $refColumnNames = [];
1082 17
        foreach ($foreignKey->getReferencedColumns() as $column) {
1083 17
            $refColumnNames[] = $this->quoteColumnName($column);
1084 17
        }
1085 17
        $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
1086 17
        if ($foreignKey->getOnDelete()) {
1087 17
            $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
1088 2
        }
1089 2
        if ($foreignKey->getOnUpdate()) {
1090 17
            $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
1091 2
        }
1092 2
1093 17
        return $def;
1094
    }
1095
1096
    /**
1097
     * Describes a database table. This is a MySQL adapter specific method.
1098
     *
1099
     * @param string $tableName Table name
1100
     * @return array
1101
     */
1102 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...
1103
    {
1104 2
        $options = $this->getOptions();
1105
1106
        // mysql specific
1107 2
        $sql = sprintf(
1108
            "SELECT *
1109
             FROM information_schema.tables
1110
             WHERE table_schema = '%s'
1111 2
             AND table_name = '%s'",
1112 2
            $options['name'],
1113
            $tableName
1114 2
        );
1115
1116 2
        return $this->fetchRow($sql);
1117
    }
1118
1119
    /**
1120
     * Returns MySQL column types (inherited and MySQL specified).
1121
     * @return array
1122
     */
1123 85
    public function getColumnTypes()
1124
    {
1125 85
        return array_merge(parent::getColumnTypes(), ['enum', 'set', 'year', 'json']);
1126
    }
1127
1128
    /**
1129
     * {@inheritDoc}
1130
     *
1131
     */
1132 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...
1133
    {
1134
        $options = $this->getOptions();
1135
        $options = [
1136
            'username' => $options['user'],
1137
            'password' => $options['pass'],
1138
            'database' => $options['name'],
1139
            'quoteIdentifiers' => true,
1140
        ] + $options;
1141
1142
        $driver = new MysqlDriver($options);
1143
        if (method_exists($driver, 'setConnection')) {
1144
            $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...
1145
        } else {
1146
            $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...
1147
        }
1148
1149
        return new Connection(['driver' => $driver] + $options);
1150
    }
1151
}
1152