Completed
Pull Request — master (#222)
by Stephanie
02:25
created

FrmForm   F

Complexity

Total Complexity 194

Size/Duplication

Total Lines 952
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 0
loc 952
rs 1.648
c 0
b 0
f 0
wmc 194
lcom 1
cbo 8

38 Methods

Rating   Name   Duplication   Size   Complexity  
F create() 0 37 11
C duplicate() 0 53 10
A after_duplicate() 0 16 3
C update() 0 42 14
B set_update_options() 0 18 8
F update_fields() 0 72 19
A sanitize_field_opt() 0 14 3
B get_settings_page_html() 0 17 8
B prepare_field_update_values() 0 21 6
A translatable_strings() 0 11 1
A set_status() 0 33 5
A trash() 0 42 4
A destroy() 0 34 4
B scheduled_delete() 0 26 6
A getName() 0 14 3
A get_id_by_key() 0 3 1
A get_key_by_id() 0 11 2
A maybe_get_form() 0 5 4
B getOne() 0 35 8
B getAll() 0 30 11
A get_published_forms() 0 11 2
B get_count() 0 46 6
A clear_form_cache() 0 3 1
A validate() 0 5 1
C get_params() 0 60 15
A list_page_params() 0 17 2
A get_admin_params() 0 27 5
A get_current_form_id() 0 10 3
A maybe_get_current_form() 0 14 6
A get_current_form() 0 8 2
A set_current_form() 0 12 2
A is_form_loaded() 0 17 6
A is_visible_to_user() 0 9 3
A show_submit() 0 6 3
A get_option() 0 6 3
A get_edit_link() 0 3 1
A getIdByKey() 0 3 1
A getKeyById() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like FrmForm often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FrmForm, and based on these observations, apply Extract Interface, too.

1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	die( 'You are not allowed to call this page directly.' );
4
}
5
6
class FrmForm {
7
8
	/**
9
	 * @return int|boolean id on success or false on failure
10
	 */
11
	public static function create( $values ) {
12
		global $wpdb;
13
14
		$new_values = array(
15
			'form_key'       => FrmAppHelper::get_unique_key( $values['form_key'], $wpdb->prefix . 'frm_forms', 'form_key' ),
16
			'name'           => $values['name'],
17
			'description'    => $values['description'],
18
			'status'         => isset( $values['status'] ) ? $values['status'] : 'published',
19
			'logged_in'      => isset( $values['logged_in'] ) ? $values['logged_in'] : 0,
20
			'is_template'    => isset( $values['is_template'] ) ? (int) $values['is_template'] : 0,
21
			'parent_form_id' => isset( $values['parent_form_id'] ) ? absint( $values['parent_form_id'] ) : 0,
22
			'editable'       => isset( $values['editable'] ) ? (int) $values['editable'] : 0,
23
			'created_at'     => isset( $values['created_at'] ) ? $values['created_at'] : current_time( 'mysql', 1 ),
24
		);
25
26
		$options = isset( $values['options'] ) ? (array) $values['options'] : array();
27
		FrmFormsHelper::fill_form_options( $options, $values );
28
29
		$options['before_html'] = isset( $values['options']['before_html'] ) ? $values['options']['before_html'] : FrmFormsHelper::get_default_html( 'before' );
30
		$options['after_html']  = isset( $values['options']['after_html'] ) ? $values['options']['after_html'] : FrmFormsHelper::get_default_html( 'after' );
31
		$options['submit_html'] = isset( $values['options']['submit_html'] ) ? $values['options']['submit_html'] : FrmFormsHelper::get_default_html( 'submit' );
32
33
		$options               = apply_filters( 'frm_form_options_before_update', $options, $values );
34
		$new_values['options'] = serialize( $options );
35
36
		//if(isset($values['id']) && is_numeric($values['id']))
37
		//    $new_values['id'] = $values['id'];
38
39
		$wpdb->insert( $wpdb->prefix . 'frm_forms', $new_values );
40
41
		$id = $wpdb->insert_id;
42
43
		// Clear form caching
44
		self::clear_form_cache();
45
46
		return $id;
47
	}
48
49
	/**
50
	 * @return int|boolean ID on success or false on failure
51
	 */
52
	public static function duplicate( $id, $template = false, $copy_keys = false, $blog_id = false ) {
53
		global $wpdb;
54
55
		$values = self::getOne( $id, $blog_id );
56
		if ( ! $values ) {
57
			return false;
58
		}
59
60
		$new_key = $copy_keys ? $values->form_key : '';
61
62
		$new_values = array(
63
			'form_key'    => FrmAppHelper::get_unique_key( $new_key, $wpdb->prefix . 'frm_forms', 'form_key' ),
64
			'name'        => $values->name,
65
			'description' => $values->description,
66
			'status'      => $values->status ? $values->status : 'published',
67
			'logged_in'   => $values->logged_in ? $values->logged_in : 0,
68
			'editable'    => $values->editable ? $values->editable : 0,
69
			'created_at'  => current_time( 'mysql', 1 ),
70
			'is_template' => $template ? 1 : 0,
71
		);
72
73
		if ( $blog_id ) {
74
			$new_values['status']    = 'published';
75
			$new_options             = $values->options;
76
			FrmAppHelper::unserialize_or_decode( $new_options );
77
			$new_options['email_to'] = get_option( 'admin_email' );
78
			$new_options['copy']     = false;
79
			$new_values['options']   = $new_options;
80
		} else {
81
			$new_values['options'] = $values->options;
82
		}
83
84
		if ( is_array( $new_values['options'] ) ) {
85
			$new_values['options'] = serialize( $new_values['options'] );
86
		}
87
88
		$query_results = $wpdb->insert( $wpdb->prefix . 'frm_forms', $new_values );
89
90
		if ( $query_results ) {
91
			// Clear form caching
92
			self::clear_form_cache();
93
94
			$form_id = $wpdb->insert_id;
95
			FrmField::duplicate( $id, $form_id, $copy_keys, $blog_id );
96
97
			// update form settings after fields are created
98
			do_action( 'frm_after_duplicate_form', $form_id, $new_values, array( 'old_id' => $id ) );
99
100
			return $form_id;
101
		}
102
103
		return false;
104
	}
105
106
	public static function after_duplicate( $form_id, $values ) {
107
		$new_opts          = $values['options'];
108
		FrmAppHelper::unserialize_or_decode( $new_opts );
109
		$values['options'] = $new_opts;
110
111
		if ( isset( $new_opts['success_msg'] ) ) {
112
			$new_opts['success_msg'] = FrmFieldsHelper::switch_field_ids( $new_opts['success_msg'] );
113
		}
114
115
		$new_opts = apply_filters( 'frm_after_duplicate_form_values', $new_opts, $form_id );
116
117
		if ( $new_opts != $values['options'] ) {
118
			global $wpdb;
119
			$wpdb->update( $wpdb->prefix . 'frm_forms', array( 'options' => maybe_serialize( $new_opts ) ), array( 'id' => $form_id ) );
120
		}
121
	}
122
123
	/**
124
	 * @return int|boolean
125
	 */
126
	public static function update( $id, $values, $create_link = false ) {
127
		global $wpdb;
128
129
		if ( ! isset( $values['status'] ) && ( $create_link || isset( $values['options'] ) || isset( $values['item_meta'] ) || isset( $values['field_options'] ) ) ) {
130
			$values['status'] = 'published';
131
		}
132
133
		if ( isset( $values['form_key'] ) ) {
134
			$values['form_key'] = FrmAppHelper::get_unique_key( $values['form_key'], $wpdb->prefix . 'frm_forms', 'form_key', $id );
135
		}
136
137
		$form_fields = array( 'form_key', 'name', 'description', 'status', 'parent_form_id' );
138
139
		$new_values = self::set_update_options( array(), $values );
140
141
		foreach ( $values as $value_key => $value ) {
142
			if ( $value_key && in_array( $value_key, $form_fields ) ) {
143
				$new_values[ $value_key ] = $value;
144
			}
145
		}
146
147
		if ( isset( $values['new_status'] ) && ! empty( $values['new_status'] ) ) {
148
			$new_values['status'] = $values['new_status'];
149
		}
150
151
		if ( ! empty( $new_values ) ) {
152
			$query_results = $wpdb->update( $wpdb->prefix . 'frm_forms', $new_values, array( 'id' => $id ) );
153
			if ( $query_results ) {
154
				self::clear_form_cache();
155
			}
156
		} else {
157
			$query_results = true;
158
		}
159
		unset( $new_values );
160
161
		$values = self::update_fields( $id, $values );
162
163
		do_action( 'frm_update_form', $id, $values );
164
		do_action( 'frm_update_form_' . $id, $values );
165
166
		return $query_results;
167
	}
168
169
	/**
170
	 * @return array
171
	 */
172
	public static function set_update_options( $new_values, $values ) {
173
		if ( ! isset( $values['options'] ) ) {
174
			return $new_values;
175
		}
176
177
		$options = isset( $values['options'] ) ? (array) $values['options'] : array();
178
		FrmFormsHelper::fill_form_options( $options, $values );
179
180
		$options['custom_style'] = isset( $values['options']['custom_style'] ) ? $values['options']['custom_style'] : 0;
181
		$options['before_html']  = isset( $values['options']['before_html'] ) ? $values['options']['before_html'] : FrmFormsHelper::get_default_html( 'before' );
182
		$options['after_html']   = isset( $values['options']['after_html'] ) ? $values['options']['after_html'] : FrmFormsHelper::get_default_html( 'after' );
183
		$options['submit_html']  = ( isset( $values['options']['submit_html'] ) && '' !== $values['options']['submit_html'] ) ? $values['options']['submit_html'] : FrmFormsHelper::get_default_html( 'submit' );
184
185
		$options               = apply_filters( 'frm_form_options_before_update', $options, $values );
186
		$new_values['options'] = serialize( $options );
187
188
		return $new_values;
189
	}
190
191
	/**
192
	 * @return array
193
	 */
194
	public static function update_fields( $id, $values ) {
195
196
		if ( ! isset( $values['item_meta'] ) && ! isset( $values['field_options'] ) ) {
197
			return $values;
198
		}
199
200
		$all_fields = FrmField::get_all_for_form( $id );
201
		if ( empty( $all_fields ) ) {
202
			return $values;
203
		}
204
205
		if ( ! isset( $values['item_meta'] ) ) {
206
			$values['item_meta'] = array();
207
		}
208
209
		$field_array   = array();
210
		$existing_keys = array_keys( $values['item_meta'] );
211
		foreach ( $all_fields as $fid ) {
212
			if ( ! in_array( $fid->id, $existing_keys ) && ( isset( $values['frm_fields_submitted'] ) && in_array( $fid->id, $values['frm_fields_submitted'] ) ) || isset( $values['options'] ) ) {
213
				$values['item_meta'][ $fid->id ] = '';
214
			}
215
			$field_array[ $fid->id ] = $fid;
216
		}
217
		unset( $all_fields );
218
219
		foreach ( $values['item_meta'] as $field_id => $default_value ) {
220
			if ( isset( $field_array[ $field_id ] ) ) {
221
				$field = $field_array[ $field_id ];
222
			} else {
223
				$field = FrmField::getOne( $field_id );
224
			}
225
226
			if ( ! $field ) {
227
				continue;
228
			}
229
230
			$is_settings_page = ( isset( $values['options'] ) || isset( $values['field_options'][ 'custom_html_' . $field_id ] ) );
231
			if ( $is_settings_page ) {
232
				self::get_settings_page_html( $values, $field );
233
234
				if ( ! defined( 'WP_IMPORTING' ) ) {
235
					continue;
236
				}
237
			}
238
239
			//updating the form
240
			$update_options = FrmFieldsHelper::get_default_field_options_from_field( $field );
241
			unset( $update_options['custom_html'] ); // don't check for POST html
242
			$update_options = apply_filters( 'frm_field_options_to_update', $update_options );
243
244
			foreach ( $update_options as $opt => $default ) {
245
				$field->field_options[ $opt ] = isset( $values['field_options'][ $opt . '_' . $field_id ] ) ? $values['field_options'][ $opt . '_' . $field_id ] : $default;
246
				self::sanitize_field_opt( $opt, $field->field_options[ $opt ] );
247
			}
248
249
			$field->field_options = apply_filters( 'frm_update_field_options', $field->field_options, $field, $values );
250
251
			$new_field = array(
252
				'field_options' => $field->field_options,
253
				'default_value' => isset( $values[ 'default_value_' . $field_id ] ) ? FrmAppHelper::maybe_json_encode( $values[ 'default_value_' . $field_id ] ) : '',
254
			);
255
256
			self::prepare_field_update_values( $field, $values, $new_field );
257
258
			FrmField::update( $field_id, $new_field );
259
260
			FrmField::delete_form_transient( $field->form_id );
261
		}
262
		self::clear_form_cache();
263
264
		return $values;
265
	}
266
267
	private static function sanitize_field_opt( $opt, &$value ) {
268
		if ( is_string( $value ) ) {
269
			if ( $opt === 'calc' ) {
270
				$allow = array( '<= ', ' >=' ); // Allow <= and >=
271
				$temp  = array( '< = ', ' > =' );
272
				$value = str_replace( $allow, $temp, $value );
273
				$value = strip_tags( $value );
274
				$value = str_replace( $temp, $allow, $value );
275
			} else {
276
				$value = FrmAppHelper::kses( $value, 'all' );
277
			}
278
			$value = trim( $value );
279
		}
280
	}
281
282
	/**
283
	 * Updating the settings page
284
	 */
285
	private static function get_settings_page_html( $values, &$field ) {
286
		if ( isset( $values['field_options'][ 'custom_html_' . $field->id ] ) ) {
287
			$prev_opts     = array();
288
			$fallback_html = isset( $field->field_options['custom_html'] ) ? $field->field_options['custom_html'] : FrmFieldsHelper::get_default_html( $field->type );
289
290
			$field->field_options['custom_html'] = isset( $values['field_options'][ 'custom_html_' . $field->id ] ) ? $values['field_options'][ 'custom_html_' . $field->id ] : $fallback_html;
291
		} elseif ( $field->type == 'hidden' || $field->type == 'user_id' ) {
292
			$prev_opts = $field->field_options;
293
		}
294
295
		if ( isset( $prev_opts ) ) {
296
			$field->field_options = apply_filters( 'frm_update_form_field_options', $field->field_options, $field, $values );
297
			if ( $prev_opts != $field->field_options ) {
298
				FrmField::update( $field->id, array( 'field_options' => $field->field_options ) );
299
			}
300
		}
301
	}
302
303
	private static function prepare_field_update_values( $field, $values, &$new_field ) {
304
		$field_cols = array(
305
			'field_order' => 0,
306
			'field_key'   => '',
307
			'required'    => false,
308
			'type'        => '',
309
			'description' => '',
310
			'options'     => '',
311
			'name'        => '',
312
		);
313
		foreach ( $field_cols as $col => $default ) {
314
			$default = ( $default === '' ) ? $field->{$col} : $default;
315
316
			$new_field[ $col ] = isset( $values['field_options'][ $col . '_' . $field->id ] ) ? $values['field_options'][ $col . '_' . $field->id ] : $default;
317
		}
318
319
		// Don't save the template option.
320
		if ( is_array( $new_field['options'] ) && isset( $new_field['options']['000'] ) ) {
321
			unset( $new_field['options']['000'] );
322
		}
323
	}
324
325
	/**
326
	 * Get a list of all form settings that should be translated
327
	 * on a multilingual site.
328
	 *
329
	 * @since 3.06.01
330
	 * @param object $form - The form object
331
	 */
332
	public static function translatable_strings( $form ) {
333
		$strings = array(
334
			'name',
335
			'description',
336
			'submit_value',
337
			'submit_msg',
338
			'success_msg',
339
		);
340
341
		return apply_filters( 'frm_form_strings', $strings, $form );
342
	}
343
344
	/**
345
	 * @param string $status
346
	 *
347
	 * @return int|boolean
348
	 */
349
	public static function set_status( $id, $status ) {
350
		if ( 'trash' == $status ) {
351
			return self::trash( $id );
352
		}
353
354
		$statuses = array( 'published', 'draft', 'trash' );
355
		if ( ! in_array( $status, $statuses ) ) {
356
			return false;
357
		}
358
359
		global $wpdb;
360
361
		if ( is_array( $id ) ) {
362
			$where = array(
363
				'id'             => $id,
364
				'parent_form_id' => $id,
365
				'or'             => 1,
366
			);
367
			FrmDb::get_where_clause_and_values( $where );
368
			array_unshift( $where['values'], $status );
369
370
			$query_results = $wpdb->query( $wpdb->prepare( 'UPDATE ' . $wpdb->prefix . 'frm_forms SET status = %s ' . $where['where'], $where['values'] ) ); // WPCS: unprepared SQL ok.
371
		} else {
372
			$query_results = $wpdb->update( $wpdb->prefix . 'frm_forms', array( 'status' => $status ), array( 'id' => $id ) );
373
			$wpdb->update( $wpdb->prefix . 'frm_forms', array( 'status' => $status ), array( 'parent_form_id' => $id ) );
374
		}
375
376
		if ( $query_results ) {
377
			self::clear_form_cache();
378
		}
379
380
		return $query_results;
381
	}
382
383
	/**
384
	 * @return int|boolean
385
	 */
386
	public static function trash( $id ) {
387
		if ( ! EMPTY_TRASH_DAYS ) {
388
			return self::destroy( $id );
389
		}
390
391
		$form = self::getOne( $id );
392
		if ( ! $form ) {
393
			return false;
394
		}
395
396
		$options               = $form->options;
397
		$options['trash_time'] = time();
398
399
		global $wpdb;
400
		$query_results = $wpdb->update(
401
			$wpdb->prefix . 'frm_forms',
402
			array(
403
				'status'  => 'trash',
404
				'options' => serialize( $options ),
405
			),
406
			array(
407
				'id' => $id,
408
			)
409
		);
410
411
		$wpdb->update(
412
			$wpdb->prefix . 'frm_forms',
413
			array(
414
				'status'  => 'trash',
415
				'options' => serialize( $options ),
416
			),
417
			array(
418
				'parent_form_id' => $id,
419
			)
420
		);
421
422
		if ( $query_results ) {
423
			self::clear_form_cache();
424
		}
425
426
		return $query_results;
427
	}
428
429
	/**
430
	 * @return int|boolean
431
	 */
432
	public static function destroy( $id ) {
433
		global $wpdb;
434
435
		$form = self::getOne( $id );
436
		if ( ! $form ) {
437
			return false;
438
		}
439
		$id = $form->id;
440
441
		// Disconnect the entries from this form
442
		$entries = FrmDb::get_col( $wpdb->prefix . 'frm_items', array( 'form_id' => $id ) );
443
		foreach ( $entries as $entry_id ) {
0 ignored issues
show
Bug introduced by
The expression $entries 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...
444
			FrmEntry::destroy( $entry_id );
445
			unset( $entry_id );
446
		}
447
448
		// Disconnect the fields from this form
449
		$wpdb->query( $wpdb->prepare( 'DELETE fi FROM ' . $wpdb->prefix . 'frm_fields AS fi LEFT JOIN ' . $wpdb->prefix . 'frm_forms fr ON (fi.form_id = fr.id) WHERE fi.form_id=%d OR parent_form_id=%d', $id, $id ) );
450
451
		$query_results = $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_forms WHERE id=%d OR parent_form_id=%d', $id, $id ) );
452
		if ( $query_results ) {
453
			// Delete all form actions linked to this form
454
			$action_control = FrmFormActionsController::get_form_actions( 'email' );
455
			$action_control->destroy( $id, 'all' );
456
457
			// Clear form caching
458
			self::clear_form_cache();
459
460
			do_action( 'frm_destroy_form', $id );
461
			do_action( 'frm_destroy_form_' . $id );
462
		}
463
464
		return $query_results;
465
	}
466
467
	/**
468
	 * Delete trashed forms based on how long they have been trashed
469
	 *
470
	 * @return int The number of forms deleted
471
	 */
472
	public static function scheduled_delete( $delete_timestamp = '' ) {
473
		global $wpdb;
474
475
		$trash_forms = FrmDb::get_results( $wpdb->prefix . 'frm_forms', array( 'status' => 'trash' ), 'id, options' );
476
477
		if ( ! $trash_forms ) {
478
			return;
479
		}
480
481
		if ( empty( $delete_timestamp ) ) {
482
			$delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS );
483
		}
484
485
		$count = 0;
486
		foreach ( $trash_forms as $form ) {
0 ignored issues
show
Bug introduced by
The expression $trash_forms of type array|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...
487
			FrmAppHelper::unserialize_or_decode( $form->options );
488
			if ( ! isset( $form->options['trash_time'] ) || $form->options['trash_time'] < $delete_timestamp ) {
489
				self::destroy( $form->id );
490
				$count ++;
491
			}
492
493
			unset( $form );
494
		}
495
496
		return $count;
497
	}
