[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/rest-api/ -> class-wp-rest-request.php (source)

   1  <?php
   2  /**
   3   * REST API: WP_REST_Request class
   4   *
   5   * @package WordPress
   6   * @subpackage REST_API
   7   * @since 4.4.0
   8   */
   9  
  10  /**
  11   * Core class used to implement a REST request object.
  12   *
  13   * Contains data from the request, to be passed to the callback.
  14   *
  15   * Note: This implements ArrayAccess, and acts as an array of parameters when
  16   * used in that manner. It does not use ArrayObject (as we cannot rely on SPL),
  17   * so be aware it may have non-array behaviour in some cases.
  18   *
  19   * Note: When using features provided by ArrayAccess, be aware that WordPress deliberately
  20   * does not distinguish between arguments of the same name for different request methods.
  21   * For instance, in a request with `GET id=1` and `POST id=2`, `$request['id']` will equal
  22   * 2 (`POST`) not 1 (`GET`). For more precision between request methods, use
  23   * WP_REST_Request::get_body_params(), WP_REST_Request::get_url_params(), etc.
  24   *
  25   * @since 4.4.0
  26   *
  27   * @link https://secure.php.net/manual/en/class.arrayaccess.php
  28   */
  29  class WP_REST_Request implements ArrayAccess {
  30  
  31      /**
  32       * HTTP method.
  33       *
  34       * @since 4.4.0
  35       * @var string
  36       */
  37      protected $method = '';
  38  
  39      /**
  40       * Parameters passed to the request.
  41       *
  42       * These typically come from the `$_GET`, `$_POST` and `$_FILES`
  43       * superglobals when being created from the global scope.
  44       *
  45       * @since 4.4.0
  46       * @var array Contains GET, POST and FILES keys mapping to arrays of data.
  47       */
  48      protected $params;
  49  
  50      /**
  51       * HTTP headers for the request.
  52       *
  53       * @since 4.4.0
  54       * @var array Map of key to value. Key is always lowercase, as per HTTP specification.
  55       */
  56      protected $headers = array();
  57  
  58      /**
  59       * Body data.
  60       *
  61       * @since 4.4.0
  62       * @var string Binary data from the request.
  63       */
  64      protected $body = null;
  65  
  66      /**
  67       * Route matched for the request.
  68       *
  69       * @since 4.4.0
  70       * @var string
  71       */
  72      protected $route;
  73  
  74      /**
  75       * Attributes (options) for the route that was matched.
  76       *
  77       * This is the options array used when the route was registered, typically
  78       * containing the callback as well as the valid methods for the route.
  79       *
  80       * @since 4.4.0
  81       * @var array Attributes for the request.
  82       */
  83      protected $attributes = array();
  84  
  85      /**
  86       * Used to determine if the JSON data has been parsed yet.
  87       *
  88       * Allows lazy-parsing of JSON data where possible.
  89       *
  90       * @since 4.4.0
  91       * @var bool
  92       */
  93      protected $parsed_json = false;
  94  
  95      /**
  96       * Used to determine if the body data has been parsed yet.
  97       *
  98       * @since 4.4.0
  99       * @var bool
 100       */
 101      protected $parsed_body = false;
 102  
 103      /**
 104       * Constructor.
 105       *
 106       * @since 4.4.0
 107       *
 108       * @param string $method     Optional. Request method. Default empty.
 109       * @param string $route      Optional. Request route. Default empty.
 110       * @param array  $attributes Optional. Request attributes. Default empty array.
 111       */
 112  	public function __construct( $method = '', $route = '', $attributes = array() ) {
 113          $this->params = array(
 114              'URL'      => array(),
 115              'GET'      => array(),
 116              'POST'     => array(),
 117              'FILES'    => array(),
 118  
 119              // See parse_json_params.
 120              'JSON'     => null,
 121  
 122              'defaults' => array(),
 123          );
 124  
 125          $this->set_method( $method );
 126          $this->set_route( $route );
 127          $this->set_attributes( $attributes );
 128      }
 129  
 130      /**
 131       * Retrieves the HTTP method for the request.
 132       *
 133       * @since 4.4.0
 134       *
 135       * @return string HTTP method.
 136       */
 137  	public function get_method() {
 138          return $this->method;
 139      }
 140  
 141      /**
 142       * Sets HTTP method for the request.
 143       *
 144       * @since 4.4.0
 145       *
 146       * @param string $method HTTP method.
 147       */
 148  	public function set_method( $method ) {
 149          $this->method = strtoupper( $method );
 150      }
 151  
 152      /**
 153       * Retrieves all headers from the request.
 154       *
 155       * @since 4.4.0
 156       *
 157       * @return array Map of key to value. Key is always lowercase, as per HTTP specification.
 158       */
 159  	public function get_headers() {
 160          return $this->headers;
 161      }
 162  
 163      /**
 164       * Canonicalizes the header name.
 165       *
 166       * Ensures that header names are always treated the same regardless of
 167       * source. Header names are always case insensitive.
 168       *
 169       * Note that we treat `-` (dashes) and `_` (underscores) as the same
 170       * character, as per header parsing rules in both Apache and nginx.
 171       *
 172       * @link https://stackoverflow.com/q/18185366
 173       * @link https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#missing-disappearing-http-headers
 174       * @link https://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers
 175       *
 176       * @since 4.4.0
 177       *
 178       * @param string $key Header name.
 179       * @return string Canonicalized name.
 180       */
 181  	public static function canonicalize_header_name( $key ) {
 182          $key = strtolower( $key );
 183          $key = str_replace( '-', '_', $key );
 184  
 185          return $key;
 186      }
 187  
 188      /**
 189       * Retrieves the given header from the request.
 190       *
 191       * If the header has multiple values, they will be concatenated with a comma
 192       * as per the HTTP specification. Be aware that some non-compliant headers
 193       * (notably cookie headers) cannot be joined this way.
 194       *
 195       * @since 4.4.0
 196       *
 197       * @param string $key Header name, will be canonicalized to lowercase.
 198       * @return string|null String value if set, null otherwise.
 199       */
 200  	public function get_header( $key ) {
 201          $key = $this->canonicalize_header_name( $key );
 202  
 203          if ( ! isset( $this->headers[ $key ] ) ) {
 204              return null;
 205          }
 206  
 207          return implode( ',', $this->headers[ $key ] );
 208      }
 209  
 210      /**
 211       * Retrieves header values from the request.
 212       *
 213       * @since 4.4.0
 214       *
 215       * @param string $key Header name, will be canonicalized to lowercase.
 216       * @return array|null List of string values if set, null otherwise.
 217       */
 218  	public function get_header_as_array( $key ) {
 219          $key = $this->canonicalize_header_name( $key );
 220  
 221          if ( ! isset( $this->headers[ $key ] ) ) {
 222              return null;
 223          }
 224  
 225          return $this->headers[ $key ];
 226      }
 227  
 228      /**
 229       * Sets the header on request.
 230       *
 231       * @since 4.4.0
 232       *
 233       * @param string $key   Header name.
 234       * @param string $value Header value, or list of values.
 235       */
 236  	public function set_header( $key, $value ) {
 237          $key   = $this->canonicalize_header_name( $key );
 238          $value = (array) $value;
 239  
 240          $this->headers[ $key ] = $value;
 241      }
 242  
 243      /**
 244       * Appends a header value for the given header.
 245       *
 246       * @since 4.4.0
 247       *
 248       * @param string $key   Header name.
 249       * @param string $value Header value, or list of values.
 250       */
 251  	public function add_header( $key, $value ) {
 252          $key   = $this->canonicalize_header_name( $key );
 253          $value = (array) $value;
 254  
 255          if ( ! isset( $this->headers[ $key ] ) ) {
 256              $this->headers[ $key ] = array();
 257          }
 258  
 259          $this->headers[ $key ] = array_merge( $this->headers[ $key ], $value );
 260      }
 261  
 262      /**
 263       * Removes all values for a header.
 264       *
 265       * @since 4.4.0
 266       *
 267       * @param string $key Header name.
 268       */
 269  	public function remove_header( $key ) {
 270          $key = $this->canonicalize_header_name( $key );
 271          unset( $this->headers[ $key ] );
 272      }
 273  
 274      /**
 275       * Sets headers on the request.
 276       *
 277       * @since 4.4.0
 278       *
 279       * @param array $headers  Map of header name to value.
 280       * @param bool  $override If true, replace the request's headers. Otherwise, merge with existing.
 281       */
 282  	public function set_headers( $headers, $override = true ) {
 283          if ( true === $override ) {
 284              $this->headers = array();
 285          }
 286  
 287          foreach ( $headers as $key => $value ) {
 288              $this->set_header( $key, $value );
 289          }
 290      }
 291  
 292      /**
 293       * Retrieves the content-type of the request.
 294       *
 295       * @since 4.4.0
 296       *
 297       * @return array Map containing 'value' and 'parameters' keys.
 298       */
 299  	public function get_content_type() {
 300          $value = $this->get_header( 'content-type' );
 301          if ( empty( $value ) ) {
 302              return null;
 303          }
 304  
 305          $parameters = '';
 306          if ( strpos( $value, ';' ) ) {
 307              list( $value, $parameters ) = explode( ';', $value, 2 );
 308          }
 309  
 310          $value = strtolower( $value );
 311          if ( strpos( $value, '/' ) === false ) {
 312              return null;
 313          }
 314  
 315          // Parse type and subtype out.
 316          list( $type, $subtype ) = explode( '/', $value, 2 );
 317  
 318          $data = compact( 'value', 'type', 'subtype', 'parameters' );
 319          $data = array_map( 'trim', $data );
 320  
 321          return $data;
 322      }
 323  
 324      /**
 325       * Retrieves the parameter priority order.
 326       *
 327       * Used when checking parameters in get_param().
 328       *
 329       * @since 4.4.0
 330       *
 331       * @return array List of types to check, in order of priority.
 332       */
 333  	protected function get_parameter_order() {
 334          $order = array();
 335  
 336          $content_type = $this->get_content_type();
 337          if ( $content_type['value'] === 'application/json' ) {
 338              $order[] = 'JSON';
 339          }
 340  
 341          $this->parse_json_params();
 342  
 343          // Ensure we parse the body data.
 344          $body = $this->get_body();
 345  
 346          if ( 'POST' !== $this->method && ! empty( $body ) ) {
 347              $this->parse_body_params();
 348          }
 349  
 350          $accepts_body_data = array( 'POST', 'PUT', 'PATCH', 'DELETE' );
 351          if ( in_array( $this->method, $accepts_body_data ) ) {
 352              $order[] = 'POST';
 353          }
 354  
 355          $order[] = 'GET';
 356          $order[] = 'URL';
 357          $order[] = 'defaults';
 358  
 359          /**
 360           * Filters the parameter order.
 361           *
 362           * The order affects which parameters are checked when using get_param() and family.
 363           * This acts similarly to PHP's `request_order` setting.
 364           *
 365           * @since 4.4.0
 366           *
 367           * @param array           $order {
 368           *    An array of types to check, in order of priority.
 369           *
 370           * @param string $type The type to check.
 371           * }
 372           * @param WP_REST_Request $this The request object.
 373           */
 374          return apply_filters( 'rest_request_parameter_order', $order, $this );
 375      }
 376  
 377      /**
 378       * Retrieves a parameter from the request.
 379       *
 380       * @since 4.4.0
 381       *
 382       * @param string $key Parameter name.
 383       * @return mixed|null Value if set, null otherwise.
 384       */
 385  	public function get_param( $key ) {
 386          $order = $this->get_parameter_order();
 387  
 388          foreach ( $order as $type ) {
 389              // Determine if we have the parameter for this type.
 390              if ( isset( $this->params[ $type ][ $key ] ) ) {
 391                  return $this->params[ $type ][ $key ];
 392              }
 393          }
 394  
 395          return null;
 396      }
 397  
 398      /**
 399       * Sets a parameter on the request.
 400       *
 401       * @since 4.4.0
 402       *
 403       * @param string $key   Parameter name.
 404       * @param mixed  $value Parameter value.
 405       */
 406  	public function set_param( $key, $value ) {
 407          $order                             = $this->get_parameter_order();
 408          $this->params[ $order[0] ][ $key ] = $value;
 409      }
 410  
 411      /**
 412       * Retrieves merged parameters from the request.
 413       *
 414       * The equivalent of get_param(), but returns all parameters for the request.
 415       * Handles merging all the available values into a single array.
 416       *
 417       * @since 4.4.0
 418       *
 419       * @return array Map of key to value.
 420       */
 421  	public function get_params() {
 422          $order = $this->get_parameter_order();
 423          $order = array_reverse( $order, true );
 424  
 425          $params = array();
 426          foreach ( $order as $type ) {
 427              // array_merge / the "+" operator will mess up
 428              // numeric keys, so instead do a manual foreach.
 429              foreach ( (array) $this->params[ $type ] as $key => $value ) {
 430                  $params[ $key ] = $value;
 431              }
 432          }
 433  
 434          return $params;
 435      }
 436  
 437      /**
 438       * Retrieves parameters from the route itself.
 439       *
 440       * These are parsed from the URL using the regex.
 441       *
 442       * @since 4.4.0
 443       *
 444       * @return array Parameter map of key to value.
 445       */
 446  	public function get_url_params() {
 447          return $this->params['URL'];
 448      }
 449  
 450      /**
 451       * Sets parameters from the route.
 452       *
 453       * Typically, this is set after parsing the URL.
 454       *
 455       * @since 4.4.0
 456       *
 457       * @param array $params Parameter map of key to value.
 458       */
 459  	public function set_url_params( $params ) {
 460          $this->params['URL'] = $params;
 461      }
 462  
 463      /**
 464       * Retrieves parameters from the query string.
 465       *
 466       * These are the parameters you'd typically find in `$_GET`.
 467       *
 468       * @since 4.4.0
 469       *
 470       * @return array Parameter map of key to value
 471       */
 472  	public function get_query_params() {
 473          return $this->params['GET'];
 474      }
 475  
 476      /**
 477       * Sets parameters from the query string.
 478       *
 479       * Typically, this is set from `$_GET`.
 480       *
 481       * @since 4.4.0
 482       *
 483       * @param array $params Parameter map of key to value.
 484       */
 485  	public function set_query_params( $params ) {
 486          $this->params['GET'] = $params;
 487      }
 488  
 489      /**
 490       * Retrieves parameters from the body.
 491       *
 492       * These are the parameters you'd typically find in `$_POST`.
 493       *
 494       * @since 4.4.0
 495       *
 496       * @return array Parameter map of key to value.
 497       */
 498  	public function get_body_params() {
 499          return $this->params['POST'];
 500      }
 501  
 502      /**
 503       * Sets parameters from the body.
 504       *
 505       * Typically, this is set from `$_POST`.
 506       *
 507       * @since 4.4.0
 508       *
 509       * @param array $params Parameter map of key to value.
 510       */
 511  	public function set_body_params( $params ) {
 512          $this->params['POST'] = $params;
 513      }
 514  
 515      /**
 516       * Retrieves multipart file parameters from the body.
 517       *
 518       * These are the parameters you'd typically find in `$_FILES`.
 519       *
 520       * @since 4.4.0
 521       *
 522       * @return array Parameter map of key to value
 523       */
 524  	public function get_file_params() {
 525          return $this->params['FILES'];
 526      }
 527  
 528      /**
 529       * Sets multipart file parameters from the body.
 530       *
 531       * Typically, this is set from `$_FILES`.
 532       *
 533       * @since 4.4.0
 534       *
 535       * @param array $params Parameter map of key to value.
 536       */
 537  	public function set_file_params( $params ) {
 538          $this->params['FILES'] = $params;
 539      }
 540  
 541      /**
 542       * Retrieves the default parameters.
 543       *
 544       * These are the parameters set in the route registration.
 545       *
 546       * @since 4.4.0
 547       *
 548       * @return array Parameter map of key to value
 549       */
 550  	public function get_default_params() {
 551          return $this->params['defaults'];
 552      }
 553  
 554      /**
 555       * Sets default parameters.
 556       *
 557       * These are the parameters set in the route registration.
 558       *
 559       * @since 4.4.0
 560       *
 561       * @param array $params Parameter map of key to value.
 562       */
 563  	public function set_default_params( $params ) {
 564          $this->params['defaults'] = $params;
 565      }
 566  
 567      /**
 568       * Retrieves the request body content.
 569       *
 570       * @since 4.4.0
 571       *
 572       * @return string Binary data from the request body.
 573       */
 574  	public function get_body() {
 575          return $this->body;
 576      }
 577  
 578      /**
 579       * Sets body content.
 580       *
 581       * @since 4.4.0
 582       *
 583       * @param string $data Binary data from the request body.
 584       */
 585  	public function set_body( $data ) {
 586          $this->body = $data;
 587  
 588          // Enable lazy parsing.
 589          $this->parsed_json    = false;
 590          $this->parsed_body    = false;
 591          $this->params['JSON'] = null;
 592      }
 593  
 594      /**
 595       * Retrieves the parameters from a JSON-formatted body.
 596       *
 597       * @since 4.4.0
 598       *
 599       * @return array Parameter map of key to value.
 600       */
 601  	public function get_json_params() {
 602          // Ensure the parameters have been parsed out.
 603          $this->parse_json_params();
 604  
 605          return $this->params['JSON'];
 606      }
 607  
 608      /**
 609       * Parses the JSON parameters.
 610       *
 611       * Avoids parsing the JSON data until we need to access it.
 612       *
 613       * @since 4.4.0
 614       * @since 4.7.0 Returns error instance if value cannot be decoded.
 615       * @return true|WP_Error True if the JSON data was passed or no JSON data was provided, WP_Error if invalid JSON was passed.
 616       */
 617  	protected function parse_json_params() {
 618          if ( $this->parsed_json ) {
 619              return true;
 620          }
 621  
 622          $this->parsed_json = true;
 623  
 624          // Check that we actually got JSON.
 625          $content_type = $this->get_content_type();
 626  
 627          if ( empty( $content_type ) || 'application/json' !== $content_type['value'] ) {
 628              return true;
 629          }
 630  
 631          $body = $this->get_body();
 632          if ( empty( $body ) ) {
 633              return true;
 634          }
 635  
 636          $params = json_decode( $body, true );
 637  
 638          /*
 639           * Check for a parsing error.
 640           *
 641           * Note that due to WP's JSON compatibility functions, json_last_error
 642           * might not be defined: https://core.trac.wordpress.org/ticket/27799
 643           */
 644          if ( null === $params && ( ! function_exists( 'json_last_error' ) || JSON_ERROR_NONE !== json_last_error() ) ) {
 645              // Ensure subsequent calls receive error instance.
 646              $this->parsed_json = false;
 647  
 648              $error_data = array(
 649                  'status' => WP_Http::BAD_REQUEST,
 650              );
 651              if ( function_exists( 'json_last_error' ) ) {
 652                  $error_data['json_error_code']    = json_last_error();
 653                  $error_data['json_error_message'] = json_last_error_msg();
 654              }
 655  
 656              return new WP_Error( 'rest_invalid_json', __( 'Invalid JSON body passed.' ), $error_data );
 657          }
 658  
 659          $this->params['JSON'] = $params;
 660          return true;
 661      }
 662  
 663      /**
 664       * Parses the request body parameters.
 665       *
 666       * Parses out URL-encoded bodies for request methods that aren't supported
 667       * natively by PHP. In PHP 5.x, only POST has these parsed automatically.
 668       *
 669       * @since 4.4.0
 670       */
 671  	protected function parse_body_params() {
 672          if ( $this->parsed_body ) {
 673              return;
 674          }
 675  
 676          $this->parsed_body = true;
 677  
 678          /*
 679           * Check that we got URL-encoded. Treat a missing content-type as
 680           * URL-encoded for maximum compatibility.
 681           */
 682          $content_type = $this->get_content_type();
 683  
 684          if ( ! empty( $content_type ) && 'application/x-www-form-urlencoded' !== $content_type['value'] ) {
 685              return;
 686          }
 687  
 688          parse_str( $this->get_body(), $params );
 689  
 690          /*
 691           * Amazingly, parse_str follows magic quote rules. Sigh.
 692           *
 693           * NOTE: Do not refactor to use `wp_unslash`.
 694           */
 695          if ( get_magic_quotes_gpc() ) {
 696              $params = stripslashes_deep( $params );
 697          }
 698  
 699          /*
 700           * Add to the POST parameters stored internally. If a user has already
 701           * set these manually (via `set_body_params`), don't override them.
 702           */
 703          $this->params['POST'] = array_merge( $params, $this->params['POST'] );
 704      }
 705  
 706      /**
 707       * Retrieves the route that matched the request.
 708       *
 709       * @since 4.4.0
 710       *
 711       * @return string Route matching regex.
 712       */
 713  	public function get_route() {
 714          return $this->route;
 715      }
 716  
 717      /**
 718       * Sets the route that matched the request.
 719       *
 720       * @since 4.4.0
 721       *
 722       * @param string $route Route matching regex.
 723       */
 724  	public function set_route( $route ) {
 725          $this->route = $route;
 726      }
 727  
 728      /**
 729       * Retrieves the attributes for the request.
 730       *
 731       * These are the options for the route that was matched.
 732       *
 733       * @since 4.4.0
 734       *
 735       * @return array Attributes for the request.
 736       */
 737  	public function get_attributes() {
 738          return $this->attributes;
 739      }
 740  
 741      /**
 742       * Sets the attributes for the request.
 743       *
 744       * @since 4.4.0
 745       *
 746       * @param array $attributes Attributes for the request.
 747       */
 748  	public function set_attributes( $attributes ) {
 749          $this->attributes = $attributes;
 750      }
 751  
 752      /**
 753       * Sanitizes (where possible) the params on the request.
 754       *
 755       * This is primarily based off the sanitize_callback param on each registered
 756       * argument.
 757       *
 758       * @since 4.4.0
 759       *
 760       * @return true|WP_Error True if parameters were sanitized, WP_Error if an error occurred during sanitization.
 761       */
 762  	public function sanitize_params() {
 763          $attributes = $this->get_attributes();
 764  
 765          // No arguments set, skip sanitizing.
 766          if ( empty( $attributes['args'] ) ) {
 767              return true;
 768          }
 769  
 770          $order = $this->get_parameter_order();
 771  
 772          $invalid_params = array();
 773  
 774          foreach ( $order as $type ) {
 775              if ( empty( $this->params[ $type ] ) ) {
 776                  continue;
 777              }
 778              foreach ( $this->params[ $type ] as $key => $value ) {
 779                  if ( ! isset( $attributes['args'][ $key ] ) ) {
 780                      continue;
 781                  }
 782                  $param_args = $attributes['args'][ $key ];
 783  
 784                  // If the arg has a type but no sanitize_callback attribute, default to rest_parse_request_arg.
 785                  if ( ! array_key_exists( 'sanitize_callback', $param_args ) && ! empty( $param_args['type'] ) ) {
 786                      $param_args['sanitize_callback'] = 'rest_parse_request_arg';
 787                  }
 788                  // If there's still no sanitize_callback, nothing to do here.
 789                  if ( empty( $param_args['sanitize_callback'] ) ) {
 790                      continue;
 791                  }
 792  
 793                  $sanitized_value = call_user_func( $param_args['sanitize_callback'], $value, $this, $key );
 794  
 795                  if ( is_wp_error( $sanitized_value ) ) {
 796                      $invalid_params[ $key ] = $sanitized_value->get_error_message();
 797                  } else {
 798                      $this->params[ $type ][ $key ] = $sanitized_value;
 799                  }
 800              }
 801          }
 802  
 803          if ( $invalid_params ) {
 804              return new WP_Error(
 805                  'rest_invalid_param',
 806                  sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ),
 807                  array(
 808                      'status' => 400,
 809                      'params' => $invalid_params,
 810                  )
 811              );
 812          }
 813  
 814          return true;
 815      }
 816  
 817      /**
 818       * Checks whether this request is valid according to its attributes.
 819       *
 820       * @since 4.4.0
 821       *
 822       * @return bool|WP_Error True if there are no parameters to validate or if all pass validation,
 823       *                       WP_Error if required parameters are missing.
 824       */
 825  	public function has_valid_params() {
 826          // If JSON data was passed, check for errors.
 827          $json_error = $this->parse_json_params();
 828          if ( is_wp_error( $json_error ) ) {
 829              return $json_error;
 830          }
 831  
 832          $attributes = $this->get_attributes();
 833          $required   = array();
 834  
 835          // No arguments set, skip validation.
 836          if ( empty( $attributes['args'] ) ) {
 837              return true;
 838          }
 839  
 840          foreach ( $attributes['args'] as $key => $arg ) {
 841  
 842              $param = $this->get_param( $key );
 843              if ( isset( $arg['required'] ) && true === $arg['required'] && null === $param ) {
 844                  $required[] = $key;
 845              }
 846          }
 847  
 848          if ( ! empty( $required ) ) {
 849              return new WP_Error(
 850                  'rest_missing_callback_param',
 851                  sprintf( __( 'Missing parameter(s): %s' ), implode( ', ', $required ) ),
 852                  array(
 853                      'status' => 400,
 854                      'params' => $required,
 855                  )
 856              );
 857          }
 858  
 859          /*
 860           * Check the validation callbacks for each registered arg.
 861           *
 862           * This is done after required checking as required checking is cheaper.
 863           */
 864          $invalid_params = array();
 865  
 866          foreach ( $attributes['args'] as $key => $arg ) {
 867  
 868              $param = $this->get_param( $key );
 869  
 870              if ( null !== $param && ! empty( $arg['validate_callback'] ) ) {
 871                  $valid_check = call_user_func( $arg['validate_callback'], $param, $this, $key );
 872  
 873                  if ( false === $valid_check ) {
 874                      $invalid_params[ $key ] = __( 'Invalid parameter.' );
 875                  }
 876  
 877                  if ( is_wp_error( $valid_check ) ) {
 878                      $invalid_params[ $key ] = $valid_check->get_error_message();
 879                  }
 880              }
 881          }
 882  
 883          if ( $invalid_params ) {
 884              return new WP_Error(
 885                  'rest_invalid_param',
 886                  sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ),
 887                  array(
 888                      'status' => 400,
 889                      'params' => $invalid_params,
 890                  )
 891              );
 892          }
 893  
 894          return true;
 895  
 896      }
 897  
 898      /**
 899       * Checks if a parameter is set.
 900       *
 901       * @since 4.4.0
 902       *
 903       * @param string $offset Parameter name.
 904       * @return bool Whether the parameter is set.
 905       */
 906  	public function offsetExists( $offset ) {
 907          $order = $this->get_parameter_order();
 908  
 909          foreach ( $order as $type ) {
 910              if ( isset( $this->params[ $type ][ $offset ] ) ) {
 911                  return true;
 912              }
 913          }
 914  
 915          return false;
 916      }
 917  
 918      /**
 919       * Retrieves a parameter from the request.
 920       *
 921       * @since 4.4.0
 922       *
 923       * @param string $offset Parameter name.
 924       * @return mixed|null Value if set, null otherwise.
 925       */
 926  	public function offsetGet( $offset ) {
 927          return $this->get_param( $offset );
 928      }
 929  
 930      /**
 931       * Sets a parameter on the request.
 932       *
 933       * @since 4.4.0
 934       *
 935       * @param string $offset Parameter name.
 936       * @param mixed  $value  Parameter value.
 937       */
 938  	public function offsetSet( $offset, $value ) {
 939          $this->set_param( $offset, $value );
 940      }
 941  
 942      /**
 943       * Removes a parameter from the request.
 944       *
 945       * @since 4.4.0
 946       *
 947       * @param string $offset Parameter name.
 948       */
 949  	public function offsetUnset( $offset ) {
 950          $order = $this->get_parameter_order();
 951  
 952          // Remove the offset from every group.
 953          foreach ( $order as $type ) {
 954              unset( $this->params[ $type ][ $offset ] );
 955          }
 956      }
 957  
 958      /**
 959       * Retrieves a WP_REST_Request object from a full URL.
 960       *
 961       * @since 4.5.0
 962       *
 963       * @param string $url URL with protocol, domain, path and query args.
 964       * @return WP_REST_Request|false WP_REST_Request object on success, false on failure.
 965       */
 966  	public static function from_url( $url ) {
 967          $bits         = parse_url( $url );
 968          $query_params = array();
 969  
 970          if ( ! empty( $bits['query'] ) ) {
 971              wp_parse_str( $bits['query'], $query_params );
 972          }
 973  
 974          $api_root = rest_url();
 975          if ( get_option( 'permalink_structure' ) && 0 === strpos( $url, $api_root ) ) {
 976              // Pretty permalinks on, and URL is under the API root.
 977              $api_url_part = substr( $url, strlen( untrailingslashit( $api_root ) ) );
 978              $route        = parse_url( $api_url_part, PHP_URL_PATH );
 979          } elseif ( ! empty( $query_params['rest_route'] ) ) {
 980              // ?rest_route=... set directly
 981              $route = $query_params['rest_route'];
 982              unset( $query_params['rest_route'] );
 983          }
 984  
 985          $request = false;
 986          if ( ! empty( $route ) ) {
 987              $request = new WP_REST_Request( 'GET', $route );
 988              $request->set_query_params( $query_params );
 989          }
 990  
 991          /**
 992           * Filters the request generated from a URL.
 993           *
 994           * @since 4.5.0
 995           *
 996           * @param WP_REST_Request|false $request Generated request object, or false if URL
 997           *                                       could not be parsed.
 998           * @param string                $url     URL the request was generated from.
 999           */
1000          return apply_filters( 'rest_request_from_url', $request, $url );
1001      }
1002  }


Generated: Mon Jul 15 01:00:03 2019 Cross-referenced by PHPXref 0.7.1