Completed
Push — issues/1038 ( 42cb72...5376e4 )
by Ravinder
20:36
created

Give_Form_API   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 497
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 497
rs 6.433
c 0
b 0
f 0
wmc 57
lcom 1
cbo 1

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A get_instance() 0 7 2
A init() 0 17 1
A register_form() 0 5 4
A render_shortcode() 0 5 1
B render_custom_form() 0 16 6
C render_form() 0 49 9
B set_default_values() 0 31 3
A process_form() 0 2 1
A get_form() 0 20 4
A get_forms() 0 3 1
B render_form_tags() 0 56 5
B register_form_api_scripts() 0 32 6
A enqueue_scripts() 0 6 4
D get_unique_id() 0 27 9

How to fix   Complexity   

Complex Class

Complex classes like Give_Form_API 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 Give_Form_API, and based on these observations, apply Extract Interface, too.

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 12 and the first side effect is on line 526.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
/**
4
 * Form API
5
 *
6
 * @package     Give
7
 * @subpackage  Classes/Give_Form_API
8
 * @copyright   Copyright (c) 2016, WordImpress
9
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
10
 * @since       2.0
11
 */
12
class Give_Form_API {
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
13
	/**
14
	 * Instance.
15
	 *
16
	 * @since  2.0
17
	 * @access private
18
	 * @var Give_Form_API
19
	 */
20
	static private $instance;
21
22
	/**
23
	 * Array of forms.
24
	 *
25
	 * @since  2.0
26
	 * @access private
27
	 * @var array
28
	 */
29
	private static $forms = array();
30
31
	/**
32
	 * Array of forms render count.
33
	 *
34
	 * @since  2.0
35
	 * @access private
36
	 * @var array
37
	 */
38
	private static $forms_render_counter = array();
39
40
	/**
41
	 * The defaults for all elements
42
	 *
43
	 * @since  2.0
44
	 * @access static
45
	 */
46
	static $field_defaults = array(
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $field_defaults.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
47
		'id'                    => '',
48
		'method'                => 'post',
49
		'action'                => '',
50
		'fields'                => array(),
51
52
		// Sort field by priority.
53
		// If this param set to true then define priority for each field.
54
		'sort_by_priority'      => false,
55
56
		// This param will help to generate w3c validated form otherwise
57
		// Some conflict can occur if you use same form twice or multiple times on same page for example id conflict.
58
		'set_unique_id'         => true,
59
60
61
		// Add custom attributes.
62
		'form_attributes'       => array(),
63
64
		// Supported form layout: simple, stepper, reveal, modal, button.
65
		'display_style'         => 'simple',
66
		'continue_button_html'  => '',
67
		'continue_button_title' => '',
68
69
		// Manually render form.
70
		'callback'              => '',
71
	);
72
73
	/**
74
	 * Display styles.
75
	 *
76
	 * @since  2.0
77
	 * @access private
78
	 * @var array
79
	 */
80
	private $display_styles = array(
81
		'simple'  => 'includes/forms/api/view/simple-form-template.php',
82
		'stepper' => 'includes/forms/api/view/stepper-form-template.php',
83
		'reveal'  => 'includes/forms/api/view/reveal-form-template.php',
84
		'modal'   => 'includes/forms/api/view/modal-form-template.php',
85
		'button'  => 'includes/forms/api/view/button-form-template.php',
86
	);
87
88
89
	private function __construct() {
90
	}
91
92
93
	/**
94
	 * Get instance.
95
	 *
96
	 * @return static
97
	 */
98
	public static function get_instance() {
99
		if ( is_null( static::$instance ) ) {
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
100
			self::$instance = new static();
101
		}
102
103
		return self::$instance;
104
	}
105
106
	/**
107
	 * Initialize this module
108
	 *
109
	 * @since  2.0
110
	 * @access static
111
	 */
112
	public function init() {
113
		self::$field_defaults['_template']             = include GIVE_PLUGIN_DIR . self::$instance->display_styles['simple'];
114
		self::$field_defaults['continue_button_title'] = __( 'Show Form', 'give' );
115
		self::$field_defaults['action']                = esc_url( $_SERVER['REQUEST_URI'] );
116
117
		// Load fields API
118
		require_once GIVE_PLUGIN_DIR . 'includes/forms/api/class-give-fields-api.php';
119
		Give_Fields_API::get_instance()->init();
120
121
		// Load form api filters
122
		require_once GIVE_PLUGIN_DIR . 'includes/forms/api/filters.php';
123
124
		// Add give_form_api shortcode.
125
		add_shortcode( 'give_form_api', array( $this, 'render_shortcode' ) );
126
		add_action( 'give_wp_enqueue_scripts', array( $this, 'register_form_api_scripts' ) );
127
		add_action( 'give_admin_enqueue_scripts', array( $this, 'register_form_api_scripts' ) );
128
	}
129
130
131
	/**
132
	 * Register form.
133
	 *
134
	 * @since  2.0
135
	 * @access public
136
	 *
137
	 * @param      $form
138
	 * @param      $form_id
139
	 * @param bool $force
140
	 */
141
	public static function register_form( $form, $form_id, $force = false ) {
142
		if ( ( ! array_key_exists( $form_id, self::$forms ) || $force ) && ! empty( $form_id ) ) {
143
			self::$forms[ $form_id ] = $form;
144
		}
145
	}
146
147
148
	/**
149
	 * Render form by shortcode.
150
	 *
151
	 * @since  2.0
152
	 * @access public
153
	 *
154
	 * @param array $attrs
155
	 *
156
	 * @return string
157
	 */
158
	public function render_shortcode( $attrs ) {
159
		$attrs = shortcode_atts( array( 'id' => '' ), $attrs, 'give_form_api' );
160
161
		return self::$instance->render_form( $attrs['id'] );
162
	}
163
164
165
	/**
166
	 * Render custom form.
167
	 *
168
	 * @since  1.0
169
	 * @access private
170
	 *
171
	 * @param array $form
172
	 *
173
	 * @return bool
174
	 */
175
	private function render_custom_form( $form ) {
176
		$form_html = '';
177
178
		if ( empty( $form['callback'] ) ) {
179
			$callback = $form['callback'];
180
181
			// Process callback to get form html.
182
			if ( is_string( $callback ) && function_exists( "$callback" ) ) {
183
				$form_html = $callback( $form );
184
			} elseif ( is_array( $callback ) && method_exists( $callback[0], "$callback[1]" ) ) {
185
				$form_html = $callback[0]->$callback[1]( $form );
186
			}
187
		}
188
189
		return $form_html;
190
	}
191
192
	/**
193
	 * Render forms.
194
	 *
195
	 * @since  2.0
196
	 * @access static
197
	 *
198
	 * @param string $form_slug Form name.
199
	 *
200
	 * @return string
201
	 */
202
	static function render_form( $form_slug ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
203
		$form_html = '';
204
205
		// Handle exception.
206
		try {
207
			if (
208
				empty( $form_slug )
209
				|| ! is_string( $form_slug )
210
				|| ! ( $form = self::get_form( $form_slug ) )
211
			) {
212
				throw new Exception( __( 'Pass valid form slug to render form.', 'give' ) );
213
			}
214
		} catch ( Exception $e ) {
215
			give_output_error( $e->getMessage(), true, 'error' );
216
217
			return $form_html;
218
		}
219
220
		// Save form render count: This will help to generate unique id for form.
221
		self::$forms_render_counter[ $form_slug ] = ! empty( self::$forms_render_counter[ $form_slug ] )
222
			? ++self::$forms_render_counter[ $form_slug ]
223
			: 1;
224
225
		// Enqueue Form API js.
226
		self::$instance->enqueue_scripts();
227
228
		// Render custom form with callback.
229
		if ( $form_html = self::$instance->render_custom_form( $form ) ) {
230
			return $form_html;
231
		}
232
233
		// Get all form tags from form template.
234
		preg_match_all( '/\{\{form_(.+?)?\}\}/', $form['_template'], $form_tags );
235
236
		// Render form tags.
237
		if ( 0 < count( $form_tags ) && ! empty( $form_tags[0] ) ) {
238
			$form_html = self::render_form_tags( $form_tags[0], $form );
239
		}
240
241
		/**
242
		 * Filter the form html.
243
		 *
244
		 * @since 2.0
245
		 *
246
		 * @param string $form_html
247
		 * @param array  $form
248
		 */
249
		return apply_filters( 'give_form_api_render_form', $form_html, $form );
250
	}
251
252
253
	/**
254
	 * Set default values form form.
255
	 *
256
	 * @since  2.0
257
	 * @access private
258
	 *
259
	 * @param $form
260
	 *
261
	 * @return array
262
	 */
263
	private static function set_default_values( $form ) {
264
		/**
265
		 * Filter the form values before set default values.
266
		 *
267
		 * @since 2.0
268
		 *
269
		 * @param array  $form
270
		 */
271
		$form = apply_filters( 'give_form_api_pre_set_default_values', $form );
272
273
		$form = wp_parse_args( $form, self::$field_defaults );
274
275
		// Set template.
276
		$form['_template'] = array_key_exists( $form['display_style'], self::$instance->display_styles )
277
			? include GIVE_PLUGIN_DIR . self::$instance->display_styles[ $form['display_style'] ]
278
			: $form['_template'];
279
280
		// Set ID.
281
		$form['form_attributes']['id'] = empty( $form['form_attributes']['id'] )
282
			? $form['id']
283
			: $form['form_attributes']['id'];
284
285
		/**
286
		 * Filter the default values after set form default values.
287
		 *
288
		 * @since 2.0
289
		 *
290
		 * @param array  $form
291
		 */
292
		return apply_filters( 'give_form_api_post_set_default_values', $form );
293
	}
294
295
296
	/**
297
	 * Process a form, filling in $values with what's been posted
298
	 *
299
	 * @since  2.0
300
	 * @access static
301
	 */
302
	static function process_form() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
303
	}
