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

737 lines 18.3 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
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 private 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 $group = str_replace( $wpdb->prefix, '', $group );
335
336 $prefix = $wpmuBaseTablePrefix ? $wpmuBaseTablePrefix : $wpdb->base_prefix;
337 $group = str_replace( $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 private static function convert_options_to_array( &$args, $order_by = '', $limit = '' ) {
348 if ( ! is_array( $args ) ) {
349 $args = array( 'order_by' => $args );
350 }
351
352 if ( ! empty( $order_by ) ) {
353 $args['order_by'] = $order_by;
354 }
355
356 if ( ! empty( $limit ) ) {
357 $args['limit'] = $limit;
358 }
359
360 $temp_args = $args;
361 foreach ( $temp_args as $k => $v ) {
362 if ( $v == '' ) {
363 unset( $args[ $k ] );
364 continue;
365 }
366
367 $db_name = strtoupper( str_replace( '_', ' ', $k ) );
368 if ( strpos( $v, $db_name ) === false ) {
369 $args[ $k ] = $db_name . ' ' . $v;
370 }
371 }
372
373 // Make sure LIMIT is the last argument
374 if ( isset( $args['order_by'] ) && isset( $args['limit'] ) ) {
375 $temp_limit = $args['limit'];
376 unset( $args['limit'] );
377 $args['limit'] = $temp_limit;
378 }
379 }
380
381 /**
382 * Get the associative array results for the given columns, table, and where query
383 *
384 * @since 2.02.05
385 *
386 * @param string $columns
387 * @param string $table
388 * @param array $where
389 *
390 * @return mixed
391 */
392 public static function get_associative_array_results( $columns, $table, $where ) {
393 $group = '';
394 self::get_group_and_table_name( $table, $group );
395
396 $query = self::generate_query_string_from_pieces( $columns, $table, $where );
397
398 $cache_key = str_replace( array( ' ', ',' ), '_', trim( implode( '_', FrmAppHelper::array_flatten( $where ) ) . $columns . '_results_ARRAY_A', ' WHERE' ) );
399 $results = self::check_cache( $cache_key, $group, $query, 'get_associative_results' );
400
401 return $results;
402 }
403
404 /**
405 * Combine the pieces of a query to form a full, prepared query
406 *
407 * @since 2.02.05
408 *
409 * @param string $columns
410 * @param string $table
411 * @param mixed $where
412 * @param array $args
413 *
414 * @return string
415 */
416 private static function generate_query_string_from_pieces( $columns, $table, $where, $args = array() ) {
417 $query = 'SELECT ' . $columns . ' FROM ' . $table;
418
419 self::esc_query_args( $args );
420
421 if ( is_array( $where ) || empty( $where ) ) {
422 self::get_where_clause_and_values( $where );
423 global $wpdb;
424 $query = $wpdb->prepare( $query . $where['where'] . ' ' . implode( ' ', $args ), $where['values'] ); // WPCS: unprepared SQL ok.
425 } else {
426 /**
427 * Allow the $where to be prepared before we recieve it here.
428 * This is a fallback for reverse compatibility, but is not recommended
429 */
430 _deprecated_argument( 'where', '2.0', esc_html__( 'Use the query in an array format so it can be properly prepared.', 'formidable' ) );
431 $query .= $where . ' ' . implode( ' ', $args );
432 }
433
434 return $query;
435 }
436
437 /**
438 * @since 2.05.07
439 */
440 private static function esc_query_args( &$args ) {
441 foreach ( $args as $param => $value ) {
442 if ( $param == 'order_by' ) {
443 $args[ $param ] = self::esc_order( $value );
444 } elseif ( $param == 'limit' ) {
445 $args[ $param ] = self::esc_limit( $value );
446 }
447
448 if ( $args[ $param ] == '' ) {
449 unset( $args[ $param ] );
450 }
451 }
452 }
453
454 /**
455 * Added for < WP 4.0 compatability
456 *
457 * @since 2.05.06
458 *
459 * @param string $term The value to escape
460 *
461 * @return string The escaped value
462 */
463 public static function esc_like( $term ) {
464 global $wpdb;
465
466 return $wpdb->esc_like( $term );
467 }
468
469 /**
470 * @since 2.05.06
471 *
472 * @param string $order_query
473 */
474 public static function esc_order( $order_query ) {
475 if ( empty( $order_query ) ) {
476 return '';
477 }
478
479 // remove ORDER BY before santizing
480 $order_query = strtolower( $order_query );
481 if ( strpos( $order_query, 'order by' ) !== false ) {
482 $order_query = str_replace( 'order by', '', $order_query );
483 }
484
485 $order_query = explode( ' ', trim( $order_query ) );
486
487 $order = trim( reset( $order_query ) );
488 $safe_order = array( 'count(*)' );
489 if ( ! in_array( strtolower( $order ), $safe_order ) ) {
490 $order = preg_replace( '/[^a-zA-Z0-9\-\_\.\+]/', '', $order );
491 }
492
493 $order_by = '';
494 if ( count( $order_query ) > 1 ) {
495 $order_by = end( $order_query );
496 self::esc_order_by( $order_by );
497 }
498
499 return ' ORDER BY ' . $order . ' ' . $order_by;
500 }
501
502 /**
503 * Make sure this is ordering by either ASC or DESC
504 *
505 * @since 2.05.06
506 */
507 public static function esc_order_by( &$order_by ) {
508 $sort_options = array( 'asc', 'desc' );
509 if ( ! in_array( strtolower( $order_by ), $sort_options ) ) {
510 $order_by = 'asc';
511 }
512 }
513
514 /**
515 * @param string $limit
516 *
517 * @since 2.05.06
518 */
519 public static function esc_limit( $limit ) {
520 if ( empty( $limit ) ) {
521 return '';
522 }
523
524 $limit = trim( str_replace( 'limit ', '', strtolower( $limit ) ) );
525 if ( is_numeric( $limit ) ) {
526 return ' LIMIT ' . $limit;
527 }
528
529 $limit = explode( ',', trim( $limit ) );
530 foreach ( $limit as $k => $l ) {
531 if ( is_numeric( $l ) ) {
532 $limit[ $k ] = $l;
533 }
534 }
535
536 $limit = implode( ',', $limit );
537
538 return ' LIMIT ' . $limit;
539 }
540
541 /**
542 * Get an array of values ready to go through $wpdb->prepare
543 *
544 * @since 2.05.06
545 */
546 public static function prepare_array_values( $array, $type = '%s' ) {
547 $placeholders = array_fill( 0, count( $array ), $type );
548
549 return implode( ', ', $placeholders );
550 }
551
552 /**
553 * @since 2.05.06
554 */
555 public static function prepend_and_or_where( $starts_with = ' WHERE ', $where = '' ) {
556 if ( empty( $where ) ) {
557 return '';
558 }
559
560 if ( is_array( $where ) ) {
561 global $wpdb;
562 self::get_where_clause_and_values( $where, $starts_with );
563 $where = $wpdb->prepare( $where['where'], $where['values'] ); // WPCS: unprepared SQL ok.
564 } else {
565 $where = $starts_with . $where;
566 }
567
568 return $where;
569 }
570
571 /**
572 * Prepare and save settings in styles and actions
573 *
574 * @param array $settings
575 * @param string $group
576 *
577 * @since 2.05.06
578 */
579 public static function save_settings( $settings, $group ) {
580 $settings = (array) $settings;
581 $settings['post_content'] = FrmAppHelper::prepare_and_encode( $settings['post_content'] );
582
583 if ( empty( $settings['ID'] ) ) {
584 unset( $settings['ID'] );
585 }
586
587 // delete all caches for this group
588 self::cache_delete_group( $group );
589
590 return self::save_json_post( $settings );
591 }
592
593 /**
594 * Since actions are JSON encoded, we don't want any filters messing with it.
595 * Remove the filters and then add them back in case any posts or views are
596 * also being imported.
597 *
598 * Used when saving form actions and styles
599 *
600 * @since 2.05.06
601 */
602 public static function save_json_post( $settings ) {
603 global $wp_filter;
604 $filters = $wp_filter['content_save_pre'];
605
606 // Remove the balanceTags filter in case WordPress is trying to validate the XHTML
607 remove_all_filters( 'content_save_pre' );
608
609 $post = wp_insert_post( $settings );
610
611 // add the content filters back for views or posts
612 $wp_filter['content_save_pre'] = $filters;
613
614 return $post;
615 }
616
617 /**
618 * Check cache before fetching values and saving to cache
619 *
620 * @since 2.05.06
621 *
622 * @param string $cache_key The unique name for this cache
623 * @param string $group The name of the cache group
624 * @param string $query If blank, don't run a db call
625 * @param string $type The wpdb function to use with this query
626 *
627 * @return mixed $results The cache or query results
628 */
629 public static function check_cache( $cache_key, $group = '', $query = '', $type = 'get_var', $time = 300 ) {
630 $results = wp_cache_get( $cache_key, $group );
631 if ( ! FrmAppHelper::is_empty_value( $results, false ) || empty( $query ) ) {
632 return $results;
633 }
634
635 if ( 'get_posts' == $type ) {
636 $results = get_posts( $query );
637 } elseif ( 'get_associative_results' == $type ) {
638 global $wpdb;
639 $results = $wpdb->get_results( $query, OBJECT_K ); // WPCS: unprepared SQL ok.
640 } else {
641 global $wpdb;
642 $results = $wpdb->{$type}( $query );
643 }
644
645 self::set_cache( $cache_key, $results, $group, $time );
646
647 return $results;
648 }
649
650 /**
651 * @since 2.05.06
652 */
653 public static function set_cache( $cache_key, $results, $group = '', $time = 300 ) {
654 if ( ! FrmAppHelper::prevent_caching() ) {
655 self::add_key_to_group_cache( $cache_key, $group );
656 wp_cache_set( $cache_key, $results, $group, $time );
657 }
658 }
659
660 /**
661 * Keep track of the keys cached in each group so they can be deleted
662 * in Redis and Memcache
663 *
664 * @since 2.05.06
665 */
666 public static function add_key_to_group_cache( $key, $group ) {
667 $cached = self::get_group_cached_keys( $group );
668 $cached[ $key ] = $key;
669 wp_cache_set( 'cached_keys', $cached, $group, 300 );
670 }
671
672 /**
673 * @since 2.05.06
674 */
675 public static function get_group_cached_keys( $group ) {
676 $cached = wp_cache_get( 'cached_keys', $group );
677 if ( ! $cached || ! is_array( $cached ) ) {
678 $cached = array();
679 }
680
681 return $cached;
682 }
683
684 /**
685 * @since 2.05.06
686 *
687 * @param string $cache_key
688 */
689 public static function delete_cache_and_transient( $cache_key, $group = 'default' ) {
690 delete_transient( $cache_key );
691 wp_cache_delete( $cache_key, $group );
692 }
693
694 /**
695 * Delete all caching in a single group
696 *
697 * @since 2.05.06
698 *
699 * @param string $group The name of the cache group
700 */
701 public static function cache_delete_group( $group ) {
702 $cached_keys = self::get_group_cached_keys( $group );
703
704 if ( ! empty( $cached_keys ) ) {
705 foreach ( $cached_keys as $key ) {
706 wp_cache_delete( $key, $group );
707 }
708
709 wp_cache_delete( 'cached_keys', $group );
710 }
711 }
712
713 /**
714 * @deprecated 2.05.06
715 * @codeCoverageIgnore
716 */
717 public function upgrade() {
718 FrmDeprecated::upgrade();
719 }
720
721 /**
722 * @deprecated 2.05.06
723 * @codeCoverageIgnore
724 */
725 public function collation() {
726 return FrmDeprecated::collation();
727 }
728
729 /**
730 * @deprecated 2.05.06
731 * @codeCoverageIgnore
732 */
733 public function uninstall() {
734 FrmDeprecated::uninstall();
735 }
736 }
737