Completed
Push — trunk ( 2096f9...2bf2b7 )
by Justin
21:19
created

CMB2_Base::get_default_args()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 12

Duplication

Lines 17
Ratio 100 %

Code Coverage

Tests 12
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 12
nc 2
nop 2
dl 17
loc 17
ccs 12
cts 12
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * CMB2 Base - Base object functionality.
4
 *
5
 * @category  WordPress_Plugin
6
 * @package   CMB2
7
 * @author    WebDevStudios
8
 * @license   GPL-2.0+
9
 * @link      http://webdevstudios.com
10
 *
11
 * @property-read $args        The objects array of properties/arguments.
12
 * @property-read $meta_box    The objects array of properties/arguments.
13
 * @property-read $properties  The objects array of properties/arguments.
14
 * @property-read $cmb_id      Current CMB2 instance ID
15
 * @property-read $object_id   Object ID
16
 * @property-read $object_type Type of object being handled. (e.g., post, user, comment, or term)
17
 */
18
abstract class CMB2_Base {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
19
20
	/**
21
	 * Current CMB2 instance ID
22
	 * @var   string
23
	 * @since 2.2.3
24
	 */
25
	protected $cmb_id = '';
26
27
	/**
28
	 * The object properties name.
29
	 * @var   string
30
	 * @since 2.2.3
31
	 */
32
	protected $properties_name = 'meta_box';
33
34
	/**
35
	 * Object ID
36
	 * @var   mixed
37
	 * @since 2.2.3
38
	 */
39
	protected $object_id = 0;
40
41
	/**
42
	 * Type of object being handled. (e.g., post, user, comment, or term)
43
	 * @var   string
44
	 * @since 2.2.3
45
	 */
46
	protected $object_type = '';
47
48
	/**
49
	 * Array of key => value data for saving. Likely $_POST data.
50
	 * @var   array
51
	 * @since 2.2.3
52
	 */
53
	public $data_to_save = array();
54
55
	/**
56
	 * Array of field param callback results
57
	 * @var   array
58
	 * @since 2.0.0
59
	 */
60
	protected $callback_results = array();
61
62
	/**
63
	 * The deprecated_param method deprecated param message signature.
64
	 */
65
	const DEPRECATED_PARAM = 1;
66
67
	/**
68
	 * The deprecated_param method deprecated callback param message signature.
69
	 */
70
	const DEPRECATED_CB_PARAM = 2;
71
72
	/**
73
	 * Get started
74
	 * @since 2.2.3
75
	 * @param array $args Object properties array
76
	 */
77
	public function __construct( $args = array() ) {
78
		if ( ! empty( $args ) ) {
79
			foreach ( array(
80
				'cmb_id',
81
				'properties_name',
82
				'object_id',
83
				'object_type',
84
				'data_to_save',
85
			) as $object_prop ) {
86
				if ( isset( $args[ $object_prop ] ) ) {
87
					$this->{$object_prop} = $args[ $object_prop ];
88
				}
89
			}
90
		}
91
	}
92
93
	/**
94
	 * Returns the object ID
95
	 * @since  2.2.3
96
	 * @param  integer $object_id Object ID
97
	 * @return integer Object ID
98
	 */
99 5
	public function object_id( $object_id = 0 ) {
100 5
		if ( $object_id ) {
101
			$this->object_id = $object_id;
102
		}
103
104 5
		return $this->object_id;
105
	}
106
107
	/**
108
	 * Returns the object type
109
	 * @since  2.2.3
110
	 * @param  string $object_type Object Type
111
	 * @return string Object type
112
	 */
113 5
	public function object_type( $object_type = '' ) {
114 5
		if ( $object_type ) {
115
			$this->object_type = $object_type;
116
		}
117
118 5
		return $this->object_type;
119
	}
120
121
	/**
122
	 * Get the object type for the current page, based on the $pagenow global.
123
	 * @since  2.2.2
124
	 * @return string  Page object type name.
125
	 */
126 View Code Duplication
	public function current_object_type() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
127
		global $pagenow;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
128
		$type = 'post';
129
130
		if ( in_array( $pagenow, array( 'user-edit.php', 'profile.php', 'user-new.php' ), true ) ) {
131
			$type = 'user';
132
		}
133
134
		if ( in_array( $pagenow, array( 'edit-comments.php', 'comment.php' ), true ) ) {
135
			$type = 'comment';
136
		}
137
138
		if ( in_array( $pagenow, array( 'edit-tags.php', 'term.php' ), true ) ) {
139
			$type = 'term';
140
		}
141
142
		return $type;
143
	}
