[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

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


Generated : Thu Nov 21 08:20:01 2024 Cross-referenced by PHPXref