Completed
Pull Request — master (#1351)
by
unknown
01:58
created

MysqlAdapter::getIndexSqlDefinition()   D

Complexity

Conditions 9
Paths 16

Size

Total Lines 37
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 37
ccs 25
cts 25
cp 1
rs 4.909
c 0
b 0
f 0
cc 9
eloc 24
nc 16
nop 1
crap 9
1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 * Copyright (c) 2015 Rob Morgan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated * documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 *
26
 * @package    Phinx
27
 * @subpackage Phinx\Db\Adapter
28
 */
29
30
namespace Phinx\Db\Adapter;
31
32
use Phinx\Db\Table\Column;
33
use Phinx\Db\Table\ForeignKey;
34
use Phinx\Db\Table\Index;
35
use Phinx\Db\Table\Table;
36
use Phinx\Db\Util\AlterInstructions;
37
use Phinx\Util\Literal;
38
39
/**
40
 * Phinx MySQL Adapter.
41
 *
42
 * @author Rob Morgan <[email protected]>
43
 */
44
class MysqlAdapter extends PdoAdapter implements AdapterInterface {
45
46
	protected $signedColumnTypes = [
47
		'integer'    => true,
48
		'biginteger' => true,
49
		'float'      => true,
50
		'decimal'    => true,
51
		'boolean'    => true
52
	];
53
54
	const TEXT_TINY = 255;
55
	const TEXT_SMALL = 255; /* deprecated, alias of TEXT_TINY */
56
	const TEXT_REGULAR = 65535;
57
	const TEXT_MEDIUM = 16777215;
58
	const TEXT_LONG = 4294967295;
59
60
	// According to https://dev.mysql.com/doc/refman/5.0/en/blob.html BLOB sizes are the same as TEXT
61
	const BLOB_TINY = 255;
62
	const BLOB_SMALL = 255; /* deprecated, alias of BLOB_TINY */
63
	const BLOB_REGULAR = 65535;
64
	const BLOB_MEDIUM = 16777215;
65
	const BLOB_LONG = 4294967295;
66
67
	const INT_TINY = 255;
68
	const INT_SMALL = 65535;
69
	const INT_MEDIUM = 16777215;
70 80
	const INT_REGULAR = 4294967295;
71
	const INT_BIG = 18446744073709551615;
72 80
73 80
	const BIT = 64;
74
75
	const TYPE_YEAR = 'year';
76
77
	/**
78
	 * {@inheritdoc}
79 80
	 */
80 80
	public function connect() {
81
		if ( $this->connection === null ) {
82 80
			if ( ! class_exists( 'PDO' ) || ! in_array( 'mysql', \PDO::getAvailableDrivers(), true ) ) {
83
				// @codeCoverageIgnoreStart
84 80
				throw new \RuntimeException( 'You need to enable the PDO_Mysql extension for Phinx to run properly.' );
85
				// @codeCoverageIgnoreEnd
86
			}
87
88
			$db      = null;
89 80
			$options = $this->getOptions();
90 80
91 80
			$dsn = 'mysql:';
92 80
93
			if ( ! empty( $options['unix_socket'] ) ) {
94
				// use socket connection
95 80
				$dsn .= 'unix_socket=' . $options['unix_socket'];
96
			} else {
97
				// use network connection
98 80
				$dsn .= 'host=' . $options['host'];
99
				if ( ! empty( $options['port'] ) ) {
100
					$dsn .= ';port=' . $options['port'];
101
				}
102 80
			}
103
104
			$dsn .= ';dbname=' . $options['name'];
105
106 80
			// charset support
107 80
			if ( ! empty( $options['charset'] ) ) {
108
				$dsn .= ';charset=' . $options['charset'];
109
			}
110 80
111
			$driverOptions = [ \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION ];
112
113 80
			// support arbitrary \PDO::MYSQL_ATTR_* driver options and pass them to PDO
114 80
			// http://php.net/manual/en/ref.pdo-mysql.php#pdo-mysql.constants
115 1 View Code Duplication
			foreach ( $options as $key => $option ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
116 1
				if ( strpos( $key, 'mysql_attr_' ) === 0 ) {
117 1
					$driverOptions[ constant( '\PDO::' . strtoupper( $key ) ) ] = $option;
118 1
				}
119
			}
120
121 80
			try {
122 80
				$db = new \PDO( $dsn, $options['user'], $options['pass'], $driverOptions );
123 80
			} catch ( \PDOException $exception ) {
124
				throw new \InvalidArgumentException( sprintf(
125
					'There was a problem connecting to the database: %s',
126
					$exception->getMessage()
127
				) );
128 81
			}
129
130 81
			$this->setConnection( $db );
131 81
		}
132
	}
133
134
	/**
135
	 * {@inheritdoc}
136 6
	 */
137
	public function disconnect() {
138 6
		$this->connection = null;
139
	}
140
141
	/**
142
	 * {@inheritdoc}
143
	 */
144 6
	public function hasTransactions() {
145
		return true;
146 6
	}
147 6
148
	/**
149
	 * {@inheritdoc}
150
	 */
151
	public function beginTransaction() {
152 6
		$this->execute( 'START TRANSACTION' );
153
	}
154 6
155 6
	/**
156
	 * {@inheritdoc}
157
	 */
158
	public function commitTransaction() {
159
		$this->execute( 'COMMIT' );
160 1
	}
161
162 1
	/**
163 1
	 * {@inheritdoc}
164
	 */
165
	public function rollbackTransaction() {
166
		$this->execute( 'ROLLBACK' );
167
	}
168 112
169
	/**
170 112
	 * {@inheritdoc}
171
	 */
172
	public function quoteTableName( $tableName ) {
173
		return str_replace( '.', '`.`', $this->quoteColumnName( $tableName ) );
174
	}
175
176 112
	/**
177
	 * {@inheritdoc}
178 112
	 */
179
	public function quoteColumnName( $columnName ) {
180
		return '`' . str_replace( '`', '``', $columnName ) . '`';
181
	}
182
183
	/**
184 82
	 * {@inheritdoc}
185
	 */
186 82 View Code Duplication
	public function hasTable( $tableName ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
187
		$options = $this->getOptions();
188 82
189
		$exists = $this->fetchRow( sprintf(
190
			"SELECT TABLE_NAME
191 82
            FROM INFORMATION_SCHEMA.TABLES
192 82
            WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'",
193
			$options['name'],
194 82
			$tableName
195
		) );
196 82
197
		return ! empty( $exists );
198
	}
199
200
	/**
201
	 * {@inheritdoc}
202 82
	 */
203
	public function createTable( Table $table, array $columns = [], array $indexes = [] ) {
204
		// This method is based on the MySQL docs here: http://dev.mysql.com/doc/refman/5.1/en/create-index.html
205
		$defaultOptions = [
206 82
			'engine'    => 'InnoDB',
207
			'collation' => 'utf8_general_ci'
208 82
		];
209 82
210
		$options = array_merge(
211
			$defaultOptions,
212 82
			array_intersect_key( $this->getOptions(), $defaultOptions ),
213 82
			$table->getOptions()
214 68
		);
215 68
216 68
		// Add the default primary key
217 68
		if ( ! isset( $options['id'] ) || ( isset( $options['id'] ) && $options['id'] === true ) ) {
218 68
			$column = new Column();
219
			$column->setName( 'id' )
220 68
			       ->setType( 'integer' )
221 68
			       ->setSigned( isset( $options['signed'] ) ? $options['signed'] : true )
222 82
			       ->setIdentity( true );
223
224 2
			array_unshift( $columns, $column );
225 2
			$options['primary_key'] = 'id';
226 2
		} elseif ( isset( $options['id'] ) && is_string( $options['id'] ) ) {
227 2
			// Handle id => "field_name" to support AUTO_INCREMENT
228
			$column = new Column();
229 2
			$column->setName( $options['id'] )
230 2
			       ->setType( 'integer' )
231 2
			       ->setSigned( isset( $options['signed'] ) ? $options['signed'] : true )
232
			       ->setIdentity( true );
233
234
			array_unshift( $columns, $column );
235
			$options['primary_key'] = $options['id'];
236 82
		}
237 82
238 82
		// TODO - process table options like collation etc
239 82
240
		// process table engine (default to InnoDB)
241
		$optionsStr = 'ENGINE = InnoDB';
242 82
		if ( isset( $options['engine'] ) ) {
243 82
			$optionsStr = sprintf( 'ENGINE = %s', $options['engine'] );
244 82
		}
245 82
246 82
		// process table collation
247
		if ( isset( $options['collation'] ) ) {
248
			$charset    = explode( '_', $options['collation'] );
249 82
			$optionsStr .= sprintf( ' CHARACTER SET %s', $charset[0] );
250 2
			$optionsStr .= sprintf( ' COLLATE %s', $options['collation'] );
251 2
		}
252
253 82
		// set the table comment
254 82
		if ( isset( $options['comment'] ) ) {
255 82
			$optionsStr .= sprintf( " COMMENT=%s ", $this->getConnection()->quote( $options['comment'] ) );
256 82
		}
257 82
258
		$sql = 'CREATE TABLE ';
259
		$sql .= $this->quoteTableName( $table->getName() ) . ' (';
260 82
		foreach ( $columns as $column ) {
261 82
			$sql .= $this->quoteColumnName( $column->getName() ) . ' ' . $this->getColumnSqlDefinition( $column ) . ', ';
262 82
		}
263 82
264 81
		// set the primary key(s)
265 82 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...
266
			$sql = rtrim( $sql );
267
			$sql .= ' PRIMARY KEY (';
268 2
			if ( is_string( $options['primary_key'] ) ) { // handle primary_key => 'id'
269 2
				$sql .= $this->quoteColumnName( $options['primary_key'] );
270 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...
271 2
				$sql .= implode( ',', array_map( [ $this, 'quoteColumnName' ], $options['primary_key'] ) );
272 2
			}
273 2
			$sql .= ')';
274 2
		} else {
275 2
			$sql = substr( rtrim( $sql ), 0, - 1 ); // no primary keys
276 2
		}
277 2
278 82
		// set the indexes
279 82
		foreach ( $indexes as $index ) {
280 1
			$sql .= ', ' . $this->getIndexSqlDefinition( $index );
281
		}
282
283
		$sql .= ') ' . $optionsStr;
284 82
		$sql = rtrim( $sql ) . ';';
285 82
286 10
		// execute the sql
287 82
		$this->execute( $sql );
288
	}
289
290 82
	/**
291 82
	 * {@inheritdoc}
292 2
	 */
293 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...
294
		$sql = sprintf(
295 82
			'RENAME TABLE %s TO %s',
296 82
			$this->quoteTableName( $tableName ),
297
			$this->quoteTableName( $newTableName )
298
		);
299 82
300 82
		return new AlterInstructions( [], [ $sql ] );
301
	}