144
145
	/**
146
	 * Set object property.
147
	 * @since  2.2.2
148
	 * @param  string $property Metabox config property to retrieve
149
	 * @param  mixed  $value    Value to set if no value found
150
	 * @return mixed            Metabox config property value or false
151
	 */
152
	public function set_prop( $property, $value ) {
153
		$this->{$this->properties_name}[ $property ] = $value;
154
155
		return $this->prop( $property );
156
	}
157
158
	/**
159
	 * Get object property and optionally set a fallback
160
	 * @since  2.0.0
161
	 * @param  string $property Metabox config property to retrieve
162
	 * @param  mixed  $fallback Fallback value to set if no value found
163
	 * @return mixed            Metabox config property value or false
164
	 */
165 43
	public function prop( $property, $fallback = null ) {
166 43
		if ( array_key_exists( $property, $this->{$this->properties_name} ) ) {
167 1
			return $this->{$this->properties_name}[ $property ];
168 42
		} elseif ( $fallback ) {
169
			return $this->{$this->properties_name}[ $property ] = $fallback;
170
		}
171 42
	}
172
173
	/**
174
	 * Get default field arguments specific to this CMB2 object.
175
	 * @since  2.2.0
176
	 * @param  array      $field_args  Metabox field config array.
177
	 * @param  CMB2_Field $field_group (optional) CMB2_Field object (group parent)
178
	 * @return array                   Array of field arguments.
179
	 */
180 6 View Code Duplication
	protected function get_default_args( $field_args, $field_group = null ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
181 6
		if ( $field_group ) {
182
			$args = array(
183 1
				'field_args'  => $field_args,
184 1
				'group_field' => $field_group,
185 1
			);
186 1
		} else {
187
			$args = array(
188 5
				'field_args'  => $field_args,
189 5
				'object_type' => $this->object_type(),
190 5
				'object_id'   => $this->object_id(),
191 5
				'cmb_id'      => $this->cmb_id,
192 5
			);
193
		}
194
195 6
		return $args;
196
	}
197
198
	/**
199
	 * Get a new field object specific to this CMB2 object.
200
	 * @since  2.2.0
201
	 * @param  array      $field_args  Metabox field config array.
202
	 * @param  CMB2_Field $field_group (optional) CMB2_Field object (group parent)
203
	 * @return CMB2_Field CMB2_Field object
204
	 */
205 6
	protected function get_new_field( $field_args, $field_group = null ) {
206 6
		return new CMB2_Field( $this->get_default_args( $field_args, $field_group ) );
207
	}
208
209
	/**
210
	 * Determine whether this cmb object should show, based on the 'show_on_cb' callback.
211
	 *
212
	 * @since 2.0.9
213
	 *
214
	 * @return bool Whether this cmb should be shown.
215
	 */
216 43
	public function should_show() {
217
		// Default to showing this cmb
218 43
		$show = true;
219
220
		// Use the callback to determine showing the cmb, if it exists
221 43
		if ( is_callable( $this->prop( 'show_on_cb' ) ) ) {
222 1
			$show = (bool) call_user_func( $this->prop( 'show_on_cb' ), $this );
223 1
		}
224
225 43
		return $show;
226
	}
227
228
	/**
229
	 * Displays the results of the param callbacks.
230
	 *
231
	 * @since 2.0.0
232
	 * @param string $param Field parameter
233
	 */
234 80
	public function peform_param_callback( $param ) {
235 80
		echo $this->get_param_callback_result( $param );
236 80
	}
237
238
	/**
239
	 * Store results of the param callbacks for continual access
240
	 * @since  2.0.0
241
	 * @param  string $param Field parameter
242
	 * @return mixed         Results of param/param callback
243
	 */
244 85
	public function get_param_callback_result( $param ) {
245
246
		// If we've already retrieved this param's value,
247 85
		if ( array_key_exists( $param, $this->callback_results ) ) {
248
249
			// send it back
250 10
			return $this->callback_results[ $param ];
251
		}
252
253
		// Check if parameter has registered a callback.
254 85
		if ( $cb = $this->maybe_callback( $param ) ) {
255
256
			// Ok, callback is good, let's run it and store the result.
257 49
			ob_start();
258 49
			$returned = $this->do_callback( $cb );
259
260
			// Grab the result from the output buffer and store it.
261 49
			$echoed = ob_get_clean();
262
263
			// This checks if the user returned or echoed their callback.
264
			// Defaults to using the echoed value.
265 49
			$this->callback_results[ $param ] = $echoed ? $echoed : $returned;
266
267 49
		} else {
268
269
			// Otherwise just get whatever is there.
270 80
			$this->callback_results[ $param ] = isset( $this->{$this->properties_name}[ $param ] ) ? $this->{$this->properties_name}[ $param ] : false;
271
		}
272
273 85
		return $this->callback_results[ $param ];
274
	}