498
499
	/**
500
	 * @return string form name
501
	 */
502
	public static function getName( $id ) {
503
		$form = FrmDb::check_cache( $id, 'frm_form' );
504
		if ( $form ) {
505
			$r = stripslashes( $form->name );
506
507
			return $r;
508
		}
509
510
		$query_key = is_numeric( $id ) ? 'id' : 'form_key';
511
		$r         = FrmDb::get_var( 'frm_forms', array( $query_key => $id ), 'name' );
512
		$r         = stripslashes( $r );
513
514
		return $r;
515
	}
516
517
	/**
518
	 * @since 3.0
519
	 *
520
	 * @param string $key
521
	 *
522
	 * @return int form id
523
	 */
524
	public static function get_id_by_key( $key ) {
525
		return (int) FrmDb::get_var( 'frm_forms', array( 'form_key' => sanitize_title( $key ) ) );
526
	}
527
528
	/**
529
	 * @since 3.0
530
	 *
531
	 * @param int $id
532
	 *
533
	 * @return string form key
534
	 */
535
	public static function get_key_by_id( $id ) {
536
		$id    = (int) $id;
537
		$cache = FrmDb::check_cache( $id, 'frm_form' );
538
		if ( $cache ) {
539
			return $cache->form_key;
540
		}
541
542
		$key = FrmDb::get_var( 'frm_forms', array( 'id' => $id ), 'form_key' );
543
544
		return $key;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $key; (array|null|string|object) is incompatible with the return type documented by FrmForm::get_key_by_id of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
545
	}
