Completed
Pull Request — trunk (#541)
by Justin
28:03 queued 25:22
created

CMB2_Base::prop()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 2
dl 0
loc 7
ccs 5
cts 6
cp 0.8333
crap 3.0416
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 = 'post';
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 15
	public function object_id( $object_id = 0 ) {
100 15
		if ( $object_id ) {
101 10
			$this->object_id = $object_id;
102 10
		}
103
104 15
		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 15
	public function object_type( $object_type = '' ) {
114 15
		if ( $object_type ) {
115 10
			$this->object_type = $object_type;
116 10
		}
117
118 15
		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 48
	public function prop( $property, $fallback = null ) {
166 48
		if ( array_key_exists( $property, $this->{$this->properties_name} ) ) {
167 1
			return $this->{$this->properties_name}[ $property ];
168 47
		} elseif ( $fallback ) {
169
			return $this->{$this->properties_name}[ $property ] = $fallback;
170
		}
171 47
	}
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 5 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 5
		if ( $field_group ) {
182
			$args = array(
183
				'field_args'  => $field_args,
184
				'group_field' => $field_group,
185
			);
186
		} 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 5
		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 5
	protected function get_new_field( $field_args, $field_group = null ) {
206 5
		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 48
	public function should_show() {
217
		// Default to showing this cmb
218 48
		$show = true;
219
220
		// Use the callback to determine showing the cmb, if it exists
221 48
		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 48
		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 85
	public function peform_param_callback( $param ) {
235 85
		echo $this->get_param_callback_result( $param );
236 85
	}
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 89
	public function get_param_callback_result( $param ) {
245
246
		// If we've already retrieved this param's value,
247 89
		if ( array_key_exists( $param, $this->callback_results ) ) {
248
249
			// send it back
250 15
			return $this->callback_results[ $param ];
251
		}
252
253
		// Check if parameter has registered a callback.
254 89
		if ( $cb = $this->maybe_callback( $param ) ) {
255
256
			// Ok, callback is good, let's run it and store the result.
257 54
			ob_start();
258 54
			$returned = $this->do_callback( $cb );
259
260
			// Grab the result from the output buffer and store it.
261 54
			$echoed = ob_get_clean();
262
263
			// This checks if the user returned or echoed their callback.
264
			// Defaults to using the echoed value.
265 54
			$this->callback_results[ $param ] = $echoed ? $echoed : $returned;
266
267 54
		} else {
268
269
			// Otherwise just get whatever is there.
270 84
			$this->callback_results[ $param ] = isset( $this->{$this->properties_name}[ $param ] ) ? $this->{$this->properties_name}[ $param ] : false;
271
		}
272
273 89
		return $this->callback_results[ $param ];
274
	}
275
276
	/**
277
	 * Handles the property callbacks, and passes this object as property.
278
	 * @since  2.2.3
279
	 * @param  callable $cb The callback method/function/closure
280
	 * @return mixed        Return of the callback function.
281
	 */
282 54
	protected function do_callback( $cb ) {
283 54
		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 107
	public function maybe_callback( $cb ) {
293 107
		$args = $this->{$this->properties_name};
294 107
		if ( ! isset( $args[ $cb ] ) ) {
295 101
			return null;
296
		}
297
298
		// Check if requesting explicitly false
299 54
		$cb = false !== $args[ $cb ] && 'false' !== $args[ $cb ] ? $args[ $cb ] : false;
300
301
		// If requesting NO validation, return false
302 54
		if ( ! $cb ) {
303 47
			return false;
304
		}
305
306 54
		if ( is_callable( $cb ) ) {
307 54
			return $cb;
308
		}
309
310 4
		return null;
311
	}
312
313
	/**
314
	 * Mark a param as deprecated and inform when it has been used.
315
	 *
316
	 * There is a default WordPress hook deprecated_argument_run that will be called
317
	 * that can be used to get the backtrace up to what file and function used the
318
	 * deprecated argument.
319
	 *
320
	 * The current behavior is to trigger a user error if WP_DEBUG is true.
321
	 *
322
	 * @since 2.2.3
323
	 *
324
	 * @param string $function The function that was called.
325
	 * @param string $version  The version of CMB2 that deprecated the argument used.
326
	 * @param string $message  Optional. A message regarding the change, or numeric
327
	 *                         key to generate message from additional arguments.
328
	 *                         Default null.
329
	 */
330 1
	protected function deprecated_param( $function, $version, $message = null ) {
331
332 1
		if ( is_numeric( $message ) ) {
333 1
			$args = func_get_args();
334
335
			switch ( $message ) {
336
337 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...
338
					$message = sprintf( __( 'The "%s" field parameter has been deprecated in favor of the "%s" parameter.', 'cmb2' ), $args[3], $args[4] );
339
					break;
340
341 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...
342 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] );
343 1
					break;
344
345
				default:
346
					$message = null;
347
					break;
348
			}
349 1
		}
350
351
		/**
352
		 * Fires when a deprecated argument is called. This is a WP core action.
353
		 *
354
		 * @since 2.2.3
355
		 *
356
		 * @param string $function The function that was called.
357
		 * @param string $message  A message regarding the change.
358
		 * @param string $version  The version of CMB2 that deprecated the argument used.
359
		 */
360 1
		do_action( 'deprecated_argument_run', $function, $message, $version );
361
362
		/**
363
		 * Filters whether to trigger an error for deprecated arguments. This is a WP core filter.
364
		 *
365
		 * @since 2.2.3
366
		 *
367
		 * @param bool $trigger Whether to trigger the error for deprecated arguments. Default true.
368
		 */
369 1
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG && apply_filters( 'deprecated_argument_trigger_error', true ) ) {
370
			if ( function_exists( '__' ) ) {
371
				if ( ! is_null( $message ) ) {
372
					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 ) );
373
				}
374
				else {
375
					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 ) );
376
				}
377
			} else {
378
				if ( ! is_null( $message ) ) {
379
					trigger_error( sprintf( '%1$s was called with a parameter that is <strong>deprecated</strong> since version %2$s! %3$s', $function, $version, $message ) );
380
				}
381
				else {
382
					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 ) );
383
				}
