CMB2_REST   D
last analyzed

Complexity

Total Complexity 92

Size/Duplication

Total Lines 754
Duplicated Lines 1.06 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 8
loc 754
ccs 0
cts 238
cp 0
rs 4.4444
c 0
b 0
f 0
wmc 92
lcom 1
cbo 6

32 Methods

Rating   Name   Duplication   Size   Complexity  
A is_protected_meta() 0 7 2
A __construct() 0 9 1
A universal_hooks() 0 12 2
A init_routes() 0 9 1
D register_cmb2_fields() 8 51 10
A register_rest_field() 0 7 1
B declare_read_edit_fields() 0 17 6
A can_read() 0 9 2
A can_edit() 0 9 2
A get_post_rest_values() 0 5 2
A get_user_rest_values() 0 5 2
A get_comment_rest_values() 0 5 2
A get_term_rest_values() 0 5 2
B get_rest_values() 0 27 5
A update_post_rest_values() 0 5 2
A update_user_rest_values() 0 5 2
A update_comment_rest_values() 0 5 2
A update_term_rest_values() 0 5 2
C update_rest_values() 0 30 7
A sanitize_box_values() 0 13 2
A sanitize_field_value() 0 20 4
A sanitize_group_value() 0 10 2
C get_object_id() 0 21 10
A field_can_read() 0 3 1
A field_can_edit() 0 3 1
A field_can() 0 7 4
A get_rest_box() 0 3 2
A remove() 0 5 2
A get_all() 0 3 1
A is_readable() 0 7 2
A is_editable() 0 6 1
B __get() 0 11 5

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

1
<?php
2
/**
3
 * Handles hooking CMB2 objects/fields into the WordPres REST API
4
 * which can allow fields to be read and/or updated.
5
 *
6
 * @since  2.2.3
7
 *
8
 * @category  WordPress_Plugin
9
 * @package   CMB2
10
 * @author    WebDevStudios
11
 * @license   GPL-2.0+
12
 * @link      http://webdevstudios.com
13
 *
14
 * @property-read read_fields Array of readable field objects.
15
 * @property-read edit_fields Array of editable field objects.
16
 * @property-read rest_read   Whether CMB2 object is readable via the rest api.
17
 * @property-read rest_edit   Whether CMB2 object is editable via the rest api.
18
 */
19
class CMB2_REST 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...
20
21
	/**
22
	 * The current CMB2 REST endpoint version
23
	 *
24
	 * @var string
25
	 * @since 2.2.3
26
	 */
27
	const VERSION = '1';
28
29
	/**
30
	 * The CMB2 REST base namespace (v should always be followed by $version)
31
	 *
32
	 * @var string
33
	 * @since 2.2.3
34
	 */
35
	const NAME_SPACE = 'cmb2/v1';
36
37
	/**
38
	 * @var   CMB2 object
39
	 * @since 2.2.3
40
	 */
41
	public $cmb;
42
43
	/**
44
	 * @var   CMB2_REST[] objects
45
	 * @since 2.2.3
46
	 */
47
	protected static $boxes = array();
48
49
	/**
50
	 * @var   array Array of cmb ids for each type.
51
	 * @since 2.2.3
52
	 */
53
	protected static $type_boxes = array(
54
		'post' => array(),
55
		'user' => array(),
56
		'comment' => array(),
57
		'term' => array(),
58
	);
59
60
	/**
61
	 * Array of readable field objects.
62
	 *
63
	 * @var   CMB2_Field[]
64
	 * @since 2.2.3
65
	 */
66
	protected $read_fields = array();
67
68
	/**
69
	 * Array of editable field objects.
70
	 *
71
	 * @var   CMB2_Field[]
72
	 * @since 2.2.3
73
	 */
74
	protected $edit_fields = array();
75
76
	/**
77
	 * Whether CMB2 object is readable via the rest api.
78
	 *
79
	 * @var boolean
80
	 */
81
	protected $rest_read = false;
82
83
	/**
84
	 * Whether CMB2 object is editable via the rest api.
85
	 *
86
	 * @var boolean
87
	 */
88
	protected $rest_edit = false;