546
547
	/**
548
	 * If $form is numeric, get the form object
549
	 *
550
	 * @param object|int $form
551
	 *
552
	 * @since 2.0.9
553
	 */
554
	public static function maybe_get_form( &$form ) {
555
		if ( ! is_object( $form ) && ! is_array( $form ) && ! empty( $form ) ) {
556
			$form = self::getOne( $form );
557
		}
558
	}
559
560
	/**
561
	 * @return object form
562
	 */
563
	public static function getOne( $id, $blog_id = false ) {
564
		global $wpdb;
565
566
		if ( $blog_id && is_multisite() ) {
567
			global $wpmuBaseTablePrefix;
568
			$prefix = $wpmuBaseTablePrefix ? $wpmuBaseTablePrefix . $blog_id . '_' : $wpdb->get_blog_prefix( $blog_id );
569
570
			$table_name = $prefix . 'frm_forms';
571
		} else {
572
			$table_name = $wpdb->prefix . 'frm_forms';
573
			$cache      = wp_cache_get( $id, 'frm_form' );
574
			if ( $cache ) {
575
				if ( isset( $cache->options ) ) {
576
					FrmAppHelper::unserialize_or_decode( $cache->options );
577
				}
578
579
				return wp_unslash( $cache );
580
			}
581
		}
582
583
		if ( is_numeric( $id ) ) {
584
			$where = array( 'id' => $id );
585
		} else {
586
			$where = array( 'form_key' => $id );
587
		}
588
589
		$results = FrmDb::get_row( $table_name, $where );
590
591
		if ( isset( $results->options ) ) {
592
			FrmDb::set_cache( $results->id, $results, 'frm_form' );
593
			FrmAppHelper::unserialize_or_decode( $results->options );
594
		}
595
596
		return apply_filters( 'frm_form_object', wp_unslash( $results ) );
597
	}
