Completed
Push — master ( be35b6...ef56f2 )
by Jamie
03:21
created

FrmDb::generate_cache_key()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 4
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
class FrmDb {
4
    var $fields;
5
    var $forms;
6
    var $entries;
7
    var $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
        global $wpdb;
15
        $this->fields         = $wpdb->prefix . 'frm_fields';
16
        $this->forms          = $wpdb->prefix . 'frm_forms';
17
        $this->entries        = $wpdb->prefix . 'frm_items';
18
        $this->entry_metas    = $wpdb->prefix . 'frm_item_metas';
19
    }
20
21
    public function upgrade( $old_db_version = false ) {
22
	    do_action( 'frm_before_install' );
23
24
        global $wpdb;
25
        //$frm_db_version is the version of the database we're moving to
26
        $frm_db_version = FrmAppHelper::$db_version;
27
        $old_db_version = (float) $old_db_version;
28
        if ( ! $old_db_version ) {
29
            $old_db_version = get_option('frm_db_version');
30
        }
31
32
        if ( $frm_db_version != $old_db_version ) {
33
			// update rewrite rules for views and other custom post types
34
			flush_rewrite_rules();
35
36
			require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
37
38
            $this->create_tables();
39
            $this->migrate_data($frm_db_version, $old_db_version);
40
41
            /***** SAVE DB VERSION *****/
42
            update_option('frm_db_version', $frm_db_version);
43
44
            /**** ADD/UPDATE DEFAULT TEMPLATES ****/
45
            FrmXMLController::add_default_templates();
46
        }
47
48
        do_action('frm_after_install');
49
50
        /**** update the styling settings ****/
51
		if ( is_admin() && function_exists( 'get_filesystem_method' ) ) {
52
			$frm_style = new FrmStyle();
53
			$frm_style->update( 'default' );
54
		}
55
    }
56
57
    public function collation() {
58
        global $wpdb;
59
        if ( ! $wpdb->has_cap( 'collation' ) ) {
60
            return '';
61
        }
62
63
        $charset_collate = '';
64
		if ( ! empty( $wpdb->charset ) ) {
65
			$charset_collate .= ' DEFAULT CHARACTER SET ' . $wpdb->charset;
66
		}
67
68
		if ( ! empty( $wpdb->collate ) ) {
69
			$charset_collate .= ' COLLATE ' . $wpdb->collate;
70
		}
71
72
        return $charset_collate;
73
    }
74
75
    private function create_tables() {
76
        $charset_collate = $this->collation();
77
        $sql = array();
78
79
        /* Create/Upgrade Fields Table */
80
		$sql[] = 'CREATE TABLE ' . $this->fields . ' (
81
				id BIGINT(20) NOT NULL auto_increment,
82
				field_key varchar(100) default NULL,
83
                name text default NULL,
84
                description longtext default NULL,
85
                type text default NULL,
86
                default_value longtext default NULL,
87
                options longtext default NULL,
88
                field_order int(11) default 0,
89
                required int(1) default NULL,
90
                field_options longtext default NULL,
91
                form_id int(11) default NULL,
92
                created_at datetime NOT NULL,
93
                PRIMARY KEY  (id),
94
                KEY form_id (form_id),
95
                UNIQUE KEY field_key (field_key)
96
        )';
97
98
        /* Create/Upgrade Forms Table */
99
		$sql[] = 'CREATE TABLE ' . $this->forms . ' (
100
                id int(11) NOT NULL auto_increment,
101
				form_key varchar(100) default NULL,
102
                name varchar(255) default NULL,
103
                description text default NULL,
104
                parent_form_id int(11) default 0,
105
                logged_in tinyint(1) default NULL,
106
                editable tinyint(1) default NULL,
107
                is_template tinyint(1) default 0,
108
                default_template tinyint(1) default 0,
109
                status varchar(255) default NULL,
110
                options longtext default NULL,
111
                created_at datetime NOT NULL,
112
                PRIMARY KEY  (id),
113
                UNIQUE KEY form_key (form_key)
114
        )';
115
116
        /* Create/Upgrade Items Table */
