Completed
Pull Request — trunk (#541)
by Justin
06:40
created

CMB2_hookup   D

Complexity

Total Complexity 104

Size/Duplication

Total Lines 622
Duplicated Lines 1.29 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 8.33%

Importance

Changes 14
Bugs 3 Features 5
Metric Value
c 14
b 3
f 5
dl 8
loc 622
ccs 24
cts 288
cp 0.0833
rs 4.7835
wmc 104
lcom 1
cbo 4

31 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 21 6
B universal_hooks() 0 15 5
A post_hooks() 0 13 3
A comment_hooks() 0 9 2
A user_hooks() 4 16 2
C term_hooks() 4 39 7
B register_styles() 0 17 6
A register_js() 0 10 3
A register_scripts() 0 4 1
B do_scripts() 0 24 4
B register_column_headers() 0 27 4
A column_display() 0 12 2
A return_column_display() 0 7 2
B add_metaboxes() 0 27 5
A close_metabox_class() 0 4 1
A metabox_callback() 0 4 2
A user_new_metabox() 0 7 3
A user_metabox() 0 3 1
A term_metabox() 0 3 1
B show_form_for_type() 0 18 5
A show_on() 0 15 1
B get_priority() 0 22 4
B save_post() 0 19 6
A save_comment() 0 8 3
A save_user() 0 6 2
A save_term() 0 8 4
A delete_term() 0 10 3
B can_save() 0 12 7
B taxonomy_can_save() 0 13 5
A enqueue_cmb_css() 0 8 2
A enqueue_cmb_js() 0 8 2

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_hookup 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_hookup, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Handles hooking CMB2 forms/metaboxes into the post/attachement/user screens
4
 * and handles hooking in and saving those fields.
5
 *
6
 * @since  2.0.0
7
 *
8
 * @category  WordPress_Plugin
9
 * @package   CMB2
10
 * @author    WebDevStudios
11
 * @license   GPL-2.0+
12
 * @link      http://webdevstudios.com
13
 */
14
class CMB2_hookup extends CMB2_Hookup_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...
15
16
	/**
17
	 * Array of all hooks done (to be run once)
18
	 * @var   array
19
	 * @since 2.0.0
20
	 */
21
	protected static $hooks_completed = array();
22
23
	/**
24
	 * Only allow JS registration once
25
	 * @var   bool
26
	 * @since 2.0.7
27
	 */
28
	protected static $js_registration_done = false;
29
30
	/**
31
	 * Only allow CSS registration once
32
	 * @var   bool
33
	 * @since 2.0.7
34
	 */
35
	protected static $css_registration_done = false;
36
37
	/**
38
	 * @var   CMB2 object
39
	 * @since 2.0.2
40
	 */
41
	protected $cmb;
42
43
	/**
44
	 * CMB taxonomies array for term meta
45
	 * @var   array
46
	 * @since 2.2.0
47
	 */
48
	protected $taxonomies = array();
49
50
	/**
51
	 * The object type we are performing the hookup for
52
	 * @var   string
53
	 * @since 2.0.9
54
	 */
55
	protected $object_type = 'post';
56
57
	/**
58
	 * Custom field columns.
59
	 * @var   array
60
	 * @since 2.2.2
61
	 */
62
	protected $columns = array();
63
64
	/**
65
	 * Constructor
66
	 * @since 2.0.0
67
	 * @param CMB2 $cmb The CMB2 object to hookup
68
	 */
69
	public function __construct( CMB2 $cmb ) {
70
		$this->cmb = $cmb;
71
		$this->object_type = $this->cmb->mb_object_type();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->cmb->mb_object_type() can also be of type false. However, the property $object_type is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
72
73
		$this->universal_hooks();
74
75
		if ( is_admin() ) {
76
77
			switch ( $this->object_type ) {
78
				case 'post':
79
					return $this->post_hooks();
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
80
				case 'comment':
81
					return $this->comment_hooks();
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
82
				case 'user':
83
					return $this->user_hooks();
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
84
				case 'term':
85
					return $this->term_hooks();
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
86
			}
87
88
		}
89
	}
90
91
	public function universal_hooks() {
92
		foreach ( get_class_methods( 'CMB2_Show_Filters' ) as $filter ) {
93
			add_filter( 'cmb2_show_on', array( 'CMB2_Show_Filters', $filter ), 10, 3 );
94
		}
95
96
		if ( is_admin() ) {
97
			// register our scripts and styles for cmb
98
			$this->once( 'admin_enqueue_scripts', array( __CLASS__, 'register_scripts' ), 8 );
99
			$this->once( 'admin_enqueue_scripts', array( $this, 'do_scripts' ) );
100
101
			if ( $this->cmb->has_columns && $this->cmb->prop( 'cmb_styles' ) ) {
0 ignored issues
show
Documentation introduced by
The property $has_columns is declared protected in CMB2. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
102
				self::enqueue_cmb_css( 'cmb2-display-styles' );
103
			}
104
		}
105
	}
106
107
	public function post_hooks() {
108
		add_action( 'add_meta_boxes', array( $this, 'add_metaboxes' ) );
109
		add_action( 'add_attachment', array( $this, 'save_post' ) );
110
		add_action( 'edit_attachment', array( $this, 'save_post' ) );
111
		add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );
112
113
		if ( $this->cmb->has_columns ) {
0 ignored issues
show
Documentation introduced by
The property $has_columns is declared protected in CMB2. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
114
			foreach ( $this->cmb->prop( 'object_types' ) as $post_type ) {
115
				add_filter( "manage_{$post_type}_posts_columns", array( $this, 'register_column_headers' ) );
116
				add_action( "manage_{$post_type}_posts_custom_column", array( $this, 'column_display' ), 10, 2 );
117
			}
118
		}
119
	}
