Passed
Push — add/43 ( b57d71...424871 )
by Chris
14:00 queued 07:35
created

CMB2_REST   F

Complexity

Total Complexity 103

Size/Duplication

Total Lines 843
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 207
c 1
b 0
f 0
dl 0
loc 843
rs 2
wmc 103

How to fix   Complexity   

Complex Class

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.

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    CMB2 team
11
 * @license   GPL-2.0+
12
 * @link      https://cmb2.io
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 {
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
	 * A functionalized constructor, used for the hookup action callbacks.
92
	 *
93
	 * @since  2.2.6
94
	 *
95
	 * @param  CMB2 $cmb The CMB2 object to hookup
96
	 *
97
	 * @return CMB2_Hookup_Base $hookup The hookup object.
98
	 */
99
	public static function maybe_init_and_hookup( CMB2 $cmb ) {
100
		if ( $cmb->prop( 'show_in_rest' ) && function_exists( 'rest_get_server' ) ) {
101
102
			$hookup = new self( $cmb );
103
104
			return $hookup->universal_hooks();
105
		}
106
107
		return false;
108
	}
109
110
	/**
111
	 * Constructor
112
	 *
113
	 * @since 2.2.3
114
	 *
115
	 * @param CMB2 $cmb The CMB2 object to be registered for the API.
116
	 */
117
	public function __construct( CMB2 $cmb ) {
118
		$this->cmb = $cmb;
119
		self::$boxes[ $cmb->cmb_id ] = $this;
120
121
		$show_value = $this->cmb->prop( 'show_in_rest' );
122
123
		$this->rest_read = self::is_readable( $show_value );
124
		$this->rest_edit = self::is_editable( $show_value );
125
	}
126
127
	/**
128
	 * Hooks to register on frontend and backend.
129
	 *
130
	 * @since  2.2.3
131
	 *
132
	 * @return void
133
	 */
134
	public function universal_hooks() {
135
		// hook up the CMB rest endpoint classes
136
		$this->once( 'rest_api_init', array( __CLASS__, 'init_routes' ), 0 );
137
138
		if ( function_exists( 'register_rest_field' ) ) {
139
			$this->once( 'rest_api_init', array( __CLASS__, 'register_cmb2_fields' ), 50 );
140
		}
141
142
		$this->declare_read_edit_fields();
143
144
		add_filter( 'is_protected_meta', array( $this, 'is_protected_meta' ), 10, 3 );
145
146
		return $this;
147
	}
148
149
	/**
150
	 * Initiate the CMB2 Boxes and Fields routes
151
	 *
152
	 * @since  2.2.3
153
	 *
154
	 * @return void
155
	 */
156
	public static function init_routes() {
157
		$wp_rest_server = rest_get_server();
158
159
		$boxes_controller = new CMB2_REST_Controller_Boxes( $wp_rest_server );
160
		$boxes_controller->register_routes();
161
162
		$fields_controller = new CMB2_REST_Controller_Fields( $wp_rest_server );
163
		$fields_controller->register_routes();
164
	}
165
166
	/**
167
	 * Loop through REST boxes and call register_rest_field for each object type.
168
	 *
169
	 * @since  2.2.3
170
	 *
171
	 * @return void
172
	 */
173
	public static function register_cmb2_fields() {
174
		$alltypes = $taxonomies = array();
175
176
		foreach ( self::$boxes as $cmb_id => $rest_box ) {
177
178
			// Hook box specific filter callbacks.
179
			$callback = $rest_box->cmb->prop( 'register_rest_field_cb' );
180
			if ( is_callable( $callback ) ) {
181
				call_user_func( $callback, $rest_box );
182
				continue;
183
			}
184
185
			$types = array_flip( $rest_box->cmb->box_types( array( 'post' ) ) );
186
187
			if ( isset( $types['user'] ) ) {
188
				unset( $types['user'] );
189
				self::$type_boxes['user'][ $cmb_id ] = $cmb_id;
190
			}
191
192
			if ( isset( $types['comment'] ) ) {
193
				unset( $types['comment'] );
194
				self::$type_boxes['comment'][ $cmb_id ] = $cmb_id;
195
			}
196
197
			if ( isset( $types['term'] ) ) {
198
				unset( $types['term'] );
199
200
				$taxonomies = array_merge(
201
					$taxonomies,
202
					CMB2_Utils::ensure_array( $rest_box->cmb->prop( 'taxonomies' ) )
203
				);
204
205
				self::$type_boxes['term'][ $cmb_id ] = $cmb_id;
206
			}
207
208
			if ( ! empty( $types ) ) {
209
				$alltypes = array_merge( $alltypes, array_flip( $types ) );
210
				self::$type_boxes['post'][ $cmb_id ] = $cmb_id;
211
			}
212
		}
213
214
		$alltypes = array_unique( $alltypes );
215
216
		if ( ! empty( $alltypes ) ) {
217
			self::register_rest_field( $alltypes, 'post' );
218
		}
219
220
		if ( ! empty( self::$type_boxes['user'] ) ) {
221
			self::register_rest_field( 'user', 'user' );
222
		}
223
224
		if ( ! empty( self::$type_boxes['comment'] ) ) {
225
			self::register_rest_field( 'comment', 'comment' );
226
		}
227
228
		if ( ! empty( self::$type_boxes['term'] ) ) {
229
			self::register_rest_field( $taxonomies, 'term' );
230
		}
231
	}
232
233
	/**
234
	 * Wrapper for register_rest_field.
235
	 *
236
	 * @since  2.2.3
237
	 *
238
	 * @param string|array $object_types Object(s) the field is being registered
239
	 *                                   to, "post"|"term"|"comment" etc.
240
	 * @param string       $object_types       Canonical object type for callbacks.
241
	 *
242
	 * @return void
243
	 */
244
	protected static function register_rest_field( $object_types, $object_type ) {
245
		register_rest_field( $object_types, 'cmb2', array(
246
			'get_callback'    => array( __CLASS__, "get_{$object_type}_rest_values" ),
247
			'update_callback' => array( __CLASS__, "update_{$object_type}_rest_values" ),
248
			'schema'          => null, // @todo add schema
249
		) );
250
	}
251
252
	/**
253
	 * Setup readable and editable fields.
254
	 *
255
	 * @since  2.2.3
256
	 *
257
	 * @return void
258
	 */
259
	protected function declare_read_edit_fields() {
260
		foreach ( $this->cmb->prop( 'fields' ) as $field ) {
261
			$show_in_rest = isset( $field['show_in_rest'] ) ? $field['show_in_rest'] : null;
262
263
			if ( false === $show_in_rest ) {
264
				continue;
265
			}
266
267
			if ( $this->can_read( $show_in_rest ) ) {
268
				$this->read_fields[] = $field['id'];
269
			}
270
271
			if ( $this->can_edit( $show_in_rest ) ) {
272
				$this->edit_fields[] = $field['id'];
273
			}
274
		}
275
	}
276
277
	/**
278
	 * Determines if a field is readable based on it's show_in_rest value
279
	 * and the box's show_in_rest value.
280
	 *
281
	 * @since  2.2.3
282
	 *
283
	 * @param  bool $show_in_rest Field's show_in_rest value. Default null.
284
	 *
285
	 * @return bool               Whether field is readable.
286
	 */
287
	protected function can_read( $show_in_rest ) {
288
		// if 'null', then use default box value.
289
		if ( null === $show_in_rest ) {
290
			return $this->rest_read;
291
		}
292
293
		// Else check if the value represents readable.
294
		return self::is_readable( $show_in_rest );
295
	}
296
297
	/**
298
	 * Determines if a field is editable based on it's show_in_rest value
299
	 * and the box's show_in_rest value.
300
	 *
301
	 * @since  2.2.3
302
	 *
303
	 * @param  bool $show_in_rest Field's show_in_rest value. Default null.
304
	 *
305
	 * @return bool               Whether field is editable.
306
	 */
307
	protected function can_edit( $show_in_rest ) {
308
		// if 'null', then use default box value.
309
		if ( null === $show_in_rest ) {
310
			return $this->rest_edit;
311
		}
312
313
		// Else check if the value represents editable.
314
		return self::is_editable( $show_in_rest );
315
	}
316
317
	/**
318
	 * Handler for getting post custom field data.
319
	 *
320
	 * @since  2.2.3
321
	 *
322
	 * @param  array           $object      The object data from the response
323
	 * @param  string          $field_name  Name of field
324
	 * @param  WP_REST_Request $request     Current request
325
	 * @param  string          $object_type The request object type
326
	 *
327
	 * @return mixed
328
	 */
329
	public static function get_post_rest_values( $object, $field_name, $request, $object_type ) {
330
		if ( 'cmb2' === $field_name ) {
331
			return self::get_rest_values( $object, $request, $object_type, 'post' );
332
		}
333
	}
334
335
	/**
336
	 * Handler for getting user custom field data.
337
	 *
338
	 * @since  2.2.3
339
	 *
340
	 * @param  array           $object      The object data from the response
341
	 * @param  string          $field_name  Name of field
342
	 * @param  WP_REST_Request $request     Current request
343
	 * @param  string          $object_type The request object type
344
	 *
345
	 * @return mixed
346
	 */
347
	public static function get_user_rest_values( $object, $field_name, $request, $object_type ) {
348
		if ( 'cmb2' === $field_name ) {
349
			return self::get_rest_values( $object, $request, $object_type, 'user' );
350
		}
351
	}
352
353
	/**
354
	 * Handler for getting comment custom field data.
355
	 *
356
	 * @since  2.2.3
357
	 *
358
	 * @param  array           $object      The object data from the response
359
	 * @param  string          $field_name  Name of field
360
	 * @param  WP_REST_Request $request     Current request
361
	 * @param  string          $object_type The request object type
362
	 *
363
	 * @return mixed
364
	 */
365
	public static function get_comment_rest_values( $object, $field_name, $request, $object_type ) {
366
		if ( 'cmb2' === $field_name ) {
367
			return self::get_rest_values( $object, $request, $object_type, 'comment' );
368
		}
369
	}
370
371
	/**
372
	 * Handler for getting term custom field data.
373
	 *
374
	 * @since  2.2.3
375
	 *
376
	 * @param  array           $object      The object data from the response
377
	 * @param  string          $field_name  Name of field
378
	 * @param  WP_REST_Request $request     Current request
379
	 * @param  string          $object_type The request object type
380
	 *
381
	 * @return mixed
382
	 */
383
	public static function get_term_rest_values( $object, $field_name, $request, $object_type ) {
384
		if ( 'cmb2' === $field_name ) {
385
			return self::get_rest_values( $object, $request, $object_type, 'term' );
386
		}
387
	}
388
389
	/**
390
	 * Handler for getting custom field data.
391
	 *
392
	 * @since  2.2.3
393
	 *
394
	 * @param  array           $object           The object data from the response
395
	 * @param  WP_REST_Request $request          Current request
396
	 * @param  string          $object_type      The request object type
397
	 * @param  string          $main_object_type The cmb main object type
398
	 *
399
	 * @return mixed
400
	 */
401
	protected static function get_rest_values( $object, $request, $object_type, $main_object_type = 'post' ) {
402
		if ( ! isset( $object['id'] ) ) {
403
			return;
404
		}
405
406
		$values = array();
407
408
		if ( ! empty( self::$type_boxes[ $main_object_type ] ) ) {
409
			foreach ( self::$type_boxes[ $main_object_type ] as $cmb_id ) {
410
				$rest_box = self::$boxes[ $cmb_id ];
411
412
				if ( ! $rest_box->cmb->is_box_type( $object_type ) ) {
413
					continue;
414
				}
415
416
				$result = self::get_box_rest_values( $rest_box, $object['id'], $main_object_type );
417
				if ( ! empty( $result ) ) {
418
					if ( empty( $values[ $cmb_id ] ) ) {
419
						$values[ $cmb_id ] = $result;
420
					} else {
421
						$values[ $cmb_id ] = array_merge( $values[ $cmb_id ], $result );
422
					}
423
				}
424
			}
425
		}
426
427
		return $values;
428
	}
429
430
	/**
431
	 * Get box rest values.
432
	 *
433
	 * @since  2.7.0
434
	 *
435
	 * @param  CMB2_REST $rest_box         The CMB2_REST object.
436
	 * @param  integer   $object_id        The object ID.
437
	 * @param  string    $main_object_type The object type (post, user, term, etc)
438
	 *
439
	 * @return array                       Array of box rest values.
440
	 */
441
	public static function get_box_rest_values( $rest_box, $object_id = 0, $main_object_type = 'post' ) {
442
443
		$rest_box->cmb->object_id( $object_id );
444
		$rest_box->cmb->object_type( $main_object_type );
445
446
		$values = array();
447
448
		foreach ( $rest_box->read_fields as $field_id ) {
449
			$field = $rest_box->cmb->get_field( $field_id );
450
			$field->object_id( $object_id );
451
			$field->object_type( $main_object_type );
452
453
			$values[ $field->id( true ) ] = $field->get_rest_value();
454
455
			if ( $field->args( 'has_supporting_data' ) ) {
456
				$field = $field->get_supporting_field();
457
				$values[ $field->id( true ) ] = $field->get_rest_value();
458
			}
459
		}
460
461
		return $values;
462
	}
463
464
	/**
465
	 * Handler for updating post custom field data.
466
	 *
467
	 * @since  2.2.3
468
	 *
469
	 * @param  mixed           $values      The value of the field
470
	 * @param  object          $object      The object from the response
471
	 * @param  string          $field_name  Name of field
472
	 * @param  WP_REST_Request $request     Current request
473
	 * @param  string          $object_type The request object type
474
	 *
475
	 * @return bool|int
476
	 */
477
	public static function update_post_rest_values( $values, $object, $field_name, $request, $object_type ) {
478
		if ( 'cmb2' === $field_name ) {
479
			return self::update_rest_values( $values, $object, $request, $object_type, 'post' );
480
		}
481
	}
482
483
	/**
484
	 * Handler for updating user custom field data.
485
	 *
486
	 * @since  2.2.3
487
	 *
488
	 * @param  mixed           $values      The value of the field
489
	 * @param  object          $object      The object from the response
490
	 * @param  string          $field_name  Name of field
491
	 * @param  WP_REST_Request $request     Current request
492
	 * @param  string          $object_type The request object type
493
	 *
494
	 * @return bool|int
495
	 */
496
	public static function update_user_rest_values( $values, $object, $field_name, $request, $object_type ) {
497
		if ( 'cmb2' === $field_name ) {
498
			return self::update_rest_values( $values, $object, $request, $object_type, 'user' );
499
		}
500
	}
501
502
	/**
503
	 * Handler for updating comment custom field data.
504
	 *
505
	 * @since  2.2.3
506
	 *
507
	 * @param  mixed           $values      The value of the field
508
	 * @param  object          $object      The object from the response
509
	 * @param  string          $field_name  Name of field
510
	 * @param  WP_REST_Request $request     Current request
511
	 * @param  string          $object_type The request object type
512
	 *
513
	 * @return bool|int
514
	 */
515
	public static function update_comment_rest_values( $values, $object, $field_name, $request, $object_type ) {
516
		if ( 'cmb2' === $field_name ) {
517
			return self::update_rest_values( $values, $object, $request, $object_type, 'comment' );
518
		}
519
	}
520
521
	/**
522
	 * Handler for updating term custom field data.
523
	 *
524
	 * @since  2.2.3
525
	 *
526
	 * @param  mixed           $values      The value of the field
527
	 * @param  object          $object      The object from the response
528
	 * @param  string          $field_name  Name of field
529
	 * @param  WP_REST_Request $request     Current request
530
	 * @param  string          $object_type The request object type
531
	 *
532
	 * @return bool|int
533
	 */
534
	public static function update_term_rest_values( $values, $object, $field_name, $request, $object_type ) {
535
		if ( 'cmb2' === $field_name ) {
536
			return self::update_rest_values( $values, $object, $request, $object_type, 'term' );
537
		}
538
	}
539
540
	/**
541
	 * Handler for updating custom field data.
542
	 *
543
	 * @since  2.2.3
544
	 *
545
	 * @param  mixed           $values           The value of the field
546
	 * @param  object          $object           The object from the response
547
	 * @param  WP_REST_Request $request          Current request
548
	 * @param  string          $object_type      The request object type
549
	 * @param  string          $main_object_type The cmb main object type
550
	 *
551
	 * @return bool|int
552
	 */
553
	protected static function update_rest_values( $values, $object, $request, $object_type, $main_object_type = 'post' ) {
554
		if ( empty( $values ) || ! is_array( $values ) ) {
555
			return;
556
		}
557
558
		$object_id = self::get_object_id( $object, $main_object_type );
559
560
		if ( ! $object_id ) {
561
			return;
562
		}
563
564
		$updated = array();
565
566
		if ( ! empty( self::$type_boxes[ $main_object_type ] ) ) {
567
			foreach ( self::$type_boxes[ $main_object_type ] as $cmb_id ) {
568
				$result = self::santize_box_rest_values( $values, self::$boxes[ $cmb_id ], $object_id, $main_object_type );
569
				if ( ! empty( $result ) ) {
570
					$updated[ $cmb_id ] = $result;
571
				}
572
			}
573
		}
574
575
		return $updated;
576
	}
577
578
	/**
579
	 * Updates box rest values.
580
	 *
581
	 * @since  2.7.0
582
	 *
583
	 * @param  array     $values           Array of values.
584
	 * @param  CMB2_REST $rest_box         The CMB2_REST object.
585
	 * @param  integer   $object_id        The object ID.
586
	 * @param  string    $main_object_type The object type (post, user, term, etc)
587
	 *
588
	 * @return mixed|bool                  Array of updated statuses if successful.
589
	 */
590
	public static function santize_box_rest_values( $values, $rest_box, $object_id = 0, $main_object_type = 'post' ) {
591
592
		if ( ! array_key_exists( $rest_box->cmb->cmb_id, $values ) ) {
593
			return false;
594
		}
595
596
		$rest_box->cmb->object_id( $object_id );
597
		$rest_box->cmb->object_type( $main_object_type );
598
599
		return $rest_box->sanitize_box_values( $values );
600
	}
601
602
	/**
603
	 * Loop through box fields and sanitize the values.
604
	 *
605
	 * @since  2.2.o
606
	 *
607
	 * @param  array $values Array of values being provided.
608
	 * @return array           Array of updated/sanitized values.
609
	 */
610
	public function sanitize_box_values( array $values ) {
611
		$updated = array();
612
613
		$this->cmb->pre_process();
614
615
		foreach ( $this->edit_fields as $field_id ) {
616
			$updated[ $field_id ] = $this->sanitize_field_value( $values, $field_id );
617
		}
618
619
		$this->cmb->after_save();
620
621
		return $updated;
622
	}
623
624
	/**
625
	 * Handles returning a sanitized field value.
626
	 *
627
	 * @since  2.2.3
628
	 *
629
	 * @param  array  $values   Array of values being provided.
630
	 * @param  string $field_id The id of the field to update.
631
	 *
632
	 * @return mixed             The results of saving/sanitizing a field value.
633
	 */
634
	protected function sanitize_field_value( array $values, $field_id ) {
635
		if ( ! array_key_exists( $field_id, $values[ $this->cmb->cmb_id ] ) ) {
636
			return;
637
		}
638
639
		$field = $this->cmb->get_field( $field_id );
640
641
		if ( 'title' == $field->type() ) {
642
			return;
643
		}
644
645
		$field->object_id( $this->cmb->object_id() );
646
		$field->object_type( $this->cmb->object_type() );
647
648
		if ( 'group' == $field->type() ) {
649
			return $this->sanitize_group_value( $values, $field );
650
		}
651
652
		return $field->save_field( $values[ $this->cmb->cmb_id ][ $field_id ] );
653
	}
654
655
	/**
656
	 * Handles returning a sanitized group field value.
657
	 *
658
	 * @since  2.2.3
659
	 *
660
	 * @param  array      $values Array of values being provided.
661
	 * @param  CMB2_Field $field  CMB2_Field object.
662
	 *
663
	 * @return mixed               The results of saving/sanitizing the group field value.
664
	 */
665
	protected function sanitize_group_value( array $values, CMB2_Field $field ) {
666
		$fields = $field->fields();
667
		if ( empty( $fields ) ) {
668
			return;
669
		}
670
671
		$this->cmb->data_to_save[ $field->_id( '', false ) ] = $values[ $this->cmb->cmb_id ][ $field->_id( '', false ) ];
672
673
		return $this->cmb->save_group_field( $field );
674
	}
675
676
	/**
677
	 * Filter whether a meta key is protected.
678
	 *
679
	 * @since 2.2.3
680
	 *
681
	 * @param bool   $protected Whether the key is protected. Default false.
682
	 * @param string $meta_key  Meta key.
683
	 * @param string $meta_type Meta type.
684
	 */
685
	public function is_protected_meta( $protected, $meta_key, $meta_type ) {
686
		if ( $this->field_can_edit( $meta_key ) ) {
687
			return false;
688
		}
689
690
		return $protected;
691
	}
692
693
	/**
694
	 * Get the object ID for the given object/type.
695
	 *
696
	 * @since  2.2.3
697
	 *
698
	 * @param  mixed  $object      The object to get the ID for.
699
	 * @param  string $object_type The object type we are looking for.
700
	 *
701
	 * @return int                 The object ID if found.
702
	 */
703
	public static function get_object_id( $object, $object_type = 'post' ) {
704
		switch ( $object_type ) {
705
			case 'user':
706
			case 'post':
707
				if ( isset( $object->ID ) ) {
708
					return intval( $object->ID );
709
				}
710
			case 'comment':
711
				if ( isset( $object->comment_ID ) ) {
712
					return intval( $object->comment_ID );
713
				}
714
			case 'term':
715
				if ( is_array( $object ) && isset( $object['term_id'] ) ) {
716
					return intval( $object['term_id'] );
717
				} elseif ( isset( $object->term_id ) ) {
718
					return intval( $object->term_id );
719
				}
720
		}
721
722
		return 0;
723
	}
724
725
	/**
726
	 * Checks if a given field can be read.
727
	 *
728
	 * @since  2.2.3
729
	 *
730
	 * @param  string|CMB2_Field $field_id      Field ID or CMB2_Field object.
731
	 * @param  boolean           $return_object Whether to return the Field object.
732
	 *
733
	 * @return mixed                            False if field can't be read or true|CMB2_Field object.
734
	 */
735
	public function field_can_read( $field_id, $return_object = false ) {
736
		return $this->field_can( 'read_fields', $field_id, $return_object );
737
	}
738
739
	/**
740
	 * Checks if a given field can be edited.
741
	 *
742
	 * @since  2.2.3
743
	 *
744
	 * @param  string|CMB2_Field $field_id      Field ID or CMB2_Field object.
745
	 * @param  boolean           $return_object Whether to return the Field object.
746
	 *
747
	 * @return mixed                            False if field can't be edited or true|CMB2_Field object.
748
	 */
749
	public function field_can_edit( $field_id, $return_object = false ) {
750
		return $this->field_can( 'edit_fields', $field_id, $return_object );
751
	}
752
753
	/**
754
	 * Checks if a given field can be read or edited.
755
	 *
756
	 * @since  2.2.3
757
	 *
758
	 * @param  string            $type          Whether we are checking for read or edit fields.
759
	 * @param  string|CMB2_Field $field_id      Field ID or CMB2_Field object.
760
	 * @param  boolean           $return_object Whether to return the Field object.
761
	 *
762
	 * @return mixed                            False if field can't be read or edited or true|CMB2_Field object.
763
	 */
764
	protected function field_can( $type = 'read_fields', $field_id, $return_object = false ) {
765
		if ( ! in_array( $field_id instanceof CMB2_Field ? $field_id->id() : $field_id, $this->{$type}, true ) ) {
766
			return false;
767
		}
768
769
		return $return_object ? $this->cmb->get_field( $field_id ) : true;
770
	}
771
772
	/**
773
	 * Get a CMB2_REST instance object from the registry by a CMB2 id.
774
	 *
775
	 * @since  2.2.3
776
	 *
777
	 * @param  string $cmb_id CMB2 config id
778
	 *
779
	 * @return CMB2_REST|false The CMB2_REST object or false.
780
	 */
781
	public static function get_rest_box( $cmb_id ) {
782
		return isset( self::$boxes[ $cmb_id ] ) ? self::$boxes[ $cmb_id ] : false;
783
	}
784
785
	/**
786
	 * Remove a CMB2_REST instance object from the registry.
787
	 *
788
	 * @since  2.2.3
789
	 *
790
	 * @param string $cmb_id A CMB2 instance id.
791
	 */
792
	public static function remove( $cmb_id ) {
793
		if ( array_key_exists( $cmb_id, self::$boxes ) ) {
794
			unset( self::$boxes[ $cmb_id ] );
795
		}
796
	}
797
798
	/**
799
	 * Retrieve all CMB2_REST instances from the registry.
800
	 *
801
	 * @since  2.2.3
802
	 * @return CMB2[] Array of all registered CMB2_REST instances.
803
	 */
804
	public static function get_all() {
805
		return self::$boxes;
806
	}
807
808
	/**
809
	 * Checks if given value is readable.
810
	 *
811
	 * Value is considered readable if it is not empty and if it does not match the editable blacklist.
812
	 *
813
	 * @since  2.2.3
814
	 *
815
	 * @param  mixed $value Value to check.
816
	 *
817
	 * @return boolean       Whether value is considered readable.
818
	 */
819
	public static function is_readable( $value ) {
820
		return ! empty( $value ) && ! in_array( $value, array(
821
			WP_REST_Server::CREATABLE,
822
			WP_REST_Server::EDITABLE,
823
			WP_REST_Server::DELETABLE,
824
		), true );
825
	}
826
827
	/**
828
	 * Checks if given value is editable.
829
	 *
830
	 * Value is considered editable if matches the editable whitelist.
831
	 *
832
	 * @since  2.2.3
833
	 *
834
	 * @param  mixed $value Value to check.
835
	 *
836
	 * @return boolean       Whether value is considered editable.
837
	 */
838
	public static function is_editable( $value ) {
839
		return in_array( $value, array(
840
			WP_REST_Server::EDITABLE,
841
			WP_REST_Server::ALLMETHODS,
842
		), true );
843
	}
844
845
	/**
846
	 * Magic getter for our object.
847
	 *
848
	 * @param string $field
849
	 * @throws Exception Throws an exception if the field is invalid.
850
	 *
851
	 * @return mixed
852
	 */
853
	public function __get( $field ) {
854
		switch ( $field ) {
855
			case 'read_fields':
856
			case 'edit_fields':
857
			case 'rest_read':
858
			case 'rest_edit':
859
				return $this->{$field};
860
			default:
861
				throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field );
862
		}
863
	}
864
865
}
866