PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.28
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.28
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / models / FrmDb.php

FrmDb.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.28, at classes/models/FrmDb.php

870 lines 20.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmDb {
7
8 /**
9 * The table name for Formidable Fields.
10 *
11 * @var string
12 */
13 public $fields;
14
15 /**
16 * The table name for Formidable Forms.
17 *
18 * @var string
19 */
20 public $forms;
21
22 /**
23 * The table name for Formidable Entries.
24 *
25 * @var string
26 */
27 public $entries;
28
29 /**
30 * The table name for Formidable Entry Metas.
31 *
32 * @var string
33 */
34 public $entry_metas;
35
36 public function __construct() {
37 if ( ! defined( 'ABSPATH' ) ) {
38 die( 'You are not allowed to call this page directly.' );
39 }
40
41 _deprecated_function( __METHOD__, '2.05.06', 'FrmMigrate' );
42 global $wpdb;
43 $this->fields = $wpdb->prefix . 'frm_fields';
44 $this->forms = $wpdb->prefix . 'frm_forms';
45 $this->entries = $wpdb->prefix . 'frm_items';
46 $this->entry_metas = $wpdb->prefix . 'frm_item_metas';
47 }
48
49 /**
50 * Change array into format $wpdb->prepare can use
51 *
52 * @param array $args
53 * @param string $starts_with
54 *
55 * @return void
56 */
57 public static function get_where_clause_and_values( &$args, $starts_with = ' WHERE ' ) {
58 if ( ! $args ) {
59 // Add an arg to prevent prepare from failing
60 $args = array(
61 'where' => $starts_with . '1=%d',
62 'values' => array( 1 ),
63 );
64
65 return;
66 }
67
68 $where = '';
69 $values = array();
70
71 if ( is_array( $args ) ) {
72 $base_where = $starts_with;
73 self::parse_where_from_array( $args, $base_where, $where, $values );
74 }
75
76 $args = compact( 'where', 'values' );
77 }
78
79 /**
80 * @param array $args
81 * @param string $base_where
82 * @param string $where
83 * @param array $values
84 *
85 * @return void
86 */
87 public static function parse_where_from_array( $args, $base_where, &$where, &$values ) {
88 $condition = ' AND';
89
90 if ( isset( $args['or'] ) ) {
91 $condition = ' OR';
92 unset( $args['or'] );
93 }
94
95 foreach ( $args as $key => $value ) {
96 $where .= $where ? $condition : $base_where;
97 // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
98 $array_inc_null = ! is_numeric( $key ) && is_array( $value ) && in_array( null, $value );
99
100 if ( is_numeric( $key ) || $array_inc_null ) {
101 $where .= ' ( ';
102 $nested_where = '';
103
104 if ( $array_inc_null ) {
105 foreach ( $value as $val ) {
106 $parse_where = array(
107 $key => $val,
108 'or' => 1,
109 );
110 self::parse_where_from_array( $parse_where, '', $nested_where, $values );
111 }
112 } else {
113 self::parse_where_from_array( $value, '', $nested_where, $values );
114 }
115
116 $where .= $nested_where;
117 $where .= ' ) ';
118 } else {
119 self::interpret_array_to_sql( $key, $value, $where, $values );
120 }//end if
121 }//end foreach
122 }
123
124 /**
125 * @param string $key
126 * @param array|string|null $value
127 * @param string $where
128 * @param array $values
129 *
130 * @return void
131 */
132 private static function interpret_array_to_sql( $key, $value, &$where, &$values ) {
133 $key = trim( $key );
134
135 if ( str_contains( $key, 'created_at' ) || str_contains( $key, 'updated_at' ) ) {
136 $k = explode( ' ', $key );
137 $where .= ' CAST(' . reset( $k ) . ' as CHAR) ' . str_replace( reset( $k ), '', $key );
138 } else {
139 $where .= ' ' . $key;
140 }
141
142 $lowercase_key = explode( ' ', strtolower( $key ) );
143 $lowercase_key = end( $lowercase_key );
144
145 if ( is_array( $value ) ) {
146 // Translate array of values to "in"
147 if ( str_contains( $lowercase_key, 'like' ) ) {
148 $where = preg_replace( '/' . $key . '$/', '', $where );
149 $where .= '(';
150 $start = true;
151
152 foreach ( $value as $v ) {
153 if ( ! $start ) {
154 $where .= ' OR ';
155 }
156
157 $start = false;
158 $where .= $key . ' %s';
159 $values[] = '%' . self::esc_like( $v ) . '%';
160 }
161
162 $where .= ')';
163 } elseif ( $value ) {
164 $where .= ' in (' . self::prepare_array_values( $value, '%s' ) . ')';
165 $values = array_merge( $values, $value );
166 }
167 } elseif ( str_contains( $lowercase_key, 'like' ) ) {
168 /**
169 * Allow string to start or end with the value
170 * If the key is like% then skip the first % for starts with
171 * If the key is %like then skip the last % for ends with
172 */
173 $start = '%';
174 $end = '%';
175
176 if ( $lowercase_key === 'like%' ) {
177 $start = '';
178 $where = rtrim( $where, '%' );
179 } elseif ( $lowercase_key === '%like' ) {
180 $end = '';
181 $where = rtrim( rtrim( $where, '%like' ), '%LIKE' );
182 $where .= 'like';
183 }
184
185 $where .= ' %s';
186 $values[] = $start . self::esc_like( $value ) . $end;
187 } elseif ( $value === null ) {
188 $where .= ' IS NULL';
189 } else {
190 // allow a - to prevent = from being added
191 if ( str_ends_with( $key, '-' ) ) {
192 $where = rtrim( $where, '-' );
193 } else {
194 $where .= '=';
195 }
196
197 self::add_query_placeholder( $key, $value, $where );
198
199 $values[] = $value;
200 }//end if
201 }
202
203 /**
204 * Add %d, or %s to query
205 *
206 * @since 2.02.05
207 *
208 * @param string $key
209 * @param int|string $value
210 * @param string $where
211 *
212 * @return void
213 */
214 private static function add_query_placeholder( $key, $value, &$where ) {
215 if ( is_numeric( $value ) && ( ! str_contains( $key, 'meta_value' ) || str_contains( $key, '+0' ) ) ) {
216 // Switch string to number.
217 $value = $value + 0;
218 $where .= is_float( $value ) ? '%f' : '%d';
219 } else {
220 $where .= '%s';
221 }
222 }
223
224 /**
225 * @param string $table
226 * @param array $where
227 * @param array $args
228 *
229 * @return int
230 */
231 public static function get_count( $table, $where = array(), $args = array() ) {
232 $count = self::get_var( $table, $where, 'COUNT(*)', $args );
233 return (int) $count;
234 }
235
236 /**
237 * @param string $table
238 * @param array $where
239 * @param string $field
240 * @param array $args
241 * @param string $limit
242 * @param string $type
243 *
244 * @return array|object|string|null
245 */
246 public static function get_var( $table, $where = array(), $field = 'id', $args = array(), $limit = '', $type = 'var' ) {
247 $group = '';
248 self::get_group_and_table_name( $table, $group );
249 self::convert_options_to_array( $args, '', $limit );
250
251 if ( $type === 'var' && ! isset( $args['limit'] ) ) {
252 $args['limit'] = 1;
253 }
254
255 $query = self::generate_query_string_from_pieces( $field, $table, $where, $args );
256 $cache_key = self::generate_cache_key( $where, $args, $field, $type );
257
258 return self::check_cache( $cache_key, $group, $query, 'get_' . $type );
259 }
260
261 /**
262 * Generate a cache key from the where query, field, type, and other arguments
263 *
264 * @since 2.03.07
265 *
266 * @param array $where
267 * @param array $args
268 * @param string $field
269 * @param string $type
270 *
271 * @return string
272 */
273 public static function generate_cache_key( $where, $args, $field, $type ) {
274 $cache_key = '';
275 $where = FrmAppHelper::array_flatten( $where );
276
277 foreach ( $where as $key => $value ) {
278 $cache_key .= $key . '_' . $value;
279 }
280
281 $cache_key .= implode( '_', $args ) . $field . '_' . $type;
282
283 return str_replace( array( ' ', ',' ), '_', $cache_key );
284 }
285
286 /**
287 * @param string $table
288 * @param array $where
289 * @param string $field
290 * @param array $args
291 * @param string $limit
292 *
293 * @return array
294 */
295 public static function get_col( $table, $where = array(), $field = 'id', $args = array(), $limit = '' ) {
296 return self::get_var( $table, $where, $field, $args, $limit, 'col' );
297 }
298
299 /**
300 * @since 2.0
301 *
302 * @param string $table
303 * @param array $where
304 * @param string $fields
305 * @param array $args
306 *
307 * @return mixed
308 */
309 public static function get_row( $table, $where = array(), $fields = '*', $args = array() ) {
310 $args['limit'] = 1;
311 return self::get_var( $table, $where, $fields, $args, '', 'row' );
312 }
313
314 /**
315 * Prepare a key/value array before DB call
316 *
317 * @since 2.0
318 *
319 * @param string $table
320 * @param array $where
321 * @param string $fields
322 * @param array $args
323 *
324 * @return array
325 */
326 public static function get_results( $table, $where = array(), $fields = '*', $args = array() ) {
327 return self::get_var( $table, $where, $fields, $args, '', 'results' );
328 }
329
330 /**
331 * Check for like, not like, in, not in, =, !=, >, <, <=, >=
332 * Return a value to append to the where array key
333 *
334 * @param string $where_is
335 *
336 * @return string
337 */
338 public static function append_where_is( $where_is ) {
339 $switch_to = array(
340 '=' => '',
341 '!=' => '!',
342 '<=' => '<',
343 '>=' => '>',
344 'like' => 'like',
345 'not like' => 'not like',
346 'in' => '',
347 'not in' => 'not',
348 'like%' => 'like%',
349 '%like' => '%like',
350 );
351
352 $where_is = strtolower( $where_is );
353
354 if ( isset( $switch_to[ $where_is ] ) ) {
355 return ' ' . $switch_to[ $where_is ];
356 }
357
358 // > and < need a little more work since we don't want them switched to >= and <=
359 if ( $where_is === '>' || $where_is === '<' ) {
360 // The - indicates that the = should not be added later.
361 return ' ' . $where_is . '-';
362 }
363
364 // fallback to = if the query is none of these
365 return '';
366 }
367
368 /**
369 * Get 'frm_forms' from wp_frm_forms or a longer table param that includes a join
370 * Also add the wpdb->prefix to the table if it's missing
371 *
372 * @param string $table
373 * @param string $group
374 *
375 * @return void
376 */
377 private static function get_group_and_table_name( &$table, &$group ) {
378 global $wpdb;
379
380 $table_parts = explode( ' ', $table );
381 $group = reset( $table_parts );
382 self::maybe_remove_prefix( $wpdb->prefix, $group );
383
384 $prefix = $wpdb->base_prefix;
385 self::maybe_remove_prefix( $prefix, $group );
386
387 if ( $group === $table ) {
388 $table = $wpdb->prefix . $table;
389 }
390
391 // Switch to singular group name
392 $group = rtrim( $group, 's' );
393 }
394
395 /**
396 * Only remove the db prefix when at the beginning.
397 *
398 * @since 4.04.02
399 *
400 * @param string $prefix Prefix to remove.
401 * @param string $name Name to strip prefix from, passed by reference.
402 *
403 * @return void
404 */
405 private static function maybe_remove_prefix( $prefix, &$name ) {
406 if ( str_starts_with( $name, $prefix ) ) {
407 $name = substr( $name, strlen( $prefix ) );
408 }
409 }
410
411 /**
412 * @param array|string $args
413 * @param string $order_by
414 * @param int|string $limit
415 *
416 * @return void
417 */
418 private static function convert_options_to_array( &$args, $order_by = '', $limit = '' ) {
419 if ( ! is_array( $args ) ) {
420 $args = array( 'order_by' => $args );
421 }
422
423 if ( $order_by ) {
424 $args['order_by'] = $order_by;
425 }
426
427 if ( $limit ) {
428 $args['limit'] = $limit;
429 }
430
431 $temp_args = $args;
432
433 foreach ( $temp_args as $k => $v ) {
434 // phpcs:ignore Universal.Operators.StrictComparisons
435 if ( $v == '' ) {
436 unset( $args[ $k ] );
437 continue;
438 }
439
440 $db_name = strtoupper( str_replace( '_', ' ', $k ) );
441
442 if ( ! str_contains( $v, $db_name ) ) {
443 $args[ $k ] = $db_name . ' ' . $v;
444 }
445 }
446
447 // Make sure LIMIT is the last argument
448 if ( ! isset( $args['order_by'] ) || ! isset( $args['limit'] ) ) {
449 return;
450 }
451
452 $temp_limit = $args['limit'];
453 unset( $args['limit'] );
454 $args['limit'] = $temp_limit;
455 }
456
457 /**
458 * Get the associative array results for the given columns, table, and where query
459 *
460 * @since 2.02.05
461 *
462 * @param string $columns
463 * @param string $table
464 * @param array $where
465 *
466 * @return mixed
467 */
468 public static function get_associative_array_results( $columns, $table, $where ) {
469 $group = '';
470 self::get_group_and_table_name( $table, $group );
471
472 $query = self::generate_query_string_from_pieces( $columns, $table, $where );
473 $cache_key = str_replace( array( ' ', ',' ), '_', trim( implode( '_', FrmAppHelper::array_flatten( $where ) ) . $columns . '_results_ARRAY_A', ' WHERE' ) );
474
475 return self::check_cache( $cache_key, $group, $query, 'get_associative_results' );
476 }
477
478 /**
479 * Combine the pieces of a query to form a full, prepared query
480 *
481 * @since 2.02.05
482 *
483 * @param string $columns
484 * @param string $table
485 * @param mixed $where
486 * @param array $args
487 *
488 * @return string
489 */
490 private static function generate_query_string_from_pieces( $columns, $table, $where, $args = array() ) {
491 $query = 'SELECT ' . $columns . ' FROM ' . $table;
492
493 self::esc_query_args( $args );
494
495 if ( ! is_array( $where ) && $where ) {
496 return $query;
497 }
498
499 self::get_where_clause_and_values( $where );
500 global $wpdb;
501
502 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
503 return $wpdb->prepare( $query . $where['where'] . ' ' . implode( ' ', $args ), $where['values'] );
504 }
505
506 /**
507 * @since 2.05.07
508 *
509 * @param array $args Query arguments, passed by reference.
510 *
511 * @return void
512 */
513 private static function esc_query_args( &$args ) {
514 foreach ( $args as $param => $value ) {
515 if ( $param === 'order_by' ) {
516 $args[ $param ] = self::esc_order( $value );
517 } elseif ( $param === 'limit' ) {
518 $args[ $param ] = self::esc_limit( $value );
519 }
520
521 // phpcs:ignore Universal.Operators.StrictComparisons
522 if ( $args[ $param ] == '' ) {
523 unset( $args[ $param ] );
524 }
525 }
526 }
527
528 /**
529 * Added for < WP 4.0 compatibility
530 *
531 * @since 2.05.06
532 *
533 * @param string $term The value to escape.
534 *
535 * @return string The escaped value
536 */
537 public static function esc_like( $term ) {
538 global $wpdb;
539 return $wpdb->esc_like( $term );
540 }
541
542 /**
543 * @since 2.05.06
544 *
545 * @param string $order_query
546 *
547 * @return string
548 */
549 public static function esc_order( $order_query ) {
550 if ( ! $order_query ) {
551 return '';
552 }
553
554 // Remove ORDER BY before sanitizing.
555 $order_query = strtolower( $order_query );
556
557 if ( str_contains( $order_query, 'order by' ) ) {
558 $order_query = str_replace( 'order by', '', $order_query );
559 }
560
561 $order_query = explode( ' ', trim( $order_query ) );
562 $order = trim( reset( $order_query ) );
563 $safe_order = array( 'count(*)' );
564
565 if ( ! in_array( strtolower( $order ), $safe_order, true ) ) {
566 $order = preg_replace( '/[^a-zA-Z0-9\-\_\.\+]/', '', $order );
567 }
568
569 $order_by = '';
570
571 if ( count( $order_query ) > 1 ) {
572 $order_by = end( $order_query );
573 self::esc_order_by( $order_by );
574 }
575
576 return ' ORDER BY ' . $order . ' ' . $order_by;
577 }
578
579 /**
580 * Make sure this is ordering by either ASC or DESC
581 *
582 * @since 2.05.06
583 *
584 * @param string $order_by Sort direction, passed by reference.
585 *
586 * @return void
587 */
588 public static function esc_order_by( &$order_by ) {
589 $sort_options = array( 'asc', 'desc' );
590
591 if ( ! in_array( strtolower( $order_by ), $sort_options, true ) ) {
592 $order_by = 'asc';
593 }
594 }
595
596 /**
597 * @since 2.05.06
598 *
599 * @param string $limit
600 *
601 * @return string
602 */
603 public static function esc_limit( $limit ) {
604 if ( ! $limit ) {
605 return '';
606 }
607
608 $limit = trim( str_replace( 'limit ', '', strtolower( $limit ) ) );
609
610 if ( is_numeric( $limit ) ) {
611 return ' LIMIT ' . $limit;
612 }
613
614 $limit = explode( ',', trim( $limit ) );
615
616 foreach ( $limit as $k => $l ) {
617 if ( is_numeric( $l ) ) {
618 $limit[ $k ] = $l;
619 }
620 }
621
622 $limit = implode( ',', $limit );
623
624 return ' LIMIT ' . $limit;
625 }
626
627 /**
628 * Get an array of values ready to go through $wpdb->prepare
629 *
630 * @since 2.05.06
631 *
632 * @param array $array Array of values.
633 * @param string $type Placeholder type.
634 *
635 * @return string
636 */
637 public static function prepare_array_values( $array, $type = '%s' ) {
638 $placeholders = array_fill( 0, count( $array ), $type );
639 return implode( ', ', $placeholders );
640 }
641
642 /**
643 * @since 2.05.06
644 *
645 * @param string $starts_with
646 * @param array|string $where
647 *
648 * @return string
649 */
650 public static function prepend_and_or_where( $starts_with = ' WHERE ', $where = '' ) {
651 if ( ! $where ) {
652 $where = '';
653 } elseif ( is_array( $where ) ) {
654 global $wpdb;
655 self::get_where_clause_and_values( $where, $starts_with );
656 $where = $wpdb->prepare( $where['where'], $where['values'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
657 } else {
658 $where = $starts_with . $where;
659 }
660
661 /**
662 * Allows modifying where clause when using FrmDb::prepend_and_or_where() method.
663 *
664 * @since 5.0.16
665 *
666 * @param string $where Where string.
667 * @param string $starts_with The start of where string.
668 */
669 return apply_filters( 'frm_prepend_and_or_where', $where, $starts_with );
670 }
671
672 /**
673 * Prepare and save settings in styles and actions
674 *
675 * @since 2.05.06
676 *
677 * @param array|object $settings
678 * @param string $group
679 *
680 * @return int|WP_Error
681 */
682 public static function save_settings( $settings, $group ) {
683 $settings = (array) $settings;
684 $settings['post_content'] = FrmAppHelper::prepare_and_encode( $settings['post_content'] );
685
686 if ( empty( $settings['ID'] ) ) {
687 unset( $settings['ID'] );
688 }
689
690 // Delete all caches for this group
691 self::cache_delete_group( $group );
692
693 return self::save_json_post( $settings );
694 }
695
696 /**
697 * Since actions are JSON encoded, we don't want any filters messing with it.
698 * Remove the filters and then add them back in case any posts or views are
699 * also being imported.
700 *
701 * Used when saving form actions and styles
702 *
703 * @since 2.05.06
704 *
705 * @param array $settings
706 *
707 * @return int|WP_Error
708 */
709 public static function save_json_post( $settings ) {
710 global $wp_filter;
711
712 if ( isset( $wp_filter['content_save_pre'] ) ) {
713 $filters = $wp_filter['content_save_pre'];
714 }
715
716 // Remove the balanceTags filter in case WordPress is trying to validate the XHTML
717 remove_all_filters( 'content_save_pre' );
718
719 $post = wp_insert_post( $settings );
720
721 // Add the content filters back for views or posts
722 if ( isset( $filters ) ) {
723 $wp_filter['content_save_pre'] = $filters;
724 }
725
726 return $post;
727 }
728
729 /**
730 * Check cache before fetching values and saving to cache
731 *
732 * @since 2.05.06
733 *
734 * @param string $cache_key The unique name for this cache.
735 * @param string $group The name of the cache group.
736 * @param string $query If blank, don't run a db call.
737 * @param string $type The wpdb function to use with this query.
738 * @param int $time Cache expiration time in seconds.
739 *
740 * @return mixed $results The cache or query results
741 */
742 public static function check_cache( $cache_key, $group = '', $query = '', $type = 'get_var', $time = 300 ) {
743 $found = null;
744 $results = wp_cache_get( $cache_key, $group, false, $found );
745
746 if ( ( $found === true && $results !== false ) || ! $query ) {
747 return $results;
748 }
749
750 if ( 'get_posts' === $type ) {
751 $results = get_posts( $query );
752 } elseif ( 'get_associative_results' === $type ) {
753 global $wpdb;
754 $results = $wpdb->get_results( $query, OBJECT_K ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
755 } else {
756 global $wpdb;
757 $results = $wpdb->{$type}( $query );
758 }
759
760 self::set_cache( $cache_key, $results, $group, $time );
761
762 return $results;
763 }
764
765 /**
766 * @since 2.05.06
767 *
768 * @param string $cache_key The unique name for this cache.
769 * @param mixed $results Cached results.
770 * @param string $group The name of the cache group.
771 * @param int $time Cache expiration time in seconds.
772 *
773 * @return void
774 */
775 public static function set_cache( $cache_key, $results, $group = '', $time = 300 ) {
776 if ( ! FrmAppHelper::prevent_caching() ) {
777 self::add_key_to_group_cache( $cache_key, $group );
778 wp_cache_set( $cache_key, $results, $group, $time );
779 }
780 }
781
782 /**
783 * Keep track of the keys cached in each group so they can be deleted
784 * in Redis and Memcache
785 *
786 * @since 2.05.06
787 *
788 * @param string $key Cache key.
789 * @param string $group Cache group name.
790 *
791 * @return void
792 */
793 public static function add_key_to_group_cache( $key, $group ) {
794 $cached = self::get_group_cached_keys( $group );
795 $cached[ $key ] = $key;
796 wp_cache_set( 'cached_keys', $cached, $group, 300 );
797 }
798
799 /**
800 * @since 2.05.06
801 *
802 * @param string $group Cache group name.
803 *
804 * @return array
805 */
806 public static function get_group_cached_keys( $group ) {
807 $cached = wp_cache_get( 'cached_keys', $group );
808
809 if ( ! $cached || ! is_array( $cached ) ) {
810 return array();
811 }
812
813 return $cached;
814 }
815
816 /**
817 * @since 2.05.06
818 *
819 * @param string $cache_key Cache key to delete.
820 * @param string $group Cache group name.
821 *
822 * @return void
823 */
824 public static function delete_cache_and_transient( $cache_key, $group = 'default' ) {
825 delete_transient( $cache_key );
826 wp_cache_delete( $cache_key, $group );
827 }
828
829 /**
830 * Delete all caching in a single group
831 *
832 * @since 2.05.06
833 *
834 * @param string $group The name of the cache group.
835 *
836 * @return void
837 */
838 public static function cache_delete_group( $group ) {
839 $cached_keys = self::get_group_cached_keys( $group );
840
841 if ( ! $cached_keys ) {
842 return;
843 }
844
845 foreach ( $cached_keys as $key ) {
846 wp_cache_delete( $key, $group );
847 }
848
849 wp_cache_delete( 'cached_keys', $group );
850 }
851
852 /**
853 * Checks if a DB column exists.
854 *
855 * @since 6.7
856 *
857 * @param string $table Table name without `$wpdb->prefix`.
858 * @param string $column Column name.
859 *
860 * @return bool
861 */
862 public static function db_column_exists( $table, $column ) {
863 global $wpdb;
864
865 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
866 $result = $wpdb->get_results( $wpdb->prepare( 'SHOW COLUMNS FROM ' . $wpdb->prefix . $table . ' LIKE %s', $column ) );
867 return ! empty( $result );
868 }
869 }
870