384
			}
385
		}
386 1
	}
387
388
	/**
389
	 * Magic getter for our object.
390
	 * @param string $field
391
	 * @throws Exception Throws an exception if the field is invalid.
392
	 * @return mixed
393
	 */
394 105
	public function __get( $field ) {
395
		switch ( $field ) {
396 105
			case 'args':
397 105
			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...
398 5
				if ( $field === $this->properties_name ) {
399 5
					return $this->{$this->properties_name};
400
				}
401 105
			case 'properties':
402
				return $this->{$this->properties_name};
403 105
			case 'cmb_id':
404 105
			case 'object_id':
405 105
			case 'object_type':
406 105
				return $this->{$field};
407 2
			default:
408 2
				throw new Exception( sprintf( esc_html__( 'Invalid %1$s property: %2$s', 'give' ), __CLASS__, $field ) );
409 2
		}
410
	}
411
412
	/**
413
	 * Allows overloading the object with methods... Whooaaa oooh it's magic, y'knoooow.
414
	 * @since 1.0.0
415
	 * @param string $method Non-existent method.
416
	 * @param array  $arguments All arguments passed to the method
0 ignored issues
show
Bug introduced by
There is no parameter named $arguments. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
417
	 */
418 2
	public function __call( $method, $args ) {
419 2
		$object_class = strtolower( get_class( $this ) );
420
421 2
		if ( ! has_filter(  "{$object_class}_inherit_{$method}" ) ) {
422 1
			throw new Exception( sprintf( esc_html__( 'Invalid %1$s method: %2$s', 'give' ), get_class( $this ), $method ) );
423
		}
424
425 1
		array_unshift( $args, $this );
426
427
		/**
428
		 * Allows overloading the object (CMB2 or CMB2_Field) with additional capabilities
429
		 * by registering hook callbacks.
430
		 *
431
		 * The first dynamic portion of the hook name, $object_class, refers to the object class,
432
		 * either cmb2 or cmb2_field.
433
		 *
434
		 * The second dynamic portion of the hook name, $method, is the non-existent method being
435
		 * called on the object. To avoid possible future methods encroaching on your hooks,
436
		 * use a unique method (aka, $cmb->prefix_my_method()).
437
		 *
438
		 * When registering your callback, you will need to ensure that you register the correct
439
		 * number of `$accepted_args`, accounting for this object instance being the first argument.
440
		 *
441
		 * @param array $args The arguments to be passed to the hook.
442
		 *                    The first argument will always be this object instance.
443
		 */
444 1
		return apply_filters_ref_array( "{$object_class}_inherit_{$method}", $args );
445
	}
446
}
447