[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/rest-api/endpoints/ -> class-wp-rest-settings-controller.php (source)

   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 objects of the controller.
  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.
 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               * Whitelist 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 whitelist 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  }


Generated: Sat Apr 4 01:00:03 2020 Cross-referenced by PHPXref 0.7.1