302
303
	/**
304
	 * {@inheritdoc}
305 5
	 */
306
	protected function getDropTableInstructions( $tableName ) {
307 5
		$sql = sprintf( 'DROP TABLE %s', $this->quoteTableName( $tableName ) );
308 5
309
		return new AlterInstructions( [], [ $sql ] );
310
	}
311
312
	/**
313 5
	 * {@inheritdoc}
314
	 */
315 5
	public function truncateTable( $tableName ) {
316 5
		$sql = sprintf(
317
			'TRUNCATE TABLE %s',
318
			$this->quoteTableName( $tableName )
319
		);
320
321 1
		$this->execute( $sql );
322
	}
323 1
324 1
	/**
325 1
	 * {@inheritdoc}
326 1
	 */
327
	public function getColumns( $tableName ) {
328 1
		$columns = [];
329 1
		$rows    = $this->fetchAll( sprintf( 'SHOW COLUMNS FROM %s', $this->quoteTableName( $tableName ) ) );
330
		foreach ( $rows as $columnInfo ) {
331
			$phinxType = $this->getPhinxType( $columnInfo['Type'] );
332
333
			$column = new Column();
334 12
			$column->setName( $columnInfo['Field'] )
335
			       ->setNull( $columnInfo['Null'] !== 'NO' )
336 12
			       ->setDefault( $columnInfo['Default'] )
337 12
			       ->setType( $phinxType['name'] )
338 12
			       ->setSigned( strpos( $columnInfo['Type'], 'unsigned' ) === false )
339 12
			       ->setLimit( $phinxType['limit'] );
340
341 12
			if ( $columnInfo['Extra'] === 'auto_increment' ) {
342 12
				$column->setIdentity( true );
343 12
			}
344 12
345 12
			if ( isset( $phinxType['values'] ) ) {
346 12
				$column->setValues( $phinxType['values'] );
347
			}
348 12
349 12
			$columns[] = $column;
350 12
		}
351
352 12
		return $columns;
353 3
	}