598
599
	/**
600
	 * @return object|array of objects
601
	 */
602
	public static function getAll( $where = array(), $order_by = '', $limit = '' ) {
603
		if ( is_array( $where ) && ! empty( $where ) ) {
604
			if ( isset( $where['is_template'] ) && $where['is_template'] && ! isset( $where['status'] ) && ! isset( $where['status !'] ) ) {
605
				// don't get trashed templates
606
				$where['status'] = array( null, '', 'published' );
607
			}
608
609
			$results = FrmDb::get_results( 'frm_forms', $where, '*', compact( 'order_by', 'limit' ) );
610
		} else {
611
			global $wpdb;
612
613
			// the query has already been prepared if this is not an array
614
			$query   = 'SELECT * FROM ' . $wpdb->prefix . 'frm_forms' . FrmDb::prepend_and_or_where( ' WHERE ', $where ) . FrmDb::esc_order( $order_by ) . FrmDb::esc_limit( $limit );
615
			$results = $wpdb->get_results( $query ); // WPCS: unprepared SQL ok.
616
		}
617
618
		if ( $results ) {
619
			foreach ( $results as $result ) {
620
				FrmDb::set_cache( $result->id, $result, 'frm_form' );
621
				FrmAppHelper::unserialize_or_decode( $result->options );
622
			}
623
		}
624
625
		if ( $limit == ' LIMIT 1' || $limit == 1 ) {
626
			// return the first form object if we are only getting one form
627
			$results = reset( $results );
628
		}
629
630
		return wp_unslash( $results );
631
	}