89
90
	/**
91
	 * Constructor
92
	 *
93
	 * @since 2.2.3
94
	 *
95
	 * @param CMB2 $cmb The CMB2 object to be registered for the API.
96
	 */
97
	public function __construct( CMB2 $cmb ) {
98
		$this->cmb = $cmb;
99
		self::$boxes[ $cmb->cmb_id ] = $this;
0 ignored issues
show
Documentation introduced by
The property $cmb_id is declared protected in CMB2_Base. 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...
100
101
		$show_value = $this->cmb->prop( 'show_in_rest' );
102
103
		$this->rest_read = self::is_readable( $show_value );
104
		$this->rest_edit = self::is_editable( $show_value );
105
	}
106
107
	/**
108
	 * Hooks to register on frontend and backend.
109
	 *
110
	 * @since  2.2.3
111
	 *
112
	 * @return void
113
	 */
114
	public function universal_hooks() {
115
		// hook up the CMB rest endpoint classes
116
		$this->once( 'rest_api_init', array( __CLASS__, 'init_routes' ), 0 );
117
118
		if ( function_exists( 'register_rest_field' ) ) {
119
			$this->once( 'rest_api_init', array( __CLASS__, 'register_cmb2_fields' ), 50 );
120
		}
121
122
		$this->declare_read_edit_fields();
123
124
		add_filter( 'is_protected_meta', array( $this, 'is_protected_meta' ), 10, 3 );
125
	}
126
127
	/**
128
	 * Initiate the CMB2 Boxes and Fields routes
129
	 *
130
	 * @since  2.2.3
131
	 *
132
	 * @return void
133
	 */
134
	public static function init_routes() {
135
		$wp_rest_server = rest_get_server();
136
137
		$boxes_controller = new CMB2_REST_Controller_Boxes( $wp_rest_server );
138
		$boxes_controller->register_routes();
139
140
		$fields_controller = new CMB2_REST_Controller_Fields( $wp_rest_server );
141
		$fields_controller->register_routes();
142
	}
143
144
	/**
145
	 * Loop through REST boxes and call register_rest_field for each object type.
146
	 *
147
	 * @since  2.2.3
148
	 *
149
	 * @return void
150
	 */
151
	public static function register_cmb2_fields() {
152
		$alltypes = $taxonomies = array();
153
154
		foreach ( self::$boxes as $cmb_id => $rest_box ) {
155
			$types = array_flip( $rest_box->cmb->box_types() );
156
157 View Code Duplication
			if ( isset( $types['user'] ) ) {
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...
158
				unset( $types['user'] );
159
				self::$type_boxes['user'][ $cmb_id ] = $cmb_id;
160
			}
161
162 View Code Duplication
			if ( isset( $types['comment'] ) ) {
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...
163
				unset( $types['comment'] );
164
				self::$type_boxes['comment'][ $cmb_id ] = $cmb_id;
165
			}
166
167
			if ( isset( $types['term'] ) ) {
168
				unset( $types['term'] );
169
170
				$taxonomies = array_merge(
171
					$taxonomies,
172
					CMB2_Utils::ensure_array( $rest_box->cmb->prop( 'taxonomies' ) )
173
				);
174
175
				self::$type_boxes['term'][ $cmb_id ] = $cmb_id;
176
			}
177
178
			if ( ! empty( $types ) ) {
179
				$alltypes = array_merge( $alltypes, array_flip( $types ) );
180
				self::$type_boxes['post'][ $cmb_id ] = $cmb_id;
181
			}
182
		}
183
184
		$alltypes = array_unique( $alltypes );
185
186
		if ( ! empty( $alltypes ) ) {
187
			self::register_rest_field( $alltypes, 'post' );
188
		}
189
190
		if ( ! empty( self::$type_boxes['user'] ) ) {
191
			self::register_rest_field( 'user', 'user' );
192
		}
193
194
		if ( ! empty( self::$type_boxes['comment'] ) ) {
195
			self::register_rest_field( 'comment', 'comment' );
196
		}
197
198
		if ( ! empty( self::$type_boxes['term'] ) ) {
199
			self::register_rest_field( $taxonomies, 'term' );
200
		}
201
	}
