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

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