117
		$sql[] = 'CREATE TABLE ' . $this->entries . ' (
118
				id BIGINT(20) NOT NULL auto_increment,
119
				item_key varchar(100) default NULL,
120
                name varchar(255) default NULL,
121
                description text default NULL,
122
                ip text default NULL,
123
				form_id BIGINT(20) default NULL,
124
				post_id BIGINT(20) default NULL,
125
				user_id BIGINT(20) default NULL,
126
				parent_item_id BIGINT(20) default 0,
127
				is_draft tinyint(1) default 0,
128
				updated_by BIGINT(20) default NULL,
129
                created_at datetime NOT NULL,
130
                updated_at datetime NOT NULL,
131
                PRIMARY KEY  (id),
132
                KEY form_id (form_id),
133
                KEY post_id (post_id),
134
                KEY user_id (user_id),
135
                KEY parent_item_id (parent_item_id),
136
                UNIQUE KEY item_key (item_key)
137
        )';
138
139
        /* Create/Upgrade Meta Table */
140
		$sql[] = 'CREATE TABLE ' . $this->entry_metas . ' (
141
				id BIGINT(20) NOT NULL auto_increment,
142
				meta_value longtext default NULL,
143
				field_id BIGINT(20) NOT NULL,
144
				item_id BIGINT(20) NOT NULL,
145
                created_at datetime NOT NULL,
146
                PRIMARY KEY  (id),
147
                KEY field_id (field_id),
148
                KEY item_id (item_id)
149
        )';
150
151
        foreach ( $sql as $q ) {
152
			if ( function_exists( 'dbDelta' ) ) {
153
				dbDelta( $q . $charset_collate . ';' );
154
			} else {
155
				global $wpdb;
156
				$wpdb->query( $q . $charset_collate );
157
			}
158
            unset($q);
159
        }
160
    }
161
162
    /**
163
     * @param integer $frm_db_version
164
	 * @param int $old_db_version
165
     */
166
	private function migrate_data( $frm_db_version, $old_db_version ) {
167
		$migrations = array( 4, 6, 11, 16, 17, 23, 25 );
168
        foreach ( $migrations as $migration ) {
169
            if ( $frm_db_version >= $migration && $old_db_version < $migration ) {
170
				$function_name = 'migrate_to_' . $migration;
171
                $this->$function_name();
172
            }
173
        }
174
    }
175
176
    /**
177
     * Change array into format $wpdb->prepare can use
178
	 *
179
	 * @param array $args
180
	 * @param string $starts_with
181
     */
182
    public static function get_where_clause_and_values( &$args, $starts_with = ' WHERE ' ) {
183
        if ( empty($args) ) {
184
			// add an arg to prevent prepare from failing
185
			$args = array( 'where' => $starts_with . '1=%d', 'values' => array( 1 ) );
186
			return;
187
        }
188
189
		$where = '';
190
		$values = array();
191
192
		if ( is_array( $args ) ) {
193
			$base_where = $starts_with;
194
			self::parse_where_from_array( $args, $base_where, $where, $values );
195
		}
196
197
		$args = compact( 'where', 'values' );
198
    }
199
200
    /**
201
	 * @param array $args
202
     * @param string $base_where
203
     * @param string $where
204
	 * @param array $values
205
     */
206
    public static function parse_where_from_array( $args, $base_where, &$where, &$values ) {
207
        $condition = ' AND';
208
        if ( isset( $args['or'] ) ) {
209
            $condition = ' OR';
210
            unset( $args['or'] );
211
        }
212
213
        foreach ( $args as $key => $value ) {
214
            $where .= empty( $where ) ? $base_where : $condition;
215
            $array_inc_null = ( ! is_numeric( $key ) && is_array( $value ) && in_array( null, $value ) );
216
            if ( is_numeric( $key ) || $array_inc_null ) {
217
                $where .= ' ( ';
218
                $nested_where = '';
219
                if ( $array_inc_null ) {
220
                    foreach ( $value as $val ) {
221
                        self::parse_where_from_array( array( $key => $val, 'or' => 1 ), '', $nested_where, $values );
222
                    }
223
                } else {
224
                    self::parse_where_from_array( $value, '', $nested_where, $values );
225
                }
226
                $where .= $nested_where;
227
                $where .= ' ) ';
228
            } else {
229
                self::interpret_array_to_sql( $key, $value, $where, $values );
230
            }
231
        }
232
    }