202
203
	/**
204
	 * Wrapper for register_rest_field.
205
	 *
206
	 * @since  2.2.3
207
	 *
208
	 * @param string|array $object_types Object(s) the field is being registered
209
	 *                                   to, "post"|"term"|"comment" etc.
210
	 * @param string       $object_types       Canonical object type for callbacks.
211
	 *
212
	 * @return void
213
	 */
214
	protected static function register_rest_field( $object_types, $object_type ) {
215
		register_rest_field( $object_types, 'cmb2', array(
216
			'get_callback'    => array( __CLASS__, "get_{$object_type}_rest_values" ),
217
			'update_callback' => array( __CLASS__, "update_{$object_type}_rest_values" ),
218
			'schema'          => null, // @todo add schema
219
		) );
220
	}
221
222
	/**
223
	 * Setup readable and editable fields.
224
	 *
225
	 * @since  2.2.3
226
	 *
227
	 * @return void
228
	 */
229
	protected function declare_read_edit_fields() {
230
		foreach ( $this->cmb->prop( 'fields' ) as $field ) {
231
			$show_in_rest = isset( $field['show_in_rest'] ) ? $field['show_in_rest'] : null;
232
233
			if ( false === $show_in_rest ) {
234
				continue;
235
			}
236
237
			if ( $this->can_read( $show_in_rest ) ) {
238
				$this->read_fields[] = $field['id'];
239
			}
240
241
			if ( $this->can_edit( $show_in_rest ) ) {
242
				$this->edit_fields[] = $field['id'];
243
			}
244
		}
245
	}
246
247
	/**
248
	 * Determines if a field is readable based on it's show_in_rest value
249
	 * and the box's show_in_rest value.
250
	 *
251
	 * @since  2.2.3
252
	 *
253
	 * @param  bool $show_in_rest Field's show_in_rest value. Default null.
254
	 *
255
	 * @return bool               Whether field is readable.
256
	 */
257
	protected function can_read( $show_in_rest ) {
258
		// if 'null', then use default box value.
259
		if ( null === $show_in_rest ) {
260
			return $this->rest_read;
261
		}
262
263
		// Else check if the value represents readable.
264
		return self::is_readable( $show_in_rest );
265
	}
266
267
	/**
268
	 * Determines if a field is editable based on it's show_in_rest value
269
	 * and the box's show_in_rest value.
270
	 *
271
	 * @since  2.2.3
272
	 *
273
	 * @param  bool $show_in_rest Field's show_in_rest value. Default null.
274
	 *
275
	 * @return bool               Whether field is editable.
276
	 */
277
	protected function can_edit( $show_in_rest ) {
278
		// if 'null', then use default box value.
279
		if ( null === $show_in_rest ) {
280
			return $this->rest_edit;
281
		}
282
283
		// Else check if the value represents editable.
284
		return self::is_editable( $show_in_rest );
285
	}
286
287
	/**
288
	 * Handler for getting post custom field data.
289
	 *
290
	 * @since  2.2.3
291
	 *
292
	 * @param  array           $object      The object data from the response
293
	 * @param  string          $field_name  Name of field
294
	 * @param  WP_REST_Request $request     Current request
295
	 * @param  string          $object_type The request object type
296
	 *
297
	 * @return mixed
298
	 */
299
	public static function get_post_rest_values( $object, $field_name, $request, $object_type ) {
300
		if ( 'cmb2' === $field_name ) {
301
			return self::get_rest_values( $object, $request, $object_type, 'post' );
302
		}
303
	}
304
305
	/**
306
	 * Handler for getting user custom field data.
307
	 *
308
	 * @since  2.2.3
309
	 *
310
	 * @param  array           $object      The object data from the response
311
	 * @param  string          $field_name  Name of field
312
	 * @param  WP_REST_Request $request     Current request
313
	 * @param  string          $object_type The request object type
314
	 *
315
	 * @return mixed
316
	 */
317
	public static function get_user_rest_values( $object, $field_name, $request, $object_type ) {
318
		if ( 'cmb2' === $field_name ) {
319
			return self::get_rest_values( $object, $request, $object_type, 'user' );
320
		}
321
	}