120
121
	public function comment_hooks() {
122
		add_action( 'add_meta_boxes_comment', array( $this, 'add_metaboxes' ) );
123
		add_action( 'edit_comment', array( $this, 'save_comment' ) );
124
125
		if ( $this->cmb->has_columns ) {
0 ignored issues
show
Documentation introduced by
The property $has_columns is declared protected in CMB2. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
126
			add_filter( 'manage_edit-comments_columns', array( $this, 'register_column_headers' ) );
127
			add_action( 'manage_comments_custom_column', array( $this, 'column_display'  ), 10, 3 );
128
		}
129
	}
130
131
	public function user_hooks() {
132
		$priority = $this->get_priority();
133
134
		add_action( 'show_user_profile', array( $this, 'user_metabox' ), $priority );
135
		add_action( 'edit_user_profile', array( $this, 'user_metabox' ), $priority );
136
		add_action( 'user_new_form', array( $this, 'user_new_metabox' ), $priority );
137
138
		add_action( 'personal_options_update', array( $this, 'save_user' ) );
139
		add_action( 'edit_user_profile_update', array( $this, 'save_user' ) );
140
		add_action( 'user_register', array( $this, 'save_user' ) );
141
142 View Code Duplication
		if ( $this->cmb->has_columns ) {
0 ignored issues
show
Documentation introduced by
The property $has_columns is declared protected in CMB2. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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...
143
			add_filter( 'manage_users_columns', array( $this, 'register_column_headers' ) );
144
			add_filter( 'manage_users_custom_column', array( $this, 'return_column_display'  ), 10, 3 );
145
		}
146
	}
