Completed
Push — master ( 02cc92...6df54b )
by Stephanie
02:42
created

FrmDb::esc_query_args()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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