275
276
	/**
277
	 * Handles the parameter callbacks, and passes this object as parameter.
278
	 * @since  2.2.3
279
	 * @param  callable $cb The callback method/function/closure
280
	 * @return mixed        Return of the callback function.
281
	 */
282 49
	protected function do_callback( $cb ) {
283 49
		return call_user_func( $cb, $this->{$this->properties_name}, $this );
284
	}
285
286
	/**
287
	 * Checks if field has a callback value
288
	 * @since  1.0.1
289
	 * @param  string $cb Callback string
290
	 * @return mixed      NULL, false for NO validation, or $cb string if it exists.
291
	 */
292 99
	public function maybe_callback( $cb ) {
293 99
		$args = $this->{$this->properties_name};
294 99
		if ( ! isset( $args[ $cb ] ) ) {
295 93
			return null;
296
		}
297
298
		// Check if requesting explicitly false
299 49
		$cb = false !== $args[ $cb ] && 'false' !== $args[ $cb ] ? $args[ $cb ] : false;
300
301
		// If requesting NO validation, return false
302 49
		if ( ! $cb ) {
303 42
			return false;
304
		}
305
306 49
		if ( is_callable( $cb ) ) {
307 49
			return $cb;
308
		}
309
310 4
		return null;
311
	}
312
313
	/**
314
	 * Checks if this object has parameter corresponding to the given filter
315
	 * which is callable. If so, it registers the callback, and if not,
316
	 * converts the maybe-modified $val to a boolean for return.
317
	 *
318
 	 * The registered handlers will have a parameter name which matches the filter, except:
319
 	 * - The 'cmb2_api' prefix will be removed
320
 	 * - A '_cb' suffix will be added (to stay inline with other '*_cb' parameters).
321
 	 *
322
	 * @since  2.2.3
323
	 *
324
	 * @param  string $hook_name     The hook name.
325
	 * @param  bool   $val           The default value.
326
	 * @param  string $hook_function The hook function. Default: 'add_filter'
327
	 *
328
	 * @return null|bool             Null if hook is registered, or bool for value.
329
	 */
330
	public function maybe_hook_parameter( $hook_name, $val = null, $hook_function = 'add_filter' ) {
331
332
		// Remove filter prefix, add param suffix.
333
		$parameter = substr( $hook_name, strlen( 'cmb2_api_' ) ) . '_cb';
334
335
		return self::maybe_hook(
336
			$this->prop( $parameter, $val ),
337
			$hook_name,
338
			$hook_function
339
		);
340
	}
341
342
	/**
343
	 * Checks if given value is callable, and registers the callback.
344
	 * If is non-callable, converts the $val to a boolean for return.
345
	 *
346
	 * @since  2.2.3
347
	 *
348
	 * @param  bool   $val           The default value.
349
	 * @param  string $hook_name     The hook name.
350
	 * @param  string $hook_function The hook function.
351
	 *
352
	 * @return null|bool         Null if hook is registered, or bool for value.
353
	 */
354
	public static function maybe_hook( $val, $hook_name, $hook_function ) {
355
		if ( is_callable( $val ) ) {
356
			$hook_function( $hook_name, $val, 10, 2 );
357
			return null;
358
		}
359
360
		// Cast to bool.
361
		return !! $val;
362
	}
363
364
	/**
365
	 * Mark a param as deprecated and inform when it has been used.
366
	 *
367
	 * There is a default WordPress hook deprecated_argument_run that will be called
368
	 * that can be used to get the backtrace up to what file and function used the
369
	 * deprecated argument.
370
	 *
371
	 * The current behavior is to trigger a user error if WP_DEBUG is true.
372
	 *
373
	 * @since 2.2.3
374
	 *
375
	 * @param string $function The function that was called.
376
	 * @param string $version  The version of CMB2 that deprecated the argument used.
377
	 * @param string $message  Optional. A message regarding the change, or numeric
378
	 *                         key to generate message from additional arguments.
379
	 *                         Default null.
380
	 */