322
323
	/**
324
	 * Handler for getting comment custom field data.
325
	 *
326
	 * @since  2.2.3
327
	 *
328
	 * @param  array           $object      The object data from the response
329
	 * @param  string          $field_name  Name of field
330
	 * @param  WP_REST_Request $request     Current request
331
	 * @param  string          $object_type The request object type
332
	 *
333
	 * @return mixed
334
	 */
335
	public static function get_comment_rest_values( $object, $field_name, $request, $object_type ) {
336
		if ( 'cmb2' === $field_name ) {
337
			return self::get_rest_values( $object, $request, $object_type, 'comment' );
338
		}
339
	}
340
341
	/**
342
	 * Handler for getting term custom field data.
343
	 *
344
	 * @since  2.2.3
345
	 *
346
	 * @param  array           $object      The object data from the response
347
	 * @param  string          $field_name  Name of field
348
	 * @param  WP_REST_Request $request     Current request
349
	 * @param  string          $object_type The request object type
350
	 *
351
	 * @return mixed
352
	 */
353
	public static function get_term_rest_values( $object, $field_name, $request, $object_type ) {
354
		if ( 'cmb2' === $field_name ) {
355
			return self::get_rest_values( $object, $request, $object_type, 'term' );
356
		}
357
	}
358
359
	/**
360
	 * Handler for getting custom field data.
361
	 *
362
	 * @since  2.2.3
363
	 *
364
	 * @param  array           $object           The object data from the response
365
	 * @param  WP_REST_Request $request          Current request
366
	 * @param  string          $object_type      The request object type
367
	 * @param  string          $main_object_type The cmb main object type
368
	 *
369
	 * @return mixed
370
	 */
371
	protected static function get_rest_values( $object, $request, $object_type, $main_object_type = 'post' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $object_type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
372
		if ( ! isset( $object['id'] ) ) {
373
			return;
374
		}
375
376
		$values = array();
377
378
		if ( ! empty( self::$type_boxes[ $main_object_type ] ) ) {
379
			foreach ( self::$type_boxes[ $main_object_type ] as $cmb_id ) {
380
				$rest_box = self::$boxes[ $cmb_id ];
381
382
				foreach ( $rest_box->read_fields as $field_id ) {
383
					$rest_box->cmb->object_id( $object['id'] );
384
					$rest_box->cmb->object_type( $main_object_type );
385
386
					$field = $rest_box->cmb->get_field( $field_id );
387
388
					$field->object_id( $object['id'] );
389
					$field->object_type( $main_object_type );
390
391
					$values[ $cmb_id ][ $field->id( true ) ] = $field->get_data();
392
				}
393
			}
394
		}
395
396
		return $values;
397
	}
398
399
	/**
400
	 * Handler for updating post custom field data.
401
	 *
402
	 * @since  2.2.3
403
	 *
404
	 * @param  mixed           $values      The value of the field
405
	 * @param  object          $object      The object from the response
406
	 * @param  string          $field_name  Name of field
407
	 * @param  WP_REST_Request $request     Current request
408
	 * @param  string          $object_type The request object type
409
	 *
410
	 * @return bool|int
411
	 */
412
	public static function update_post_rest_values( $values, $object, $field_name, $request, $object_type ) {
413
		if ( 'cmb2' === $field_name ) {
414
			return self::update_rest_values( $values, $object, $request, $object_type, 'post' );
415
		}
416
	}
417
418
	/**
419
	 * Handler for updating user custom field data.
420
	 *
421
	 * @since  2.2.3
422
	 *
423
	 * @param  mixed           $values      The value of the field
424
	 * @param  object          $object      The object from the response
425
	 * @param  string          $field_name  Name of field
426
	 * @param  WP_REST_Request $request     Current request
427
	 * @param  string          $object_type The request object type
428
	 *
429
	 * @return bool|int
430
	 */
431
	public static function update_user_rest_values( $values, $object, $field_name, $request, $object_type ) {
432
		if ( 'cmb2' === $field_name ) {
433
			return self::update_rest_values( $values, $object, $request, $object_type, 'user' );
434
		}
435
	}
