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

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