147
148
	public function term_hooks() {
149
		if ( ! function_exists( 'get_term_meta' ) ) {
150
			wp_die( __( 'Term Metadata is a WordPress > 4.4 feature. Please upgrade your WordPress install.', 'cmb2' ) );
151
		}
152
153
		if ( ! $this->cmb->prop( 'taxonomies' ) ) {
154
			wp_die( __( 'Term metaboxes configuration requires a \'taxonomies\' parameter', 'cmb2' ) );
155
		}
156
157
		$this->taxonomies = (array) $this->cmb->prop( 'taxonomies' );
158
		$show_on_term_add = $this->cmb->prop( 'new_term_section' );
159
		$priority         = $this->get_priority( 8 );
160
161
		foreach ( $this->taxonomies as $taxonomy ) {
162
			// Display our form data
163
			add_action( "{$taxonomy}_edit_form", array( $this, 'term_metabox' ), $priority, 2 );
164
165
			$show_on_add = is_array( $show_on_term_add )
166
				? in_array( $taxonomy, $show_on_term_add )
167
				: (bool) $show_on_term_add;
168
169
			$show_on_add = apply_filters( "cmb2_show_on_term_add_form_{$this->cmb->cmb_id}", $show_on_add, $this->cmb );
170
171
			// Display form in add-new section (unless specified not to)
172
			if ( $show_on_add ) {
173
				add_action( "{$taxonomy}_add_form_fields", array( $this, 'term_metabox' ), $priority, 2 );
174
			}
175
176 View Code Duplication
			if ( $this->cmb->has_columns ) {
0 ignored issues
show
Documentation introduced by
The property $has_columns is declared protected in CMB2. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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...
177
				add_filter( "manage_edit-{$taxonomy}_columns", array( $this, 'register_column_headers' ) );
178
				add_filter( "manage_{$taxonomy}_custom_column", array( $this, 'return_column_display'  ), 10, 3 );
179
			}
180
		}
181
182
		add_action( 'created_term', array( $this, 'save_term' ), 10, 3 );
183
		add_action( 'edited_terms', array( $this, 'save_term' ), 10, 2 );
184
		add_action( 'delete_term', array( $this, 'delete_term' ), 10, 3 );
185
186
	}
187
188
	/**
189
	 * Registers styles for CMB2
190
	 * @since 2.0.7
191
	 */
192 1
	protected static function register_styles() {
193 1
		if ( self::$css_registration_done ) {
194
			return;
195
		}
196
197
		// Only use minified files if SCRIPT_DEBUG is off
198 1
		$min   = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
199 1
		$front = is_admin() ? '' : '-front';
200 1
		$rtl   = is_rtl() ? '-rtl' : '';
201
202
		// Filter required styles and register stylesheet
203 1
		$dependencies = apply_filters( 'cmb2_style_dependencies', array() );
204 1
		wp_register_style( 'cmb2-styles', cmb2_utils()->url( "css/cmb2{$front}{$rtl}{$min}.css" ), $dependencies );
205 1
		wp_register_style( 'cmb2-display-styles', cmb2_utils()->url( "css/cmb2-display{$rtl}{$min}.css" ), $dependencies );
206
207 1
		self::$css_registration_done = true;
208 1
	}
209
210
	/**
211
	 * Registers scripts for CMB2
212
	 * @since  2.0.7
213
	 */
214 1
	protected static function register_js() {
215 1
		if ( self::$js_registration_done ) {
216
			return;
217
		}
218
219 1
		$hook = is_admin() ? 'admin_footer' : 'wp_footer';
220 1
		add_action( $hook, array( 'CMB2_JS', 'enqueue' ), 8 );
221
222 1
		self::$js_registration_done = true;
223 1
	}
224
225
	/**
226
	 * Registers scripts and styles for CMB2
227
	 * @since  1.0.0
228
	 */
229
	public static function register_scripts() {
230
		self::register_styles();
231
		self::register_js();
232
	}
233
234
	/**
235
	 * Enqueues scripts and styles for CMB2 in admin_head.
236
	 * @since  1.0.0
237
	 */
238
	public function do_scripts( $hook ) {
239
		$hooks = array(
240
			'post.php',
241
			'post-new.php',
242
			'page-new.php',
243
			'page.php',
244
			'comment.php',
245
			'edit-tags.php',
246
			'term.php',
247
			'user-new.php',
248
			'profile.php',
249
			'user-edit.php',
250
		);
251
		// only pre-enqueue our scripts/styles on the proper pages
252
		// show_form_for_type will have us covered if we miss something here.
253
		if ( in_array( $hook, $hooks, true ) ) {
254
			if ( $this->cmb->prop( 'cmb_styles' ) ) {
255
				self::enqueue_cmb_css();
256
			}
257
			if ( $this->cmb->prop( 'enqueue_js' ) ) {
258
				self::enqueue_cmb_js();
259
			}
260
		}
261
	}
262
263
	/**
264
	 * Register the CMB2 field column headers.
265
	 * @since 2.2.2
266
	 */
