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() ]; |
||
0 ignored issues
–
show
|
|||
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; |
||
0 ignored issues
–
show
|
|||
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; |
||
0 ignored issues
–
show
It seems like
null of type null is incompatible with the declared type array<integer,string> of property $options .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..
Loading history...
|
|||
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]; |
||
0 ignored issues
–
show
The property
$precision was declared of type integer , but $matches[1] is of type string . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
Loading history...
|
|||
524 | $this->scale = $matches[2]; |
||
0 ignored issues
–
show
The property
$scale was declared of type integer , but $matches[2] is of type string . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
Loading history...
|
|||
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->get_comment(), |
||
571 | $this->get_referenced_table() |
||
572 | ); |
||
573 | } |
||
574 | |||
575 | /** |
||
576 | * Alter this column. |
||
577 | * |
||
578 | * @param string $new_name The column name. |
||
579 | * @param string $xtype_name The x-type name. Must exist. |
||
580 | * @param string $size Either a single integer, or a x,y string of two integers. |
||
581 | * @param boolean $nullable Whether to allow NULl values. |
||
582 | * @param string $default The default value. |
||
583 | * @param boolean $auto_increment Auto-increment or not. |
||
584 | * @param boolean $unique Whether a unique constraint should apply. |
||
585 | * @param string $comment The column's comment. Default empty. |
||
586 | * @param \WordPress\Tabulate\DB\Table $target_table The target table for a foreign key. |
||
587 | * @param string $after The column that this one will be after. |
||
588 | * @throws Exception If unable to alter the table. |
||
589 | */ |
||
590 | 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 ) { |
||
591 | // Any that have not been set explicitly should be unchanged. |
||
592 | $new_name = ! is_null( $new_name ) ? (string) $new_name : $this->get_name(); |
||
593 | $xtype_name = ! is_null( $xtype_name ) ? (string) $xtype_name : $this->get_xtype()['name']; |
||
594 | $size = ! is_null( $size ) ? $size : $this->get_size(); |
||
595 | $nullable = ! is_null( $nullable ) ? (boolean) $nullable : $this->nullable(); |
||
596 | $default = ! is_null( $default ) ? (string) $default : $this->get_default(); |
||
597 | $auto_increment = ! is_null( $auto_increment ) ? (boolean) $auto_increment : $this->is_auto_increment(); |
||
598 | $unique = ! is_null( $unique ) ? (boolean) $unique : $this->is_unique(); |
||
599 | $comment = ! is_null( $comment ) ? (string) $comment : $this->get_comment(); |
||
600 | $target_table = ! is_null( $target_table ) ? $target_table : $this->get_referenced_table(); |
||
601 | |||
602 | // Check the current column definition. |
||
603 | $col_def = self::get_column_definition( $new_name, $xtype_name, $size, $nullable, $default, $auto_increment, $unique, $comment, $target_table, $after ); |
||
604 | if ( $this->get_current_column_definition() === $col_def ) { |
||
605 | return; |
||
606 | } |
||
607 | |||
608 | // Drop the unique key if it exists; it'll be re-created after. |
||
609 | $table = $this->get_table(); |
||
610 | $wpdb = $table->get_database()->get_wpdb(); |
||
611 | if ( $this->is_unique() ) { |
||
612 | $sql = 'SHOW INDEXES FROM `' . $table->get_name() . '` WHERE Column_name LIKE "' . $this->get_name() . '"'; |
||
613 | foreach ( $wpdb->get_results( $sql, ARRAY_A ) as $index ) { |
||
614 | $sql = "DROP INDEX `" . $index['Key_name'] . "` ON `" . $table->get_name() . "`"; |
||
615 | $wpdb->query( $sql ); |
||
616 | } |
||
617 | } |
||
618 | |||
619 | // Drop any foreign keys if they exist; they'll be re-created after. |
||
620 | if ( $this->is_foreign_key() ) { |
||
621 | $fks_sql = 'SELECT CONSTRAINT_NAME AS fk_name FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS ' |
||
622 | . ' WHERE TABLE_SCHEMA = SCHEMA() ' |
||
623 | . ' AND TABLE_NAME = "' . $table->get_name() . '" ' |
||
624 | . ' AND CONSTRAINT_TYPE = "FOREIGN KEY" '; |
||
625 | $fks = $wpdb->get_results( $fks_sql ); |
||
626 | foreach ( $fks as $key ) { |
||
627 | $sql = 'ALTER TABLE `' . $table->get_name() . '` DROP FOREIGN KEY `' . $key->fk_name . '`'; |
||
628 | $wpdb->query( $sql ); |
||
629 | } |
||
630 | $this->references = false; |
||
631 | } |
||
632 | |||
633 | // Alter the column. |
||
634 | $sql = "ALTER TABLE `" . $table->get_name() . "` CHANGE COLUMN `" . $this->get_name() . "` $col_def"; |
||
635 | $wpdb->hide_errors(); |
||
636 | $altered = $wpdb->query( $sql ); |
||
637 | if ( false === $altered ) { |
||
638 | $err = "Unable to alter '" . $table->get_name() . "." . $this->get_name() . "'" |
||
639 | . " — $wpdb->last_error — <code>$sql</code>"; |
||
640 | throw new Exception( $err ); |
||
641 | } |
||
642 | $wpdb->show_errors(); |
||
643 | |||
644 | // Reset the Column and Table objects' data. |
||
645 | $table->reset(); |
||
646 | $sql = "SHOW FULL COLUMNS FROM `" . $table->get_name() . "` LIKE '$new_name'"; |
||
647 | $column_info = $table->get_database()->get_wpdb()->get_row( $sql, ARRAY_A ); |
||
648 | $this->parse_info( $column_info ); |
||
649 | if ( $this->is_foreign_key() ) { |
||
650 | $this->get_referenced_table()->reset(); |
||
651 | } |
||
652 | } |
||
653 | |||
654 | /** |
||
655 | * Get an SQL column definition. |
||
656 | * |
||
657 | * @param string $name The column name. |
||
658 | * @param string $xtype_name The x-type name. Must exist. |
||
659 | * @param string $size Either a single integer, or a x,y string of two integers. |
||
660 | * @param boolean $nullable Whether to allow NULl values. |
||
661 | * @param string $default The default value. |
||
662 | * @param boolean $auto_increment Auto-increment or not. |
||
663 | * @param boolean $unique Whether a unique constraint should apply. |
||
664 | * @param string $comment The column's comment. Default empty. |
||
665 | * @param \WordPress\Tabulate\DB\Table $target_table The target table for a foreign key. |
||
666 | * @param string $after The column that this one will be after. |
||
667 | * @return string |
||
668 | */ |
||
669 | 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 ) { |
||
670 | // Type. |
||
671 | $xtypes = self::get_xtypes(); |
||
672 | $xtype = ( isset( $xtypes[ $xtype_name ] ) ) ? $xtypes[ $xtype_name ] : $xtypes['text_short']; |
||
673 | $type_str = $xtype['type']; |
||
674 | // Size or options. |
||
675 | $size_str = ''; |
||
676 | if ( is_numeric( $xtype['sizes'] ) && $xtype['sizes'] > 0 ) { |
||
677 | $size_str = '(' . ( $size ? : 50 ) . ')'; |
||
678 | } |
||
679 | if ( 'enum' === $xtype_name ) { |
||
680 | // If not already wraped in quotes, explode and quote each option. |
||
681 | if ( 0 === preg_match( '/^["\'].*["\']$/', $size ) ) { |
||
682 | $size = "'" . join( "','", explode( ',', $size ) ) . "'"; |
||
683 | } |
||
684 | $size_str = "($size)"; |
||
685 | } |
||
686 | if ( 'boolean' === $xtype_name ) { |
||
687 | $size_str = '(1)'; |
||
688 | } |
||
689 | // Nullable. |
||
690 | $null_str = (true === $nullable) ? 'NULL' : 'NOT NULL'; |
||
691 | // Default. |
||
692 | $default_str = ''; |
||
693 | if ( 'text_long' !== $xtype_name ) { |
||
694 | $default_str = ! empty( $default ) ? "DEFAULT '$default'" : ( $nullable ? 'DEFAULT NULL' : '' ); |
||
695 | } |
||
696 | $auto_increment_str = ''; |
||
697 | if ( $auto_increment && 'integer' === $xtype['name'] ) { |
||
698 | $auto_increment_str = 'AUTO_INCREMENT'; |
||
699 | } |
||
700 | $unique_str = $unique ? 'UNIQUE' : ''; |
||
701 | $comment_str = ! is_null( $comment ) ? "COMMENT '$comment'" : ''; |
||
702 | |||
703 | $after_str = ( ! empty( $after ) ) ? "AFTER `$after`" : ''; |
||
704 | if ( 'FIRST' === strtoupper( $after ) ) { |
||
705 | $after_str = " FIRST "; |
||
706 | } |
||
707 | |||
708 | $ref_str = ''; |
||
709 | $sign_str = ''; |
||
710 | if ( $target_table instanceof \WordPress\Tabulate\DB\Table ) { |
||
711 | $pk_col = $target_table->get_pk_column(); |
||
712 | $ref_str = ', ADD CONSTRAINT `' . $name . '_fk_to_' . $target_table->get_name() . '`' |
||
713 | . ' FOREIGN KEY (`' . $name . '`) ' |
||
714 | . ' REFERENCES `' . $target_table->get_name() . '` ' |
||
715 | . ' (`' . $pk_col->get_name() . '`)'; |
||
716 | $type_str = $pk_col->get_type(); |
||
717 | $size_str = '(' . $pk_col->get_size() . ')'; |
||
718 | $sign_str = ($pk_col->is_unsigned()) ? 'UNSIGNED' : ''; |
||
719 | } |
||
720 | |||
721 | // Put it all together. |
||
722 | $col_def = "`$name` $type_str$size_str $sign_str $null_str $default_str $auto_increment_str $unique_str $comment_str $after_str $ref_str"; |
||
723 | return preg_replace( '/ +/', ' ', trim( $col_def ) ); |
||
724 | |||
725 | } |
||
726 | } |
||
727 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.