]> scripts.mit.edu Git - autoinstalls/wordpress.git/blobdiff - wp-includes/rest-api/class-wp-rest-server.php
WordPress 4.7.2
[autoinstalls/wordpress.git] / wp-includes / rest-api / class-wp-rest-server.php
index dad4070384e60ffd074e81432e3999211d6a3e9f..ec8d2a0baf7c8b4b88f0dcb264a916fff800013a 100644 (file)
@@ -114,7 +114,7 @@ class WP_REST_Server {
         */
        public function check_authentication() {
                /**
         */
        public function check_authentication() {
                /**
-                * Pass an authentication error to the API
+                * Filters REST authentication errors.
                 *
                 * This is used to pass a WP_Error from an authentication method back to
                 * the API.
                 *
                 * This is used to pass a WP_Error from an authentication method back to
                 * the API.
@@ -226,15 +226,21 @@ class WP_REST_Server {
        public function serve_request( $path = null ) {
                $content_type = isset( $_GET['_jsonp'] ) ? 'application/javascript' : 'application/json';
                $this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) );
        public function serve_request( $path = null ) {
                $content_type = isset( $_GET['_jsonp'] ) ? 'application/javascript' : 'application/json';
                $this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) );
+               $this->send_header( 'X-Robots-Tag', 'noindex' );
+
+               $api_root = get_rest_url();
+               if ( ! empty( $api_root ) ) {
+                       $this->send_header( 'Link', '<' . esc_url_raw( $api_root ) . '>; rel="https://api.w.org/"' );
+               }
 
                /*
                 * Mitigate possible JSONP Flash attacks.
                 *
 
                /*
                 * Mitigate possible JSONP Flash attacks.
                 *
-                * http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
+                * https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
                 */
                $this->send_header( 'X-Content-Type-Options', 'nosniff' );
                $this->send_header( 'Access-Control-Expose-Headers', 'X-WP-Total, X-WP-TotalPages' );
                 */
                $this->send_header( 'X-Content-Type-Options', 'nosniff' );
                $this->send_header( 'Access-Control-Expose-Headers', 'X-WP-Total, X-WP-TotalPages' );
-               $this->send_header( 'Access-Control-Allow-Headers', 'Authorization' );
+               $this->send_header( 'Access-Control-Allow-Headers', 'Authorization, Content-Type' );
 
                /**
                 * Send nocache headers on authenticated requests.
 
                /**
                 * Send nocache headers on authenticated requests.
@@ -251,16 +257,17 @@ class WP_REST_Server {
                }
 
                /**
                }
 
                /**
-                * Filter whether the REST API is enabled.
+                * Filters whether the REST API is enabled.
                 *
                 * @since 4.4.0
                 *
                 * @since 4.4.0
+                * @deprecated 4.7.0 Use the rest_authentication_errors filter to restrict access to the API
                 *
                 * @param bool $rest_enabled Whether the REST API is enabled. Default true.
                 */
                 *
                 * @param bool $rest_enabled Whether the REST API is enabled. Default true.
                 */
-               $enabled = apply_filters( 'rest_enabled', true );
+               apply_filters_deprecated( 'rest_enabled', array( true ), '4.7.0', 'rest_authentication_errors', __( 'The REST API can no longer be completely disabled, the rest_authentication_errors can be used to restrict access to the API, instead.' ) );
 
                /**
 
                /**
-                * Filter whether jsonp is enabled.
+                * Filters whether jsonp is enabled.
                 *
                 * @since 4.4.0
                 *
                 *
                 * @since 4.4.0
                 *
@@ -270,25 +277,15 @@ class WP_REST_Server {
 
                $jsonp_callback = null;
 
 
                $jsonp_callback = null;
 
-               if ( ! $enabled ) {
-                       echo $this->json_error( 'rest_disabled', __( 'The REST API is disabled on this site.' ), 404 );
-                       return false;
-               }
                if ( isset( $_GET['_jsonp'] ) ) {
                        if ( ! $jsonp_enabled ) {
                                echo $this->json_error( 'rest_callback_disabled', __( 'JSONP support is disabled on this site.' ), 400 );
                                return false;
                        }
 
                if ( isset( $_GET['_jsonp'] ) ) {
                        if ( ! $jsonp_enabled ) {
                                echo $this->json_error( 'rest_callback_disabled', __( 'JSONP support is disabled on this site.' ), 400 );
                                return false;
                        }
 
-                       // Check for invalid characters (only alphanumeric allowed).
-                       if ( is_string( $_GET['_jsonp'] ) ) {
-                               $jsonp_callback = preg_replace( '/[^\w\.]/', '', wp_unslash( $_GET['_jsonp'] ), -1, $illegal_char_count );
-                               if ( 0 !== $illegal_char_count ) {
-                                       $jsonp_callback = null;
-                               }
-                       }
-                       if ( null === $jsonp_callback ) {
-                               echo $this->json_error( 'rest_callback_invalid', __( 'The JSONP callback function is invalid.' ), 400 );
+                       $jsonp_callback = $_GET['_jsonp'];
+                       if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) {
+                               echo $this->json_error( 'rest_callback_invalid', __( 'Invalid JSONP callback function.' ), 400 );
                                return false;
                        }
                }
                                return false;
                        }
                }
@@ -303,10 +300,10 @@ class WP_REST_Server {
 
                $request = new WP_REST_Request( $_SERVER['REQUEST_METHOD'], $path );
 
 
                $request = new WP_REST_Request( $_SERVER['REQUEST_METHOD'], $path );
 
-               $request->set_query_params( $_GET );
-               $request->set_body_params( $_POST );
+               $request->set_query_params( wp_unslash( $_GET ) );
+               $request->set_body_params( wp_unslash( $_POST ) );
                $request->set_file_params( $_FILES );
                $request->set_file_params( $_FILES );
-               $request->set_headers( $this->get_headers( $_SERVER ) );
+               $request->set_headers( $this->get_headers( wp_unslash( $_SERVER ) ) );
                $request->set_body( $this->get_raw_data() );
 
                /*
                $request->set_body( $this->get_raw_data() );
 
                /*
@@ -335,11 +332,12 @@ class WP_REST_Server {
                }
 
                /**
                }
 
                /**
-                * Filter the API response.
+                * Filters the API response.
                 *
                 * Allows modification of the response before returning.
                 *
                 * @since 4.4.0
                 *
                 * Allows modification of the response before returning.
                 *
                 * @since 4.4.0
+                * @since 4.5.0 Applied to embedded responses.
                 *
                 * @param WP_HTTP_Response $result  Result to send to the client. Usually a WP_REST_Response.
                 * @param WP_REST_Server   $this    Server instance.
                 *
                 * @param WP_HTTP_Response $result  Result to send to the client. Usually a WP_REST_Response.
                 * @param WP_REST_Server   $this    Server instance.
@@ -360,7 +358,7 @@ class WP_REST_Server {
                $this->set_status( $code );
 
                /**
                $this->set_status( $code );
 
                /**
-                * Filter whether the request has already been served.
+                * Filters whether the request has already been served.
                 *
                 * Allow sending the request manually - by returning true, the API result
                 * will not be sent to the client.
                 *
                 * Allow sending the request manually - by returning true, the API result
                 * will not be sent to the client.
@@ -393,8 +391,8 @@ class WP_REST_Server {
                        }
 
                        if ( $jsonp_callback ) {
                        }
 
                        if ( $jsonp_callback ) {
-                               // Prepend '/**/' to mitigate possible JSONP Flash attacks
-                               // http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
+                               // Prepend '/**/' to mitigate possible JSONP Flash attacks.
+                               // https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
                                echo '/**/' . $jsonp_callback . '(' . $result . ')';
                        } else {
                                echo $result;
                                echo '/**/' . $jsonp_callback . '(' . $result . ')';
                        } else {
                                echo $result;
@@ -420,7 +418,7 @@ class WP_REST_Server {
         */
        public function response_to_data( $response, $embed ) {
                $data  = $response->get_data();
         */
        public function response_to_data( $response, $embed ) {
                $data  = $response->get_data();
-               $links = $this->get_response_links( $response );
+               $links = $this->get_compact_response_links( $response );
 
                if ( ! empty( $links ) ) {
                        // Convert links to part of the data.
 
                if ( ! empty( $links ) ) {
                        // Convert links to part of the data.
@@ -453,7 +451,6 @@ class WP_REST_Server {
         */
        public static function get_response_links( $response ) {
                $links = $response->get_links();
         */
        public static function get_response_links( $response ) {
                $links = $response->get_links();
-
                if ( empty( $links ) ) {
                        return array();
                }
                if ( empty( $links ) ) {
                        return array();
                }
@@ -473,6 +470,59 @@ class WP_REST_Server {
                return $data;
        }
 
                return $data;
        }
 
+       /**
+        * Retrieves the CURIEs (compact URIs) used for relations.
+        *
+        * Extracts the links from a response into a structured hash, suitable for
+        * direct output.
+        *
+        * @since 4.5.0
+        * @access public
+        * @static
+        *
+        * @param WP_REST_Response $response Response to extract links from.
+        * @return array Map of link relation to list of link hashes.
+        */
+       public static function get_compact_response_links( $response ) {
+               $links = self::get_response_links( $response );
+
+               if ( empty( $links ) ) {
+                       return array();
+               }
+
+               $curies = $response->get_curies();
+               $used_curies = array();
+
+               foreach ( $links as $rel => $items ) {
+
+                       // Convert $rel URIs to their compact versions if they exist.
+                       foreach ( $curies as $curie ) {
+                               $href_prefix = substr( $curie['href'], 0, strpos( $curie['href'], '{rel}' ) );
+                               if ( strpos( $rel, $href_prefix ) !== 0 ) {
+                                       continue;
+                               }
+
+                               // Relation now changes from '$uri' to '$curie:$relation'.
+                               $rel_regex = str_replace( '\{rel\}', '(.+)', preg_quote( $curie['href'], '!' ) );
+                               preg_match( '!' . $rel_regex . '!', $rel, $matches );
+                               if ( $matches ) {
+                                       $new_rel = $curie['name'] . ':' . $matches[1];
+                                       $used_curies[ $curie['name'] ] = $curie;
+                                       $links[ $new_rel ] = $items;
+                                       unset( $links[ $rel ] );
+                                       break;
+                               }
+                       }
+               }
+
+               // Push the curies onto the start of the links array.
+               if ( $used_curies ) {
+                       $links['curies'] = array_values( $used_curies );
+               }
+
+               return $links;
+       }
+
        /**
         * Embeds the links from the data into the request.
         *
        /**
         * Embeds the links from the data into the request.
         *
@@ -493,7 +543,6 @@ class WP_REST_Server {
                }
 
                $embedded = array();
                }
 
                $embedded = array();
-               $api_root = rest_url();
 
                foreach ( $data['_links'] as $rel => $links ) {
                        // Ignore links to self, for obvious reasons.
 
                foreach ( $data['_links'] as $rel => $links ) {
                        // Ignore links to self, for obvious reasons.
@@ -505,47 +554,35 @@ class WP_REST_Server {
 
                        foreach ( $links as $item ) {
                                // Determine if the link is embeddable.
 
                        foreach ( $links as $item ) {
                                // Determine if the link is embeddable.
-                               if ( empty( $item['embeddable'] ) || strpos( $item['href'], $api_root ) !== 0 ) {
+                               if ( empty( $item['embeddable'] ) ) {
                                        // Ensure we keep the same order.
                                        $embeds[] = array();
                                        continue;
                                }
 
                                // Run through our internal routing and serve.
                                        // Ensure we keep the same order.
                                        $embeds[] = array();
                                        continue;
                                }
 
                                // Run through our internal routing and serve.
-                               $route = substr( $item['href'], strlen( untrailingslashit( $api_root ) ) );
-                               $query_params = array();
-
-                               // Parse out URL query parameters.
-                               $parsed = parse_url( $route );
-                               if ( empty( $parsed['path'] ) ) {
+                               $request = WP_REST_Request::from_url( $item['href'] );
+                               if ( ! $request ) {
                                        $embeds[] = array();
                                        continue;
                                }
 
                                        $embeds[] = array();
                                        continue;
                                }
 
-                               if ( ! empty( $parsed['query'] ) ) {
-                                       parse_str( $parsed['query'], $query_params );
-
-                                       // Ensure magic quotes are stripped.
-                                       if ( get_magic_quotes_gpc() ) {
-                                               $query_params = stripslashes_deep( $query_params );
-                                       }
-                               }
-
                                // Embedded resources get passed context=embed.
                                // Embedded resources get passed context=embed.
-                               if ( empty( $query_params['context'] ) ) {
-                                       $query_params['context'] = 'embed';
+                               if ( empty( $request['context'] ) ) {
+                                       $request['context'] = 'embed';
                                }
 
                                }
 
-                               $request = new WP_REST_Request( 'GET', $parsed['path'] );
-
-                               $request->set_query_params( $query_params );
                                $response = $this->dispatch( $request );
 
                                $response = $this->dispatch( $request );
 
+                               /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */
+                               $response = apply_filters( 'rest_post_dispatch', rest_ensure_response( $response ), $this, $request );
+
                                $embeds[] = $this->response_to_data( $response, false );
                        }
 
                        // Determine if any real links were found.
                        $has_links = count( array_filter( $embeds ) );
                                $embeds[] = $this->response_to_data( $response, false );
                        }
 
                        // Determine if any real links were found.
                        $has_links = count( array_filter( $embeds ) );
+
                        if ( $has_links ) {
                                $embedded[ $rel ] = $embeds;
                        }
                        if ( $has_links ) {
                                $embedded[ $rel ] = $embeds;
                        }
@@ -580,7 +617,7 @@ class WP_REST_Server {
                );
 
                /**
                );
 
                /**
-                * Filter the enveloped form of a response.
+                * Filters the enveloped form of a response.
                 *
                 * @since 4.4.0
                 *
                 *
                 * @since 4.4.0
                 *
@@ -602,7 +639,7 @@ class WP_REST_Server {
         * @param string $namespace  Namespace.
         * @param string $route      The REST route.
         * @param array  $route_args Route arguments.
         * @param string $namespace  Namespace.
         * @param string $route      The REST route.
         * @param array  $route_args Route arguments.
-        * @param bool   $override   Optional. Whether the route should be overriden if it already exists.
+        * @param bool   $override   Optional. Whether the route should be overridden if it already exists.
         *                           Default false.
         */
        public function register_route( $namespace, $route, $route_args, $override = false ) {
         *                           Default false.
         */
        public function register_route( $namespace, $route, $route_args, $override = false ) {
@@ -660,7 +697,7 @@ class WP_REST_Server {
        public function get_routes() {
 
                /**
        public function get_routes() {
 
                /**
-                * Filter the array of available endpoints.
+                * Filters the array of available endpoints.
                 *
                 * @since 4.4.0
                 *
                 *
                 * @since 4.4.0
                 *
@@ -705,7 +742,7 @@ class WP_REST_Server {
                                // Allow comma-separated HTTP methods.
                                if ( is_string( $handler['methods'] ) ) {
                                        $methods = explode( ',', $handler['methods'] );
                                // Allow comma-separated HTTP methods.
                                if ( is_string( $handler['methods'] ) ) {
                                        $methods = explode( ',', $handler['methods'] );
-                               } else if ( is_array( $handler['methods'] ) ) {
+                               } elseif ( is_array( $handler['methods'] ) ) {
                                        $methods = $handler['methods'];
                                } else {
                                        $methods = array();
                                        $methods = $handler['methods'];
                                } else {
                                        $methods = array();
@@ -719,6 +756,7 @@ class WP_REST_Server {
                                }
                        }
                }
                                }
                        }
                }
+
                return $endpoints;
        }
 
                return $endpoints;
        }
 
@@ -762,7 +800,7 @@ class WP_REST_Server {
         */
        public function dispatch( $request ) {
                /**
         */
        public function dispatch( $request ) {
                /**
-                * Filter the pre-calculated result of a REST dispatch request.
+                * Filters the pre-calculated result of a REST dispatch request.
                 *
                 * Allow hijacking the request before dispatching by returning a non-empty. The returned value
                 * will be used to serve the request instead.
                 *
                 * Allow hijacking the request before dispatching by returning a non-empty. The returned value
                 * will be used to serve the request instead.
@@ -794,7 +832,11 @@ class WP_REST_Server {
                                $callback  = $handler['callback'];
                                $response = null;
 
                                $callback  = $handler['callback'];
                                $response = null;
 
-                               $checked_method = 'HEAD' === $method ? 'GET' : $method;
+                               // Fallback to GET method if no HEAD method is registered.
+                               $checked_method = $method;
+                               if ( 'HEAD' === $method && empty( $handler['methods']['HEAD'] ) ) {
+                                       $checked_method = 'GET';
+                               }
                                if ( empty( $handler['methods'][ $checked_method ] ) ) {
                                        continue;
                                }
                                if ( empty( $handler['methods'][ $checked_method ] ) ) {
                                        continue;
                                }
@@ -810,8 +852,6 @@ class WP_REST_Server {
                                        $request->set_url_params( $args );
                                        $request->set_attributes( $handler );
 
                                        $request->set_url_params( $args );
                                        $request->set_attributes( $handler );
 
-                                       $request->sanitize_params();
-
                                        $defaults = array();
 
                                        foreach ( $handler['args'] as $arg => $options ) {
                                        $defaults = array();
 
                                        foreach ( $handler['args'] as $arg => $options ) {
@@ -825,9 +865,32 @@ class WP_REST_Server {
                                        $check_required = $request->has_valid_params();
                                        if ( is_wp_error( $check_required ) ) {
                                                $response = $check_required;
                                        $check_required = $request->has_valid_params();
                                        if ( is_wp_error( $check_required ) ) {
                                                $response = $check_required;
+                                       } else {
+                                               $check_sanitized = $request->sanitize_params();
+                                               if ( is_wp_error( $check_sanitized ) ) {
+                                                       $response = $check_sanitized;
+                                               }
                                        }
                                }
 
                                        }
                                }
 
+                               /**
+                                * Filters the response before executing any REST API callbacks.
+                                *
+                                * Allows plugins to perform additional validation after a
+                                * request is initialized and matched to a registered route,
+                                * but before it is executed.
+                                *
+                                * Note that this filter will not be called for requests that
+                                * fail to authenticate or match to a registered route.
+                                *
+                                * @since 4.7.0
+                                *
+                                * @param WP_HTTP_Response $response Result to send to the client. Usually a WP_REST_Response.
+                                * @param WP_REST_Server   $handler  ResponseHandler instance (usually WP_REST_Server).
+                                * @param WP_REST_Request  $request  Request used to generate the response.
+                                */
+                               $response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request );
+
                                if ( ! is_wp_error( $response ) ) {
                                        // Check permission specified on the route.
                                        if ( ! empty( $handler['permission_callback'] ) ) {
                                if ( ! is_wp_error( $response ) ) {
                                        // Check permission specified on the route.
                                        if ( ! empty( $handler['permission_callback'] ) ) {
@@ -835,24 +898,27 @@ class WP_REST_Server {
 
                                                if ( is_wp_error( $permission ) ) {
                                                        $response = $permission;
 
                                                if ( is_wp_error( $permission ) ) {
                                                        $response = $permission;
-                                               } else if ( false === $permission || null === $permission ) {
-                                                       $response = new WP_Error( 'rest_forbidden', __( "You don't have permission to do this." ), array( 'status' => 403 ) );
+                                               } elseif ( false === $permission || null === $permission ) {
+                                                       $response = new WP_Error( 'rest_forbidden', __( 'Sorry, you are not allowed to do that.' ), array( 'status' => 403 ) );
                                                }
                                        }
                                }
 
                                if ( ! is_wp_error( $response ) ) {
                                        /**
                                                }
                                        }
                                }
 
                                if ( ! is_wp_error( $response ) ) {
                                        /**
-                                        * Filter the REST dispatch request result.
+                                        * Filters the REST dispatch request result.
                                         *
                                         * Allow plugins to override dispatching the request.
                                         *
                                         * @since 4.4.0
                                         *
                                         * Allow plugins to override dispatching the request.
                                         *
                                         * @since 4.4.0
+                                        * @since 4.5.0 Added `$route` and `$handler` parameters.
                                         *
                                         * @param bool            $dispatch_result Dispatch result, will be used if not empty.
                                         * @param WP_REST_Request $request         Request used to generate the response.
                                         *
                                         * @param bool            $dispatch_result Dispatch result, will be used if not empty.
                                         * @param WP_REST_Request $request         Request used to generate the response.
+                                        * @param string          $route           Route matched for the request.
+                                        * @param array           $handler         Route handler used for the request.
                                         */
                                         */
-                                       $dispatch_result = apply_filters( 'rest_dispatch_request', null, $request );
+                                       $dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler );
 
                                        // Allow plugins to halt the request via this filter.
                                        if ( null !== $dispatch_result ) {
 
                                        // Allow plugins to halt the request via this filter.
                                        if ( null !== $dispatch_result ) {
@@ -862,6 +928,28 @@ class WP_REST_Server {
                                        }
                                }
 
                                        }
                                }
 
+                               /**
+                                * Filters the response immediately after executing any REST API
+                                * callbacks.
+                                *
+                                * Allows plugins to perform any needed cleanup, for example,
+                                * to undo changes made during the {@see 'rest_request_before_callbacks'}
+                                * filter.
+                                *
+                                * Note that this filter will not be called for requests that
+                                * fail to authenticate or match to a registered route.
+                                *
+                                * Note that an endpoint's `permission_callback` can still be
+                                * called after this filter - see `rest_send_allow_header()`.
+                                *
+                                * @since 4.7.0
+                                *
+                                * @param WP_HTTP_Response $response Result to send to the client. Usually a WP_REST_Response.
+                                * @param WP_REST_Server   $handler  ResponseHandler instance (usually WP_REST_Server).
+                                * @param WP_REST_Request  $request  Request used to generate the response.
+                                */
+                               $response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request );
+
                                if ( is_wp_error( $response ) ) {
                                        $response = $this->error_to_response( $response );
                                } else {
                                if ( is_wp_error( $response ) ) {
                                        $response = $this->error_to_response( $response );
                                } else {
@@ -925,6 +1013,7 @@ class WP_REST_Server {
                        'name'           => get_option( 'blogname' ),
                        'description'    => get_option( 'blogdescription' ),
                        'url'            => get_option( 'siteurl' ),
                        'name'           => get_option( 'blogname' ),
                        'description'    => get_option( 'blogdescription' ),
                        'url'            => get_option( 'siteurl' ),
+                       'home'           => home_url(),
                        'namespaces'     => array_keys( $this->namespaces ),
                        'authentication' => array(),
                        'routes'         => $this->get_data_for_routes( $this->get_routes(), $request['context'] ),
                        'namespaces'     => array_keys( $this->namespaces ),
                        'authentication' => array(),
                        'routes'         => $this->get_data_for_routes( $this->get_routes(), $request['context'] ),
@@ -935,7 +1024,7 @@ class WP_REST_Server {
                $response->add_link( 'help', 'http://v2.wp-api.org/' );
 
                /**
                $response->add_link( 'help', 'http://v2.wp-api.org/' );
 
                /**
-                * Filter the API root index data.
+                * Filters the API root index data.
                 *
                 * This contains the data describing the API. This includes information
                 * about supported authentication schemes, supported namespaces, routes
                 *
                 * This contains the data describing the API. This includes information
                 * about supported authentication schemes, supported namespaces, routes
@@ -978,7 +1067,7 @@ class WP_REST_Server {
                $response->add_link( 'up', rest_url( '/' ) );
 
                /**
                $response->add_link( 'up', rest_url( '/' ) );
 
                /**
-                * Filter the namespace index data.
+                * Filters the namespace index data.
                 *
                 * This typically is just the route data for the namespace, but you can
                 * add any data you'd like here.
                 *
                 * This typically is just the route data for the namespace, but you can
                 * add any data you'd like here.
@@ -1012,7 +1101,7 @@ class WP_REST_Server {
                        }
 
                        /**
                        }
 
                        /**
-                        * Filter the REST endpoint data.
+                        * Filters the REST endpoint data.
                         *
                         * @since 4.4.0
                         *
                         *
                         * @since 4.4.0
                         *
@@ -1022,7 +1111,7 @@ class WP_REST_Server {
                }
 
                /**
                }
 
                /**
-                * Filter the publicly-visible data for routes.
+                * Filters the publicly-visible data for routes.
                 *
                 * This data is exposed on indexes and can be used by clients or
                 * developers to investigate the site and find out how to use it. It
                 *
                 * This data is exposed on indexes and can be used by clients or
                 * developers to investigate the site and find out how to use it. It
@@ -1094,6 +1183,12 @@ class WP_REST_Server {
                                        if ( isset( $opts['description'] ) ) {
                                                $arg_data['description'] = $opts['description'];
                                        }
                                        if ( isset( $opts['description'] ) ) {
                                                $arg_data['description'] = $opts['description'];
                                        }
+                                       if ( isset( $opts['type'] ) ) {
+                                               $arg_data['type'] = $opts['type'];
+                                       }
+                                       if ( isset( $opts['items'] ) ) {
+                                               $arg_data['items'] = $opts['items'];
+                                       }
                                        $endpoint_data['args'][ $key ] = $arg_data;
                                }
                        }
                                        $endpoint_data['args'][ $key ] = $arg_data;
                                }
                        }