267
	public function register_column_headers( $columns ) {
268
		$fields = $this->cmb->prop( 'fields' );
269
270
		foreach ( $fields as $key => $field ) {
271
			if ( ! isset( $field['column'] ) ) {
272
				continue;
273
			}
274
275
			$column = $field['column'];
276
277
			if ( false === $column['position'] ) {
278
279
				$columns[ $field['id'] ] = $column['name'];
280
281
			} else {
282
283
				$before = array_slice( $columns, 0, absint( $column['position'] ) );
284
				$before[ $field['id'] ] = $column['name'];
285
				$columns = $before + $columns;
286
			}
287
288
			$column['field'] = $field;
289
			$this->columns[ $field['id'] ] = $column;
290
		}
291
292
		return $columns;
293
	}
294
295
	/**
296
	 * The CMB2 field column display output.
297
	 * @since 2.2.2
298
	 */
299
	public function column_display( $column_name, $object_id ) {
300
		if ( isset( $this->columns[ $column_name ] ) ) {
301
 			$field = new CMB2_Field( array(
302
				'field_args'  => $this->columns[ $column_name ]['field'],
303
				'object_type' => $this->object_type,
304
				'object_id'   => $this->cmb->object_id( $object_id ),
305
				'cmb_id'      => $this->cmb->cmb_id,
306
			) );
307
308
			$this->cmb->get_field( $field )->render_column();
309
		}
310
	}
311
312
	/**
313
	 * Returns the column display.
314
	 * @since 2.2.2
315
	 */
316
	public function return_column_display( $empty, $custom_column, $object_id ) {
317
		ob_start();
318
		$this->column_display( $custom_column, $object_id );
319
		$column = ob_get_clean();
320
321
		return $column ? $column : $empty;
322
	}
323
324
	/**
325
	 * Add metaboxes (to 'post' or 'comment' object types)
326
	 * @since 1.0.0
327
	 */
328
	public function add_metaboxes() {
329
330
		if ( ! $this->show_on() ) {
331
			return;
332
		}
333
334
		/**
335
		 * To keep from registering an actual post-screen metabox,
336
		 * omit the 'title' attribute from the metabox registration array.
337
		 *
338
		 * (WordPress will not display metaboxes without titles anyway)
339
		 *
340
		 * This is a good solution if you want to output your metaboxes
341
		 * Somewhere else in the post-screen
342
		 */
343
		if ( ! $this->cmb->prop( 'title' ) ) {
344
			return;
345
		}
346
347
		foreach ( $this->cmb->prop( 'object_types' ) as $post_type ) {
348
			if ( $this->cmb->prop( 'closed' ) ) {
349
				add_filter( "postbox_classes_{$post_type}_{$this->cmb->cmb_id}", array( $this, 'close_metabox_class' ) );
350
			}
351
352
			add_meta_box( $this->cmb->cmb_id, $this->cmb->prop( 'title' ), array( $this, 'metabox_callback' ), $post_type, $this->cmb->prop( 'context' ), $this->cmb->prop( 'priority' ) );
353
		}
354
	}
355
356
	/**
357
	 * Add 'closed' class to metabox
358
	 * @since  2.0.0
359
	 * @param  array  $classes Array of classes
360
	 * @return array           Modified array of classes
361
	 */
362
	public function close_metabox_class( $classes ) {
363
		$classes[] = 'closed';
364
		return $classes;
365
	}
366
367
	/**
368
	 * Display metaboxes for a post or comment object
369
	 * @since  1.0.0
370
	 */
371
	public function metabox_callback() {
372
		$object_id = 'comment' == $this->object_type ? get_comment_ID() : get_the_ID();
373
		$this->cmb->show_form( $object_id, $this->object_type );
374
	}
375
376
	/**
377
	 * Display metaboxes for new user page
378
	 * @since  1.0.0
379
	 */