233
234
    /**
235
     * @param string $key
236
	 * @param string|array $value
237
     * @param string $where
238
	 * @param array $values
239
     */
240
    private static function interpret_array_to_sql( $key, $value, &$where, &$values ) {
241
		$key = trim( $key );
242
243
		if ( strpos( $key, 'created_at' ) !== false || strpos( $key, 'updated_at' ) !== false ) {
244
            $k = explode(' ', $key);
245
            $where .= ' DATE_FORMAT(' . reset( $k ) . ', %s) ' . str_replace( reset( $k ), '', $key );
246
            $values[] = '%Y-%m-%d %H:%i:%s';
247
        } else {
248
			$where .= ' ' . $key;
249
        }
250
251
		$lowercase_key = explode( ' ', strtolower( $key ) );
252
		$lowercase_key = end( $lowercase_key );
253
254
        if ( is_array( $value ) ) {
255
            // translate array of values to "in"
256
			if ( strpos( $lowercase_key, 'like' ) !== false ) {
257
				$where = preg_replace('/' . $key . '$/', '', $where);
258
				$where .= '(';
259
				$start = true;
260
				foreach ( $value as $v ) {
261
					if ( ! $start ) {
262
						$where .= ' OR ';
263
					}
264
					$start = false;
265
					$where .= $key . ' %s';
266
					$values[] = '%' . FrmAppHelper::esc_like( $v ) . '%';
267
				}
268
				$where .= ')';
269
			} else if ( ! empty( $value ) ) {
270
				$where .= ' in (' . FrmAppHelper::prepare_array_values( $value, '%s' ) . ')';
271
				$values = array_merge( $values, $value );
272
			}
273
        } else if ( strpos( $lowercase_key, 'like' ) !== false ) {
274
			/**
275
			 * Allow string to start or end with the value
276
			 * If the key is like% then skip the first % for starts with
277
			 * If the key is %like then skip the last % for ends with
278
			 */
279
			$start = '%';
280
			$end = '%';
281
			if ( $lowercase_key == 'like%' ) {
282
				$start = '';
283
				$where = rtrim( $where, '%' );
284
			} else if ( $lowercase_key == '%like' ) {
285
				$end = '';
286
				$where = rtrim( rtrim( $where, '%like' ), '%LIKE' );
287
				$where .= 'like';
288
			}
289
290
			$where .= ' %s';
291
			$values[] = $start . FrmAppHelper::esc_like( $value ) . $end;
292
293
        } else if ( $value === null ) {
294
            $where .= ' IS NULL';
295
        } else {
296
			// allow a - to prevent = from being added
297
			if ( substr( $key, -1 ) == '-' ) {
298
				$where = rtrim( $where, '-' );
299
			} else {
300
				$where .= '=';
301
			}
302
303
			self::add_query_placeholder( $key, $value, $where );
304
305
            $values[] = $value;
306
        }
307
    }
308
309
	/**
310
	 * Add %d, or %s to query
311
	 *
312
	 * @since 2.02.05
313
	 * @param string $key
314
	 * @param int|string $value
315
	 * @param string $where
316
	 */
317
    private static function add_query_placeholder( $key, $value, &$where ) {
318
		if ( is_numeric( $value ) && strpos( $key, 'meta_value' ) === false ) {
319
			$where .= '%d';
320
		} else {
321
			$where .= '%s';
322
		}
323
	}
324
325
    /**
326
     * @param string $table
327
	 * @param array $where
328
	 * @param array $args
329
	 * @return int
330
     */
331
    public static function get_count( $table, $where = array(), $args = array() ) {
332
        $count = self::get_var( $table, $where, 'COUNT(*)', $args );
333
        return $count;
334
    }