436
437
	/**
438
	 * Handler for updating comment custom field data.
439
	 *
440
	 * @since  2.2.3
441
	 *
442
	 * @param  mixed           $values      The value of the field
443
	 * @param  object          $object      The object from the response
444
	 * @param  string          $field_name  Name of field
445
	 * @param  WP_REST_Request $request     Current request
446
	 * @param  string          $object_type The request object type
447
	 *
448
	 * @return bool|int
449
	 */
450
	public static function update_comment_rest_values( $values, $object, $field_name, $request, $object_type ) {
451
		if ( 'cmb2' === $field_name ) {
452
			return self::update_rest_values( $values, $object, $request, $object_type, 'comment' );
453
		}
454
	}
455
456
	/**
457
	 * Handler for updating term custom field data.
458
	 *
459
	 * @since  2.2.3
460
	 *
461
	 * @param  mixed           $values      The value of the field
462
	 * @param  object          $object      The object from the response
463
	 * @param  string          $field_name  Name of field
464
	 * @param  WP_REST_Request $request     Current request
465
	 * @param  string          $object_type The request object type
466
	 *
467
	 * @return bool|int
468
	 */
469
	public static function update_term_rest_values( $values, $object, $field_name, $request, $object_type ) {
470
		if ( 'cmb2' === $field_name ) {
471
			return self::update_rest_values( $values, $object, $request, $object_type, 'term' );
472
		}
473
	}
474
475
	/**
476
	 * Handler for updating custom field data.
477
	 *
478
	 * @since  2.2.3
479
	 *
480
	 * @param  mixed           $values           The value of the field
481
	 * @param  object          $object           The object from the response
482
	 * @param  WP_REST_Request $request          Current request
483
	 * @param  string          $object_type      The request object type
484
	 * @param  string          $main_object_type The cmb main object type
485
	 *
486
	 * @return bool|int
487
	 */
488
	protected static function update_rest_values( $values, $object, $request, $object_type, $main_object_type = 'post' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $object_type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
489
		if ( empty( $values ) || ! is_array( $values ) ) {
490
			return;
491
		}
492
493
		$object_id = self::get_object_id( $object, $main_object_type );
494
495
		if ( ! $object_id ) {
496
			return;
497
		}
498
499
		$updated = array();
500
501
		if ( ! empty( self::$type_boxes[ $main_object_type ] ) ) {
502
			foreach ( self::$type_boxes[ $main_object_type ] as $cmb_id ) {
503
				$rest_box = self::$boxes[ $cmb_id ];
504
505
				if ( ! array_key_exists( $cmb_id, $values ) ) {
506
					continue;
507
				}
508
509
				$rest_box->cmb->object_id( $object_id );
510
				$rest_box->cmb->object_type( $main_object_type );
511
512
				$updated[ $cmb_id ] = $rest_box->sanitize_box_values( $values );
513
			}
514
		}
515
516
		return $updated;
517
	}
518
519
	/**
520
	 * Loop through box fields and sanitize the values.
521
	 *
522
	 * @since  2.2.o
523
	 *
524
	 * @param  array $values Array of values being provided.
525
	 * @return array           Array of updated/sanitized values.
526
	 */
527
	public function sanitize_box_values( array $values ) {
528
		$updated = array();
529
530
		$this->cmb->pre_process();
531
532
		foreach ( $this->edit_fields as $field_id ) {
533
			$updated[ $field_id ] = $this->sanitize_field_value( $values, $field_id );
0 ignored issues
show
Documentation introduced by
$field_id is of type object<CMB2_Field>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
534
		}
535
536
		$this->cmb->after_save();
537
538
		return $updated;
539
	}
540
541
	/**
542
	 * Handles returning a sanitized field value.
543
	 *
544
	 * @since  2.2.3
545
	 *
546
	 * @param  array  $values   Array of values being provided.
547
	 * @param  string $field_id The id of the field to update.
548
	 *
549
	 * @return mixed             The results of saving/sanitizing a field value.
550
	 */
