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