--- a/wp/wp-includes/rest-api/class-wp-rest-server.php Tue Dec 15 15:52:01 2020 +0100
+++ b/wp/wp-includes/rest-api/class-wp-rest-server.php Wed Sep 21 18:19:35 2022 +0200
@@ -94,7 +94,7 @@
public function __construct() {
$this->endpoints = array(
// Meta endpoints.
- '/' => array(
+ '/' => array(
'callback' => array( $this, 'get_index' ),
'methods' => 'GET',
'args' => array(
@@ -103,6 +103,51 @@
),
),
),
+ '/batch/v1' => array(
+ 'callback' => array( $this, 'serve_batch_request_v1' ),
+ 'methods' => 'POST',
+ 'args' => array(
+ 'validation' => array(
+ 'type' => 'string',
+ 'enum' => array( 'require-all-validate', 'normal' ),
+ 'default' => 'normal',
+ ),
+ 'requests' => array(
+ 'required' => true,
+ 'type' => 'array',
+ 'maxItems' => $this->get_max_batch_size(),
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'method' => array(
+ 'type' => 'string',
+ 'enum' => array( 'POST', 'PUT', 'PATCH', 'DELETE' ),
+ 'default' => 'POST',
+ ),
+ 'path' => array(
+ 'type' => 'string',
+ 'required' => true,
+ ),
+ 'body' => array(
+ 'type' => 'object',
+ 'properties' => array(),
+ 'additionalProperties' => true,
+ ),
+ 'headers' => array(
+ 'type' => 'object',
+ 'properties' => array(),
+ 'additionalProperties' => array(
+ 'type' => array( 'string', 'array' ),
+ 'items' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
);
}
@@ -117,7 +162,7 @@
*/
public function check_authentication() {
/**
- * Filters REST authentication errors.
+ * Filters REST API authentication errors.
*
* This is used to pass a WP_Error from an authentication method back to
* the API.
@@ -151,41 +196,13 @@
* list in JSON rather than an object/map.
*
* @since 4.4.0
+ * @since 5.7.0 Converted to a wrapper of {@see rest_convert_error_to_response()}.
*
* @param WP_Error $error WP_Error instance.
* @return WP_REST_Response List of associative arrays with code and message keys.
*/
protected function error_to_response( $error ) {
- $error_data = $error->get_error_data();
-
- if ( is_array( $error_data ) && isset( $error_data['status'] ) ) {
- $status = $error_data['status'];
- } else {
- $status = 500;
- }
-
- $errors = array();
-
- foreach ( (array) $error->errors as $code => $messages ) {
- foreach ( (array) $messages as $message ) {
- $errors[] = array(
- 'code' => $code,
- 'message' => $message,
- 'data' => $error->get_error_data( $code ),
- );
- }
- }
-
- $data = $errors[0];
- if ( count( $errors ) > 1 ) {
- // Remove the primary error.
- array_shift( $errors );
- $data['additional_errors'] = $errors;
- }
-
- $response = new WP_REST_Response( $data, $status );
-
- return $response;
+ return rest_convert_error_to_response( $error );
}
/**
@@ -214,7 +231,7 @@
}
/**
- * Handles serving an API request.
+ * Handles serving a REST API request.
*
* Matches the current server URI to a route and runs the first matching
* callback then outputs a JSON representation of the returned value.
@@ -223,12 +240,45 @@
*
* @see WP_REST_Server::dispatch()
*
+ * @global WP_User $current_user The currently authenticated user.
+ *
* @param string $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used.
* Default null.
* @return null|false Null if not served and a HEAD request, false otherwise.
*/
public function serve_request( $path = null ) {
- $content_type = isset( $_GET['_jsonp'] ) ? 'application/javascript' : 'application/json';
+ /* @var WP_User|null $current_user */
+ global $current_user;
+
+ if ( $current_user instanceof WP_User && ! $current_user->exists() ) {
+ /*
+ * If there is no current user authenticated via other means, clear
+ * the cached lack of user, so that an authenticate check can set it
+ * properly.
+ *
+ * This is done because for authentications such as Application
+ * Passwords, we don't want it to be accepted unless the current HTTP
+ * request is a REST API request, which can't always be identified early
+ * enough in evaluation.
+ */
+ $current_user = null;
+ }
+
+ /**
+ * Filters whether JSONP is enabled for the REST API.
+ *
+ * @since 4.4.0
+ *
+ * @param bool $jsonp_enabled Whether JSONP is enabled. Default true.
+ */
+ $jsonp_enabled = apply_filters( 'rest_jsonp_enabled', true );
+
+ $jsonp_callback = false;
+ if ( isset( $_GET['_jsonp'] ) ) {
+ $jsonp_callback = $_GET['_jsonp'];
+ }
+
+ $content_type = ( $jsonp_callback && $jsonp_enabled ) ? 'application/javascript' : 'application/json';
$this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) );
$this->send_header( 'X-Robots-Tag', 'noindex' );
@@ -246,11 +296,11 @@
$expose_headers = array( 'X-WP-Total', 'X-WP-TotalPages', 'Link' );
/**
- * Filters the list of response headers that are exposed to CORS requests.
+ * Filters the list of response headers that are exposed to REST API CORS requests.
*
* @since 5.5.0
*
- * @param string[] $expose_headers The list of headers to expose.
+ * @param string[] $expose_headers The list of response headers to expose.
*/
$expose_headers = apply_filters( 'rest_exposed_cors_headers', $expose_headers );
@@ -265,7 +315,7 @@
);
/**
- * Filters the list of request headers that are allowed for CORS requests.
+ * Filters the list of request headers that are allowed for REST API CORS requests.
*
* The allowed headers are passed to the browser to specify which
* headers can be passed to the REST API. By default, we allow the
@@ -274,14 +324,14 @@
*
* @since 5.5.0
*
- * @param string[] $allow_headers The list of headers to allow.
+ * @param string[] $allow_headers The list of request headers to allow.
*/
$allow_headers = apply_filters( 'rest_allowed_cors_headers', $allow_headers );
$this->send_header( 'Access-Control-Allow-Headers', implode( ', ', $allow_headers ) );
/**
- * Send nocache headers on authenticated requests.
+ * Filters whether to send nocache headers on a REST API request.
*
* @since 4.4.0
*
@@ -303,7 +353,7 @@
*
* @since 4.4.0
* @deprecated 4.7.0 Use the {@see 'rest_authentication_errors'} filter to
- * restrict access to the API.
+ * restrict access to the REST API.
*
* @param bool $rest_enabled Whether the REST API is enabled. Default true.
*/
@@ -319,24 +369,12 @@
)
);
- /**
- * Filters whether jsonp is enabled.
- *
- * @since 4.4.0
- *
- * @param bool $jsonp_enabled Whether jsonp is enabled. Default true.
- */
- $jsonp_enabled = apply_filters( 'rest_jsonp_enabled', true );
-
- $jsonp_callback = null;
-
- if ( isset( $_GET['_jsonp'] ) ) {
+ if ( $jsonp_callback ) {
if ( ! $jsonp_enabled ) {
echo $this->json_error( 'rest_callback_disabled', __( 'JSONP support is disabled on this site.' ), 400 );
return false;
}
- $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;
@@ -385,15 +423,15 @@
}
/**
- * Filters the API response.
+ * Filters the REST API response.
*
* 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 $server Server instance.
* @param WP_REST_Request $request Request used to generate the response.
*/
$result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $request );
@@ -411,7 +449,7 @@
$this->set_status( $code );
/**
- * Filters whether the request has already been served.
+ * Filters whether the REST API request has already been served.
*
* Allow sending the request manually - by returning true, the API result
* will not be sent to the client.
@@ -420,9 +458,9 @@
*
* @param bool $served Whether the request has already been served.
* Default false.
- * @param WP_HTTP_Response $result Result to send to the client. Usually a WP_REST_Response.
+ * @param WP_HTTP_Response $result Result to send to the client. Usually a `WP_REST_Response`.
* @param WP_REST_Request $request Request used to generate the response.
- * @param WP_REST_Server $this Server instance.
+ * @param WP_REST_Server $server Server instance.
*/
$served = apply_filters( 'rest_pre_serve_request', false, $result, $request, $this );
@@ -436,7 +474,7 @@
$result = $this->response_to_data( $result, $embed );
/**
- * Filters the API response.
+ * Filters the REST API response.
*
* Allows modification of the response data after inserting
* embedded data (if any) and before echoing the response data.
@@ -444,7 +482,7 @@
* @since 4.8.1
*
* @param array $result Response data to send to the client.
- * @param WP_REST_Server $this Server instance.
+ * @param WP_REST_Server $server Server instance.
* @param WP_REST_Request $request Request used to generate the response.
*/
$result = apply_filters( 'rest_pre_echo_response', $result, $this, $request );
@@ -466,7 +504,7 @@
);
$result = $this->error_to_response( $json_error_obj );
- $result = wp_json_encode( $result->data[0] );
+ $result = wp_json_encode( $result->data );
}
if ( $jsonp_callback ) {
@@ -493,7 +531,7 @@
* Data with sub-requests embedded.
*
* @type array $_links Links.
- * @type array $_embedded Embeddeds.
+ * @type array $_embedded Embedded objects.
* }
*/
public function response_to_data( $response, $embed ) {
@@ -617,7 +655,7 @@
* Data with sub-requests embedded.
*
* @type array $_links Links.
- * @type array $_embedded Embeddeds.
+ * @type array $_embedded Embedded objects.
* }
*/
protected function embed_links( $data, $embed = true ) {
@@ -704,11 +742,17 @@
);
/**
- * Filters the enveloped form of a response.
+ * Filters the enveloped form of a REST API response.
*
* @since 4.4.0
*
- * @param array $envelope Envelope data.
+ * @param array $envelope {
+ * Envelope data.
+ *
+ * @type array $body Response data.
+ * @type int $status The 3-digit HTTP status code.
+ * @type array $headers Map of header name to header value.
+ * }
* @param WP_REST_Response $response Original response data.
*/
$envelope = apply_filters( 'rest_envelope_response', $envelope, $response );
@@ -793,7 +837,7 @@
}
/**
- * Filters the array of available endpoints.
+ * Filters the array of available REST API endpoints.
*
* @since 4.4.0
*
@@ -893,7 +937,7 @@
*/
public function dispatch( $request ) {
/**
- * Filters the pre-calculated result of a REST dispatch request.
+ * Filters the pre-calculated result of a REST API dispatch request.
*
* Allow hijacking the request before dispatching by returning a non-empty. The returned value
* will be used to serve the request instead.
@@ -902,7 +946,7 @@
*
* @param mixed $result Response to replace the requested version with. Can be anything
* a normal endpoint can return, or null to not hijack the request.
- * @param WP_REST_Server $this Server instance.
+ * @param WP_REST_Server $server Server instance.
* @param WP_REST_Request $request Request used to generate the response.
*/
$result = apply_filters( 'rest_pre_dispatch', null, $this, $request );
@@ -911,6 +955,48 @@
return $result;
}
+ $error = null;
+ $matched = $this->match_request_to_handler( $request );
+
+ if ( is_wp_error( $matched ) ) {
+ return $this->error_to_response( $matched );
+ }
+
+ list( $route, $handler ) = $matched;
+
+ if ( ! is_callable( $handler['callback'] ) ) {
+ $error = new WP_Error(
+ 'rest_invalid_handler',
+ __( 'The handler for the route is invalid.' ),
+ array( 'status' => 500 )
+ );
+ }
+
+ if ( ! is_wp_error( $error ) ) {
+ $check_required = $request->has_valid_params();
+ if ( is_wp_error( $check_required ) ) {
+ $error = $check_required;
+ } else {
+ $check_sanitized = $request->sanitize_params();
+ if ( is_wp_error( $check_sanitized ) ) {
+ $error = $check_sanitized;
+ }
+ }
+ }
+
+ return $this->respond_to_request( $request, $route, $handler, $error );
+ }
+
+ /**
+ * Matches a request object to its handler.
+ *
+ * @access private
+ * @since 5.6.0
+ *
+ * @param WP_REST_Request $request The request object.
+ * @return array|WP_Error The route and request handler on success or a WP_Error instance if no handler was found.
+ */
+ protected function match_request_to_handler( $request ) {
$method = $request->get_method();
$path = $request->get_route();
@@ -957,145 +1043,140 @@
}
if ( ! is_callable( $callback ) ) {
- $response = new WP_Error(
- 'rest_invalid_handler',
- __( 'The handler for the route is invalid' ),
- array( 'status' => 500 )
- );
+ return array( $route, $handler );
}
- if ( ! is_wp_error( $response ) ) {
- // Remove the redundant preg_match argument.
- unset( $args[0] );
-
- $request->set_url_params( $args );
- $request->set_attributes( $handler );
-
- $defaults = array();
+ $request->set_url_params( $args );
+ $request->set_attributes( $handler );
- foreach ( $handler['args'] as $arg => $options ) {
- if ( isset( $options['default'] ) ) {
- $defaults[ $arg ] = $options['default'];
- }
- }
-
- $request->set_default_params( $defaults );
+ $defaults = array();
- $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;
- }
+ foreach ( $handler['args'] as $arg => $options ) {
+ if ( isset( $options['default'] ) ) {
+ $defaults[ $arg ] = $options['default'];
}
}
- /**
- * 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_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. Usually a WP_REST_Response or WP_Error.
- * @param array $handler Route handler used for the request.
- * @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'] ) ) {
- $permission = call_user_func( $handler['permission_callback'], $request );
-
- if ( is_wp_error( $permission ) ) {
- $response = $permission;
- } elseif ( false === $permission || null === $permission ) {
- $response = new WP_Error(
- 'rest_forbidden',
- __( 'Sorry, you are not allowed to do that.' ),
- array( 'status' => rest_authorization_required_code() )
- );
- }
- }
- }
+ $request->set_default_params( $defaults );
- if ( ! is_wp_error( $response ) ) {
- /**
- * Filters the REST dispatch request result.
- *
- * Allow plugins to override dispatching the request.
- *
- * @since 4.4.0
- * @since 4.5.0 Added `$route` and `$handler` parameters.
- *
- * @param mixed $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, $route, $handler );
-
- // Allow plugins to halt the request via this filter.
- if ( null !== $dispatch_result ) {
- $response = $dispatch_result;
- } else {
- $response = call_user_func( $callback, $request );
- }
- }
-
- /**
- * 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_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. Usually a WP_REST_Response or WP_Error.
- * @param array $handler Route handler used for the request.
- * @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 {
- $response = rest_ensure_response( $response );
- }
-
- $response->set_matched_route( $route );
- $response->set_matched_handler( $handler );
-
- return $response;
+ return array( $route, $handler );
}
}
- return $this->error_to_response(
- new WP_Error(
- 'rest_no_route',
- __( 'No route was found matching the URL and request method' ),
- array( 'status' => 404 )
- )
+ return new WP_Error(
+ 'rest_no_route',
+ __( 'No route was found matching the URL and request method.' ),
+ array( 'status' => 404 )
);
}
/**
+ * Dispatches the request to the callback handler.
+ *
+ * @access private
+ * @since 5.6.0
+ *
+ * @param WP_REST_Request $request The request object.
+ * @param array $handler The matched route handler.
+ * @param string $route The matched route regex.
+ * @param WP_Error|null $response The current error object if any.
+ * @return WP_REST_Response
+ */
+ protected function respond_to_request( $request, $route, $handler, $response ) {
+ /**
+ * 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_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
+ * Usually a WP_REST_Response or WP_Error.
+ * @param array $handler Route handler used for the request.
+ * @param WP_REST_Request $request Request used to generate the response.
+ */
+ $response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request );
+
+ // Check permission specified on the route.
+ if ( ! is_wp_error( $response ) && ! empty( $handler['permission_callback'] ) ) {
+ $permission = call_user_func( $handler['permission_callback'], $request );
+
+ if ( is_wp_error( $permission ) ) {
+ $response = $permission;
+ } elseif ( false === $permission || null === $permission ) {
+ $response = new WP_Error(
+ 'rest_forbidden',
+ __( 'Sorry, you are not allowed to do that.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
+ }
+ }
+
+ if ( ! is_wp_error( $response ) ) {
+ /**
+ * Filters the REST API dispatch request result.
+ *
+ * Allow plugins to override dispatching the request.
+ *
+ * @since 4.4.0
+ * @since 4.5.0 Added `$route` and `$handler` parameters.
+ *
+ * @param mixed $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, $route, $handler );
+
+ // Allow plugins to halt the request via this filter.
+ if ( null !== $dispatch_result ) {
+ $response = $dispatch_result;
+ } else {
+ $response = call_user_func( $handler['callback'], $request );
+ }
+ }
+
+ /**
+ * 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_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
+ * Usually a WP_REST_Response or WP_Error.
+ * @param array $handler Route handler used for the request.
+ * @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 {
+ $response = rest_ensure_response( $response );
+ }
+
+ $response->set_matched_route( $route );
+ $response->set_matched_handler( $handler );
+
+ return $response;
+ }
+
+ /**
* Returns if an error occurred during most recent JSON encode/decode.
*
* Strings to be translated will be in format like
@@ -1103,7 +1184,7 @@
*
* @since 4.4.0
*
- * @return bool|string Boolean false or string error message.
+ * @return false|string Boolean false or string error message.
*/
protected function get_json_last_error() {
$last_error_code = json_last_error();
@@ -1144,11 +1225,12 @@
);
$response = new WP_REST_Response( $available );
-
- $response->add_link( 'help', 'http://v2.wp-api.org/' );
+ $response->add_link( 'help', 'https://developer.wordpress.org/rest-api/' );
+ $this->add_active_theme_link_to_index( $response );
+ $this->add_site_logo_to_index( $response );
/**
- * Filters the API root index data.
+ * Filters the REST API root index data.
*
* This contains the data describing the API. This includes information
* about supported authentication schemes, supported namespaces, routes
@@ -1162,6 +1244,58 @@
}
/**
+ * Adds a link to the active theme for users who have proper permissions.
+ *
+ * @since 5.7.0
+ *
+ * @param WP_REST_Response $response REST API response.
+ */
+ protected function add_active_theme_link_to_index( WP_REST_Response $response ) {
+ $should_add = current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' );
+
+ if ( ! $should_add && current_user_can( 'edit_posts' ) ) {
+ $should_add = true;
+ }
+
+ if ( ! $should_add ) {
+ foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
+ if ( current_user_can( $post_type->cap->edit_posts ) ) {
+ $should_add = true;
+ break;
+ }
+ }
+ }
+
+ if ( $should_add ) {
+ $theme = wp_get_theme();
+ $response->add_link( 'https://api.w.org/active-theme', rest_url( 'wp/v2/themes/' . $theme->get_stylesheet() ) );
+ }
+ }
+
+ /**
+ * Exposes the site logo through the WordPress REST API.
+ * This is used for fetching this information when user has no rights
+ * to update settings.
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Response $response REST API response.
+ */
+ protected function add_site_logo_to_index( WP_REST_Response $response ) {
+ $site_logo_id = get_theme_mod( 'custom_logo' );
+ $response->data['site_logo'] = $site_logo_id;
+ if ( $site_logo_id ) {
+ $response->add_link(
+ 'https://api.w.org/featuredmedia',
+ rest_url( 'wp/v2/media/' . $site_logo_id ),
+ array(
+ 'embeddable' => true,
+ )
+ );
+ }
+ }
+
+ /**
* Retrieves the index for a namespace.
*
* @since 4.4.0
@@ -1194,7 +1328,7 @@
$response->add_link( 'up', rest_url( '/' ) );
/**
- * Filters the namespace index data.
+ * Filters the REST API namespace index data.
*
* This typically is just the route data for the namespace, but you can
* add any data you'd like here.
@@ -1227,7 +1361,7 @@
}
/**
- * Filters the REST endpoint data.
+ * Filters the REST API endpoint data.
*
* @since 4.4.0
*
@@ -1237,7 +1371,7 @@
}
/**
- * Filters the publicly-visible data for routes.
+ * Filters the publicly-visible data for REST API 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
@@ -1280,6 +1414,8 @@
}
}
+ $allowed_schema_keywords = array_flip( rest_get_allowed_schema_keywords() );
+
$route = preg_replace( '#\(\?P<(\w+?)>.*?\)#', '{$1}', $route );
foreach ( $callbacks as $callback ) {
@@ -1297,24 +1433,9 @@
$endpoint_data['args'] = array();
foreach ( $callback['args'] as $key => $opts ) {
- $arg_data = array(
- 'required' => ! empty( $opts['required'] ),
- );
- if ( isset( $opts['default'] ) ) {
- $arg_data['default'] = $opts['default'];
- }
- if ( isset( $opts['enum'] ) ) {
- $arg_data['enum'] = $opts['enum'];
- }
- 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'];
- }
+ $arg_data = array_intersect_key( $opts, $allowed_schema_keywords );
+ $arg_data['required'] = ! empty( $opts['required'] );
+
$endpoint_data['args'][ $key ] = $arg_data;
}
}
@@ -1342,6 +1463,180 @@
}
/**
+ * Gets the maximum number of requests that can be included in a batch.
+ *
+ * @since 5.6.0
+ *
+ * @return int The maximum requests.
+ */
+ protected function get_max_batch_size() {
+ /**
+ * Filters the maximum number of REST API requests that can be included in a batch.
+ *
+ * @since 5.6.0
+ *
+ * @param int $max_size The maximum size.
+ */
+ return apply_filters( 'rest_get_max_batch_size', 25 );
+ }
+
+ /**
+ * Serves the batch/v1 request.
+ *
+ * @since 5.6.0
+ *
+ * @param WP_REST_Request $batch_request The batch request object.
+ * @return WP_REST_Response The generated response object.
+ */
+ public function serve_batch_request_v1( WP_REST_Request $batch_request ) {
+ $requests = array();
+
+ foreach ( $batch_request['requests'] as $args ) {
+ $parsed_url = wp_parse_url( $args['path'] );
+
+ if ( false === $parsed_url ) {
+ $requests[] = new WP_Error( 'parse_path_failed', __( 'Could not parse the path.' ), array( 'status' => 400 ) );
+
+ continue;
+ }
+
+ $single_request = new WP_REST_Request( isset( $args['method'] ) ? $args['method'] : 'POST', $parsed_url['path'] );
+
+ if ( ! empty( $parsed_url['query'] ) ) {
+ $query_args = null; // Satisfy linter.
+ wp_parse_str( $parsed_url['query'], $query_args );
+ $single_request->set_query_params( $query_args );
+ }
+
+ if ( ! empty( $args['body'] ) ) {
+ $single_request->set_body_params( $args['body'] );
+ }
+
+ if ( ! empty( $args['headers'] ) ) {
+ $single_request->set_headers( $args['headers'] );
+ }
+
+ $requests[] = $single_request;
+ }
+
+ $matches = array();
+ $validation = array();
+ $has_error = false;
+
+ foreach ( $requests as $single_request ) {
+ $match = $this->match_request_to_handler( $single_request );
+ $matches[] = $match;
+ $error = null;
+
+ if ( is_wp_error( $match ) ) {
+ $error = $match;
+ }
+
+ if ( ! $error ) {
+ list( $route, $handler ) = $match;
+
+ if ( isset( $handler['allow_batch'] ) ) {
+ $allow_batch = $handler['allow_batch'];
+ } else {
+ $route_options = $this->get_route_options( $route );
+ $allow_batch = isset( $route_options['allow_batch'] ) ? $route_options['allow_batch'] : false;
+ }
+
+ if ( ! is_array( $allow_batch ) || empty( $allow_batch['v1'] ) ) {
+ $error = new WP_Error(
+ 'rest_batch_not_allowed',
+ __( 'The requested route does not support batch requests.' ),
+ array( 'status' => 400 )
+ );
+ }
+ }
+
+ if ( ! $error ) {
+ $check_required = $single_request->has_valid_params();
+ if ( is_wp_error( $check_required ) ) {
+ $error = $check_required;
+ }
+ }
+
+ if ( ! $error ) {
+ $check_sanitized = $single_request->sanitize_params();
+ if ( is_wp_error( $check_sanitized ) ) {
+ $error = $check_sanitized;
+ }
+ }
+
+ if ( $error ) {
+ $has_error = true;
+ $validation[] = $error;
+ } else {
+ $validation[] = true;
+ }
+ }
+
+ $responses = array();
+
+ if ( $has_error && 'require-all-validate' === $batch_request['validation'] ) {
+ foreach ( $validation as $valid ) {
+ if ( is_wp_error( $valid ) ) {
+ $responses[] = $this->envelope_response( $this->error_to_response( $valid ), false )->get_data();
+ } else {
+ $responses[] = null;
+ }
+ }
+
+ return new WP_REST_Response(
+ array(
+ 'failed' => 'validation',
+ 'responses' => $responses,
+ ),
+ WP_Http::MULTI_STATUS
+ );
+ }
+
+ foreach ( $requests as $i => $single_request ) {
+ $clean_request = clone $single_request;
+ $clean_request->set_url_params( array() );
+ $clean_request->set_attributes( array() );
+ $clean_request->set_default_params( array() );
+
+ /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */
+ $result = apply_filters( 'rest_pre_dispatch', null, $this, $clean_request );
+
+ if ( empty( $result ) ) {
+ $match = $matches[ $i ];
+ $error = null;
+
+ if ( is_wp_error( $validation[ $i ] ) ) {
+ $error = $validation[ $i ];
+ }
+
+ if ( is_wp_error( $match ) ) {
+ $result = $this->error_to_response( $match );
+ } else {
+ list( $route, $handler ) = $match;
+
+ if ( ! $error && ! is_callable( $handler['callback'] ) ) {
+ $error = new WP_Error(
+ 'rest_invalid_handler',
+ __( 'The handler for the route is invalid' ),
+ array( 'status' => 500 )
+ );
+ }
+
+ $result = $this->respond_to_request( $single_request, $route, $handler, $error );
+ }
+ }
+
+ /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */
+ $result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $single_request );
+
+ $responses[] = $this->envelope_response( $result, false )->get_data();
+ }
+
+ return new WP_REST_Response( array( 'responses' => $responses ), WP_Http::MULTI_STATUS );
+ }
+
+ /**
* Sends an HTTP status code.
*
* @since 4.4.0