335
336
	/**
337
	 * @param string $table
338
	 * @param array $where
339
	 * @param string $field
340
	 * @param array $args
341
	 * @param string $limit
342
	 * @param string $type
343
	 * @return array|null|string|object
344
	 */
345
    public static function get_var( $table, $where = array(), $field = 'id', $args = array(), $limit = '', $type = 'var' ) {
346
        $group = '';
347
        self::get_group_and_table_name( $table, $group );
348
		self::convert_options_to_array( $args, '', $limit );
349
350
		$query = self::generate_query_string_from_pieces( $field, $table, $where, $args );
351
352
	    $cache_key = self::generate_cache_key( $where, $args, $field, $type );
353
		$results = FrmAppHelper::check_cache( $cache_key, $group, $query, 'get_' . $type );
354
        return $results;
355
    }
356
357
	/**
358
	 * Generate a cache key from the where query, field, type, and other arguments
359
	 *
360
	 * @since 2.03.07
361
	 *
362
	 * @param array $where
363
	 * @param array $args
364
	 * @param string $field
365
	 * @param string $type
366
	 *
367
	 * @return string
368
	 */
369
    private static function generate_cache_key( $where, $args, $field, $type ) {
370
	    $cache_key = '';
371
	    $where = FrmAppHelper::array_flatten( $where );
372
	    foreach ( $where as $key => $value ) {
373
			$cache_key .= $key . '_' . $value;
374
	    }
375
	    $cache_key .= implode( '_', $args ) . $field . '_' . $type;
376
	    $cache_key = str_replace( array( ' ', ',' ), '_', $cache_key );
377
378
	    return $cache_key;
379
    }
380
381
    /**
382
     * @param string $table
383
     * @param array $where
384
	 * @param string $field
385
	 * @param array $args
386
	 * @param string $limit
387
	 * @return mixed
388
     */
389
    public static function get_col( $table, $where = array(), $field = 'id', $args = array(), $limit = '' ) {
390
        return self::get_var( $table, $where, $field, $args, $limit, 'col' );
391
    }
392
393
    /**
394
     * @since 2.0
395
     * @param string $table
396
	 * @param array $where
397
	 * @param string $fields
398
	 * @param array $args
399
	 * @return mixed
400
     */
401
    public static function get_row( $table, $where = array(), $fields = '*', $args = array() ) {
402
        $args['limit'] = 1;
403
        return self::get_var( $table, $where, $fields, $args, '', 'row' );
404
    }
405
406
    /**
407
     * Prepare a key/value array before DB call
408
	 *
409
     * @since 2.0
410
     * @param string $table
411
	 * @param array $where
412
	 * @param string $fields
413
	 * @param array $args
414
	 * @return mixed
415
     */
416
    public static function get_results( $table, $where = array(), $fields = '*', $args = array() ) {
417
        return self::get_var( $table, $where, $fields, $args, '', 'results' );
418
    }
419
420
	/**
421
	 * Check for like, not like, in, not in, =, !=, >, <, <=, >=
422
	 * Return a value to append to the where array key
423
	 *
424
	 * @param string $where_is
425
	 * @return string
426
	 */
427
	public static function append_where_is( $where_is ) {
428
		$switch_to = array(
429
			'='		=> '',
430
			'!=' 	=> '!',
431
			'<='	=> '<',
432
			'>='	=> '>',
433
			'like'	=> 'like',
434
			'not like' => 'not like',
435
			'in'	=> '',
436
			'not in' => 'not',
437
			'like%'	=> 'like%',
438
			'%like'	=> '%like',
439
		);
440
441
		$where_is = strtolower( $where_is );
442
		if ( isset( $switch_to[ $where_is ] ) ) {
443
			return ' ' . $switch_to[ $where_is ];
444
		}
445
446
		// > and < need a little more work since we don't want them switched to >= and <=
447
		if ( $where_is == '>' || $where_is == '<' ) {
448
			return ' ' . $where_is . '-'; // the - indicates that the = should not be added later
449
		}
450
451
		// fallback to = if the query is none of these
452
		return '';
453
	}
