[ 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://www.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|null Map containing 'value' and 'parameters' keys
 298       *                    or null when no valid content-type header was
 299       *                    available.
 300       */
 301  	public function get_content_type() {
 302          $value = $this->get_header( 'content-type' );
 303          if ( empty( $value ) ) {
 304              return null;
 305          }
 306  
 307          $parameters = '';
 308          if ( strpos( $value, ';' ) ) {
 309              list( $value, $parameters ) = explode( ';', $value, 2 );
 310          }
 311  
 312          $value = strtolower( $value );
 313          if ( false === strpos( $value, '/' ) ) {
 314              return null;
 315          }
 316  
 317          // Parse type and subtype out.
 318          list( $type, $subtype ) = explode( '/', $value, 2 );
 319  
 320          $data = compact( 'value', 'type', 'subtype', 'parameters' );
 321          $data = array_map( 'trim', $data );
 322  
 323          return $data;
 324      }
 325  
 326      /**
 327       * Checks if the request has specified a JSON content-type.
 328       *
 329       * @since 5.6.0
 330       *
 331       * @return bool True if the content-type header is JSON.
 332       */
 333  	public function is_json_content_type() {
 334          $content_type = $this->get_content_type();
 335  
 336          return isset( $content_type['value'] ) && wp_is_json_media_type( $content_type['value'] );
 337      }
 338  
 339      /**
 340       * Retrieves the parameter priority order.
 341       *
 342       * Used when checking parameters in WP_REST_Request::get_param().
 343       *
 344       * @since 4.4.0
 345       *
 346       * @return string[] Array of types to check, in order of priority.
 347       */
 348  	protected function get_parameter_order() {
 349          $order = array();
 350  
 351          if ( $this->is_json_content_type() ) {
 352              $order[] = 'JSON';
 353          }
 354  
 355          $this->parse_json_params();
 356  
 357          // Ensure we parse the body data.
 358          $body = $this->get_body();
 359  
 360          if ( 'POST' !== $this->method && ! empty( $body ) ) {
 361              $this->parse_body_params();
 362          }
 363  
 364          $accepts_body_data = array( 'POST', 'PUT', 'PATCH', 'DELETE' );
 365          if ( in_array( $this->method, $accepts_body_data, true ) ) {
 366              $order[] = 'POST';
 367          }
 368  
 369          $order[] = 'GET';
 370          $order[] = 'URL';
 371          $order[] = 'defaults';
 372  
 373          /**
 374           * Filters the parameter priority order for a REST API request.
 375           *
 376           * The order affects which parameters are checked when using WP_REST_Request::get_param()
 377           * and family. This acts similarly to PHP's `request_order` setting.
 378           *
 379           * @since 4.4.0
 380           *
 381           * @param string[]        $order   Array of types to check, in order of priority.
 382           * @param WP_REST_Request $request The request object.
 383           */
 384          return apply_filters( 'rest_request_parameter_order', $order, $this );
 385      }
 386  
 387      /**
 388       * Retrieves a parameter from the request.
 389       *
 390       * @since 4.4.0
 391       *
 392       * @param string $key Parameter name.
 393       * @return mixed|null Value if set, null otherwise.
 394       */
 395  	public function get_param( $key ) {
 396          $order = $this->get_parameter_order();
 397  
 398          foreach ( $order as $type ) {
 399              // Determine if we have the parameter for this type.
 400              if ( isset( $this->params[ $type ][ $key ] ) ) {
 401                  return $this->params[ $type ][ $key ];
 402              }
 403          }
 404  
 405          return null;
 406      }
 407  
 408      /**
 409       * Checks if a parameter exists in the request.
 410       *
 411       * This allows distinguishing between an omitted parameter,
 412       * and a parameter specifically set to null.
 413       *
 414       * @since 5.3.0
 415       *
 416       * @param string $key Parameter name.
 417       * @return bool True if a param exists for the given key.
 418       */
 419  	public function has_param( $key ) {
 420          $order = $this->get_parameter_order();
 421  
 422          foreach ( $order as $type ) {
 423              if ( is_array( $this->params[ $type ] ) && array_key_exists( $key, $this->params[ $type ] ) ) {
 424                  return true;
 425              }
 426          }
 427  
 428          return false;
 429      }
 430  
 431      /**
 432       * Sets a parameter on the request.
 433       *
 434       * If the given parameter key exists in any parameter type an update will take place,
 435       * otherwise a new param will be created in the first parameter type (respecting
 436       * get_parameter_order()).
 437       *
 438       * @since 4.4.0
 439       *
 440       * @param string $key   Parameter name.
 441       * @param mixed  $value Parameter value.
 442       */
 443  	public function set_param( $key, $value ) {
 444          $order     = $this->get_parameter_order();
 445          $found_key = false;
 446  
 447          foreach ( $order as $type ) {
 448              if ( 'defaults' !== $type && is_array( $this->params[ $type ] ) && array_key_exists( $key, $this->params[ $type ] ) ) {
 449                  $this->params[ $type ][ $key ] = $value;
 450                  $found_key                     = true;
 451              }
 452          }
 453  
 454          if ( ! $found_key ) {
 455              $this->params[ $order[0] ][ $key ] = $value;
 456          }
 457      }
 458  
 459      /**
 460       * Retrieves merged parameters from the request.
 461       *
 462       * The equivalent of get_param(), but returns all parameters for the request.
 463       * Handles merging all the available values into a single array.
 464       *
 465       * @since 4.4.0
 466       *
 467       * @return array Map of key to value.
 468       */
 469  	public function get_params() {
 470          $order = $this->get_parameter_order();
 471          $order = array_reverse( $order, true );
 472  
 473          $params = array();
 474          foreach ( $order as $type ) {
 475              // array_merge() / the "+" operator will mess up
 476              // numeric keys, so instead do a manual foreach.
 477              foreach ( (array) $this->params[ $type ] as $key => $value ) {
 478                  $params[ $key ] = $value;
 479              }
 480          }
 481  
 482          return $params;
 483      }
 484  
 485      /**
 486       * Retrieves parameters from the route itself.
 487       *
 488       * These are parsed from the URL using the regex.
 489       *
 490       * @since 4.4.0
 491       *
 492       * @return array Parameter map of key to value.
 493       */
 494  	public function get_url_params() {
 495          return $this->params['URL'];
 496      }
 497  
 498      /**
 499       * Sets parameters from the route.
 500       *
 501       * Typically, this is set after parsing the URL.
 502       *
 503       * @since 4.4.0
 504       *
 505       * @param array $params Parameter map of key to value.
 506       */
 507  	public function set_url_params( $params ) {
 508          $this->params['URL'] = $params;
 509      }
 510  
 511      /**
 512       * Retrieves parameters from the query string.
 513       *
 514       * These are the parameters you'd typically find in `$_GET`.
 515       *
 516       * @since 4.4.0
 517       *
 518       * @return array Parameter map of key to value
 519       */
 520  	public function get_query_params() {
 521          return $this->params['GET'];
 522      }
 523  
 524      /**
 525       * Sets parameters from the query string.
 526       *
 527       * Typically, this is set from `$_GET`.
 528       *
 529       * @since 4.4.0
 530       *
 531       * @param array $params Parameter map of key to value.
 532       */
 533  	public function set_query_params( $params ) {
 534          $this->params['GET'] = $params;
 535      }
 536  
 537      /**
 538       * Retrieves parameters from the body.
 539       *
 540       * These are the parameters you'd typically find in `$_POST`.
 541       *
 542       * @since 4.4.0
 543       *
 544       * @return array Parameter map of key to value.
 545       */
 546  	public function get_body_params() {
 547          return $this->params['POST'];
 548      }
 549  
 550      /**
 551       * Sets parameters from the body.
 552       *
 553       * Typically, this is set from `$_POST`.
 554       *
 555       * @since 4.4.0
 556       *
 557       * @param array $params Parameter map of key to value.
 558       */
 559  	public function set_body_params( $params ) {
 560          $this->params['POST'] = $params;
 561      }
 562  
 563      /**
 564       * Retrieves multipart file parameters from the body.
 565       *
 566       * These are the parameters you'd typically find in `$_FILES`.
 567       *
 568       * @since 4.4.0
 569       *
 570       * @return array Parameter map of key to value
 571       */
 572  	public function get_file_params() {
 573          return $this->params['FILES'];
 574      }
 575  
 576      /**
 577       * Sets multipart file parameters from the body.
 578       *
 579       * Typically, this is set from `$_FILES`.
 580       *
 581       * @since 4.4.0
 582       *
 583       * @param array $params Parameter map of key to value.
 584       */
 585  	public function set_file_params( $params ) {
 586          $this->params['FILES'] = $params;
 587      }
 588  
 589      /**
 590       * Retrieves the default parameters.
 591       *
 592       * These are the parameters set in the route registration.
 593       *
 594       * @since 4.4.0
 595       *
 596       * @return array Parameter map of key to value
 597       */
 598  	public function get_default_params() {
 599          return $this->params['defaults'];
 600      }
 601  
 602      /**
 603       * Sets default parameters.
 604       *
 605       * These are the parameters set in the route registration.
 606       *
 607       * @since 4.4.0
 608       *
 609       * @param array $params Parameter map of key to value.
 610       */
 611  	public function set_default_params( $params ) {
 612          $this->params['defaults'] = $params;
 613      }
 614  
 615      /**
 616       * Retrieves the request body content.
 617       *
 618       * @since 4.4.0
 619       *
 620       * @return string Binary data from the request body.
 621       */
 622  	public function get_body() {
 623          return $this->body;
 624      }
 625  
 626      /**
 627       * Sets body content.
 628       *
 629       * @since 4.4.0
 630       *
 631       * @param string $data Binary data from the request body.
 632       */
 633  	public function set_body( $data ) {
 634          $this->body = $data;
 635  
 636          // Enable lazy parsing.
 637          $this->parsed_json    = false;
 638          $this->parsed_body    = false;
 639          $this->params['JSON'] = null;
 640      }
 641  
 642      /**
 643       * Retrieves the parameters from a JSON-formatted body.
 644       *
 645       * @since 4.4.0
 646       *
 647       * @return array Parameter map of key to value.
 648       */
 649  	public function get_json_params() {
 650          // Ensure the parameters have been parsed out.
 651          $this->parse_json_params();
 652  
 653          return $this->params['JSON'];
 654      }
 655  
 656      /**
 657       * Parses the JSON parameters.
 658       *
 659       * Avoids parsing the JSON data until we need to access it.
 660       *
 661       * @since 4.4.0
 662       * @since 4.7.0 Returns error instance if value cannot be decoded.
 663       * @return true|WP_Error True if the JSON data was passed or no JSON data was provided, WP_Error if invalid JSON was passed.
 664       */
 665  	protected function parse_json_params() {
 666          if ( $this->parsed_json ) {
 667              return true;
 668          }
 669  
 670          $this->parsed_json = true;
 671  
 672          // Check that we actually got JSON.
 673          if ( ! $this->is_json_content_type() ) {
 674              return true;
 675          }
 676  
 677          $body = $this->get_body();
 678          if ( empty( $body ) ) {
 679              return true;
 680          }
 681  
 682          $params = json_decode( $body, true );
 683  
 684          /*
 685           * Check for a parsing error.
 686           */
 687          if ( null === $params && JSON_ERROR_NONE !== json_last_error() ) {
 688              // Ensure subsequent calls receive error instance.
 689              $this->parsed_json = false;
 690  
 691              $error_data = array(
 692                  'status'             => WP_Http::BAD_REQUEST,
 693                  'json_error_code'    => json_last_error(),
 694                  'json_error_message' => json_last_error_msg(),
 695              );
 696  
 697              return new WP_Error( 'rest_invalid_json', __( 'Invalid JSON body passed.' ), $error_data );
 698          }
 699  
 700          $this->params['JSON'] = $params;
 701  
 702          return true;
 703      }
 704  
 705      /**
 706       * Parses the request body parameters.
 707       *
 708       * Parses out URL-encoded bodies for request methods that aren't supported
 709       * natively by PHP. In PHP 5.x, only POST has these parsed automatically.
 710       *
 711       * @since 4.4.0
 712       */
 713  	protected function parse_body_params() {
 714          if ( $this->parsed_body ) {
 715              return;
 716          }
 717  
 718          $this->parsed_body = true;
 719  
 720          /*
 721           * Check that we got URL-encoded. Treat a missing content-type as
 722           * URL-encoded for maximum compatibility.
 723           */
 724          $content_type = $this->get_content_type();
 725  
 726          if ( ! empty( $content_type ) && 'application/x-www-form-urlencoded' !== $content_type['value'] ) {
 727              return;
 728          }
 729  
 730          parse_str( $this->get_body(), $params );
 731  
 732          /*
 733           * Add to the POST parameters stored internally. If a user has already
 734           * set these manually (via `set_body_params`), don't override them.
 735           */
 736          $this->params['POST'] = array_merge( $params, $this->params['POST'] );
 737      }
 738  
 739      /**
 740       * Retrieves the route that matched the request.
 741       *
 742       * @since 4.4.0
 743       *
 744       * @return string Route matching regex.
 745       */
 746  	public function get_route() {
 747          return $this->route;
 748      }
 749  
 750      /**
 751       * Sets the route that matched the request.
 752       *
 753       * @since 4.4.0
 754       *
 755       * @param string $route Route matching regex.
 756       */
 757  	public function set_route( $route ) {
 758          $this->route = $route;
 759      }
 760  
 761      /**
 762       * Retrieves the attributes for the request.
 763       *
 764       * These are the options for the route that was matched.
 765       *
 766       * @since 4.4.0
 767       *
 768       * @return array Attributes for the request.
 769       */
 770  	public function get_attributes() {
 771          return $this->attributes;
 772      }
 773  
 774      /**
 775       * Sets the attributes for the request.
 776       *
 777       * @since 4.4.0
 778       *
 779       * @param array $attributes Attributes for the request.
 780       */
 781  	public function set_attributes( $attributes ) {
 782          $this->attributes = $attributes;
 783      }
 784  
 785      /**
 786       * Sanitizes (where possible) the params on the request.
 787       *
 788       * This is primarily based off the sanitize_callback param on each registered
 789       * argument.
 790       *
 791       * @since 4.4.0
 792       *
 793       * @return true|WP_Error True if parameters were sanitized, WP_Error if an error occurred during sanitization.
 794       */
 795  	public function sanitize_params() {
 796          $attributes = $this->get_attributes();
 797  
 798          // No arguments set, skip sanitizing.
 799          if ( empty( $attributes['args'] ) ) {
 800              return true;
 801          }
 802  
 803          $order = $this->get_parameter_order();
 804  
 805          $invalid_params  = array();
 806          $invalid_details = array();
 807  
 808          foreach ( $order as $type ) {
 809              if ( empty( $this->params[ $type ] ) ) {
 810                  continue;
 811              }
 812  
 813              foreach ( $this->params[ $type ] as $key => $value ) {
 814                  if ( ! isset( $attributes['args'][ $key ] ) ) {
 815                      continue;
 816                  }
 817  
 818                  $param_args = $attributes['args'][ $key ];
 819  
 820                  // If the arg has a type but no sanitize_callback attribute, default to rest_parse_request_arg.
 821                  if ( ! array_key_exists( 'sanitize_callback', $param_args ) && ! empty( $param_args['type'] ) ) {
 822                      $param_args['sanitize_callback'] = 'rest_parse_request_arg';
 823                  }
 824                  // If there's still no sanitize_callback, nothing to do here.
 825                  if ( empty( $param_args['sanitize_callback'] ) ) {
 826                      continue;
 827                  }
 828  
 829                  /** @var mixed|WP_Error $sanitized_value */
 830                  $sanitized_value = call_user_func( $param_args['sanitize_callback'], $value, $this, $key );
 831  
 832                  if ( is_wp_error( $sanitized_value ) ) {
 833                      $invalid_params[ $key ]  = implode( ' ', $sanitized_value->get_error_messages() );
 834                      $invalid_details[ $key ] = rest_convert_error_to_response( $sanitized_value )->get_data();
 835                  } else {
 836                      $this->params[ $type ][ $key ] = $sanitized_value;
 837                  }
 838              }
 839          }
 840  
 841          if ( $invalid_params ) {
 842              return new WP_Error(
 843                  'rest_invalid_param',
 844                  /* translators: %s: List of invalid parameters. */
 845                  sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ),
 846                  array(
 847                      'status'  => 400,
 848                      'params'  => $invalid_params,
 849                      'details' => $invalid_details,
 850                  )
 851              );
 852          }
 853  
 854          return true;
 855      }
 856  
 857      /**
 858       * Checks whether this request is valid according to its attributes.
 859       *
 860       * @since 4.4.0
 861       *
 862       * @return true|WP_Error True if there are no parameters to validate or if all pass validation,
 863       *                       WP_Error if required parameters are missing.
 864       */
 865  	public function has_valid_params() {
 866          // If JSON data was passed, check for errors.
 867          $json_error = $this->parse_json_params();
 868          if ( is_wp_error( $json_error ) ) {
 869              return $json_error;
 870          }
 871  
 872          $attributes = $this->get_attributes();
 873          $required   = array();
 874  
 875          $args = empty( $attributes['args'] ) ? array() : $attributes['args'];
 876  
 877          foreach ( $args as $key => $arg ) {
 878              $param = $this->get_param( $key );
 879              if ( isset( $arg['required'] ) && true === $arg['required'] && null === $param ) {
 880                  $required[] = $key;
 881              }
 882          }
 883  
 884          if ( ! empty( $required ) ) {
 885              return new WP_Error(
 886                  'rest_missing_callback_param',
 887                  /* translators: %s: List of required parameters. */
 888                  sprintf( __( 'Missing parameter(s): %s' ), implode( ', ', $required ) ),
 889                  array(
 890                      'status' => 400,
 891                      'params' => $required,
 892                  )
 893              );
 894          }
 895  
 896          /*
 897           * Check the validation callbacks for each registered arg.
 898           *
 899           * This is done after required checking as required checking is cheaper.
 900           */
 901          $invalid_params  = array();
 902          $invalid_details = array();
 903  
 904          foreach ( $args as $key => $arg ) {
 905  
 906              $param = $this->get_param( $key );
 907  
 908              if ( null !== $param && ! empty( $arg['validate_callback'] ) ) {
 909                  /** @var bool|\WP_Error $valid_check */
 910                  $valid_check = call_user_func( $arg['validate_callback'], $param, $this, $key );
 911  
 912                  if ( false === $valid_check ) {
 913                      $invalid_params[ $key ] = __( 'Invalid parameter.' );
 914                  }
 915  
 916                  if ( is_wp_error( $valid_check ) ) {
 917                      $invalid_params[ $key ]  = implode( ' ', $valid_check->get_error_messages() );
 918                      $invalid_details[ $key ] = rest_convert_error_to_response( $valid_check )->get_data();
 919                  }
 920              }
 921          }
 922  
 923          if ( $invalid_params ) {
 924              return new WP_Error(
 925                  'rest_invalid_param',
 926                  /* translators: %s: List of invalid parameters. */
 927                  sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ),
 928                  array(
 929                      'status'  => 400,
 930                      'params'  => $invalid_params,
 931                      'details' => $invalid_details,
 932                  )
 933              );
 934          }
 935  
 936          if ( isset( $attributes['validate_callback'] ) ) {
 937              $valid_check = call_user_func( $attributes['validate_callback'], $this );
 938  
 939              if ( is_wp_error( $valid_check ) ) {
 940                  return $valid_check;
 941              }
 942  
 943              if ( false === $valid_check ) {
 944                  // A WP_Error instance is preferred, but false is supported for parity with the per-arg validate_callback.
 945                  return new WP_Error( 'rest_invalid_params', __( 'Invalid parameters.' ), array( 'status' => 400 ) );
 946              }
 947          }
 948  
 949          return true;
 950      }
 951  
 952      /**
 953       * Checks if a parameter is set.
 954       *
 955       * @since 4.4.0
 956       *
 957       * @param string $offset Parameter name.
 958       * @return bool Whether the parameter is set.
 959       */
 960      #[ReturnTypeWillChange]
 961  	public function offsetExists( $offset ) {
 962          $order = $this->get_parameter_order();
 963  
 964          foreach ( $order as $type ) {
 965              if ( isset( $this->params[ $type ][ $offset ] ) ) {
 966                  return true;
 967              }
 968          }
 969  
 970          return false;
 971      }
 972  
 973      /**
 974       * Retrieves a parameter from the request.
 975       *
 976       * @since 4.4.0
 977       *
 978       * @param string $offset Parameter name.
 979       * @return mixed|null Value if set, null otherwise.
 980       */
 981      #[ReturnTypeWillChange]
 982  	public function offsetGet( $offset ) {
 983          return $this->get_param( $offset );
 984      }
 985  
 986      /**
 987       * Sets a parameter on the request.
 988       *
 989       * @since 4.4.0
 990       *
 991       * @param string $offset Parameter name.
 992       * @param mixed  $value  Parameter value.
 993       */
 994      #[ReturnTypeWillChange]
 995  	public function offsetSet( $offset, $value ) {
 996          $this->set_param( $offset, $value );
 997      }
 998  
 999      /**
1000       * Removes a parameter from the request.
1001       *
1002       * @since 4.4.0
1003       *
1004       * @param string $offset Parameter name.
1005       */
1006      #[ReturnTypeWillChange]
1007  	public function offsetUnset( $offset ) {
1008          $order = $this->get_parameter_order();
1009  
1010          // Remove the offset from every group.
1011          foreach ( $order as $type ) {
1012              unset( $this->params[ $type ][ $offset ] );
1013          }
1014      }
1015  
1016      /**
1017       * Retrieves a WP_REST_Request object from a full URL.
1018       *
1019       * @since 4.5.0
1020       *
1021       * @param string $url URL with protocol, domain, path and query args.
1022       * @return WP_REST_Request|false WP_REST_Request object on success, false on failure.
1023       */
1024  	public static function from_url( $url ) {
1025          $bits         = parse_url( $url );
1026          $query_params = array();
1027  
1028          if ( ! empty( $bits['query'] ) ) {
1029              wp_parse_str( $bits['query'], $query_params );
1030          }
1031  
1032          $api_root = rest_url();
1033          if ( get_option( 'permalink_structure' ) && 0 === strpos( $url, $api_root ) ) {
1034              // Pretty permalinks on, and URL is under the API root.
1035              $api_url_part = substr( $url, strlen( untrailingslashit( $api_root ) ) );
1036              $route        = parse_url( $api_url_part, PHP_URL_PATH );
1037          } elseif ( ! empty( $query_params['rest_route'] ) ) {
1038              // ?rest_route=... set directly.
1039              $route = $query_params['rest_route'];
1040              unset( $query_params['rest_route'] );
1041          }
1042  
1043          $request = false;
1044          if ( ! empty( $route ) ) {
1045              $request = new WP_REST_Request( 'GET', $route );
1046              $request->set_query_params( $query_params );
1047          }
1048  
1049          /**
1050           * Filters the REST API request generated from a URL.
1051           *
1052           * @since 4.5.0
1053           *
1054           * @param WP_REST_Request|false $request Generated request object, or false if URL
1055           *                                       could not be parsed.
1056           * @param string                $url     URL the request was generated from.
1057           */
1058          return apply_filters( 'rest_request_from_url', $request, $url );
1059      }
1060  }


Generated: Thu Nov 21 01:00:03 2024 Cross-referenced by PHPXref 0.7.1