632
633
	/**
634
	 * Get all published forms
635
	 *
636
	 * @since 2.0
637
	 * @return array of forms
638
	 */
639
	public static function get_published_forms( $query = array(), $limit = 999, $inc_children = 'exclude' ) {
640
		$query['is_template'] = 0;
641
		$query['status']      = array( null, '', 'published' );
642
		if ( $inc_children == 'exclude' ) {
643
			$query['parent_form_id'] = array( null, 0 );
644
		}
645
646
		$forms = self::getAll( $query, 'name', $limit );
647
648
		return $forms;
649
	}
650
651
	/**
652
	 * @return object count of forms
653
	 */
654
	public static function get_count() {
655
		global $wpdb;
656
657
		$cache_key = 'frm_form_counts';
658
659
		$counts = wp_cache_get( $cache_key, 'frm_form' );
660
		if ( false !== $counts ) {
661
			return $counts;
662
		}
663
664
		$results = (array) FrmDb::get_results(
665
			'frm_forms',
666
			array(
667
				'or'               => 1,
668
				'parent_form_id'   => null,
669
				'parent_form_id <' => 0,
670
			),
671
			'status, is_template'
672
		);
673
674
		$statuses = array( 'published', 'draft', 'template', 'trash' );
675
		$counts   = array_fill_keys( $statuses, 0 );
676
677
		foreach ( $results as $row ) {
678
			if ( 'trash' != $row->status ) {
679
				if ( $row->is_template ) {
680
					$counts['template'] ++;
681
				} else {
682
					$counts['published'] ++;
683
				}
684
			} else {
685
				$counts['trash'] ++;
686
			}
687
688
			if ( 'draft' == $row->status ) {
689
				$counts['draft'] ++;
690
			}
691
692
			unset( $row );
693
		}
694
695
		$counts = (object) $counts;
696
		FrmDb::set_cache( $cache_key, $counts, 'frm_form' );
697
698
		return $counts;
699
	}