354 3
355
	/**
356 12
	 * {@inheritdoc}
357 12
	 */
358 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...
359 12
		$rows = $this->fetchAll( sprintf( 'SHOW COLUMNS FROM %s', $this->quoteTableName( $tableName ) ) );
360
		foreach ( $rows as $column ) {
361
			if ( strcasecmp( $column['Field'], $columnName ) === 0 ) {
362
				return true;
363
			}
364
		}
365 79
366
		return false;
367 79
	}
368 79
369 79
	/**
370 77
	 * {@inheritdoc}
371
	 */
372 77
	protected function getAddColumnInstructions( Table $table, Column $column ) {
373
		$alter = sprintf(
374 21
			'ADD %s %s',
375
			$this->quoteColumnName( $column->getName() ),
376
			$this->getColumnSqlDefinition( $column )
377
		);
378
379
		if ( $column->getAfter() ) {
380
			$alter .= ' AFTER ' . $this->quoteColumnName( $column->getAfter() );
381
		}
382
383 95
		return new AlterInstructions( [ $alter ] );
384
	}
385 95
386 10
	/**
387 95
	 * {@inheritdoc}
388 79
	 */
389 79
	protected function getRenameColumnInstructions( $tableName, $columnName, $newColumnName ) {
390 95
		$rows = $this->fetchAll( sprintf( 'DESCRIBE %s', $this->quoteTableName( $tableName ) ) );
391
		foreach ( $rows as $row ) {
392
			if ( strcasecmp( $row['Field'], $columnName ) === 0 ) {
393
				$null  = ( $row['Null'] == 'NO' ) ? 'NOT NULL' : 'NULL';
394
				$extra = ' ' . strtoupper( $row['Extra'] );
395
				if ( ! is_null( $row['Default'] ) ) {
396 18
					$extra .= $this->getDefaultValueDefinition( $row['Default'] );
397
				}
398 18
				$definition = $row['Type'] . ' ' . $null . $extra;
399 18
400 18
				$alter = sprintf(
401 18
					'CHANGE COLUMN %s %s %s',
402 18
					$this->quoteColumnName( $columnName ),
403 18
					$this->quoteColumnName( $newColumnName ),
404
					$definition
405 18
				);
406 2
407 2
				return new AlterInstructions( [ $alter ] );
408
			}
409 18
		}
410 18
411
		throw new \InvalidArgumentException( sprintf(
412
			'The specified column doesn\'t exist: ' .
413
			$columnName
414
		) );
415 7
	}
416
417 7
	/**
418 7
	 * {@inheritdoc}
419 7
	 */