551
	protected function sanitize_field_value( array $values, $field_id ) {
552
		if ( ! array_key_exists( $field_id, $values[ $this->cmb->cmb_id ] ) ) {
0 ignored issues
show
Documentation introduced by
The property $cmb_id is declared protected in CMB2_Base. 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...
553
			return;
554
		}
555
556
		$field = $this->cmb->get_field( $field_id );
557
558
		if ( 'title' == $field->type() ) {
559
			return;
560
		}
561
562
		$field->object_id( $this->cmb->object_id() );
563
		$field->object_type( $this->cmb->object_type() );
564
565
		if ( 'group' == $field->type() ) {
566
			return $this->sanitize_group_value( $values, $field );
0 ignored issues
show
Security Bug introduced by
It seems like $field defined by $this->cmb->get_field($field_id) on line 556 can also be of type false; however, CMB2_REST::sanitize_group_value() does only seem to accept object<CMB2_Field>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
567
		}
568
569
		return $field->save_field( $values[ $this->cmb->cmb_id ][ $field_id ] );
0 ignored issues
show
Documentation introduced by
The property $cmb_id is declared protected in CMB2_Base. 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...
570
	}
571
572
	/**
573
	 * Handles returning a sanitized group field value.
574
	 *
575
	 * @since  2.2.3
576
	 *
577
	 * @param  array      $values Array of values being provided.
578
	 * @param  CMB2_Field $field  CMB2_Field object.
579
	 *
580
	 * @return mixed               The results of saving/sanitizing the group field value.
581
	 */
582
	protected function sanitize_group_value( array $values, CMB2_Field $field ) {
583
		$fields = $field->fields();
584
		if ( empty( $fields ) ) {
585
			return;
586
		}
587
588
		$this->cmb->data_to_save[ $field->_id() ] = $values[ $this->cmb->cmb_id ][ $field->_id() ];
0 ignored issues
show
Documentation introduced by
The property $cmb_id is declared protected in CMB2_Base. 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...
589
590
		return $this->cmb->save_group_field( $field );
591
	}
592
593
	/**
594
	 * Filter whether a meta key is protected.
595
	 *
596
	 * @since 2.2.3
597
	 *
598
	 * @param bool   $protected Whether the key is protected. Default false.
599
	 * @param string $meta_key  Meta key.
600
	 * @param string $meta_type Meta type.
601
	 */
602
	public function is_protected_meta( $protected, $meta_key, $meta_type ) {
0 ignored issues
show
Unused Code introduced by
The parameter $meta_type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
603
		if ( $this->field_can_edit( $meta_key ) ) {
604
			return false;
605
		}
606
607
		return $protected;
608
	}
609
610
	protected static function get_object_id( $object, $object_type = 'post' ) {
611
		switch ( $object_type ) {
612
			case 'user':
613
			case 'post':
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...
614
				if ( isset( $object->ID ) ) {
615
					return intval( $object->ID );
616
				}
617
			case 'comment':
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...
618
				if ( isset( $object->comment_ID ) ) {
619
					return intval( $object->comment_ID );
620
				}
621
			case 'term':
622
				if ( is_array( $object ) && isset( $object['term_id'] ) ) {
623
					return intval( $object['term_id'] );
624
				} elseif ( isset( $object->term_id ) ) {
625
					return intval( $object->term_id );
626
				}
627
		}
628
629
		return 0;
630
	}
631
632
	/**
633
	 * Checks if a given field can be read.
634
	 *
635
	 * @since  2.2.3
636
	 *
637
	 * @param  string|CMB2_Field $field_id      Field ID or CMB2_Field object.
638
	 * @param  boolean           $return_object Whether to return the Field object.
639
	 *
640
	 * @return mixed                            False if field can't be read or true|CMB2_Field object.
641
	 */
642
	public function field_can_read( $field_id, $return_object = false ) {
643
		return $this->field_can( 'read_fields', $field_id, $return_object );
644
	}
645
646
	/**
647
	 * Checks if a given field can be edited.
648
	 *
649
	 * @since  2.2.3
650
	 *
651
	 * @param  string|CMB2_Field $field_id      Field ID or CMB2_Field object.
652
	 * @param  boolean           $return_object Whether to return the Field object.
653
	 *
654
	 * @return mixed                            False if field can't be edited or true|CMB2_Field object.
655
	 */
