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

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