[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * REST API: WP_REST_Settings_Controller class 4 * 5 * @package WordPress 6 * @subpackage REST_API 7 * @since 4.7.0 8 */ 9 10 /** 11 * Core class used to manage a site's settings via the REST API. 12 * 13 * @since 4.7.0 14 * 15 * @see WP_REST_Controller 16 */ 17 class WP_REST_Settings_Controller extends WP_REST_Controller { 18 19 /** 20 * Constructor. 21 * 22 * @since 4.7.0 23 */ 24 public function __construct() { 25 $this->namespace = 'wp/v2'; 26 $this->rest_base = 'settings'; 27 } 28 29 /** 30 * Registers the routes for the site's settings. 31 * 32 * @since 4.7.0 33 * 34 * @see register_rest_route() 35 */ 36 public function register_routes() { 37 38 register_rest_route( 39 $this->namespace, 40 '/' . $this->rest_base, 41 array( 42 array( 43 'methods' => WP_REST_Server::READABLE, 44 'callback' => array( $this, 'get_item' ), 45 'args' => array(), 46 'permission_callback' => array( $this, 'get_item_permissions_check' ), 47 ), 48 array( 49 'methods' => WP_REST_Server::EDITABLE, 50 'callback' => array( $this, 'update_item' ), 51 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), 52 'permission_callback' => array( $this, 'get_item_permissions_check' ), 53 ), 54 'schema' => array( $this, 'get_public_item_schema' ), 55 ) 56 ); 57 58 } 59 60 /** 61 * Checks if a given request has access to read and manage settings. 62 * 63 * @since 4.7.0 64 * 65 * @param WP_REST_Request $request Full details about the request. 66 * @return bool True if the request has read access for the item, otherwise false. 67 */ 68 public function get_item_permissions_check( $request ) { 69 return current_user_can( 'manage_options' ); 70 } 71 72 /** 73 * Retrieves the settings. 74 * 75 * @since 4.7.0 76 * 77 * @param WP_REST_Request $request Full details about the request. 78 * @return array|WP_Error Array on success, or WP_Error object on failure. 79 */ 80 public function get_item( $request ) { 81 $options = $this->get_registered_options(); 82 $response = array(); 83 84 foreach ( $options as $name => $args ) { 85 /** 86 * Filters the value of a setting recognized by the REST API. 87 * 88 * Allow hijacking the setting value and overriding the built-in behavior by returning a 89 * non-null value. The returned value will be presented as the setting value instead. 90 * 91 * @since 4.7.0 92 * 93 * @param mixed $result Value to use for the requested setting. Can be a scalar 94 * matching the registered schema for the setting, or null to 95 * follow the default get_option() behavior. 96 * @param string $name Setting name (as shown in REST API responses). 97 * @param array $args Arguments passed to register_setting() for this setting. 98 */ 99 $response[ $name ] = apply_filters( 'rest_pre_get_setting', null, $name, $args ); 100 101 if ( is_null( $response[ $name ] ) ) { 102 // Default to a null value as "null" in the response means "not set". 103 $response[ $name ] = get_option( $args['option_name'], $args['schema']['default'] ); 104 } 105 106 /* 107 * Because get_option() is lossy, we have to 108 * cast values to the type they are registered with. 109 */ 110 $response[ $name ] = $this->prepare_value( $response[ $name ], $args['schema'] ); 111 } 112 113 return $response; 114 } 115 116 /** 117 * Prepares a value for output based off a schema array. 118 * 119 * @since 4.7.0 120 * 121 * @param mixed $value Value to prepare. 122 * @param array $schema Schema to match. 123 * @return mixed The prepared value. 124 */ 125 protected function prepare_value( $value, $schema ) { 126 /* 127 * If the value is not valid by the schema, set the value to null. 128 * Null values are specifically non-destructive, so this will not cause 129 * overwriting the current invalid value to null. 130 */ 131 if ( is_wp_error( rest_validate_value_from_schema( $value, $schema ) ) ) { 132 return null; 133 } 134 135 return rest_sanitize_value_from_schema( $value, $schema ); 136 } 137 138 /** 139 * Updates settings for the settings object. 140 * 141 * @since 4.7.0 142 * 143 * @param WP_REST_Request $request Full details about the request. 144 * @return array|WP_Error Array on success, or error object on failure. 145 */ 146 public function update_item( $request ) { 147 $options = $this->get_registered_options(); 148 149 $params = $request->get_params(); 150 151 foreach ( $options as $name => $args ) { 152 if ( ! array_key_exists( $name, $params ) ) { 153 continue; 154 } 155 156 /** 157 * Filters whether to preempt a setting value update via the REST API. 158 * 159 * Allows hijacking the setting update logic and overriding the built-in behavior by 160 * returning true. 161 * 162 * @since 4.7.0 163 * 164 * @param bool $result Whether to override the default behavior for updating the 165 * value of a setting. 166 * @param string $name Setting name (as shown in REST API responses). 167 * @param mixed $value Updated setting value. 168 * @param array $args Arguments passed to register_setting() for this setting. 169 */ 170 $updated = apply_filters( 'rest_pre_update_setting', false, $name, $request[ $name ], $args ); 171 172 if ( $updated ) { 173 continue; 174 } 175 176 /* 177 * A null value for an option would have the same effect as 178 * deleting the option from the database, and relying on the 179 * default value. 180 */ 181 if ( is_null( $request[ $name ] ) ) { 182 /* 183 * A null value is returned in the response for any option 184 * that has a non-scalar value. 185 * 186 * To protect clients from accidentally including the null 187 * values from a response object in a request, we do not allow 188 * options with values that don't pass validation to be updated to null. 189 * Without this added protection a client could mistakenly 190 * delete all options that have invalid values from the 191 * database. 192 */ 193 if ( is_wp_error( rest_validate_value_from_schema( get_option( $args['option_name'], false ), $args['schema'] ) ) ) { 194 return new WP_Error( 195 'rest_invalid_stored_value', 196 /* translators: %s: Property name. */ 197 sprintf( __( 'The %s property has an invalid stored value, and cannot be updated to null.' ), $name ), 198 array( 'status' => 500 ) 199 ); 200 } 201 202 delete_option( $args['option_name'] ); 203 } else { 204 update_option( $args['option_name'], $request[ $name ] ); 205 } 206 } 207 208 return $this->get_item( $request ); 209 } 210 211 /** 212 * Retrieves all of the registered options for the Settings API. 213 * 214 * @since 4.7.0 215 * 216 * @return array Array of registered options. 217 */ 218 protected function get_registered_options() { 219 $rest_options = array(); 220 221 foreach ( get_registered_settings() as $name => $args ) { 222 if ( empty( $args['show_in_rest'] ) ) { 223 continue; 224 } 225 226 $rest_args = array(); 227 228 if ( is_array( $args['show_in_rest'] ) ) { 229 $rest_args = $args['show_in_rest']; 230 } 231 232 $defaults = array( 233 'name' => ! empty( $rest_args['name'] ) ? $rest_args['name'] : $name, 234 'schema' => array(), 235 ); 236 237 $rest_args = array_merge( $defaults, $rest_args ); 238 239 $default_schema = array( 240 'type' => empty( $args['type'] ) ? null : $args['type'], 241 'description' => empty( $args['description'] ) ? '' : $args['description'], 242 'default' => isset( $args['default'] ) ? $args['default'] : null, 243 ); 244 245 $rest_args['schema'] = array_merge( $default_schema, $rest_args['schema'] ); 246 $rest_args['option_name'] = $name; 247 248 // Skip over settings that don't have a defined type in the schema. 249 if ( empty( $rest_args['schema']['type'] ) ) { 250 continue; 251 } 252 253 /* 254 * Allow the supported types for settings, as we don't want invalid types 255 * to be updated with arbitrary values that we can't do decent sanitizing for. 256 */ 257 if ( ! in_array( $rest_args['schema']['type'], array( 'number', 'integer', 'string', 'boolean', 'array', 'object' ), true ) ) { 258 continue; 259 } 260 261 $rest_args['schema'] = $this->set_additional_properties_to_false( $rest_args['schema'] ); 262 263 $rest_options[ $rest_args['name'] ] = $rest_args; 264 } 265 266 return $rest_options; 267 } 268 269 /** 270 * Retrieves the site setting schema, conforming to JSON Schema. 271 * 272 * @since 4.7.0 273 * 274 * @return array Item schema data. 275 */ 276 public function get_item_schema() { 277 if ( $this->schema ) { 278 return $this->add_additional_fields_schema( $this->schema ); 279 } 280 281 $options = $this->get_registered_options(); 282 283 $schema = array( 284 '$schema' => 'http://json-schema.org/draft-04/schema#', 285 'title' => 'settings', 286 'type' => 'object', 287 'properties' => array(), 288 ); 289 290 foreach ( $options as $option_name => $option ) { 291 $schema['properties'][ $option_name ] = $option['schema']; 292 $schema['properties'][ $option_name ]['arg_options'] = array( 293 'sanitize_callback' => array( $this, 'sanitize_callback' ), 294 ); 295 } 296 297 $this->schema = $schema; 298 299 return $this->add_additional_fields_schema( $this->schema ); 300 } 301 302 /** 303 * Custom sanitize callback used for all options to allow the use of 'null'. 304 * 305 * By default, the schema of settings will throw an error if a value is set to 306 * `null` as it's not a valid value for something like "type => string". We 307 * provide a wrapper sanitizer to allow the use of `null`. 308 * 309 * @since 4.7.0 310 * 311 * @param mixed $value The value for the setting. 312 * @param WP_REST_Request $request The request object. 313 * @param string $param The parameter name. 314 * @return mixed|WP_Error 315 */ 316 public function sanitize_callback( $value, $request, $param ) { 317 if ( is_null( $value ) ) { 318 return $value; 319 } 320 321 return rest_parse_request_arg( $value, $request, $param ); 322 } 323 324 /** 325 * Recursively add additionalProperties = false to all objects in a schema. 326 * 327 * This is need to restrict properties of objects in settings values to only 328 * registered items, as the REST API will allow additional properties by 329 * default. 330 * 331 * @since 4.9.0 332 * 333 * @param array $schema The schema array. 334 * @return array 335 */ 336 protected function set_additional_properties_to_false( $schema ) { 337 switch ( $schema['type'] ) { 338 case 'object': 339 foreach ( $schema['properties'] as $key => $child_schema ) { 340 $schema['properties'][ $key ] = $this->set_additional_properties_to_false( $child_schema ); 341 } 342 343 $schema['additionalProperties'] = false; 344 break; 345 case 'array': 346 $schema['items'] = $this->set_additional_properties_to_false( $schema['items'] ); 347 break; 348 } 349 350 return $schema; 351 } 352 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Dec 22 01:00:02 2024 | Cross-referenced by PHPXref 0.7.1 |