700
701
	/**
702
	 * Clear form caching
703
	 * Called when a form is created, updated, duplicated, or deleted
704
	 * or when the form status is changed
705
	 *
706
	 * @since 2.0.4
707
	 */
708
	public static function clear_form_cache() {
709
		FrmDb::cache_delete_group( 'frm_form' );
710
	}
711
712
	/**
713
	 * @return array of errors
714
	 */
715
	public static function validate( $values ) {
716
		$errors = array();
717
718
		return apply_filters( 'frm_validate_form', $errors, $values );
719
	}
720
721
	public static function get_params( $form = null ) {
722
		global $frm_vars;
723
724
		if ( ! $form ) {
725
			$form = self::getAll( array(), 'name', 1 );
726
		} else {
727
			self::maybe_get_form( $form );
728
		}
729
730
		if ( isset( $frm_vars['form_params'] ) && is_array( $frm_vars['form_params'] ) && isset( $frm_vars['form_params'][ $form->id ] ) ) {
731
			return $frm_vars['form_params'][ $form->id ];
732
		}
733
734
		$action_var = isset( $_REQUEST['frm_action'] ) ? 'frm_action' : 'action'; // WPCS: CSRF ok.
735
		$action     = apply_filters( 'frm_show_new_entry_page', FrmAppHelper::get_param( $action_var, 'new', 'get', 'sanitize_title' ), $form );
736
737
		$default_values = array(
738
			'id'        => '',
739
			'form_name' => '',
740
			'paged'     => 1,
741
			'form'      => $form->id,
742
			'form_id'   => $form->id,
743
			'field_id'  => '',
744
			'search'    => '',
745
			'sort'      => '',
746
			'sdir'      => '',
747
			'action'    => $action,
748
		);
749
750
		$values                   = array();
751
		$values['posted_form_id'] = FrmAppHelper::get_param( 'form_id', '', 'get', 'absint' );
752
		if ( ! $values['posted_form_id'] ) {
753
			$values['posted_form_id'] = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
754
		}
755
756
		if ( $form->id == $values['posted_form_id'] ) {
757
			//if there are two forms on the same page, make sure not to submit both
758
			foreach ( $default_values as $var => $default ) {
759
				if ( $var == 'action' ) {
760
					$values[ $var ] = FrmAppHelper::get_param( $action_var, $default, 'get', 'sanitize_title' );
761
				} else {
762
					$values[ $var ] = FrmAppHelper::get_param( $var, $default, 'get', 'sanitize_text_field' );
763
				}
764
				unset( $var, $default );
765
			}
766
		} else {
767
			foreach ( $default_values as $var => $default ) {
768
				$values[ $var ] = $default;
769
				unset( $var, $default );
770
			}
771
		}
772
773
		if ( in_array( $values['action'], array( 'create', 'update' ) ) &&
774
			( ! $_POST || ( ! isset( $_POST['action'] ) && ! isset( $_POST['frm_action'] ) ) ) // WPCS: CSRF ok.
775
			) {
776
			$values['action'] = 'new';
777
		}
778
779
		return $values;
780
	}
