Completed
Push — master ( 6cab57...287bd7 )
by Sam
02:34
created

src/DB/Column.php (2 issues)

mismatching argument types.

Documentation Minor

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This file contains only a single class.
4
 *
5
 * @file
6
 * @package Tabulate
7
 */
8
9
namespace WordPress\Tabulate\DB;
10
11
/**
12
 * The column class represents a single column in a single table in the database.
13
 */
14
class Column {
15
16
	/**
17
	 * The table to which this column belongs.
18
	 *
19
	 * @var Table
20
	 */
21
	private $table;
22
23
	/**
24
	 * The name of this column.
25
	 *
26
	 * @var string
27
	 */
28
	private $name;
29
30
	/**
31
	 * The type of this column.
32
	 *
33
	 * @var string
34
	 */
35
	private $type;
36
37
	/**
38
	 * The size, or length, of this column.
39
	 *
40
	 * @var integer
41
	 */
42
	private $size;
43
44
	/**
45
	 * This column's collation.
46
	 *
47
	 * @var string
48
	 */
49
	private $collation;
50
51
	/**
52
	 * The total number of digits in a DECIMAL column.
53
	 *
54
	 * @var integer
55
	 */
56
	private $precision;
57
58
	/**
59
	 * The number of digits after the decimal point in a DECIMAL column.
60
	 *
61
	 * @var integer
62
	 */
63
	private $scale;
64
65
	/**
66
	 * Whether or not this column is the Primary Key.
67
	 *
68
	 * @var boolean
69
	 */
70
	private $is_primary_key = false;
71
72
	/**
73
	 * Whether or not this column is a Unique Key.
74
	 *
75
	 * @var boolean
76
	 */
77
	private $is_unique = false;
78
79
	/**
80
	 * The default value for this column.
81
	 *
82
	 * @var mixed
83
	 */
84
	private $default_value;
85
86
	/**
87
	 * Whether or not this column is auto-incrementing.
88
	 *
89
	 * @var boolean
90
	 */
91
	private $is_auto_increment = false;
92
93
	/**
94
	 * Whether NULL values are allowed for this column.
95
	 *
96
	 * @var boolean
97
	 */
98
	private $nullable;
99
100
	/**
101
	 * Is this an unsigned number?
102
	 *
103
	 * @var boolean
104
	 */
105
	private $unsigned = false;
106
107
	/**
108
	 * ENUM options.
109
	 *
110
	 * @var string[]
111
	 */
112
	private $options;
113
114
	/**
115
	 * The comment attached to this column.
116
	 *
117
	 * @var string
118
	 */
119
	private $comment;
120
121
	/**
122
	 * The table that this column refers to, or false if it is not a foreign key.
123
	 *
124
	 * @var Table|false
125
	 */
126
	private $references = false;
127
128
	/**
129
	 * Create a column of a given table and based on given info.
130
	 *
131
	 * @param \WordPress\Tabulate\DB\Table $table The table that this column belongs to.
132
	 * @param string[]                     $info The output array of a SHOW COLUMNS query.
133
	 */
134
	public function __construct( Table $table, $info = false ) {
135
		$this->table = $table;
136
		$this->parse_info( $info );
137
	}
138
139
	/**
140
	 * Take the output of SHOW COLUMNS and populate this object's data.
141
	 *
142
	 * @param string[] $info The output array of a SHOW COLUMNS query.
143
	 */
144
	protected function parse_info( $info ) {
145
146
		// Name.
147
		$this->name = $info['Field'];
148
149
		// Type.
150
		$this->parse_type( $info['Type'] );
151
152
		// Default.
153
		$this->default_value = $info['Default'];
154
155
		// Primary key.
156
		if ( 'PRI' === strtoupper( $info['Key'] ) ) {
157
			$this->is_primary_key = true;
158
			if ( 'auto_increment' === $info['Extra'] ) {
159
				$this->is_auto_increment = true;
160
			}
161
		}
162
163
		// Unique key.
164
		$this->is_unique = ( 'UNI' === strtoupper( $info['Key'] ) );
165
166
		// Comment.
167
		$this->comment = $info['Comment'];
168
169
		// Collation.
170
		$this->collation = $info['Collation'];
171
172
		// Is this column NULL?
173
		$this->nullable = ( 'YES' === $info['Null'] );
174
175
		// Is this a foreign key?
176
		if ( in_array( $this->get_name(), $this->get_table()->get_foreign_key_names(), true ) ) {
177
			$referenced_tables = $this->get_table()->get_referenced_tables( false );
178
			$this->references = $referenced_tables[ $this->get_name() ];
179
		}
180
181
	}
182
183
	/**
184
	 * Get this column's name.
185
	 *
186
	 * @return string The name of this column.
187
	 */
188
	public function get_name() {
189
		return $this->name;
190
	}
191
192
	/**
193
	 * Get the valid options for this column; only applies to ENUM and SET.
194
	 *
195
	 * @return array The available options.
196
	 */
197
	public function get_options() {
198
		return $this->options;
199
	}
200
201
	/**
202
	 * Get the human-readable title of this column.
203
	 */
204
	public function get_title() {
205
		return \WordPress\Tabulate\Text::titlecase( $this->get_name() );
206
	}
207
208
	/**
209
	 * Get this column's type.
210
	 *
211
	 * @return string The type of this column.
212
	 */
213
	public function get_type() {
214
		return $this->type;
215
	}
216
217
	/**
218
	 * Get the definitive list of xtypes.
219
	 *
220
	 * @return string[] The xtypes.
221
	 */
222
	public static function get_xtypes() {
223
		return array(
224
			'text_short' => array(
225
				'name' => 'text_short',
226
				'title' => 'Text (short)',
227
				'type' => 'VARCHAR',
228
				'sizes' => 1,
229
				'options' => array(),
230
			),
231
			'text_long' => array(
232
				'name' => 'text_long',
233
				'title' => 'Text (long)',
234
				'type' => 'TEXT',
235
				'sizes' => 0,
236
				'options' => array( 'autop', 'html', 'md', 'rst', 'plain' ),
237
			),
238
			'integer' => array(
239
				'name' => 'integer',
240
				'title' => 'Integer',
241
				'type' => 'INT',
242
				'sizes' => 1,
243
				'options' => array(),
244
			),
245
			'boolean' => array(
246
				'name' => 'boolean',
247
				'title' => 'Boolean',
248
				'type' => 'TINYINT',
249
				'sizes' => 0,
250
				'options' => array(),
251
			),
252
			'decimal' => array(
253
				'name' => 'decimal',
254
				'title' => 'Decimal',
255
				'type' => 'DECIMAL',
256
				'sizes' => 2,
257
				'options' => array(),
258
			),
259
			'date' => array(
260
				'name' => 'date',
261
				'title' => 'Date',
262
				'type' => 'DATE',
263
				'sizes' => 0,
264
				'options' => array(),
265
			),
266
			'time' => array(
267
				'name' => 'time',
268
				'title' => 'Time',
269
				'type' => 'TIME',
270
				'sizes' => 0,
271
				'options' => array(),
272
			),
273
			'datetime' => array(
274
				'name' => 'datetime',
275
				'title' => 'Date & Time',
276
				'type' => 'DATETIME',
277
				'sizes' => 0,
278
				'options' => array(),
279
			),
280
			'fk' => array(
281
				'name' => 'fk',
282
				'title' => 'Cross Reference',
283
				'type' => 'INT',
284
				'sizes' => 1,
285
				'options' => array(),
286
			),
287
			'point' => array(
288
				'name' => 'point',
289
				'title' => 'Geographic location',
290
				'type' => 'POINT',
291
				'sizes' => 0,
292
				'options' => array(),
293
			),
294
			'enum' => array(
295
				'name' => 'enum',
296
				'title' => 'Fixed list',
297
				'type' => 'ENUM',
298
				'sizes' => 0,
299
				'options' => array(),
300
			),
301
		);
302
	}
303
304
	/**
305
	 * Get the X-Type of this column.
306
	 *
307
	 * @return string[] An array containing details of the xtype: name, title, type, sizes, and options.
308
	 */
309
	public function get_xtype() {
310
		$xtypes = self::get_xtypes();
311
		if ( $this->is_foreign_key() ) {
312
			return $xtypes['fk'];
313
		}
314
		if ( $this->is_boolean() ) {
315
			return $xtypes['boolean'];
316
		}
317
		// Otherwise fall back on the first xtype with a matching type.
318
		foreach ( $xtypes as $xtype ) {
319
			if ( strtoupper( $this->get_type() ) === $xtype['type'] ) {
320
				return $xtype;
321
			}
322
		}
323
		return false;
324
	}
325
326
	/**
327
	 * Set the X-Type of this column.
328
	 *
329
	 * @param string $type The name of the type.
330
	 */
331
	public function set_xtype( $type ) {
332
		$option_name = TABULATE_SLUG . '_xtypes';
333
		$xtypes = update_option( $option_name );
334
		$table_name = $this->get_table()->get_name();
335
		if ( ! is_array( $xtypes[ $table_name ] ) ) {
336
			$xtypes[ $table_name ] = array();
337
		}
338
		$xtypes[ $table_name ][ $this->get_name() ] = $type;
339
		update_option( $option_name, $xtypes );
340
	}
341
342
	/**
343
	 * Get the column's comment.
344
	 *
345
	 * @return string
346
	 */
347
	public function get_comment() {
348
		return $this->comment;
349
	}
350
351
	/**
352
	 * Get the default value for this column.
353
	 *
354
	 * @return mixed
355
	 */
356
	public function get_default() {
357
		if ( 'CURRENT_TIMESTAMP' === $this->default_value ) {
358
			return date( 'Y-m-d h:i:s' );
359
		}
360
		return $this->default_value;
361
	}
362
363
	/**
364
	 * Get this column's size, or (for ENUM columns) its CSV options string.
365
	 *
366
	 * @return string The size of this column.
367
	 */
368
	public function get_size() {
369
		$size = $this->size;
370
		if ( 'decimal' === $this->get_type() ) {
371
			$size = "$this->precision,$this->scale";
372
		}
373
		if ( 'enum' === $this->get_type() ) {
374
			return "'" . join( "','", $this->get_options() ) . "'";
375
		}
376
		return $size;
377
	}
378
379
	/**
380
	 * Whether or not a non-NULL value needs to be supplied for this column.
381
	 *
382
	 * Not-NULL columns that have default values are *not* considered to be
383
	 * required.
384
	 *
385
	 * @return boolean
386
	 */
387
	public function is_required() {
388
		$has_default = ( $this->get_default() !== null || $this->is_auto_increment() );
389
		return ( ! $this->nullable() && ! $has_default );
390
	}
391
392
	/**
393
	 * Whether or not this column is the Primary Key for its table.
394
	 *
395
	 * @return boolean True if this is the PK, false otherwise.
396
	 */
397
	public function is_primary_key() {
398
		return $this->is_primary_key;
399
	}
400
401
	/**
402
	 * Whether or not this column is a unique key.
403
	 *
404
	 * @return boolean True if this is a Unique Key, false otherwise.
405
	 */
406
	public function is_unique() {
407
		return $this->is_unique;
408
	}
409
410
	/**
411
	 * Whether or not this column is an auto-incrementing integer.
412
	 *
413
	 * @return boolean True if this column has AUTO_INCREMENT set, false otherwise.
414
	 */
415
	public function is_auto_increment() {
416
		return $this->is_auto_increment;
417
	}
418
419
	/**
420
	 * Whether or not this column is allowed to have NULL values.
421
	 *
422
	 * @return boolean
423
	 */
424
	public function nullable() {
425
		return $this->nullable;
426
	}
427
428
	/**
429
	 * Only NOT NULL text fields are allowed to have empty strings.
430
	 *
431
	 * @return boolean
432
	 */
433
	public function allows_empty_string() {
434
		$text_types = array( 'text', 'varchar', 'char' );
435
		$is_text_type = in_array( $this->get_type(), $text_types, true );
436
		return ( ! $this->nullable() ) && $is_text_type;
437
	}
438
439
	/**
440
	 * Is this a boolean field?
441
	 *
442
	 * This method deals with the silliness that is MySQL's boolean datatype. Or, rather, it will do when it's finished.
443
	 * For now, it just reports true when this is a TINYINT(1) column.
444
	 *
445
	 * @return boolean
446
	 */
447
	public function is_boolean() {
448
		return $this->get_type() === 'tinyint' && $this->get_size() === 1;
449
	}
450
451
	/**
452
	 * Whether this column is an unsigned number.
453
	 *
454
	 * @return boolean
455
	 */
456
	public function is_unsigned() {
457
		return $this->unsigned;
458
	}
459
460
	/**
461
	 * Whether or not this column is an integer, float, or decimal column.
462
	 */
463
	public function is_numeric() {
464
		$is_int = substr( $this->get_type(), 0, 3 ) === 'int';
465
		$is_decimal = substr( $this->get_type(), 0, 7 ) === 'decimal';
466
		$is_float = substr( $this->get_type(), 0, 5 ) === 'float';
467
		return $is_int || $is_decimal || $is_float;
468
	}
469
470
	/**
471
	 * Whether or not this column is a foreign key.
472
	 *
473
	 * @return boolean True if $this->_references is not empty, otherwise false.
474
	 */
475
	public function is_foreign_key() {
476
		return ! empty( $this->references );
477
	}
478
479
	/**
480
	 * Get the table object of the referenced table, if this column is a foreign
481
	 * key.
482
	 *
483
	 * @return Table The referenced table.
484
	 */
485
	public function get_referenced_table() {
486
		return $this->table->get_database()->get_table( $this->references );
487
	}
488
489
	/**
490
	 * Get the table that this column belongs to.
491
	 *
492
	 * @return Table The table object.
493
	 */
494
	public function get_table() {
495
		return $this->table;
496
	}
497
498
	/**
499
	 * Take an SQL string and parse out column information.
500
	 *
501
	 * @param string $type_string The SQL.
502
	 */
503
	private function parse_type( $type_string ) {
504
505
		$this->unsigned = ( false !== stripos( $type_string, 'unsigned' ) );
506
507
		$varchar_pattern = '/^((?:var)?char)\((\d+)\)/';
508
		$decimal_pattern = '/^decimal\((\d+),(\d+)\)/';
509
		$float_pattern = '/^float\((\d+),(\d+)\)/';
510
		$integer_pattern = '/^((?:big|medium|small|tiny)?int|year)\(?(\d+)\)?/';
511
		$enum_pattern = '/^(enum|set)\(\'(.*?)\'\)/';
512
513
		$this->type = $type_string;
514
		$this->size = null;
515
		$this->precision = null;
516
		$this->scale = null;
517
		$this->options = null;
518
		if ( preg_match( $varchar_pattern, $type_string, $matches ) ) {
519
			$this->type = $matches[1];
520
			$this->size = (int) $matches[2];
521
		} elseif ( preg_match( $decimal_pattern, $type_string, $matches ) ) {
522
			$this->type = 'decimal';
523
			$this->precision = $matches[1];
524
			$this->scale = $matches[2];
525
		} elseif ( preg_match( $float_pattern, $type_string, $matches ) ) {
526
			$this->type = 'float';
527
			$this->precision = $matches[1];
528
			$this->scale = $matches[2];
529
		} elseif ( preg_match( $integer_pattern, $type_string, $matches ) ) {
530
			$this->type = $matches[1];
531
			$this->size = (int) $matches[2];
532
		} elseif ( preg_match( $enum_pattern, $type_string, $matches ) ) {
533
			$this->type = $matches[1];
534
			$values = explode( "','", $matches[2] );
535
			$this->options = array_combine( $values, $values );
536
		}
537
	}
538
539
	/**
540
	 * Get a human-readable string representation of this column.
541
	 *
542
	 * @return string
543
	 */
544
	public function __toString() {
545
		$pk = ($this->is_primary_key) ? ' PK' : '';
546
		$auto = ($this->is_auto_increment) ? ' AI' : '';
547
		if ( $this->references ) {
548
			$ref = ' References ' . $this->references . '.';
549
		} else {
550
			$ref = '';
551
		}
552
		$size = ($this->size > 0) ? "($this->size)" : '';
553
		return $this->name . ' ' . strtoupper( $this->type ) . $size . $pk . $auto . $ref;
554
	}
555
556
	/**
557
	 * Get the defining SQL for this column.
558
	 *
559
	 * @return string
560
	 */
561
	public function get_current_column_definition() {
562
		return self::get_column_definition(
563
			$this->get_name(),
564
			$this->get_xtype()['name'],
565
			$this->get_size(),
566
			$this->nullable(),
567
			$this->get_default(),
568
			$this->is_auto_increment(),
569
			$this->is_unique(),
570
			$this->is_primary_key(),
0 ignored issues
show
$this->is_primary_key() is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
571
			$this->get_comment(),
0 ignored issues
show
$this->get_comment() is of type string, but the function expects a object<WordPress\Tabulate\DB\Table>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
572
			$this->get_referenced_table()
573
		);
574
	}
575
576
	/**
577
	 * Alter this column.
578
	 *
579
	 * @param string                       $new_name The column name.
580
	 * @param string                       $xtype_name The x-type name. Must exist.
581
	 * @param string                       $size Either a single integer, or a x,y string of two integers.
582
	 * @param boolean                      $nullable Whether to allow NULl values.
583
	 * @param string                       $default The default value.
584
	 * @param boolean                      $auto_increment Auto-increment or not.
585
	 * @param boolean                      $unique Whether a unique constraint should apply.
586
	 * @param string                       $comment The column's comment. Default empty.
587
	 * @param \WordPress\Tabulate\DB\Table $target_table The target table for a foreign key.
588
	 * @param string                       $after The column that this one will be after.
589
	 * @throws Exception If unable to alter the table.
590
	 */
591
	public function alter( $new_name = null, $xtype_name = null, $size = null, $nullable = null, $default = null, $auto_increment = null, $unique = null, $comment = null, $target_table = null, $after = null ) {
592
		// Any that have not been set explicitely should be unchanged.
593
		$new_name = ! is_null( $new_name ) ? (string) $new_name : $this->get_name();
594
		$xtype_name = ! is_null( $xtype_name ) ? (string) $xtype_name : $this->get_xtype()['name'];
595
		$size = ! is_null( $size ) ? $size : $this->get_size();
596
		$nullable = ! is_null( $nullable ) ? (boolean) $nullable : $this->nullable();
597
		$default = ! is_null( $default ) ? (string) $default : $this->get_default();
598
		$auto_increment = ! is_null( $auto_increment ) ? (boolean) $auto_increment : $this->is_auto_increment();
599
		$unique = ! is_null( $unique ) ? (boolean) $unique : $this->is_unique();
600
		$comment = ! is_null( $comment ) ? (string) $comment : $this->get_comment();
601
		$target_table = ! is_null( $target_table ) ? $target_table : $this->get_referenced_table();
602
603
		// Check the current column definition.
604
		$col_def = self::get_column_definition( $new_name, $xtype_name, $size, $nullable, $default, $auto_increment, $unique, $comment, $target_table, $after );
605
		if ( $this->get_current_column_definition() === $col_def ) {
606
			return;
607
		}
608
609
		// Drop the unique key if it exists; it'll be re-created after.
610
		$table = $this->get_table();
611
		$wpdb = $table->get_database()->get_wpdb();
612
		if ( $this->is_unique() ) {
613
			$sql = 'SHOW INDEXES FROM `' . $table->get_name() . '` WHERE Column_name LIKE "' . $this->get_name() . '"';
614
			foreach ( $wpdb->get_results( $sql, ARRAY_A ) as $index ) {
615
				$sql = "DROP INDEX `" . $index['Key_name'] . "` ON `" . $table->get_name() . "`";
616
				$wpdb->query( $sql );
617
			}
618
		}
619
620
		// Drop the FK if it exists; it'll be re-created after.
621
		if ( $this->is_foreign_key() ) {
622
			$fks_sql = 'SELECT CONSTRAINT_NAME AS fk_name FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS '
623
				. ' WHERE TABLE_SCHEMA = SCHEMA() '
624
				. ' AND TABLE_NAME = "' . $table->get_name() . '" '
625
				. ' AND CONSTRAINT_TYPE = "FOREIGN KEY" ';
626
			$fks = $wpdb->get_results( $fks_sql );
627
			foreach ( $fks as $key ) {
628
				$sql = 'ALTER TABLE `' . $table->get_name() . '` DROP FOREIGN KEY `' . $key->fk_name . '`';
629
				$wpdb->query( $sql );
630
			}
631
			$this->references = false;
632
		}
633
634
		// Alter the column.
635
		$sql = "ALTER TABLE `" . $table->get_name() . "` CHANGE COLUMN `" . $this->get_name() . "` $col_def";
636
		$wpdb->hide_errors();
637
		$altered = $wpdb->query( $sql );
638
		if ( false === $altered ) {
639
			$err = "Unable to alter '" . $table->get_name() . "." . $this->get_name() . "' "
640
				. " &mdash; $wpdb->last_error &mdash; <code>$sql</code>";
641
			throw new Exception( $err );
642
		}
643
		$wpdb->show_errors();
644
645
		// Reset the Column and Table objects' data.
646
		$table->reset();
647
		$sql = "SHOW FULL COLUMNS FROM `" . $table->get_name() . "` LIKE '$new_name'";
648
		$column_info = $table->get_database()->get_wpdb()->get_row( $sql, ARRAY_A );
649
		$this->parse_info( $column_info );
650
		if ( $this->is_foreign_key() ) {
651
			$this->get_referenced_table()->reset();
652
		}
653
	}
654
655
	/**
656
	 * Get an SQL column definition.
657
	 *
658
	 * @param string                       $name The column name.
659
	 * @param string                       $xtype_name The x-type name. Must exist.
660
	 * @param string                       $size Either a single integer, or a x,y string of two integers.
661
	 * @param boolean                      $nullable Whether to allow NULl values.
662
	 * @param string                       $default The default value.
663
	 * @param boolean                      $auto_increment Auto-increment or not.
664
	 * @param boolean                      $unique Whether a unique constraint should apply.
665
	 * @param string                       $comment The column's comment. Default empty.
666
	 * @param \WordPress\Tabulate\DB\Table $target_table The target table for a foreign key.
667
	 * @param string                       $after The column that this one will be after.
668
	 * @return string
669
	 */
670
	public static function get_column_definition( $name, $xtype_name = null, $size = null, $nullable = true, $default = null, $auto_increment = null, $unique = null, $comment = null, $target_table = null, $after = null ) {
671
		// Type.
672
		$xtypes = self::get_xtypes();
673
		$xtype = ( isset( $xtypes[ $xtype_name ] ) ) ? $xtypes[ $xtype_name ] : $xtypes['text_short'];
674
		$type_str = $xtype['type'];
675
		// Size or options.
676
		$size_str = '';
677
		if ( is_numeric( $xtype['sizes'] ) && $xtype['sizes'] > 0 ) {
678
			$size_str = '(' . ( $size ? : 50 ) . ')';
679
		}
680
		if ( 'enum' === $xtype_name ) {
681
			// If not already wraped in quotes, explode and quote each option.
682
			if ( 0 === preg_match( '/^["\'].*["\']$/', $size ) ) {
683
				$size = "'" . join( "','", explode( ',', $size ) ) . "'";
684
			}
685
			$size_str = "($size)";
686
		}
687
		if ( 'boolean' === $xtype_name ) {
688
			$size_str = '(1)';
689
		}
690
		// Nullable.
691
		$null_str = (true === $nullable) ? 'NULL' : 'NOT NULL';
692
		// Default.
693
		$default_str = '';
694
		if ( 'text_long' !== $xtype_name ) {
695
			$default_str = ! empty( $default ) ? "DEFAULT '$default'" : ( $nullable ? 'DEFAULT NULL' : '' );
696
		}
697
		$auto_increment_str = '';
698
		if ( $auto_increment && 'integer' === $xtype['name'] ) {
699
			$auto_increment_str = 'AUTO_INCREMENT';
700
		}
701
		$unique_str = $unique ? 'UNIQUE' : '';
702
		$comment_str = ! is_null( $comment ) ? "COMMENT '$comment'" : '';
703
704
		$after_str = ( ! empty( $after ) ) ? "AFTER `$after`" : '';
705
		if ( 'FIRST' === strtoupper( $after ) ) {
706
			$after_str = " FIRST ";
707
		}
708
709
		$ref_str = '';
710
		$sign_str = '';
711
		if ( $target_table instanceof \WordPress\Tabulate\DB\Table ) {
712
			$pk_col = $target_table->get_pk_column();
713
			$ref_str = ', ADD CONSTRAINT `' . $name . '_fk_to_' . $target_table->get_name() . '`'
714
				. ' FOREIGN KEY (`' . $name . '`) '
715
				. ' REFERENCES `' . $target_table->get_name() . '` '
716
				. ' (`' . $pk_col->get_name() . '`)';
717
			$type_str = $pk_col->get_type();
718
			$size_str = '(' . $pk_col->get_size() . ')';
719
			$sign_str = ($pk_col->is_unsigned()) ? 'UNSIGNED' : '';
720
		}
721
722
		// Put it all together.
723
		$col_def = "`$name` $type_str$size_str $sign_str $null_str $default_str $auto_increment_str $unique_str $comment_str $after_str $ref_str";
724
		return preg_replace( '/ +/', ' ', trim( $col_def ) );
725
726
	}
727
}
728