420 5
	protected function getChangeColumnInstructions( $tableName, $columnName, Column $newColumn ) {
421 5
		$after = $newColumn->getAfter() ? ' AFTER ' . $this->quoteColumnName( $newColumn->getAfter() ) : '';
422 5
		$alter = sprintf(
423 1
			'CHANGE %s %s %s%s',
424 1
			$this->quoteColumnName( $columnName ),
425 5
			$this->quoteColumnName( $newColumn->getName() ),
426
			$this->getColumnSqlDefinition( $newColumn ),
427 5
			$after
428 5
		);
429 5
430 5
		return new AlterInstructions( [ $alter ] );
431 5
	}
432 5
433
	/**
434 5
	 * {@inheritdoc}
435 5
	 */
436 5
	protected function getDropColumnInstructions( $tableName, $columnName ) {
437
		$alter = sprintf( 'DROP COLUMN %s', $this->quoteColumnName( $columnName ) );
438 6
439
		return new AlterInstructions( [ $alter ] );
440 2
	}
441
442
	/**
443 2
	 * Get an array of indexes from a particular table.
444
	 *
445
	 * @param string $tableName Table Name
446
	 *
447
	 * @return array
448
	 */
449 5 View Code Duplication
	protected function getIndexes( $tableName ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
450
		$indexes = [];
451 5
		$rows    = $this->fetchAll( sprintf( 'SHOW INDEXES FROM %s', $this->quoteTableName( $tableName ) ) );
452 5
		foreach ( $rows as $row ) {
453 5
			if ( ! isset( $indexes[ $row['Key_name'] ] ) ) {
454 5
				$indexes[ $row['Key_name'] ] = [ 'columns' => [] ];
455 5
			}
456 5
			$indexes[ $row['Key_name'] ]['columns'][] = strtolower( $row['Column_name'] );
457 5
		}
458 5
459
		return $indexes;
460 5
	}
461 5
462 5
	/**
463
	 * {@inheritdoc}
464
	 */
465
	public function hasIndex( $tableName, $columns ) {
466
		if ( is_string( $columns ) ) {
467 5
			$columns = [ $columns ]; // str to array
468
		}
469 5
470 5
		$columns = array_map( 'strtolower', $columns );
471 5
		$indexes = $this->getIndexes( $tableName );
472 5
473 5
		foreach ( $indexes as $index ) {
474 5
			if ( $columns == $index['columns'] ) {
475 5
				return true;
476 5
			}
477
		}
478
479
		return false;
480
	}
481
482
	/**
483
	 * {@inheritdoc}
484 19
	 */
485 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...
486 19
		$indexes = $this->getIndexes( $tableName );
487 19
488 19
		foreach ( $indexes as $name => $index ) {
489 18
			if ( $name === $indexName ) {
490 18
				return true;
491 18
			}
492 18
		}
493 19
494 19
		return false;
495
	}
496
497
	/**
498
	 * {@inheritdoc}
499
	 */
500 14
	protected function getAddIndexInstructions( Table $table, Index $index ) {
501
		$alter = sprintf(
502 14
			'ADD %s',
503 6
			$this->getIndexSqlDefinition( $index )
504 6
		);
505
506 14
		return new AlterInstructions( [ $alter ] );
507 14
	}
508
509 14
	/**
510 14
	 * {@inheritdoc}
511 12
	 */
512
	protected function getDropIndexByColumnsInstructions( $tableName, $columns ) {
513 13
		if ( is_string( $columns ) ) {
514
			$columns = [ $columns ]; // str to array
515 11
		}
516
517
		$indexes = $this->getIndexes( $tableName );
518
		$columns = array_map( 'strtolower', $columns );
519
520 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...
521 1
			if ( $columns == $index['columns'] ) {
522
				return new AlterInstructions( [
523 1
					sprintf(
524
						'DROP INDEX %s',
525 1
						$this->quoteColumnName( $indexName )
526 1
					)
527 1
				] );
528
			}
529 1
		}
530
531
		throw new \InvalidArgumentException( sprintf(
532
			"The specified index on columns '%s' does not exist",
533
			implode( ',', $columns )
534
		) );
535
	}
536
537 4
	/**
538
	 * {@inheritdoc}
539 4
	 */
540 4
	protected function getDropIndexByNameInstructions( $tableName, $indexName ) {
541 4
542 4
		$indexes = $this->getIndexes( $tableName );
543 4
544 4 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...
545 4
			if ( $name === $indexName ) {
546 4
				return new AlterInstructions( [
547
					sprintf(
548
						'DROP INDEX %s',
549
						$this->quoteColumnName( $indexName )
550
					)
551 3
				] );
552
			}
553 3
		}
554 2
555 2
		throw new \InvalidArgumentException( sprintf(
556
			"The specified index name '%s' does not exist",
557 3
			$indexName
558 3
		) );
559
	}
560 3
561 3
	/**
562 3
	 * {@inheritdoc}
563 3
	 */
564 3 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...
565 3
		if ( is_string( $columns ) ) {
566 3
			$columns = [ $columns ]; // str to array
567 3
		}
568 3
		$foreignKeys = $this->getForeignKeys( $tableName );
569 3
		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...
570
			if ( isset( $foreignKeys[ $constraint ] ) ) {
571 3
				return ! empty( $foreignKeys[ $constraint ] );
572 1
			}
573
574
			return false;