781
782
	public static function list_page_params() {
783
		$values   = array();
784
		$defaults = array(
785
			'template' => 0,
786
			'id'       => '',
787
			'paged'    => 1,
788
			'form'     => '',
789
			'search'   => '',
790
			'sort'     => '',
791
			'sdir'     => '',
792
		);
793
		foreach ( $defaults as $var => $default ) {
794
			$values[ $var ] = FrmAppHelper::get_param( $var, $default, 'get', 'sanitize_text_field' );
795
		}
796
797
		return $values;
798
	}
799
800
	public static function get_admin_params( $form = null ) {
801
		$form_id = $form;
802
		if ( $form === null ) {
803
			$form_id = self::get_current_form_id();
804
		} elseif ( $form && is_object( $form ) ) {
805
			$form_id = $form->id;
806
		}
807
808
		$values   = array();
809
		$defaults = array(
810
			'id'        => '',
811
			'form_name' => '',
812
			'paged'     => 1,
813
			'form'      => $form_id,
814
			'field_id'  => '',
815
			'search'    => '',
816
			'sort'      => '',
817
			'sdir'      => '',
818
			'fid'       => '',
819
			'keep_post' => '',
820
		);
821
		foreach ( $defaults as $var => $default ) {
822
			$values[ $var ] = FrmAppHelper::get_param( $var, $default, 'get', 'sanitize_text_field' );
823
		}
824
825
		return $values;
826
	}
