PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.1.0
Kirki – Freeform Page Builder, Website Builder & Customizer v6.1.0
6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / libraries / framework / Database / Schema / Compiler.php
kirki / libraries / framework / Database / Schema Last commit date
Definitions 3 weeks ago Compiler.php 3 weeks ago SchemaManager.php 3 weeks ago Structure.php 3 weeks ago
Compiler.php
806 lines
1 <?php
2
3 /**
4 * Compiles database table structure definitions into executable SQL statements.
5 * Translates fluent schema blueprints into CREATE and ALTER commands for WordPress database tables.
6 * Coordinates column modifiers, indexes, and foreign keys through the schema definition layer.
7 *
8 * @package Framework
9 * @subpackage Database\Schema
10 * @since 1.0.0
11 */
12 namespace Kirki\Framework\Database\Schema;
13
14 \defined('ABSPATH') || exit;
15 use Kirki\Framework\Database\Query\Expression;
16 use Kirki\Framework\Database\Schema\Definitions\Definition;
17 use Kirki\Framework\Database\Schema\Definitions\ForeignKeyDefinition;
18 use Exception;
19 use Kirki\Framework\Database\Connection\Connection;
20 class Compiler
21 {
22 /**
23 * List of column modifiers to apply.
24 *
25 * @var list<string>
26 *
27 * @since 1.0.0
28 */
29 protected $modifiers = ['unsigned', 'nullable', 'default', 'auto_increment', 'comment'];
30 /**
31 * List of table commands to generate.
32 *
33 * @var list<string>
34 *
35 * @since 1.0.0
36 */
37 protected $commands = ['primary', 'unique', 'index', 'foreign'];
38 /**
39 * The database connection instance.
40 *
41 * @var Connection
42 *
43 * @since 1.0.0
44 */
45 protected $connection = null;
46 /**
47 * Compiler constructor.
48 *
49 * @param Connection $connection The database connection instance.
50 *
51 * @return void
52 *
53 * @since 1.0.0
54 */
55 public function __construct(Connection $connection)
56 {
57 $this->connection = $connection;
58 }
59 /**
60 * Compile the SQL statement for creating a table.
61 *
62 * @param Structure $structure The structure.
63 *
64 * @return string
65 *
66 * @since 1.0.0
67 */
68 public function compile_create(Structure $structure)
69 {
70 $sql = $this->compile_create_table($structure);
71 $sql = $this->compile_create_engine($sql, $structure);
72 $sql = $this->compile_create_encoding($sql, $structure);
73 return $sql;
74 }
75 /**
76 * Compile the CREATE TABLE SQL statement.
77 *
78 * @param Structure $structure The structure.
79 *
80 * @return string
81 *
82 * @since 1.0.0
83 */
84 protected function compile_create_table(Structure $structure)
85 {
86 $table_name = $structure->get_table();
87 $columns = $this->compile_table_structure($structure);
88 $this->extract_commands_from_columns($structure);
89 $commands = $this->compile_commands($structure);
90 return \sprintf('CREATE TABLE IF NOT EXISTS %s (%s)', $structure->wrap_table($table_name), \implode(', ', $columns) . (!$commands ? '' : ',' . $commands));
91 }
92 /**
93 * Compile all table commands (primary, unique, index, foreign).
94 *
95 * @param Structure $structure The structure.
96 *
97 * @return string
98 *
99 * @since 1.0.0
100 */
101 protected function compile_commands(Structure $structure)
102 {
103 $command_sql = [];
104 foreach ($this->commands as $name) {
105 $command_method = 'generate_' . \strtolower($name);
106 $command_sql[] = $this->{$command_method}($structure);
107 }
108 return \implode(", ", \array_filter($command_sql));
109 }
110 /**
111 * Generate the PRIMARY KEY SQL statement.
112 *
113 * @param Structure $structure The structure.
114 *
115 * @return string|null
116 *
117 * @since 1.0.0
118 */
119 protected function generate_primary(Structure $structure)
120 {
121 if (!$this->has_command($structure, 'primary')) {
122 return;
123 }
124 $primary = $this->get_command($structure, 'primary');
125 $key_name = $primary['name'];
126 $keys = $primary['keys'];
127 if (!empty($key_name)) {
128 return \sprintf(' CONSTRAINT `%s` PRIMARY KEY (`%s`)', $key_name, \implode('`, `', $keys));
129 }
130 return \sprintf(' PRIMARY KEY (`%s`)', \implode('`, `', $keys));
131 }
132 /**
133 * Generate the UNIQUE KEY SQL statements.
134 *
135 * @param Structure $structure The structure.
136 *
137 * @return string|null
138 *
139 * @since 1.0.0
140 */
141 protected function generate_unique(Structure $structure)
142 {
143 if (!$this->has_command($structure, 'unique')) {
144 return;
145 }
146 $unique = $this->get_command($structure, 'unique');
147 if (empty($unique)) {
148 return;
149 }
150 $sql = [];
151 foreach ($unique as $command) {
152 $key_name = $command['name'];
153 $keys = $command['keys'];
154 $sql[] = \sprintf(' UNIQUE KEY `%s` (`%s`)', $key_name, \implode('`, `', $keys));
155 }
156 return \implode(", ", $sql);
157 }
158 /**
159 * Generate the INDEX KEY SQL statements.
160 *
161 * @param Structure $structure The structure.
162 *
163 * @return string|null
164 *
165 * @since 1.0.0
166 */
167 protected function generate_index(Structure $structure)
168 {
169 if (!$this->has_command($structure, 'index')) {
170 return;
171 }
172 $index = $this->get_command($structure, 'index');
173 if (empty($index)) {
174 return;
175 }
176 $sql = [];
177 foreach ($index as $command) {
178 $key_name = $command['name'];
179 $keys = $command['keys'];
180 $sql[] = \sprintf(' KEY `%s` (`%s`)', $key_name, \implode('`, `', $keys));
181 }
182 return \implode(", ", $sql);
183 }
184 /**
185 * Generate the FOREIGN KEY SQL statements.
186 *
187 * @param Structure $structure The structure.
188 *
189 * @return string|null
190 *
191 * @since 1.0.0
192 */
193 protected function generate_foreign(Structure $structure)
194 {
195 if (!$this->has_command($structure, 'foreign')) {
196 return;
197 }
198 $foreign = $this->get_command($structure, 'foreign');
199 if (empty($foreign)) {
200 return;
201 }
202 $sql = [];
203 foreach ($foreign as $command) {
204 $sql[] = $this->compile_foreign($command, $structure);
205 }
206 return \implode(", ", $sql);
207 }
208 /**
209 * Compile a single FOREIGN KEY SQL statement.
210 *
211 * @param ForeignKeyDefinition $command The command.
212 * @param Structure $structure The structure.
213 *
214 * @return string|null
215 *
216 * @since 1.0.0
217 */
218 protected function compile_foreign(ForeignKeyDefinition $command, Structure $structure)
219 {
220 if (!$command->column || !$command->on || !$command->references) {
221 return;
222 }
223 $sql = \sprintf('FOREIGN KEY (`%s`) REFERENCES %s (`%s`)', $command->column, $structure->connection()->get_query_compiler()->wrap_table($command->on), $command->references);
224 if ($command->on_update) {
225 $sql .= \sprintf(' ON UPDATE %s', $command->on_update);
226 }
227 if ($command->on_delete) {
228 $sql .= \sprintf(' ON DELETE %s', $command->on_delete);
229 }
230 return $sql;
231 }
232 /**
233 * Append the character set and collation to the SQL statement.
234 *
235 * @param mixed $sql The sql.
236 * @param Structure $structure The structure.
237 *
238 * @return string
239 *
240 * @since 1.0.0
241 */
242 protected function compile_create_encoding($sql, Structure $structure)
243 {
244 $charset = $structure->get_charset();
245 $collate = $structure->get_collate();
246 $sql .= \sprintf(' DEFAULT CHARSET=%s COLLATE=%s', $charset, $collate);
247 return $sql;
248 }
249 /**
250 * Append the storage engine to the SQL statement.
251 *
252 * @param mixed $sql The sql.
253 * @param Structure $structure The structure.
254 *
255 * @return string
256 *
257 * @since 1.0.0
258 */
259 protected function compile_create_engine($sql, Structure $structure)
260 {
261 $engine = $structure->get_engine();
262 $sql .= \sprintf(' ENGINE=%s', $engine);
263 return $sql;
264 }
265 /**
266 * Compile the column definitions for the table.
267 *
268 * @param Structure $structure The structure.
269 *
270 * @return array<int, string>
271 *
272 * @since 1.0.0
273 */
274 protected function compile_table_structure(Structure $structure)
275 {
276 $column_structure = [];
277 foreach ($structure->get_columns() as $column) {
278 $column_structure[] = $this->compile_column($column, $structure);
279 }
280 return $column_structure;
281 }
282 /**
283 * Extract and register commands (primary, unique, index) from column definitions.
284 *
285 * @param Structure $structure The structure.
286 *
287 * @return void
288 *
289 * @since 1.0.0
290 */
291 protected function extract_commands_from_columns(Structure $structure)
292 {
293 $columns = $structure->get_columns();
294 foreach ($columns as $column) {
295 if ($column->primary) {
296 $structure->primary([$column->name]);
297 }
298 if ($column->unique) {
299 $structure->unique([$column->name]);
300 }
301 if ($column->index) {
302 $structure->index([$column->name]);
303 }
304 }
305 }
306 /**
307 * Compile a single column definition.
308 *
309 * @param mixed $column The column.
310 * @param mixed $structure The structure.
311 *
312 * @return string
313 *
314 * @since 1.0.0
315 */
316 protected function compile_column($column, $structure)
317 {
318 $sql = \sprintf('%s %s', $this->get_column_name($column), $this->get_column_type($column));
319 return $this->add_modifiers($sql, $column, $structure);
320 }
321 /**
322 * Compile the columns query.
323 *
324 * @param string $table The table.
325 *
326 * @return string
327 *
328 * @since 1.0.0
329 */
330 public function compile_database_columns($table)
331 {
332 return \sprintf('select column_name as `name`, data_type as `type_name`, column_type as `type`, ' . 'collation_name as `collation`, is_nullable as `nullable`, ' . 'column_default as `default`, column_comment as `comment`, ' . 'generation_expression as `expression`, extra as `extra` ' . 'from information_schema.columns where table_schema = schema() and table_name = %s ' . 'order by ordinal_position asc', $this->connection->quote_string($table));
333 }
334 /**
335 * Add all applicable modifiers to a column definition.
336 *
337 * @param mixed $sql The sql.
338 * @param mixed $column The column.
339 * @param mixed $structure The structure.
340 *
341 * @return string
342 *
343 * @since 1.0.0
344 */
345 protected function add_modifiers($sql, $column, $structure)
346 {
347 foreach ($this->modifiers as $modifier) {
348 $modifier_method = 'apply_' . \strtolower($modifier);
349 $sql .= $this->{$modifier_method}($column, $structure);
350 }
351 return $sql;
352 }
353 /**
354 * Add UNSIGNED modifier to a column if applicable.
355 *
356 * @param mixed $column The column.
357 * @param mixed $structure The structure.
358 *
359 * @return string|null
360 *
361 * @since 1.0.0
362 */
363 protected function apply_unsigned($column, $structure)
364 {
365 if ($column->unsigned) {
366 return ' unsigned';
367 }
368 }
369 /**
370 * Add NULL/NOT NULL modifier to a column if applicable.
371 *
372 * @param Definition $column The column.
373 * @param Structure $structure The structure.
374 *
375 * @return string|null
376 *
377 * @since 1.0.0
378 */
379 protected function apply_nullable(Definition $column, Structure $structure)
380 {
381 if (isset($column->nullable)) {
382 return $column->nullable ? ' null' : ' not null';
383 }
384 return ' not null';
385 }
386 /**
387 * Add DEFAULT value modifier to a column if applicable.
388 *
389 * @param Definition $column The column.
390 * @param Structure $structure The structure.
391 *
392 * @return string|null
393 *
394 * @since 1.0.0
395 */
396 protected function apply_default(Definition $column, Structure $structure)
397 {
398 if (!\is_null($column->default)) {
399 return \sprintf(' default %s', $this->get_default_value($column->default));
400 }
401 }
402 /**
403 * Add COMMENT modifier to a column if applicable.
404 *
405 * @param Definition $column The column.
406 * @param Structure $structure The structure.
407 *
408 * @return string|null
409 *
410 * @since 1.0.0
411 */
412 protected function apply_comment(Definition $column, Structure $structure)
413 {
414 if ($column->comment) {
415 return \sprintf(" comment '%s'", \addslashes($column->comment));
416 }
417 }
418 /**
419 * Add AUTO_INCREMENT modifier to a column if applicable.
420 *
421 * @param Definition $column The column.
422 * @param Structure $structure The structure.
423 *
424 * @return string|null
425 *
426 * @since 1.0.0
427 */
428 protected function apply_auto_increment(Definition $column, Structure $structure)
429 {
430 if ($column->auto_increment) {
431 return ' auto_increment';
432 }
433 }
434 /**
435 * Format the default value for SQL.
436 *
437 * @param mixed $value The value.
438 *
439 * @return string
440 *
441 * @since 1.0.0
442 */
443 protected function get_default_value($value)
444 {
445 if ($value instanceof Expression) {
446 return $this->get_value($value);
447 }
448 if (\is_bool($value)) {
449 return \sprintf("'%d'", (int) $value);
450 }
451 return \sprintf("'%s'", $value);
452 }
453 /**
454 * Get the value.
455 *
456 * @param mixed $value The value.
457 *
458 * @return mixed
459 *
460 * @since 1.0.0
461 */
462 protected function get_value($value)
463 {
464 if ($value instanceof Expression) {
465 return $this->get_value($value->get_value());
466 }
467 return $value;
468 }
469 /**
470 * Get the formatted column name for SQL.
471 *
472 * @param mixed $column The column.
473 *
474 * @return string
475 *
476 * @since 1.0.0
477 */
478 protected function get_column_name($column)
479 {
480 return \sprintf('`%s`', $column->name);
481 }
482 /**
483 * Get the SQL data type for a column.
484 *
485 * @param mixed $column The column.
486 *
487 * @return string
488 *
489 * @throws \Exception
490 *
491 * @since 1.0.0
492 */
493 protected function get_column_type($column)
494 {
495 $column_type = $column->type;
496 $getter_method = 'type_' . \strtolower($column_type);
497 if (!\method_exists($this, $getter_method)) {
498 throw new Exception(\sprintf('Method %s not found.', $getter_method));
499 }
500 return $this->{$getter_method}($column);
501 }
502 /**
503 * Get the SQL type for a string column.
504 *
505 * @param Definition $column The column.
506 *
507 * @return string
508 *
509 * @since 1.0.0
510 */
511 protected function type_string(Definition $column)
512 {
513 return \sprintf('varchar(%d)', $column->length);
514 }
515 /**
516 * Get the SQL type for a text column.
517 *
518 * @param Definition $column The column.
519 *
520 * @return string
521 *
522 * @since 1.0.0
523 */
524 protected function type_text(Definition $column)
525 {
526 return 'text';
527 }
528 /**
529 * Get the SQL type for a medium text column.
530 *
531 * @param Definition $column The column.
532 *
533 * @return string
534 *
535 * @since 1.0.0
536 */
537 protected function type_medium_text(Definition $column)
538 {
539 return 'mediumtext';
540 }
541 /**
542 * Get the SQL type for a long text column.
543 *
544 * @param Definition $column The column.
545 *
546 * @return string
547 *
548 * @since 1.0.0
549 */
550 protected function type_long_text(Definition $column)
551 {
552 return 'longtext';
553 }
554 /**
555 * Get the SQL type for a big integer column.
556 *
557 * @param Definition $column The column.
558 *
559 * @return string
560 *
561 * @since 1.0.0
562 */
563 protected function type_big_integer(Definition $column)
564 {
565 return 'bigint';
566 }
567 /**
568 * Get the SQL type for an integer column.
569 *
570 * @param Definition $column The column.
571 *
572 * @return string
573 *
574 * @since 1.0.0
575 */
576 protected function type_integer(Definition $column)
577 {
578 return 'int';
579 }
580 /**
581 * Get the SQL type for a float column.
582 *
583 * @param Definition $column The column.
584 *
585 * @return string
586 *
587 * @since 1.0.0
588 */
589 protected function type_float(Definition $column)
590 {
591 if ($column->precision) {
592 return \sprintf('float(%d)', $column->precision);
593 }
594 return 'float';
595 }
596 /**
597 * Get the SQL type for a double column.
598 *
599 * @param Definition $column The column.
600 *
601 * @return string
602 *
603 * @since 1.0.0
604 */
605 protected function type_double(Definition $column)
606 {
607 return 'double';
608 }
609 /**
610 * Get the SQL type for a decimal column.
611 *
612 * @param Definition $column The column.
613 *
614 * @return string
615 *
616 * @since 1.0.0
617 */
618 protected function type_decimal(Definition $column)
619 {
620 return \sprintf('decimal(%d, %d)', $column->total, $column->places);
621 }
622 /**
623 * Get the SQL type for a boolean column.
624 *
625 * @param Definition $column The column.
626 *
627 * @return string
628 *
629 * @since 1.0.0
630 */
631 protected function type_boolean(Definition $column)
632 {
633 return 'tinyint(1)';
634 }
635 /**
636 * Get the SQL type for a tinyint column.
637 *
638 * @param Definition $column The column.
639 *
640 * @return string
641 *
642 * @since 1.0.0
643 */
644 protected function type_tinyint(Definition $column)
645 {
646 if ($column->length) {
647 return \sprintf('tinyint(%d)', $column->length);
648 }
649 return 'tinyint';
650 }
651 /**
652 * Get the SQL type for a date column.
653 *
654 * @param Definition $column The column.
655 *
656 * @return string
657 *
658 * @since 1.0.0
659 */
660 protected function type_date(Definition $column)
661 {
662 return 'date';
663 }
664 /**
665 * Get the SQL type for a datetime column.
666 *
667 * @param Definition $column The column.
668 *
669 * @return string
670 *
671 * @since 1.0.0
672 */
673 protected function type_datetime(Definition $column)
674 {
675 $current = $column->precision ? \sprintf('CURRENT_TIMESTAMP(%d)', $column->precision) : 'CURRENT_TIMESTAMP';
676 if ($column->use_current) {
677 $column->default(new Expression($current));
678 }
679 return $column->precision ? \sprintf('datetime(%d)', $column->precision) : 'datetime';
680 }
681 /**
682 * Get the SQL type for a time column.
683 *
684 * @param Definition $column The column.
685 *
686 * @return string
687 *
688 * @since 1.0.0
689 */
690 protected function type_time(Definition $column)
691 {
692 return $column->precision ? \sprintf('time(%d)', $column->precision) : 'time';
693 }
694 /**
695 * Get the SQL type for a timestamp column.
696 *
697 * @param Definition $column The column.
698 *
699 * @return string
700 *
701 * @since 1.0.0
702 */
703 protected function type_timestamp(Definition $column)
704 {
705 $current = $column->precision ? \sprintf('CURRENT_TIMESTAMP(%d)', $column->precision) : 'CURRENT_TIMESTAMP';
706 if ($column->use_current) {
707 $column->default(new Expression($current));
708 }
709 return $column->precision ? \sprintf('timestamp(%d)', $column->precision) : 'timestamp';
710 }
711 /**
712 * Get the SQL type for an enum column.
713 *
714 * @param Definition $column The column.
715 *
716 * @return string
717 *
718 * @since 1.0.0
719 */
720 protected function type_enum(Definition $column)
721 {
722 return \sprintf("enum('%s')", \implode("', '", $column->values));
723 }
724 /**
725 * Get the SQL type for a set column.
726 *
727 * @param Definition $column The column.
728 *
729 * @return string
730 *
731 * @since 1.0.0
732 */
733 protected function type_set(Definition $column)
734 {
735 return \sprintf('set(%s)', \implode(',', $column->values));
736 }
737 /**
738 * Get the SQL type for an IP address column.
739 *
740 * @param Definition $column The column.
741 *
742 * @return string
743 *
744 * @since 1.0.0
745 */
746 protected function type_ip_address(Definition $column)
747 {
748 return 'varchar(45)';
749 }
750 /**
751 * Get the SQL type for a UUID column.
752 *
753 * @param Definition $column The column.
754 *
755 * @return string
756 *
757 * @since 1.0.0
758 */
759 protected function type_uuid(Definition $column)
760 {
761 return 'char(36)';
762 }
763 /**
764 * Get the SQL type for a year column.
765 *
766 * @param Definition $column The column.
767 *
768 * @return string
769 *
770 * @since 1.0.0
771 */
772 protected function type_year(Definition $column)
773 {
774 return 'year';
775 }
776 /**
777 * Check if a command exists in the structure.
778 *
779 * @param Structure $structure The structure.
780 * @param mixed $name The name.
781 *
782 * @return bool
783 *
784 * @since 1.0.0
785 */
786 protected function has_command(Structure $structure, $name)
787 {
788 $commands = $structure->get_commands();
789 return !empty($commands[$name]);
790 }
791 /**
792 * Get a command from the structure.
793 *
794 * @param Structure $structure The structure.
795 * @param mixed $name The name.
796 *
797 * @return mixed|null
798 *
799 * @since 1.0.0
800 */
801 protected function get_command(Structure $structure, $name)
802 {
803 return $this->has_command($structure, $name) ? $structure->get_commands()[$name] : null;
804 }
805 }
806