304
305
	/**
306
	 * Recursively process a meta form element, filling in $values accordingly
307
	 *
308
	 * @since  2.0
309
	 * @access static
310
	 *
311
	 * @param string $form_slug
312
	 *
313
	 * @return array
314
	 */
315
	static function get_form( $form_slug ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
316
		$form = array();
317
318
		if ( ! empty( self::$forms ) && array_key_exists( $form_slug, self::$forms ) ) {
319
			$form       = self::$forms[ $form_slug ];
320
			$form['id'] = empty( $form_args['id'] ) ? $form_slug : $form_args['id'];
0 ignored issues
show
Bug introduced by
The variable $form_args seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
321
			$form       = self::$instance->set_default_values( $form );
322
		}
323
324
		/**
325
		 * Filter the result form.
326
		 *
327
		 * @since 2.0
328
		 *
329
		 * @param array  $form
330
		 * @param string $form_slug
331
		 * @param        array self::$forms
332
		 */
333
		return apply_filters( 'give_form_api_get_form', $form, $form_slug, self::$forms );
334
	}
335
336
337
	/**
338
	 * Get forms.
339
	 *
340
	 * @since  1.0
341
	 * @access public
342
	 *
343
	 * @return array
344
	 */
345
	public static function get_forms() {
346
		return self::$forms;
347
	}
