PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.6
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.6
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.6, at classes/models/FrmDb.php

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