575
		} else {
576
			foreach ( $foreignKeys as $key ) {
577 2
				if ( $columns == $key['columns'] ) {
578
					return true;
579 2
				}
580
			}
581 2
582
			return false;
583 2
		}
584 2
	}
585 2
586 2
	/**
587 2
	 * Get an array of foreign keys from a particular table.
588 2
	 *
589 2
	 * @param string $tableName Table Name
590 2
	 *
591 2
	 * @return array
592
	 */
593 2 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...
594
		$foreignKeys = [];
595
		$rows        = $this->fetchAll( sprintf(
596
			"SELECT
597
              CONSTRAINT_NAME,
598
              TABLE_NAME,
599 21
              COLUMN_NAME,
600
              REFERENCED_TABLE_NAME,
601 21
              REFERENCED_COLUMN_NAME
602 5
            FROM information_schema.KEY_COLUMN_USAGE
603 5
            WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
604 21
              AND REFERENCED_TABLE_NAME IS NOT NULL
605 21
              AND TABLE_NAME = '%s'
606 6
            ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
607 4
			$tableName
608
		) );
609 4
		foreach ( $rows as $row ) {
610
			$foreignKeys[ $row['CONSTRAINT_NAME'] ]['table']                = $row['TABLE_NAME'];
611 15
			$foreignKeys[ $row['CONSTRAINT_NAME'] ]['columns'][]            = $row['COLUMN_NAME'];
612 12
			$foreignKeys[ $row['CONSTRAINT_NAME'] ]['referenced_table']     = $row['REFERENCED_TABLE_NAME'];
613 10
			$foreignKeys[ $row['CONSTRAINT_NAME'] ]['referenced_columns'][] = $row['REFERENCED_COLUMN_NAME'];
614
		}
615 11
616 11
		return $foreignKeys;
617
	}
618
619
	/**
620
	 * {@inheritdoc}
621
	 */
622 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...
623
		$alter = sprintf(
624
			'ADD %s',
625
			$this->getForeignKeySqlDefinition( $foreignKey )
626 22
		);
627
628 22
		return new AlterInstructions( [ $alter ] );
629 22
	}
630
631
	/**
632
	 * {@inheritdoc}
633
	 */
634
	protected function getDropForeignKeyInstructions( $tableName, $constraint ) {
635
		$alter = sprintf(
636
			'DROP FOREIGN KEY %s',
637
			$constraint
638
		);
639
640 22
		return new AlterInstructions( [ $alter ] );
641
	}
642 22
643 22
	/**
644 19
	 * {@inheritdoc}
645 19
	 */
646 19
	protected function getDropForeignKeyByColumnsInstructions( $tableName, $columns ) {
647 19
		$instructions = new AlterInstructions();
648 22
649 22
		foreach ( $columns as $column ) {
650
			$rows = $this->fetchAll( sprintf(
651
				"SELECT
652
                    CONSTRAINT_NAME
653
                  FROM information_schema.KEY_COLUMN_USAGE
654
                  WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
655 15
                    AND REFERENCED_TABLE_NAME IS NOT NULL
656
                    AND TABLE_NAME = '%s'
657 15
                    AND COLUMN_NAME = '%s'
658 15
                  ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
659 15
				$tableName,
660 15
				$column
661 15
			) );
662 15
663 15
			foreach ( $rows as $row ) {
664 15
				$instructions->merge( $this->getDropForeignKeyInstructions( $tableName, $row['CONSTRAINT_NAME'] ) );
665
			}
666
		}
667
668
		if ( empty( $instructions->getAlterParts() ) ) {
669 8
			throw new \InvalidArgumentException( sprintf(
670
				"Not foreign key on columns '%s' exist",
671 8
				implode( ',', $columns )
672 3
			) );
673 3
		}
674
675
		return $instructions;
676 8
	}
677 8
678 8
	/**
679 8
	 * {@inheritdoc}
680 8
	 */