381 1
	protected function deprecated_param( $function, $version, $message = null ) {
382
383 1
		if ( is_numeric( $message ) ) {
384 1
			$args = func_get_args();
385
386
			switch ( $message ) {
387
388 1 View Code Duplication
				case self::DEPRECATED_PARAM:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
389
					$message = sprintf( __( 'The "%s" field parameter has been deprecated in favor of the "%s" parameter.', 'cmb2' ), $args[3], $args[4] );
390
					break;
391
392 1 View Code Duplication
				case self::DEPRECATED_CB_PARAM:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
393 1
					$message = sprintf( __( 'Using the "%s" field parameter as a callback has been deprecated in favor of the "%s" parameter.', 'cmb2' ), $args[3], $args[4] );
394 1
					break;
395
396
				default:
397
					$message = null;
398
					break;
399
			}
400 1
		}
401
402
		/**
403
		 * Fires when a deprecated argument is called. This is a WP core action.
404
		 *
405
		 * @since 2.2.3
406
		 *
407
		 * @param string $function The function that was called.
408
		 * @param string $message  A message regarding the change.
409
		 * @param string $version  The version of CMB2 that deprecated the argument used.
410
		 */
411 1
		do_action( 'deprecated_argument_run', $function, $message, $version );
412
413
		/**
414
		 * Filters whether to trigger an error for deprecated arguments. This is a WP core filter.
415
		 *
416
		 * @since 2.2.3
417
		 *
418
		 * @param bool $trigger Whether to trigger the error for deprecated arguments. Default true.
419
		 */
420 1
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG && apply_filters( 'deprecated_argument_trigger_error', true ) ) {
421
			if ( function_exists( '__' ) ) {
422
				if ( ! is_null( $message ) ) {
423
					trigger_error( sprintf( __( '%1$s was called with a parameter that is <strong>deprecated</strong> since version %2$s! %3$s', 'cmb2' ), $function, $version, $message ) );
424
				}
425
				else {
426
					trigger_error( sprintf( __( '%1$s was called with a parameter that is <strong>deprecated</strong> since version %2$s with no alternative available.', 'cmb2' ), $function, $version ) );
427
				}
428
			} else {
429
				if ( ! is_null( $message ) ) {
430
					trigger_error( sprintf( '%1$s was called with a parameter that is <strong>deprecated</strong> since version %2$s! %3$s', $function, $version, $message ) );
431
				}
432
				else {
433
					trigger_error( sprintf( '%1$s was called with a parameter that is <strong>deprecated</strong> since version %2$s with no alternative available.', $function, $version ) );
434
				}
435
			}
436
		}
437 1
	}
438
439
	/**
440
	 * Magic getter for our object.
441
	 * @param string $field
442
	 * @throws Exception Throws an exception if the field is invalid.
443
	 * @return mixed
444
	 */
445 69
	public function __get( $field ) {
446
		switch ( $field ) {
447 69
			case 'args':
448 69
			case 'meta_box':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
449 3
				if ( $field === $this->properties_name ) {
450 3
					return $this->{$this->properties_name};
451
				}
452 69
			case 'properties':
453
				return $this->{$this->properties_name};
454 69
			case 'cmb_id':
455 69
			case 'object_id':
456 69
			case 'object_type':
457 69
				return $this->{$field};
458 2
			default:
459 2
				throw new Exception( sprintf( esc_html__( 'Invalid %1$s property: %2$s', 'cmb2' ), __CLASS__, $field ) );
460 2
		}
461
	}
462
463
	/**
464
	 * Allows overloading the object with methods... Whooaaa oooh it's magic, y'knoooow.
465
	 * @since 1.0.0
466
	 * @param string $method Non-existent method.
467
	 * @param array  $args   All arguments passed to the method
468
	 */
469 2
	public function __call( $method, $args ) {
470 2
		$object_class = strtolower( get_class( $this ) );
471
472 2
		if ( ! has_filter(  "{$object_class}_inherit_{$method}" ) ) {
473 1
			throw new Exception( sprintf( esc_html__( 'Invalid %1$s method: %2$s', 'cmb2' ), get_class( $this ), $method ) );
474
		}
475
476 1
		array_unshift( $args, $this );
477
478
		/**
479
		 * Allows overloading the object (CMB2 or CMB2_Field) with additional capabilities
480
		 * by registering hook callbacks.
481
		 *
482
		 * The first dynamic portion of the hook name, $object_class, refers to the object class,
483
		 * either cmb2 or cmb2_field.
484
		 *
485
		 * The second dynamic portion of the hook name, $method, is the non-existent method being
486
		 * called on the object. To avoid possible future methods encroaching on your hooks,
487
		 * use a unique method (aka, $cmb->prefix_my_method()).
488
		 *
489
		 * When registering your callback, you will need to ensure that you register the correct
490
		 * number of `$accepted_args`, accounting for this object instance being the first argument.
491
		 *
492
		 * @param array $args The arguments to be passed to the hook.
493
		 *                    The first argument will always be this object instance.
494
		 */
495 1
		return apply_filters_ref_array( "{$object_class}_inherit_{$method}", $args );
496
	}
497
}
498