454
455
    /**
456
     * Get 'frm_forms' from wp_frm_forms or a longer table param that includes a join
457
     * Also add the wpdb->prefix to the table if it's missing
458
     *
459
     * @param string $table
460
     * @param string $group
461
     */
462
    private static function get_group_and_table_name( &$table, &$group ) {
463
		global $wpdb, $wpmuBaseTablePrefix;
464
465
        $table_parts = explode(' ', $table);
466
        $group = reset($table_parts);
467
        $group = str_replace( $wpdb->prefix, '', $group );
468
469
		$prefix = $wpmuBaseTablePrefix ? $wpmuBaseTablePrefix : $wpdb->base_prefix;
470
		$group = str_replace( $prefix, '', $group );
471
472
        if ( $group == $table ) {
473
            $table = $wpdb->prefix . $table;
474
        }
475
476
		// switch to singular group name
477
		$group = rtrim( $group, 's' );
478
    }
479
480
    private static function convert_options_to_array( &$args, $order_by = '', $limit = '' ) {
481
        if ( ! is_array($args) ) {
482
			$args = array( 'order_by' => $args );
483
        }
484
485
        if ( ! empty( $order_by ) ) {
486
            $args['order_by'] = $order_by;
487
        }
488
489
        if ( ! empty( $limit ) ) {
490
            $args['limit'] = $limit;
491
        }
492
493
        $temp_args = $args;
494
        foreach ( $temp_args as $k => $v ) {
495
            if ( $v == '' ) {
496
				unset( $args[ $k ] );
497
                continue;
498
            }
499
500
            if ( $k == 'limit' ) {
501
				$args[ $k ] = FrmAppHelper::esc_limit( $v );
502
            }
503
            $db_name = strtoupper( str_replace( '_', ' ', $k ) );
504
            if ( strpos( $v, $db_name ) === false ) {
505
				$args[ $k ] = $db_name . ' ' . $v;
506
            }
507
        }
508
509
		// Make sure LIMIT is the last argument
510
		if ( isset( $args['order_by'] ) && isset( $args['limit'] ) ) {
511
			$temp_limit = $args['limit'];
512
			unset( $args['limit'] );
513
			$args['limit'] = $temp_limit;
514
		}
515
    }
516
517
	/**
518
	 * Get the associative array results for the given columns, table, and where query
519
	 *
520
	 * @since 2.02.05
521
	 * @param string $columns
522
	 * @param string $table
523
	 * @param array $where
524
	 * @return mixed
525
	 */
526
	public static function get_associative_array_results( $columns, $table, $where ) {
527
		$group = '';
528
		self::get_group_and_table_name( $table, $group );
529
530
		$query = self::generate_query_string_from_pieces( $columns, $table, $where );
531
532
		$cache_key = str_replace( array( ' ', ',' ), '_', trim( implode( '_', FrmAppHelper::array_flatten( $where ) ) . $columns . '_results_ARRAY_A' , ' WHERE' ) );
533
		$results = FrmAppHelper::check_cache( $cache_key, $group, $query, 'get_associative_results' );
534
535
		return $results;
536
	}
537
538
	/**
539
	 * Combine the pieces of a query to form a full, prepared query
540
	 *
541
	 * @since 2.02.05
542
	 *
543
	 * @param string $columns
544
	 * @param string $table
545
	 * @param mixed $where
546
	 * @param array $args
547
	 * @return string
548
	 */
549
	private static function generate_query_string_from_pieces( $columns, $table, $where, $args = array() ) {
550
		$query = 'SELECT ' . $columns . ' FROM ' . $table;
551
552
		if ( is_array( $where ) || empty( $where ) ) {
553
			self::get_where_clause_and_values( $where );
554
			global $wpdb;
555
			$query = $wpdb->prepare( $query . $where['where'] . ' ' . implode( ' ', $args ), $where['values'] );
556
		} else {
557
			/**
558
			 * Allow the $where to be prepared before we recieve it here.
559
			 * This is a fallback for reverse compatability, but is not recommended
560
			 */
561
			_deprecated_argument( 'where', '2.0', __( 'Use the query in an array format so it can be properly prepared.', 'formidable' ) );
562
			$query .= $where . ' ' . implode( ' ', $args );
563
		}
564
565
		return $query;
566
	}