681
	public function getSqlType( $type, $limit = null ) {
682 8
		switch ( $type ) {
683 8
			case static::PHINX_TYPE_FLOAT:
684 8
			case static::PHINX_TYPE_DECIMAL:
685
			case static::PHINX_TYPE_DATE:
686 7
			case static::PHINX_TYPE_ENUM:
687 7
			case static::PHINX_TYPE_SET:
688
			case static::PHINX_TYPE_JSON:
689
				// Geospatial database types
690
			case static::PHINX_TYPE_GEOMETRY:
691
			case static::PHINX_TYPE_POINT:
692
			case static::PHINX_TYPE_LINESTRING:
693
			case static::PHINX_TYPE_POLYGON:
694
				return [ 'name' => $type ];
695 7
			case static::PHINX_TYPE_DATETIME:
696 7
			case static::PHINX_TYPE_TIMESTAMP:
697
			case static::PHINX_TYPE_TIME:
698 7
				return [ 'name' => $type, 'limit' => $limit ];
699 7
			case static::PHINX_TYPE_STRING:
700 7
				return [ 'name' => 'varchar', 'limit' => $limit ?: 255 ];
701 7
			case static::PHINX_TYPE_CHAR:
702 7
				return [ 'name' => 'char', 'limit' => $limit ?: 255 ];
703 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...
704 7
				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...
705
					$sizes = [
706
						// Order matters! Size must always be tested from longest to shortest!
707
						'longtext'   => static::TEXT_LONG,
708
						'mediumtext' => static::TEXT_MEDIUM,
709 96
						'text'       => static::TEXT_REGULAR,
710
						'tinytext'   => static::TEXT_SMALL,
711
					];
712 96
					foreach ( $sizes as $name => $length ) {
713 87
						if ( $limit >= $length ) {
714
							return [ 'name' => $name ];
715 96
						}
716 4
					}
717
				}
718 96
719 9
				return [ 'name' => 'text' ];
720
			case static::PHINX_TYPE_BINARY:
721
				return [ 'name' => 'binary', 'limit' => $limit ?: 255 ];
722 6
			case static::PHINX_TYPE_VARBINARY:
723 6
				return [ 'name' => 'varbinary', 'limit' => $limit ?: 255 ];
724 6 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...
725 6
				if ( $limit ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
726 6
					$sizes = [
727 6
						// Order matters! Size must always be tested from longest to shortest!
728 6
						'longblob'   => static::BLOB_LONG,
729 6
						'mediumblob' => static::BLOB_MEDIUM,
730
						'blob'       => static::BLOB_REGULAR,
731 5
						'tinyblob'   => static::BLOB_SMALL,
732
					];
733 5
					foreach ( $sizes as $name => $length ) {
734
						if ( $limit >= $length ) {
735 95
							return [ 'name' => $name ];
736 5
						}
737
					}
738 95
				}
739 3
740
				return [ 'name' => 'blob' ];
741 95
			case static::PHINX_TYPE_BIT:
742 1
				return [ 'name' => 'bit', 'limit' => $limit ?: 64 ];
743
			case static::PHINX_TYPE_INTEGER:
744
				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...
745 1
					$sizes  = [
746 1
						// Order matters! Size must always be tested from longest to shortest!
747 1
						'bigint'    => static::INT_BIG,
748 1
						'int'       => static::INT_REGULAR,
749 1
						'mediumint' => static::INT_MEDIUM,
750 1
						'smallint'  => static::INT_SMALL,
751 1
						'tinyint'   => static::INT_TINY,
752 1
					];
753
					$limits = [
754 1
						'int'    => 11,
755
						'bigint' => 20,
756 1
					];
757
					foreach ( $sizes as $name => $length ) {
758 95
						if ( $limit >= $length ) {
759 82
							$def = [ 'name' => $name ];
760
							if ( isset( $limits[ $name ] ) ) {
761
								$def['limit'] = $limits[ $name ];
762 6
							}
763 6
764 6
							return $def;
765 6
						}
766 6
					}
767 6
				} 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...
768
					$limit = 11;
769 6
				}
770 6
771 6
				return [ 'name' => 'int', 'limit' => $limit ];
772 6
			case static::PHINX_TYPE_BIG_INTEGER:
773 6
				return [ 'name' => 'bigint', 'limit' => 20 ];
774 6
			case static::PHINX_TYPE_BOOLEAN:
775 6
				return [ 'name' => 'tinyint', 'limit' => 1 ];
776 2
			case static::PHINX_TYPE_UUID:
777 2
				return [ 'name' => 'char', 'limit' => 36 ];
778 6
			case static::TYPE_YEAR:
779
				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...
780 5
					$limit = 4;
781 82
				}
782 76
783 76
				return [ 'name' => 'year', 'limit' => $limit ];
784 82
			default:
785
				throw new \RuntimeException( 'The type: "' . $type . '" is not supported.' );
786 86
		}
787 82
	}
788
789 86
	/**
790 7
	 * Returns Phinx type by SQL type
791
	 *
792 84
	 * @param string $sqlTypeDef
793 5
	 *
794
	 * @throws \RuntimeException
795 83
	 * @internal param string $sqlType SQL type
796 7
	 * @returns string Phinx type
797
	 */
