Completed
Push — trunk ( 89b293...402eef )
by Justin
12:15
created

CMB2_Base   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 395
Duplicated Lines 10.38 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 58.96%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 41
loc 395
ccs 79
cts 134
cp 0.5896
rs 7.9487
wmc 52
lcom 1
cbo 1

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 4
A object_id() 0 7 2
A object_type() 0 7 2
A current_object_type() 18 18 4
A set_prop() 0 5 1
A prop() 0 7 3
A get_default_args() 17 17 2
A get_new_field() 0 3 1
A should_show() 0 11 2
A peform_param_callback() 0 3 1
B get_param_callback_result() 0 31 5
A do_callback() 0 3 1
B maybe_callback() 0 20 6
C deprecated_param() 6 57 10
B __get() 0 17 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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

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 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 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 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 84
	public function get_param_callback_result( $param ) {
245
246
		// If we've already retrieved this param's value,
247 84
		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 84
		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 79
			$this->callback_results[ $param ] = isset( $this->{$this->properties_name}[ $param ] ) ? $this->{$this->properties_name}[ $param ] : false;
271
		}
272
273 84
		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 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 97
	public function maybe_callback( $cb ) {
293 97
		$args = $this->{$this->properties_name};
294 97
		if ( ! isset( $args[ $cb ] ) ) {
295 91
			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
	 * 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 2
	protected function deprecated_param( $function, $version, $message = null ) {
331
332 2
		if ( is_numeric( $message ) ) {
333 2
			$args = func_get_args();
334
335
			switch ( $message ) {
336
337 2 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 2
					$message = sprintf( __( 'The "%s" field parameter has been deprecated in favor of the "%s" parameter.', 'cmb2' ), $args[3], $args[4] );
339 2
					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 2
		}
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 2
		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 2
		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 2
	}
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 65
	public function __get( $field ) {
395
		switch ( $field ) {
396 65
			case 'args':
397 65
			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 3
				if ( $field === $this->properties_name ) {
399 3
					return $this->{$this->properties_name};
400
				}
401 65
			case 'properties':
402
				return $this->{$this->properties_name};
403 65
			case 'cmb_id':
404 65
			case 'object_id':
405 65
			case 'object_type':
406 65
				return $this->{$field};
407 2
			default:
408 2
				throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field );
409 2
		}
410
	}
411
412
}
413