WebDevStudios /
CMB2
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 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.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_REST extends CMB2_Hookup_Base { |
||
|
0 ignored issues
–
show
|
|||
| 15 | |||
| 16 | /** |
||
| 17 | * The current CMB2 REST endpoint version |
||
| 18 | * @var string |
||
| 19 | * @since 2.2.0 |
||
| 20 | */ |
||
| 21 | const VERSION = '1'; |
||
| 22 | |||
| 23 | /** |
||
| 24 | * The CMB2 REST base namespace (v should always be followed by $version) |
||
| 25 | * @var string |
||
| 26 | * @since 2.2.0 |
||
| 27 | */ |
||
| 28 | const BASE = 'cmb2/v1'; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * @var CMB2[] objects |
||
| 32 | * @since 2.2.0 |
||
| 33 | */ |
||
| 34 | protected static $boxes; |
||
| 35 | |||
| 36 | /** |
||
| 37 | * Whether metabox fields can be read via REST API |
||
| 38 | * @var bool |
||
| 39 | * @since 2.2.0 |
||
| 40 | */ |
||
| 41 | protected $can_read = true; |
||
| 42 | |||
| 43 | /** |
||
| 44 | * Array of readable field objects. |
||
| 45 | * @var CMB2_Field[] |
||
| 46 | * @since 2.2.0 |
||
| 47 | */ |
||
| 48 | protected static $read_fields = array(); |
||
| 49 | |||
| 50 | /** |
||
| 51 | * Array of writeable field objects. |
||
| 52 | * @var CMB2_Field[] |
||
| 53 | * @since 2.2.0 |
||
| 54 | */ |
||
| 55 | protected static $write_fields = array(); |
||
| 56 | |||
| 57 | /** |
||
| 58 | * Whether metabox fields can be written via REST API |
||
| 59 | * @var bool |
||
| 60 | * @since 2.2.0 |
||
| 61 | */ |
||
| 62 | protected $can_write = false; |
||
| 63 | |||
| 64 | public function __construct( CMB2 $cmb ) { |
||
| 65 | $this->cmb = $cmb; |
||
| 66 | self::$boxes[ $cmb->cmb_id ] = $cmb; |
||
| 67 | |||
| 68 | $show_value = $this->cmb->prop( 'show_in_rest' ); |
||
| 69 | $this->cmb->rest_read = 'write_only' !== $show_value; |
||
|
0 ignored issues
–
show
The property
rest_read does not seem to exist in CMB2.
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. Loading history...
|
|||
| 70 | $this->cmb->rest_write = in_array( $show_value, array( 'read_and_write', 'write_only' ), true ); |
||
|
0 ignored issues
–
show
The property
rest_write does not seem to exist in CMB2.
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. Loading history...
|
|||
| 71 | } |
||
| 72 | |||
| 73 | public function universal_hooks() { |
||
| 74 | $this->once( 'rest_api_init', array( __CLASS__, 'register_fields' ), 50 ); |
||
| 75 | |||
| 76 | // hook up the CMB rest endpoint classes |
||
| 77 | $this->once( 'rest_api_init', array( $this, 'init_routes' ), 0 ); |
||
| 78 | |||
| 79 | $this->prepare_read_write_fields(); |
||
| 80 | |||
| 81 | add_filter( 'is_protected_meta', array( $this, 'is_protected_meta' ), 10, 3 ); |
||
| 82 | } |
||
| 83 | |||
| 84 | public function init_routes() { |
||
| 85 | global $wp_rest_server; |
||
|
0 ignored issues
–
show
Compatibility
Best Practice
introduced
by
Use of
global functionality is not recommended; it makes your code harder to test, and less reusable.
Instead of relying on 1. Pass all data via parametersfunction myFunction($a, $b) {
// Do something
}
2. Create a class that maintains your stateclass MyClass {
private $a;
private $b;
public function __construct($a, $b) {
$this->a = $a;
$this->b = $b;
}
public function myFunction() {
// Do something
}
}
Loading history...
|
|||
| 86 | |||
| 87 | $boxes_controller = new CMB2_REST_Controller_Boxes( $wp_rest_server ); |
||
| 88 | $boxes_controller->register_routes(); |
||
| 89 | |||
| 90 | $fields_controller = new CMB2_REST_Controller_Fields( $wp_rest_server ); |
||
| 91 | $fields_controller->register_routes(); |
||
| 92 | } |
||
| 93 | |||
| 94 | public static function register_fields() { |
||
| 95 | |||
| 96 | $types = array(); |
||
| 97 | foreach ( self::$boxes as $cmb_id => $cmb ) { |
||
| 98 | $types = array_merge( $types, $cmb->prop( 'object_types' ) ); |
||
| 99 | } |
||
| 100 | $types = array_unique( $types ); |
||
| 101 | |||
| 102 | register_api_field( |
||
| 103 | $types, |
||
| 104 | 'cmb2', |
||
| 105 | array( |
||
| 106 | 'get_callback' => array( __CLASS__, 'get_restable_field_values' ), |
||
| 107 | 'update_callback' => array( __CLASS__, 'update_restable_field_values' ), |
||
| 108 | 'schema' => null, |
||
| 109 | ) |
||
| 110 | ); |
||
| 111 | } |
||
| 112 | |||
| 113 | protected function prepare_read_write_fields() { |
||
| 114 | foreach ( $this->cmb->prop( 'fields' ) as $field ) { |
||
| 115 | $show_in_rest = isset( $field['show_in_rest'] ) ? $field['show_in_rest'] : null; |
||
| 116 | |||
| 117 | if ( false === $show_in_rest ) { |
||
| 118 | continue; |
||
| 119 | } |
||
| 120 | |||
| 121 | $this->maybe_add_read_field( $field['id'], $show_in_rest ); |
||
| 122 | $this->maybe_add_write_field( $field['id'], $show_in_rest ); |
||
| 123 | } |
||
| 124 | } |
||
| 125 | |||
| 126 | View Code Duplication | protected function maybe_add_read_field( $field_id, $show_in_rest ) { |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 127 | $can_read = $this->cmb->rest_read |
||
|
0 ignored issues
–
show
The property
rest_read does not exist on object<CMB2>. Since you implemented __get, maybe consider adding a @property annotation.
Since your code implements the magic getter <?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.");
}
}
}
If the property has read access only, you can use the @property-read 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...
|
|||
| 128 | ? 'write_only' !== $show_in_rest |
||
| 129 | : in_array( $show_in_rest, array( 'read_and_write', 'read_only' ), true ); |
||
| 130 | |||
| 131 | if ( $can_read ) { |
||
| 132 | self::$read_fields[ $this->cmb->cmb_id ][] = $field_id; |
||
| 133 | } |
||
| 134 | } |
||
| 135 | |||
| 136 | View Code Duplication | protected function maybe_add_write_field( $field_id, $show_in_rest ) { |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 137 | $can_update = $this->cmb->rest_write |
||
|
0 ignored issues
–
show
The property
rest_write does not exist on object<CMB2>. Since you implemented __get, maybe consider adding a @property annotation.
Since your code implements the magic getter <?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.");
}
}
}
If the property has read access only, you can use the @property-read 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...
|
|||
| 138 | ? 'read_only' !== $show_in_rest |
||
| 139 | : in_array( $show_in_rest, array( 'read_and_write', 'write_only' ), true ); |
||
| 140 | |||
| 141 | if ( $can_update ) { |
||
| 142 | self::$write_fields[ $this->cmb->cmb_id ][] = $field_id; |
||
| 143 | } |
||
| 144 | } |
||
| 145 | |||
| 146 | /** |
||
| 147 | * Handler for getting custom field data. |
||
| 148 | * @since 2.2.0 |
||
| 149 | * @param array $object The object from the response |
||
| 150 | * @param string $field_id Name of field |
||
| 151 | * @param WP_REST_Request $request Current request |
||
| 152 | * @return mixed |
||
| 153 | */ |
||
| 154 | public static function get_restable_field_values( $object, $field_id, $request ) { |
||
|
0 ignored issues
–
show
|
|||
| 155 | $values = array(); |
||
| 156 | if ( ! isset( $object['id'] ) ) { |
||
| 157 | return; |
||
| 158 | } |
||
| 159 | |||
| 160 | foreach ( self::$read_fields as $cmb_id => $fields ) { |
||
| 161 | foreach ( $fields as $field_id ) { |
||
|
0 ignored issues
–
show
|
|||
| 162 | $field = self::$boxes[ $cmb_id ]->get_field( $field_id ); |
||
| 163 | $field->object_id = $object['id']; |
||
| 164 | |||
| 165 | if ( isset( $object->type ) ) { |
||
| 166 | $field->object_type = $object->type; |
||
| 167 | } |
||
| 168 | |||
| 169 | $values[ $cmb_id ][ $field->id( true ) ] = $field->get_data(); |
||
| 170 | } |
||
| 171 | } |
||
| 172 | |||
| 173 | return $values; |
||
| 174 | } |
||
| 175 | |||
| 176 | /** |
||
| 177 | * Handler for updating custom field data. |
||
| 178 | * @since 2.2.0 |
||
| 179 | * @param mixed $value The value of the field |
||
|
0 ignored issues
–
show
There is no parameter named
$value. Did you maybe mean $values?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit. Consider the following example. The parameter /**
* @param array $germany
* @param array $ireland
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was changed, but the annotation was not. Loading history...
|
|||
| 180 | * @param object $object The object from the response |
||
| 181 | * @param string $field_id Name of field |
||
| 182 | * @return bool|int |
||
| 183 | */ |
||
| 184 | public static function update_restable_field_values( $values, $object, $field_id ) { |
||
| 185 | if ( empty( $values ) || ! is_array( $values ) || 'cmb2' !== $field_id ) { |
||
| 186 | return; |
||
| 187 | } |
||
| 188 | |||
| 189 | $data = self::get_object_data( $object ); |
||
| 190 | if ( ! $data ) { |
||
| 191 | return; |
||
| 192 | } |
||
| 193 | |||
| 194 | $object_id = $data['object_id']; |
||
| 195 | $object_type = $data['object_type']; |
||
| 196 | $updated = array(); |
||
| 197 | |||
| 198 | foreach ( self::$write_fields as $cmb_id => $fields ) { |
||
| 199 | if ( ! array_key_exists( $cmb_id, $values ) ) { |
||
| 200 | continue; |
||
| 201 | } |
||
| 202 | |||
| 203 | $cmb = self::$boxes[ $cmb_id ]; |
||
| 204 | |||
| 205 | $cmb->object_type( $object_id ); |
||
| 206 | $cmb->object_type( $object_type ); |
||
| 207 | |||
| 208 | $cmb->pre_process(); |
||
| 209 | |||
| 210 | foreach ( $fields as $field_id ) { |
||
| 211 | if ( ! array_key_exists( $field_id, $values[ $cmb_id ] ) ) { |
||
| 212 | continue; |
||
| 213 | } |
||
| 214 | |||
| 215 | $field = $cmb->get_field( $field_id ); |
||
| 216 | |||
| 217 | if ( 'title' == $field->type() ) { |
||
| 218 | continue; |
||
| 219 | } |
||
| 220 | |||
| 221 | $field->object_id = $object_id; |
||
| 222 | $field->object_type = $object_type; |
||
| 223 | |||
| 224 | if ( 'group' == $field->type() ) { |
||
| 225 | $fields = $field->fields(); |
||
| 226 | if ( empty( $fields ) ) { |
||
| 227 | continue; |
||
| 228 | } |
||
| 229 | |||
| 230 | $cmb->data_to_save[ $field_id ] = $values[ $cmb_id ][ $field_id ]; |
||
| 231 | $updated[ $cmb_id ][ $field_id ] = $cmb->save_group_field( $field ); |
||
|
0 ignored issues
–
show
$field is of type object<CMB2_Field>|false, but the function expects a array.
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...
|
|||
| 232 | |||
| 233 | } else { |
||
| 234 | $updated[ $cmb_id ][ $field_id ] = $field->save_field( $values[ $cmb_id ][ $field_id ] ); |
||
| 235 | } |
||
| 236 | |||
| 237 | } |
||
| 238 | |||
| 239 | $cmb->after_save(); |
||
| 240 | } |
||
| 241 | |||
| 242 | return $updated; |
||
| 243 | } |
||
| 244 | |||
| 245 | /** |
||
| 246 | * Filter whether a meta key is protected. |
||
| 247 | * @since 2.2.0 |
||
| 248 | * @param bool $protected Whether the key is protected. Default false. |
||
| 249 | * @param string $meta_key Meta key. |
||
| 250 | * @param string $meta_type Meta type. |
||
| 251 | */ |
||
| 252 | public function is_protected_meta( $protected, $meta_key, $meta_type ) { |
||
|
0 ignored issues
–
show
|
|||
| 253 | if ( $this->field_can_update( $meta_key ) ) { |
||
| 254 | return false; |
||
| 255 | } |
||
| 256 | |||
| 257 | return $protected; |
||
| 258 | } |
||
| 259 | |||
| 260 | public function field_can_update( $field_id ) { |
||
| 261 | |||
| 262 | $field = $this->cmb->get_field( $field_id ); |
||
| 263 | $show_in_rest = $field ? $field->args( 'show_in_rest' ) : 'no'; |
||
| 264 | $can_update = $this->cmb->rest_write |
||
|
0 ignored issues
–
show
The property
rest_write does not exist on object<CMB2>. Since you implemented __get, maybe consider adding a @property annotation.
Since your code implements the magic getter <?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.");
}
}
}
If the property has read access only, you can use the @property-read 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...
|
|||
| 265 | ? 'read_only' !== $show_in_rest |
||
| 266 | : in_array( $show_in_rest, array( 'read_and_write', 'write_only' ), true ); |
||
| 267 | |||
| 268 | return $can_update ? $field : false; |
||
| 269 | } |
||
| 270 | |||
| 271 | protected static function get_object_data( $object ) { |
||
| 272 | $object_id = 0; |
||
| 273 | if ( isset( $object->ID ) ) { |
||
| 274 | $object_id = intval( $object->ID ); |
||
| 275 | $object_type = isset( $object->user_login ) ? 'user' : 'post'; |
||
| 276 | } elseif ( isset( $object->comment_ID ) ) { |
||
| 277 | $object_id = intval( $object->comment_ID ); |
||
| 278 | $object_type = 'comment'; |
||
| 279 | } elseif ( is_array( $object ) && isset( $object['term_id'] ) ) { |
||
| 280 | $object_id = intval( $object['term_id'] ); |
||
| 281 | $object_type = 'term'; |
||
| 282 | } elseif ( isset( $object->term_id ) ) { |
||
| 283 | $object_id = intval( $object->term_id ); |
||
| 284 | $object_type = 'term'; |
||
| 285 | } |
||
| 286 | |||
| 287 | if ( empty( $object_id ) ) { |
||
| 288 | return false; |
||
| 289 | } |
||
| 290 | |||
| 291 | return compact( 'object_id', 'object_type' ); |
||
| 292 | } |
||
| 293 | |||
| 294 | } |
||
| 295 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.