798 83
	public function getPhinxType( $sqlTypeDef ) {
799 80
		$matches = [];
800
		if ( ! preg_match( '/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*(.+)*$/', $sqlTypeDef, $matches ) ) {
801 83
			throw new \RuntimeException( 'Column type ' . $sqlTypeDef . ' is not supported' );
802 4
		} else {
803
			$limit     = null;
804 83
			$precision = null;
805 4
			$type      = $matches[1];
806
			if ( count( $matches ) > 2 ) {
807 83
				$limit = $matches[3] ? (int) $matches[3] : null;
808 80
			}
809
			if ( count( $matches ) > 4 ) {
810 10
				$precision = (int) $matches[5];
811 2
			}
812 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...
813 10
				$type  = static::PHINX_TYPE_BOOLEAN;
814 10
				$limit = null;
815 10
			}
816 10
			switch ( $type ) {
817 5
				case 'varchar':
818 8
					$type = static::PHINX_TYPE_STRING;
819 5
					if ( $limit === 255 ) {
820
						$limit = null;
821 6
					}
822 4
					break;
823 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...
824 2
					$type = static::PHINX_TYPE_CHAR;
825
					if ( $limit === 255 ) {
826
						$limit = null;
827
					}
828
					if ( $limit === 36 ) {
829
						$type = static::PHINX_TYPE_UUID;
830 2
					}
831
					break;
832
				case 'tinyint':
833 2
					$type  = static::PHINX_TYPE_INTEGER;
834 2
					$limit = static::INT_TINY;
835 2
					break;
836
				case 'smallint':
837
					$type  = static::PHINX_TYPE_INTEGER;
838
					$limit = static::INT_SMALL;
839
					break;
840
				case 'mediumint':
841
					$type  = static::PHINX_TYPE_INTEGER;
842
					$limit = static::INT_MEDIUM;
843
					break;
844
				case 'int':
845
					$type = static::PHINX_TYPE_INTEGER;
846 17
					if ( $limit === 11 ) {
847
						$limit = null;
848 17
					}
849 17
					break;
850 1
				case 'bigint':
851
					if ( $limit === 20 ) {
852 16
						$limit = null;
853 16
					}
854 16
					$type = static::PHINX_TYPE_BIG_INTEGER;
855 16
					break;
856 14
				case 'bit':
857 14
					$type = static::PHINX_TYPE_BIT;
858 16
					if ( $limit === 64 ) {
859 4
						$limit = null;
860 4
					}
861 16
					break;
862 3
				case 'blob':
863 3
					$type = static::PHINX_TYPE_BINARY;
864 3
					break;
865
				case 'tinyblob':
866 16
					$type  = static::PHINX_TYPE_BINARY;
867 6
					$limit = static::BLOB_TINY;
868 6
					break;
869 3
				case 'mediumblob':
870 3
					$type  = static::PHINX_TYPE_BINARY;
871 6
					$limit = static::BLOB_MEDIUM;
872 16
					break;
873 5
				case 'longblob':
874 5
					$type  = static::PHINX_TYPE_BINARY;
875 1
					$limit = static::BLOB_LONG;
876 1
					break;
877 5
				case 'tinytext':
878 2
					$type  = static::PHINX_TYPE_TEXT;
879 2
					$limit = static::TEXT_TINY;
880 5
					break;
881 16
				case 'mediumtext':
882 2
					$type  = static::PHINX_TYPE_TEXT;
883 2
					$limit = static::TEXT_MEDIUM;
884 2
					break;
885 16
				case 'longtext':
886 2
					$type  = static::PHINX_TYPE_TEXT;
887 2
					$limit = static::TEXT_LONG;
888 2
					break;
889 16
			}
890 2
891 2
			// Call this to check if parsed type is supported.
892 2
			$this->getSqlType( $type, $limit );
893 16
894 15
			$phinxType = [
895 15
				'name'      => $type,
896 12
				'limit'     => $limit,
897 12
				'precision' => $precision
898 15
			];
899 11
900 6
			if ( static::PHINX_TYPE_ENUM == $type ) {
901 4
				$phinxType['values'] = explode( "','", trim( $matches[6], "()'" ) );
902 4
			}
903 6
904 6
			return $phinxType;
905 10
		}
906 2
	}
907 2
908 10
	/**
909 1
	 * {@inheritdoc}
910 1
	 */
911 1
	public function createDatabase( $name, $options = [] ) {
912 10
		$charset = isset( $options['charset'] ) ? $options['charset'] : 'utf8';
913 1
914 1 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...
915 1
			$this->execute( sprintf( 'CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s` COLLATE `%s`', $name, $charset, $options['collation'] ) );
916 10
		} else {
917 1
			$this->execute( sprintf( 'CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s`', $name, $charset ) );
918 1
		}
919 1
	}
920 10
921 2
	/**
922 2
	 * {@inheritdoc}
923 2
	 */
924 9
	public function hasDatabase( $name ) {
925 2
		$rows = $this->fetchAll(
926 2
			sprintf(
927 2
				'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = \'%s\'',
928 8
				$name
929 2
			)
930 2
		);
931 2
932
		foreach ( $rows as $row ) {
933
			if ( ! empty( $row ) ) {
934
				return true;
935 16
			}
936
		}
937
938 15
		return false;
939 15
	}
940
941 15
	/**
942
	 * {@inheritdoc}
943 15
	 */
944 3
	public function dropDatabase( $name ) {
945 3
		$this->execute( sprintf( 'DROP DATABASE IF EXISTS `%s`', $name ) );
946
	}
947 15
948
	/**
949
	 * Gets the MySQL Column Definition for a Column object.
950
	 *
951
	 * @param \Phinx\Db\Table\Column $column Column
952
	 *
953
	 * @return string
954 83
	 */
955
	protected function getColumnSqlDefinition( Column $column ) {
956 83
		if ( $column->getType() instanceof Literal ) {
957
			$def = (string) $column->getType();
958 83
		} else {
959 1
			$sqlType = $this->getSqlType( $column->getType(), $column->getLimit() );
960 1
			$def     = strtoupper( $sqlType['name'] );
961 82
		}
962
		if ( $column->getPrecision() && $column->getScale() ) {
963 83
			$def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
964
		} elseif ( isset( $sqlType['limit'] ) ) {
965
			$def .= '(' . $sqlType['limit'] . ')';
966
		}
967 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...
968 4
			$def .= "('" . implode( "', '", $values ) . "')";
969
		}
970 4
		$def .= $column->getEncoding() ? ' CHARACTER SET ' . $column->getEncoding() : '';
971 4
		$def .= $column->getCollation() ? ' COLLATE ' . $column->getCollation() : '';