348
349
	/**
350
	 * Get forms.
351
	 *
352
	 * @since  1.0
353
	 * @access public
354
	 *
355
	 * @param array $form_tags
356
	 * @param array $form
357
	 *
358
	 * @return string
359
	 */
360
	private static function render_form_tags( $form_tags, $form ) {
361
		$form_html = $form['_template'];
362
363
		/**
364
		 *  Filter the for tags which you want to handle manually.
365
		 *
366
		 * @since 2.0
367
		 *
368
		 * @param       array
369
		 * @param array $form
370
		 * @param array $form_tag
371
		 */
372
		$custom_handler_for_form_tags = apply_filters(
373
			'give_form_api_manually_render_form_tags',
374
			array( '{{form_attributes}}', '{{form_fields}}' ),
375
			$form,
376
			$form_tags
377
		);
378
379
		// Replace form tags.
380
		foreach ( $form_tags as $form_tag ) {
381
			$form_param = str_replace( array( '{{form_', '}}' ), '', $form_tag );
382
383
			// Process form tags which:
384
			// 1. Has a value in form arguments.
385
			// 2. Only has scalar value.
386
			// 3. Developer do not want to handle them manually.
387
			if (
388
				! isset( $form[ $form_param ] )
389
				|| ! is_scalar( $form[ $form_param ] )
390
				|| in_array( $form_tag, $custom_handler_for_form_tags )
391
			) {
392
				continue;
393
			}
394
395
			$form_html = str_replace( $form_tag, $form[ $form_param ], $form_html );
396
		}
397
398
		/**
399
		 *  Filters the form tags.
400
		 *
401
		 * @since 2.0
402
		 *
403
		 * @param string $form_html
404
		 * @param array  $form
405
		 * @param array  $form_tags
406
		 */
407
		$form_html = apply_filters(
408
			'give_form_api_render_form_tags',
409
			$form_html,
410
			$form,
411
			$form_tags
412
		);
413
414
		return $form_html;
415
	}