827
828
	public static function get_current_form_id( $default_form = 'none' ) {
829
		if ( 'first' == $default_form ) {
830
			$form = self::get_current_form();
831
		} else {
832
			$form = self::maybe_get_current_form();
833
		}
834
		$form_id = $form ? $form->id : 0;
835
836
		return $form_id;
837
	}
838
839
	public static function maybe_get_current_form( $form_id = 0 ) {
840
		global $frm_vars;
841
842
		if ( isset( $frm_vars['current_form'] ) && $frm_vars['current_form'] && ( ! $form_id || $form_id == $frm_vars['current_form']->id ) ) {
843
			return $frm_vars['current_form'];
844
		}
845
846
		$form_id = FrmAppHelper::get_param( 'form', $form_id, 'get', 'absint' );
847
		if ( $form_id ) {
848
			$form_id = self::set_current_form( $form_id );
849
		}
850
851
		return $form_id;
852
	}
853
854
	public static function get_current_form( $form_id = 0 ) {
855
		$form = self::maybe_get_current_form( $form_id );
856
		if ( is_numeric( $form ) ) {
857
			$form = self::set_current_form( $form );
858
		}
859
860
		return $form;
861
	}
862
863
	public static function set_current_form( $form_id ) {
864
		global $frm_vars;
865
866
		$query = array();
867
		if ( $form_id ) {
868
			$query['id'] = $form_id;
869
		}
870
871
		$frm_vars['current_form'] = self::get_published_forms( $query, 1 );
872
873
		return $frm_vars['current_form'];
874
	}
875
876
	public static function is_form_loaded( $form, $this_load, $global_load ) {
877
		global $frm_vars;
878
		$small_form = new stdClass();
879
		foreach ( array( 'id', 'form_key', 'name' ) as $var ) {
880
			$small_form->{$var} = $form->{$var};
881
			unset( $var );
882
		}
883
884
		$frm_vars['forms_loaded'][] = $small_form;
885
886
		if ( $this_load && empty( $global_load ) ) {
887
			$global_load          = true;
888
			$frm_vars['load_css'] = true;
889
		}
890
891
		return ( ( ! isset( $frm_vars['css_loaded'] ) || ! $frm_vars['css_loaded'] ) && $global_load );
892
	}
893
894
	/**
895
	 * @since 4.06.03
896
	 *
897
	 * @param object $form
898
	 *
899
	 * @return bool
900
	 */
901
	public static function &is_visible_to_user( $form ) {
902
		if ( $form->logged_in && isset( $form->options['logged_in_role'] ) ) {
903
			$visible = FrmAppHelper::user_has_permission( $form->options['logged_in_role'] );
904
		} else {
905
			$visible = true;
906
		}
907
908
		return $visible;
909
	}
910
911
	public static function show_submit( $form ) {
912
		$show = ( ! $form->is_template && $form->status == 'published' && ! FrmAppHelper::is_admin() );
913
		$show = apply_filters( 'frm_show_submit_button', $show, $form );
914
915
		return $show;
916
	}
917
918
	/**
919
	 * @since 2.3
920
	 */
921
	public static function get_option( $atts ) {
922
		$form = $atts['form'];
923
		$default = isset( $atts['default'] ) ? $atts['default'] : '';
924
925
		return isset( $form->options[ $atts['option'] ] ) ? $form->options[ $atts['option'] ] : $default;
926
	}
927
928
	/**
929
	 * Get the link to edit this form.
930
	 *
931
	 * @since 4.0
932
	 * @param int $form_id The id of the form.
933
	 */
934
	public static function get_edit_link( $form_id ) {
935
		return admin_url( 'admin.php?page=formidable&frm_action=edit&id=' . $form_id );
936
	}
937
938
	/**
939
	 * @deprecated 3.0
940
	 * @codeCoverageIgnore
941
	 *
942
	 * @param string $key
943
	 *
944
	 * @return int form id
945
	 */
946
	public static function getIdByKey( $key ) {
947
		return FrmFormDeprecated::getIdByKey( $key );
948
	}
949
950
	/**
951
	 * @deprecated 3.0
952
	 * @codeCoverageIgnore
953
	 */
954
	public static function getKeyById( $id ) {
955
		return FrmFormDeprecated::getKeyById( $id );
956
	}
957
}
958