972 4
		$def .= ( ! $column->isSigned() && isset( $this->signedColumnTypes[ $column->getType() ] ) ) ? ' unsigned' : '';
973
		$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...
974 4
		$def .= $column->isIdentity() ? ' AUTO_INCREMENT' : '';
975 4
		$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...
976
977 4
		if ( $column->getComment() ) {
978 3
			$def .= ' COMMENT ' . $this->getConnection()->quote( $column->getComment() );
979 3
		}
980
981 3
		if ( $column->getUpdate() ) {
982
			$def .= ' ON UPDATE ' . $column->getUpdate();
983 3
		}
984
985
		return $def;
986
	}
987
988
	/**
989 81
	 * Gets the MySQL Index Definition for an Index object.
990
	 *
991 81
	 * @param \Phinx\Db\Table\Index $index Index
992 81
	 *
993
	 * @return string
994
	 */
995
	protected function getIndexSqlDefinition( Index $index ) {
996
		$def   = '';
997
		$limit = '';
0 ignored issues
show
Unused Code introduced by
$limit is not used, you could remove the assignment.

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

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

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

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

Loading history...
998
999
		if ( $index->getType() == Index::UNIQUE ) {
1000 89
			$def .= ' UNIQUE';
1001
		}
1002 89
1003
		if ( $index->getType() == Index::FULLTEXT ) {
1004 89
			$def .= ' FULLTEXT';
1005 89
		}
1006 89
1007 2
		$def .= ' KEY';
1008 89
1009 86
		if ( is_string( $index->getName() ) ) {
1010 86
			$def .= ' `' . $index->getName() . '`';
1011 89
		}
1012 5
1013 5
		if ( ! is_array( $index->getLimit() ) ) {
1014 89
			$limit = '(' . $index->getLimit() . ')';
1015 89
			$def   .= ' (`' . implode( '`,`', $index->getColumns() ) . '`' . $limit . ')';
1016 89
		} else {
1017 89
			$columns = $index->getColumns();
1018 89
			$limits  = $index->getLimit();
1019 89
			$def     .= ' (';
1020
			for ( $i = 0; $i < count( $columns ); $i ++ ) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

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

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

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1021 89
				$limit = ! isset( $limits[ $i ] ) || $limits[ $i ] <= 0 ? '' : '(' . $limits[ $i ] . ')';
1022 2
				$def   .= '`' . $columns[ $i ] . '`' . $limit;
1023 2
				if ( $i + 1 < count( $columns ) ) {
1024
					$def .= ',';
1025 89
				}
1026 1
			}
1027 1
			$def .= ' )';
1028
		}
1029 89
1030
		return $def;
1031
	}
1032
1033
	/**
1034
	 * Gets the MySQL Foreign Key Definition for an ForeignKey object.
1035
	 *
1036
	 * @param \Phinx\Db\Table\ForeignKey $foreignKey
1037
	 *
1038 16
	 * @return string
1039
	 */
1040 16 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...
1041 16
		$def = '';
1042 16
		if ( $foreignKey->getConstraint() ) {
1043 2
			$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...
1044 2
		}
1045
		$columnNames = [];
1046 16
		foreach ( $foreignKey->getColumns() as $column ) {
1047 5
			$columnNames[] = $this->quoteColumnName( $column );
1048 5
		}
1049
		$def            .= ' FOREIGN KEY (' . implode( ',', $columnNames ) . ')';
1050 16
		$refColumnNames = [];
1051 1
		foreach ( $foreignKey->getReferencedColumns() as $column ) {
1052 1
			$refColumnNames[] = $this->quoteColumnName( $column );
1053
		}
1054 16
		$def .= ' REFERENCES ' . $this->quoteTableName( $foreignKey->getReferencedTable()->getName() ) . ' (' . implode( ',', $refColumnNames ) . ')';
1055
		if ( $foreignKey->getOnDelete() ) {
1056 16
			$def .= ' ON DELETE ' . $foreignKey->getOnDelete();
1057 5
		}
1058 5
		if ( $foreignKey->getOnUpdate() ) {
1059
			$def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
1060 16
		}
1061
1062 16
		return $def;
1063
	}
1064
1065
	/**
1066
	 * Describes a database table. This is a MySQL adapter specific method.
1067
	 *
1068
	 * @param string $tableName Table name
1069
	 *
1070
	 * @return array
1071 17
	 */
1072 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...
1073 17
		$options = $this->getOptions();
1074 17
1075 5
		// mysql specific
1076 5
		$sql = sprintf(
1077 17
			"SELECT *
1078 17
             FROM information_schema.tables
1079 17
             WHERE table_schema = '%s'
1080 17
             AND table_name = '%s'",
1081 17
			$options['name'],
1082 17
			$tableName
1083 17
		);
1084 17
1085 17
		return $this->fetchRow( $sql );
1086 17
	}
1087 17
1088 2
	/**
1089 2
	 * Returns MySQL column types (inherited and MySQL specified).
1090 17
	 * @return array
1091 2
	 */
1092 2
	public function getColumnTypes() {
1093 17
		return array_merge( parent::getColumnTypes(), [ 'enum', 'set', 'year', 'json' ] );
1094
	}
1095
}
1096