567
568
    public function uninstall() {
569
		if ( ! current_user_can( 'administrator' ) ) {
570
            $frm_settings = FrmAppHelper::get_settings();
571
            wp_die($frm_settings->admin_permission);
572
        }
573
574
        global $wpdb, $wp_roles;
575
576
		$wpdb->query( 'DROP TABLE IF EXISTS ' . $this->fields );
577
		$wpdb->query( 'DROP TABLE IF EXISTS ' . $this->forms );
578
		$wpdb->query( 'DROP TABLE IF EXISTS ' . $this->entries );
579
		$wpdb->query( 'DROP TABLE IF EXISTS ' . $this->entry_metas );
580
581
        delete_option('frm_options');
582
        delete_option('frm_db_version');
583
584
        //delete roles
585
        $frm_roles = FrmAppHelper::frm_capabilities();
586
        $roles = get_editable_roles();
587
        foreach ( $frm_roles as $frm_role => $frm_role_description ) {
588
            foreach ( $roles as $role => $details ) {
589
                $wp_roles->remove_cap( $role, $frm_role );
590
                unset($role, $details);
591
    		}
592
    		unset($frm_role, $frm_role_description);
593
		}
594
		unset($roles, $frm_roles);
595
596
		// delete actions, views, and styles
597
598
		// prevent the post deletion from triggering entries to be deleted
599
		remove_action( 'before_delete_post', 'FrmProDisplaysController::before_delete_post' );
600
		remove_action( 'deleted_post', 'FrmProEntriesController::delete_entry' );
601
602
		$post_ids = $wpdb->get_col( $wpdb->prepare( 'SELECT ID FROM ' . $wpdb->posts . ' WHERE post_type in (%s, %s, %s)', FrmFormActionsController::$action_post_type, FrmStylesController::$post_type, 'frm_display' ) );
603
		foreach ( $post_ids as $post_id ) {
604
			// Delete's each post.
605
			wp_delete_post( $post_id, true );
606
		}
607
		unset( $post_ids );
608
609
		// delete transients
610
		delete_transient( 'frmpro_css' );
611
		delete_transient( 'frm_options' );
612
		delete_transient( 'frmpro_options' );
613
614
		$wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE %s OR option_name LIKE %s', '_transient_timeout_frm_form_fields_%', '_transient_frm_form_fields_%' ) );
615
616
        do_action('frm_after_uninstall');
617
        return true;
618
    }
619
620
	/**
621
	 * Migrate old styling settings. If sites are using the old
622
	 * default 400px field width, switch it to 100%
623
	 *
624
	 * @since 2.0.4
625
	 */
626
	private function migrate_to_25() {
627
		// get the style that was created with the style migration
628
		$frm_style = new FrmStyle();
629
		$styles = $frm_style->get_all( 'post_date', 'ASC', 1 );
630
		if ( empty( $styles ) ) {
631
			return;
632
		}
633
634
		foreach ( $styles as $style ) {
635
			if ( $style->post_content['field_width'] == '400px' ) {
636
				$style->post_content['field_width'] = '100%';
637
				$frm_style->save( (array) $style );
638
				return;
639
			}
640
		}
641
	}
642
643
	/**
644
	 * Check if the parent_form_id columns exists.
645
	 * If not, try and add it again
646
	 *
647
	 * @since 2.0.2
648
	 */
649
	private function migrate_to_23() {
650
		global $wpdb;
651
		$exists = $wpdb->get_row( 'SHOW COLUMNS FROM ' . $this->forms . ' LIKE "parent_form_id"' );
652
		if ( empty( $exists ) ) {
653
			$wpdb->query( 'ALTER TABLE ' . $this->forms . ' ADD parent_form_id int(11) default 0' );
654
		}
655
	}
656
657
    /**
658
     * Change field size from character to pixel -- Multiply by 9
659
     */