380
	public function user_new_metabox( $section ) {
0 ignored issues
show
Coding Style introduced by
user_new_metabox uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
381
		if ( $section == $this->cmb->prop( 'new_user_section' ) ) {
382
			$object_id = $this->cmb->object_id();
383
			$this->cmb->object_id( isset( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : $object_id );
384
			$this->user_metabox();
385
		}
386
	}
387
388
	/**
389
	 * Display metaboxes for a user object
390
	 * @since  1.0.0
391
	 */
392
	public function user_metabox() {
393
		$this->show_form_for_type( 'user' );
394
	}
395
396
	/**
397
	 * Display metaboxes for a taxonomy term object
398
	 * @since  2.2.0
399
	 */
400
	public function term_metabox() {
401
		$this->show_form_for_type( 'term' );
402
	}
403
404
	/**
405
	 * Display metaboxes for an object type
406
	 * @since  2.2.0
407
	 * @param  string $type Object type
408
	 * @return void
409
	 */
410
	public function show_form_for_type( $type ) {
411
		if ( $type != $this->cmb->mb_object_type() ) {
412
			return;
413
		}
414
415
		if ( ! $this->show_on() ) {
416
			return;
417
		}
418
419
		if ( $this->cmb->prop( 'cmb_styles' ) ) {
420
			self::enqueue_cmb_css();
421
		}
422
		if ( $this->cmb->prop( 'enqueue_js' ) ) {
423
			self::enqueue_cmb_js();
424
		}
425
426
		$this->cmb->show_form( 0, $type );
427
	}
428
429
	/**
430
	 * Determines if metabox should be shown in current context
431
	 * @since  2.0.0
432
	 * @return bool Whether metabox should be added/shown
433
	 */
434
	public function show_on() {
435
		// If metabox is requesting to be conditionally shown
436
		$show = $this->cmb->should_show();
437
438
		/**
439
		 * Filter to determine if metabox should show. Default is true
440
		 *
441
		 * @param array  $show          Default is true, show the metabox
442
		 * @param mixed  $meta_box_args Array of the metabox arguments
443
		 * @param mixed  $cmb           The CMB2 instance
444
		 */
445
		$show = (bool) apply_filters( 'cmb2_show_on', $show, $this->cmb->meta_box, $this->cmb );
446
447
		return $show;
448
	}
449
450
	/**
451
	 * Get the CMB priority property set to numeric hook priority.
452
	 * @since  2.2.0
453
	 * @param  integer $default Default display hook priority.
454
	 * @return integer          Hook priority.
455
	 */
456
	public function get_priority( $default = 10 ) {
457
		$priority = $this->cmb->prop( 'priority' );
458
459
		if ( ! is_numeric( $priority ) ) {
460
			switch ( $priority ) {
461
462
				case 'high':
463
					$priority = 5;
464
					break;
465
466
				case 'low':
467
					$priority = 20;
468
					break;
469
470
				default:
471
					$priority = $default;
472
					break;
473
			}
474
		}
475
476
		return $priority;
477
	}
478
479
	/**
480
	 * Save data from post metabox
481
	 * @since  1.0.0
482
	 * @param  int    $post_id Post ID
483
	 * @param  mixed  $post    Post object
484
	 * @return null
485
	 */
486
	public function save_post( $post_id, $post = false ) {
0 ignored issues
show
Coding Style introduced by
save_post uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
487
488
		$post_type = $post ? $post->post_type : get_post_type( $post_id );
489
490
		$do_not_pass_go = (
491
			! $this->can_save( $post_type )
492
			// check user editing permissions
493
			|| ( 'page' == $post_type && ! current_user_can( 'edit_page', $post_id ) )
494
			|| ! current_user_can( 'edit_post', $post_id )
495
		);
496
497
		if ( $do_not_pass_go ) {
498
			// do not collect $200
499
			return;
500
		}
501
502
		// take a trip to reading railroad – if you pass go collect $200
503
		$this->cmb->save_fields( $post_id, 'post', $_POST );
504
	}
505
506
	/**
507
	 * Save data from comment metabox
508
	 * @since  2.0.9
509
	 * @param  int    $comment_id Comment ID
510
	 * @return null
511
	 */
512
	public function save_comment( $comment_id ) {
0 ignored issues
show
Coding Style introduced by
save_comment uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
513
514
		$can_edit = current_user_can( 'moderate_comments', $comment_id );
515
516
		if ( $this->can_save( get_comment_type( $comment_id ) ) && $can_edit ) {
517
			$this->cmb->save_fields( $comment_id, 'comment', $_POST );
518
		}
519
	}
520
521
	/**
522
	 * Save data from user fields
523
	 * @since  1.0.x
524
	 * @param  int   $user_id  User ID
525
	 * @return null
526
	 */
527
	public function save_user( $user_id ) {
0 ignored issues
show
Coding Style introduced by
save_user uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
528
		// check permissions
529
		if ( $this->can_save( 'user' ) ) {
530
			$this->cmb->save_fields( $user_id, 'user', $_POST );
531
		}
532
	}
533
534
	/**
535
	 * Save data from term fields
536
	 * @since  2.2.0
537
	 * @param  int    $term_id  Term ID
538
	 * @param  int    $tt_id    Term Taxonomy ID
539
	 * @param  string $taxonomy Taxonomy
540
	 * @return null
541
	 */
542
	public function save_term( $term_id, $tt_id, $taxonomy = '' ) {
0 ignored issues
show
Coding Style introduced by
save_term uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
543
		$taxonomy = $taxonomy ? $taxonomy : $tt_id;
544
545
		// check permissions
546
		if ( $this->taxonomy_can_save( $taxonomy ) && $this->can_save( 'term' ) ) {
547
			$this->cmb->save_fields( $term_id, 'term', $_POST );
548
		}
549
	}
550
551
	/**
552
	 * Delete term meta when a term is deleted.
553
	 * @since  2.2.0
554
	 * @param  int    $term_id  Term ID
555
	 * @param  int    $tt_id    Term Taxonomy ID
556
	 * @param  string $taxonomy Taxonomy
557
	 * @return null
558
	 */
559
	public function delete_term( $term_id, $tt_id, $taxonomy = '' ) {
560
		if ( $this->taxonomy_can_save( $taxonomy ) ) {
561
562
			foreach ( $this->cmb->prop( 'fields' ) as $field ) {
563
				$data_to_delete[ $field['id'] ] = '';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data_to_delete was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data_to_delete = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
564
			}
565
566
			$this->cmb->save_fields( $term_id, 'term', $data_to_delete );
0 ignored issues
show
Bug introduced by
The variable $data_to_delete does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
567
		}
568
	}
569
570
	/**
571
	 * Determines if the current object is able to be saved
572
	 * @since  2.0.9
573
	 * @param  string  $type Current post_type or comment_type
574
	 * @return bool          Whether object can be saved
575
	 */
576
	public function can_save( $type = '' ) {
0 ignored issues
show
Coding Style introduced by
can_save uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
577
		return (
578
			$this->cmb->prop( 'save_fields' )
579
			// check nonce
580
			&& isset( $_POST[ $this->cmb->nonce() ] )
581
			&& wp_verify_nonce( $_POST[ $this->cmb->nonce() ], $this->cmb->nonce() )
582
			// check if autosave
583
			&& ! ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
584
			// get the metabox types & compare it to this type
585
			&& ( $type && in_array( $type, $this->cmb->prop( 'object_types' ) ) )
586
		);
587
	}
588
589
	/**
590
	 * Determine if taxonomy of term being modified is cmb2-editable.
591
	 * @since  2.2.0
592
	 * @param  string $taxonomy Taxonomy of term being modified.
593
	 * @return bool             Whether taxonomy is editable.
594
	 */
595
	public function taxonomy_can_save( $taxonomy ) {
596
		if ( empty( $this->taxonomies ) || ! in_array( $taxonomy, $this->taxonomies ) ) {
597
			return false;
598
		}
599
600
		$taxonomy_object = get_taxonomy( $taxonomy );
601
		// Can the user edit this term?
602
		if ( ! isset( $taxonomy_object->cap ) || ! current_user_can( $taxonomy_object->cap->edit_terms ) ) {
603
			return false;
604
		}
605
606
		return true;
607
	}
608
609
	/**
610
	 * Includes CMB2 styles
611
	 * @since  2.0.0
612
	 */
613 1
	public static function enqueue_cmb_css( $handle = 'cmb2-styles' ) {
614 1
		if ( ! apply_filters( 'cmb2_enqueue_css', true ) ) {
615
			return false;
616
		}
617
618 1
		self::register_styles();
619 1
		return wp_enqueue_style( $handle );
620
	}
621
622
	/**
623
	 * Includes CMB2 JS
624
	 * @since  2.0.0
625
	 */
626 1
	public static function enqueue_cmb_js() {
627 1
		if ( ! apply_filters( 'cmb2_enqueue_js', true ) ) {
628
			return false;
629
		}
630
631 1
		self::register_js();
632 1
		return true;
633
	}
634
635
}
636