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