660
    private function migrate_to_17() {
661
        global $wpdb;
662
		$pixel_conversion = 9;
663
664
        // Get query arguments
665
		$field_types = array( 'textarea', 'text', 'number', 'email', 'url', 'rte', 'date', 'phone', 'password', 'image', 'tag', 'file' );
666
		$query = array( 'type' => $field_types, 'field_options like' => 's:4:"size";', 'field_options not like' => 's:4:"size";s:0:' );
667
668
        // Get results
669
		$fields = FrmDb::get_results( $this->fields, $query, 'id, field_options' );
670
671
        $updated = 0;
672
        foreach ( $fields as $f ) {
0 ignored issues
show
Bug introduced by
The expression $fields of type array|null|string|object is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
673
            $f->field_options = maybe_unserialize($f->field_options);
674
            if ( empty($f->field_options['size']) || ! is_numeric($f->field_options['size']) ) {
675
                continue;
676
            }
677
678
			$f->field_options['size'] = round( $pixel_conversion * (int) $f->field_options['size'] );
679
            $f->field_options['size'] .= 'px';
680
            $u = FrmField::update( $f->id, array( 'field_options' => $f->field_options ) );
681
            if ( $u ) {
682
                $updated++;
683
            }
684
            unset($f);
685
        }
686
687
        // Change the characters in widgets to pixels
688
        $widgets = get_option('widget_frm_show_form');
689
        if ( empty($widgets) ) {
690
            return;
691
        }
692
693
        $widgets = maybe_unserialize($widgets);
694
        foreach ( $widgets as $k => $widget ) {
695
            if ( ! is_array($widget) || ! isset($widget['size']) ) {
696
                continue;
697
            }
698
			$size = round( $pixel_conversion * (int) $widget['size'] );
699
            $size .= 'px';
700
			$widgets[ $k ]['size'] = $size;
701
        }
702
        update_option('widget_frm_show_form', $widgets);
703
    }
704
705
    /**
706
     * Migrate post and email notification settings into actions
707
     */
708
    private function migrate_to_16() {
709
        global $wpdb;
710
711
        $forms = FrmDb::get_results( $this->forms, array(), 'id, options, is_template, default_template' );
712
713
        /**
714
        * Old email settings format:
715
        * email_to: Email or field id
716
        * also_email_to: array of fields ids
717
        * reply_to: Email, field id, 'custom'
718
        * cust_reply_to: string
719
        * reply_to_name: field id, 'custom'
720
        * cust_reply_to_name: string
721
        * plain_text: 0|1
722
        * email_message: string or ''
723
        * email_subject: string or ''
724
        * inc_user_info: 0|1
725
        * update_email: 0, 1, 2
726
        *
727
        * Old autoresponder settings format:
728
        * auto_responder: 0|1
729
        * ar_email_message: string or ''
730
        * ar_email_to: field id
731
        * ar_plain_text: 0|1
732
        * ar_reply_to_name: string
733
        * ar_reply_to: string
734
        * ar_email_subject: string
735
        * ar_update_email: 0, 1, 2
736
        *
737
        * New email settings:
738
        * post_content: json settings
739
        * post_title: form id
740
        * post_excerpt: message
741
        *
742
        */
743
744
        foreach ( $forms as $form ) {
0 ignored issues
show
Bug introduced by
The expression $forms of type array|null|string|object is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
745
			if ( $form->is_template && $form->default_template ) {
746
				// don't migrate the default templates since the email will be added anyway
747
				continue;
748
			}
749
750
            // Format form options
751
            $form_options = maybe_unserialize($form->options);
752
753
            // Migrate settings to actions
754
            FrmXMLHelper::migrate_form_settings_to_actions( $form_options, $form->id );
755
        }
756
    }