656
	public function field_can_edit( $field_id, $return_object = false ) {
657
		return $this->field_can( 'edit_fields', $field_id, $return_object );
658
	}
659
660
	/**
661
	 * Checks if a given field can be read or edited.
662
	 *
663
	 * @since  2.2.3
664
	 *
665
	 * @param  string            $type          Whether we are checking for read or edit fields.
666
	 * @param  string|CMB2_Field $field_id      Field ID or CMB2_Field object.
667
	 * @param  boolean           $return_object Whether to return the Field object.
668
	 *
669
	 * @return mixed                            False if field can't be read or edited or true|CMB2_Field object.
670
	 */
671
	protected function field_can( $type = 'read_fields', $field_id, $return_object = false ) {
672
		if ( ! in_array( $field_id instanceof CMB2_Field ? $field_id->id() : $field_id, $this->{$type}, true ) ) {
673
			return false;
674
		}
675
676
		return $return_object ? $this->cmb->get_field( $field_id ) : true;
677
	}
678
679
	/**
680
	 * Get a CMB2_REST instance object from the registry by a CMB2 id.
681
	 *
682
	 * @since  2.2.3
683
	 *
684
	 * @param  string $cmb_id CMB2 config id
685
	 *
686
	 * @return CMB2_REST|false The CMB2_REST object or false.
687
	 */
688
	public static function get_rest_box( $cmb_id ) {
689
		return isset( self::$boxes[ $cmb_id ] ) ? self::$boxes[ $cmb_id ] : false;
690
	}
691
692
	/**
693
	 * Remove a CMB2_REST instance object from the registry.
694
	 *
695
	 * @since  2.2.3
696
	 *
697
	 * @param string $cmb_id A CMB2 instance id.
698
	 */
699
	public static function remove( $cmb_id ) {
700
		if ( array_key_exists( $cmb_id, self::$boxes ) ) {
701
			unset( self::$boxes[ $cmb_id ] );
702
		}
703
	}
704
705
	/**
706
	 * Retrieve all CMB2_REST instances from the registry.
707
	 *
708
	 * @since  2.2.3
709
	 * @return CMB2[] Array of all registered CMB2_REST instances.
710
	 */
711
	public static function get_all() {
712
		return self::$boxes;
713
	}
714
715
	/**
716
	 * Checks if given value is readable.
717
	 *
718
	 * Value is considered readable if it is not empty and if it does not match the editable blacklist.
719
	 *
720
	 * @since  2.2.3
721
	 *
722
	 * @param  mixed $value Value to check.
723
	 *
724
	 * @return boolean       Whether value is considered readable.
725
	 */
726
	public static function is_readable( $value ) {
727
		return ! empty( $value ) && ! in_array( $value, array(
728
			WP_REST_Server::CREATABLE,
729
			WP_REST_Server::EDITABLE,
730
			WP_REST_Server::DELETABLE,
731
		), true );
732
	}
733
734
	/**
735
	 * Checks if given value is editable.
736
	 *
737
	 * Value is considered editable if matches the editable whitelist.
738
	 *
739
	 * @since  2.2.3
740
	 *
741
	 * @param  mixed $value Value to check.
742
	 *
743
	 * @return boolean       Whether value is considered editable.
744
	 */
745
	public static function is_editable( $value ) {
746
		return in_array( $value, array(
747
			WP_REST_Server::EDITABLE,
748
			WP_REST_Server::ALLMETHODS,
749
		), true );
750
	}
751
752
	/**
753
	 * Magic getter for our object.
754
	 *
755
	 * @param string $field
756
	 * @throws Exception Throws an exception if the field is invalid.
757
	 *
758
	 * @return mixed
759
	 */
760
	public function __get( $field ) {
761
		switch ( $field ) {
762
			case 'read_fields':
763
			case 'edit_fields':
764
			case 'rest_read':
765
			case 'rest_edit':
766
				return $this->{$field};
767
			default:
768
				throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field );
769
		}
770
	}
771
772
}
773