416
417
	/**
418
	 * Enqueue form api scripts.
419
	 *
420
	 * @since  2.0
421
	 * @access public
422
	 */
423
	public function register_form_api_scripts() {
424
		$js_plugins     = GIVE_PLUGIN_URL . 'assets/js/plugins/';
425
426
		// Use minified libraries if SCRIPT_DEBUG is turned off.
427
		$suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
428
429
		wp_register_script( 'give-repeatable-fields', $js_plugins . 'repeatable-fields' . $suffix . '.js', array( 'jquery' ), GIVE_VERSION, false );
430
		wp_register_script( 'give-form-api-js', $js_plugins . "give-form-api{$suffix}.js", array( 'jquery', 'give-repeatable-fields', 'jquery-ui-sortable' ), GIVE_VERSION, false );
431
432
		/**
433
		 * Filter the js var.
434
		 *
435
		 * @since 2.0
436
		 */
437
		$give_form_api_var = apply_filters( 'give_form_api_js_vars', array(
438
			'metabox_fields' => array(
439
				'media' => array(
440
					'button_title' => esc_html__( 'Choose Attachment', 'give' ),
441
				)
442
			),
443
			/* translators : %s: Donation form options metabox */
444
			'confirm_before_remove_row_text' => __( 'Do you want to delete this level?', 'give' ),
445
		));
446
447
448
		if ( is_admin() || ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ) {
449
			wp_localize_script( 'give-form-api-js', 'give_form_api_var', $give_form_api_var );
450
		} else {
451
			wp_localize_script( 'give', 'give_form_api_var', $give_form_api_var );
452
		}
453
454
	}
455
456
	/**
457
	 * Load Form API js var.
458
	 *
459
	 * @since  2.0
460
	 * @access public
461
	 */
462
	public static function enqueue_scripts() {
463
		if ( is_admin() || ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ) {
464
			wp_enqueue_script('give-repeatable-fields');
465
			wp_enqueue_script('give-form-api-js');
466
		}
467
	}
468
469
470
	/**
471
	 * Get unique form/field id
472
	 *
473
	 * @since  2.0
474
	 * @access public
475
	 *
476
	 * @param array $form
477
	 * @param array $field
478
	 *
479
	 * @return string
480
	 */
481
	public static function get_unique_id( $form, $field = array() ) {
482
		$field_id = '';
483
484
		if( ! is_null( $form )  && empty( $field ) ) {
485
			// Generate unique and constant id for form.
486
			$field_id = ! empty( $form['field_attributes']['id'] )
487
				? $form['field_attributes']['id']
488
				: $form['id'];
489
490
			$field_id = "{$field_id}-" . self::$forms_render_counter[ $form['id'] ];
491
492
		} else {
493
494
			// Generate unique id for form (this id will change on each page refresh).
495
			$field_id = ! empty( $field['field_attributes']['id'] )
496
				? $field['field_attributes']['id']
497
				: $field['id'];
498
499
			if ( ( ! is_null( $form ) && ! empty( $form['set_unique_id'] ) ) || ! empty( $field['set_unique_id'] ) ) {
500
				$field_id = empty( $field['unique_id'] )
501
					? $field_id . '-' . uniqid()
502
					: $field['unique_id'];
503
			}
504
		}
505
506
		return $field_id;
507
	}
508
}
509
510
/**
511
 * Initialize field API.
512
 *
513
 * @since 2.0
514
 */
515
function give_init_forms_api() {
516
	Give_Form_API::get_instance()->init();
517
518
	/**
519
	 * Fire the action when form api loaded.
520
	 *
521
	 * @since 2.0
522
	 */
523
	do_action( 'give_forms_api_loaded' );
524
}
525
526
add_action( 'init', 'give_init_forms_api', 99 );