757
758
    private function migrate_to_11() {
759
        global $wpdb;
760
761
        $forms = FrmDb::get_results( $this->forms, array(), 'id, options');
762
763
        $sending = __( 'Sending', 'formidable' );
764
		$img = FrmAppHelper::plugin_url() . '/images/ajax_loader.gif';
765
        $old_default_html = <<<DEFAULT_HTML
766
<div class="frm_submit">
767
[if back_button]<input type="submit" value="[back_label]" name="frm_prev_page" formnovalidate="formnovalidate" [back_hook] />[/if back_button]
768
<input type="submit" value="[button_label]" [button_action] />
769
<img class="frm_ajax_loading" src="$img" alt="$sending" style="visibility:hidden;" />
770
</div>
771
DEFAULT_HTML;
772
        unset($sending, $img);
773
774
        $new_default_html = FrmFormsHelper::get_default_html('submit');
775
        $draft_link = FrmFormsHelper::get_draft_link();
776
		foreach ( $forms as $form ) {
0 ignored issues
show
Bug introduced by
The expression $forms of type array|null|string|object is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
777
            $form->options = maybe_unserialize($form->options);
778
            if ( ! isset($form->options['submit_html']) || empty($form->options['submit_html']) ) {
779
                continue;
780
            }
781
782
            if ( $form->options['submit_html'] != $new_default_html && $form->options['submit_html'] == $old_default_html ) {
783
                $form->options['submit_html'] = $new_default_html;
784
				$wpdb->update( $this->forms, array( 'options' => serialize( $form->options ) ), array( 'id' => $form->id ) );
785
			} else if ( ! strpos( $form->options['submit_html'], 'save_draft' ) ) {
786
				$form->options['submit_html'] = preg_replace( '~\<\/div\>(?!.*\<\/div\>)~', $draft_link . "\r\n</div>", $form->options['submit_html'] );
787
				$wpdb->update( $this->forms, array( 'options' => serialize( $form->options ) ), array( 'id' => $form->id ) );
788
            }
789
            unset($form);
790
        }
791
        unset($forms);
792
    }
793
794
    private function migrate_to_6() {
795
        global $wpdb;
796
797
		$no_save = array_merge( FrmField::no_save_fields(), array( 'form', 'hidden', 'user_id' ) );
798
		$fields = FrmDb::get_results( $this->fields, array( 'type NOT' => $no_save ), 'id, field_options' );
799
800
        $default_html = <<<DEFAULT_HTML
801
<div id="frm_field_[id]_container" class="form-field [required_class] [error_class]">
802
    <label class="frm_pos_[label_position]">[field_name]
803
        <span class="frm_required">[required_label]</span>
804
    </label>
805
    [input]
806
    [if description]<div class="frm_description">[description]</div>[/if description]
807
</div>
808
DEFAULT_HTML;
809
810
        $old_default_html = <<<DEFAULT_HTML
811
<div id="frm_field_[id]_container" class="form-field [required_class] [error_class]">
812
    <label class="frm_pos_[label_position]">[field_name]
813
        <span class="frm_required">[required_label]</span>
814
    </label>
815
    [input]
816
    [if description]<p class="frm_description">[description]</p>[/if description]
817
</div>
818
DEFAULT_HTML;
819
820
        $new_default_html = FrmFieldsHelper::get_default_html('text');
821
        foreach ( $fields as $field ) {
0 ignored issues
show
Bug introduced by
The expression $fields of type array|null|string|object is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
822
            $field->field_options = maybe_unserialize($field->field_options);
823
			if ( ! FrmField::is_option_empty( $field, 'custom_html' ) || $field->field_options['custom_html'] == $default_html || $field->field_options['custom_html'] == $old_default_html ) {
824
                $field->field_options['custom_html'] = $new_default_html;
825
				$wpdb->update( $this->fields, array( 'field_options' => maybe_serialize( $field->field_options ) ), array( 'id' => $field->id ) );
826
            }
827
            unset($field);
828
        }
829
        unset($default_html, $old_default_html, $fields);
830
    }
831
832
    private function migrate_to_4() {
833
        global $wpdb;
834
		$user_ids = FrmEntryMeta::getAll( array( 'fi.type' => 'user_id' ) );
835
        foreach ( $user_ids as $user_id ) {
836
			$wpdb->update( $this->entries, array( 'user_id' => $user_id->meta_value ), array( 'id' => $user_id->item_id ) );
837
        }
838
    }
839
}
840