wp/wp-includes/rest-api/class-wp-rest-server.php
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 19 3d72ae0968f4
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
    92 	 * @since 4.4.0
    92 	 * @since 4.4.0
    93 	 */
    93 	 */
    94 	public function __construct() {
    94 	public function __construct() {
    95 		$this->endpoints = array(
    95 		$this->endpoints = array(
    96 			// Meta endpoints.
    96 			// Meta endpoints.
    97 			'/' => array(
    97 			'/'         => array(
    98 				'callback' => array( $this, 'get_index' ),
    98 				'callback' => array( $this, 'get_index' ),
    99 				'methods'  => 'GET',
    99 				'methods'  => 'GET',
   100 				'args'     => array(
   100 				'args'     => array(
   101 					'context' => array(
   101 					'context' => array(
   102 						'default' => 'view',
   102 						'default' => 'view',
   103 					),
   103 					),
   104 				),
   104 				),
   105 			),
   105 			),
       
   106 			'/batch/v1' => array(
       
   107 				'callback' => array( $this, 'serve_batch_request_v1' ),
       
   108 				'methods'  => 'POST',
       
   109 				'args'     => array(
       
   110 					'validation' => array(
       
   111 						'type'    => 'string',
       
   112 						'enum'    => array( 'require-all-validate', 'normal' ),
       
   113 						'default' => 'normal',
       
   114 					),
       
   115 					'requests'   => array(
       
   116 						'required' => true,
       
   117 						'type'     => 'array',
       
   118 						'maxItems' => $this->get_max_batch_size(),
       
   119 						'items'    => array(
       
   120 							'type'       => 'object',
       
   121 							'properties' => array(
       
   122 								'method'  => array(
       
   123 									'type'    => 'string',
       
   124 									'enum'    => array( 'POST', 'PUT', 'PATCH', 'DELETE' ),
       
   125 									'default' => 'POST',
       
   126 								),
       
   127 								'path'    => array(
       
   128 									'type'     => 'string',
       
   129 									'required' => true,
       
   130 								),
       
   131 								'body'    => array(
       
   132 									'type'                 => 'object',
       
   133 									'properties'           => array(),
       
   134 									'additionalProperties' => true,
       
   135 								),
       
   136 								'headers' => array(
       
   137 									'type'                 => 'object',
       
   138 									'properties'           => array(),
       
   139 									'additionalProperties' => array(
       
   140 										'type'  => array( 'string', 'array' ),
       
   141 										'items' => array(
       
   142 											'type' => 'string',
       
   143 										),
       
   144 									),
       
   145 								),
       
   146 							),
       
   147 						),
       
   148 					),
       
   149 				),
       
   150 			),
   106 		);
   151 		);
   107 	}
   152 	}
   108 
   153 
   109 
   154 
   110 	/**
   155 	/**
   115 	 * @return WP_Error|null WP_Error indicates unsuccessful login, null indicates successful
   160 	 * @return WP_Error|null WP_Error indicates unsuccessful login, null indicates successful
   116 	 *                       or no authentication provided
   161 	 *                       or no authentication provided
   117 	 */
   162 	 */
   118 	public function check_authentication() {
   163 	public function check_authentication() {
   119 		/**
   164 		/**
   120 		 * Filters REST authentication errors.
   165 		 * Filters REST API authentication errors.
   121 		 *
   166 		 *
   122 		 * This is used to pass a WP_Error from an authentication method back to
   167 		 * This is used to pass a WP_Error from an authentication method back to
   123 		 * the API.
   168 		 * the API.
   124 		 *
   169 		 *
   125 		 * Authentication methods should check first if they're being used, as
   170 		 * Authentication methods should check first if they're being used, as
   149 	 * This iterates over all error codes and messages to change it into a flat
   194 	 * This iterates over all error codes and messages to change it into a flat
   150 	 * array. This enables simpler client behaviour, as it is represented as a
   195 	 * array. This enables simpler client behaviour, as it is represented as a
   151 	 * list in JSON rather than an object/map.
   196 	 * list in JSON rather than an object/map.
   152 	 *
   197 	 *
   153 	 * @since 4.4.0
   198 	 * @since 4.4.0
       
   199 	 * @since 5.7.0 Converted to a wrapper of {@see rest_convert_error_to_response()}.
   154 	 *
   200 	 *
   155 	 * @param WP_Error $error WP_Error instance.
   201 	 * @param WP_Error $error WP_Error instance.
   156 	 * @return WP_REST_Response List of associative arrays with code and message keys.
   202 	 * @return WP_REST_Response List of associative arrays with code and message keys.
   157 	 */
   203 	 */
   158 	protected function error_to_response( $error ) {
   204 	protected function error_to_response( $error ) {
   159 		$error_data = $error->get_error_data();
   205 		return rest_convert_error_to_response( $error );
   160 
       
   161 		if ( is_array( $error_data ) && isset( $error_data['status'] ) ) {
       
   162 			$status = $error_data['status'];
       
   163 		} else {
       
   164 			$status = 500;
       
   165 		}
       
   166 
       
   167 		$errors = array();
       
   168 
       
   169 		foreach ( (array) $error->errors as $code => $messages ) {
       
   170 			foreach ( (array) $messages as $message ) {
       
   171 				$errors[] = array(
       
   172 					'code'    => $code,
       
   173 					'message' => $message,
       
   174 					'data'    => $error->get_error_data( $code ),
       
   175 				);
       
   176 			}
       
   177 		}
       
   178 
       
   179 		$data = $errors[0];
       
   180 		if ( count( $errors ) > 1 ) {
       
   181 			// Remove the primary error.
       
   182 			array_shift( $errors );
       
   183 			$data['additional_errors'] = $errors;
       
   184 		}
       
   185 
       
   186 		$response = new WP_REST_Response( $data, $status );
       
   187 
       
   188 		return $response;
       
   189 	}
   206 	}
   190 
   207 
   191 	/**
   208 	/**
   192 	 * Retrieves an appropriate error representation in JSON.
   209 	 * Retrieves an appropriate error representation in JSON.
   193 	 *
   210 	 *
   212 
   229 
   213 		return wp_json_encode( $error );
   230 		return wp_json_encode( $error );
   214 	}
   231 	}
   215 
   232 
   216 	/**
   233 	/**
   217 	 * Handles serving an API request.
   234 	 * Handles serving a REST API request.
   218 	 *
   235 	 *
   219 	 * Matches the current server URI to a route and runs the first matching
   236 	 * Matches the current server URI to a route and runs the first matching
   220 	 * callback then outputs a JSON representation of the returned value.
   237 	 * callback then outputs a JSON representation of the returned value.
   221 	 *
   238 	 *
   222 	 * @since 4.4.0
   239 	 * @since 4.4.0
   223 	 *
   240 	 *
   224 	 * @see WP_REST_Server::dispatch()
   241 	 * @see WP_REST_Server::dispatch()
       
   242 	 *
       
   243 	 * @global WP_User $current_user The currently authenticated user.
   225 	 *
   244 	 *
   226 	 * @param string $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used.
   245 	 * @param string $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used.
   227 	 *                     Default null.
   246 	 *                     Default null.
   228 	 * @return null|false Null if not served and a HEAD request, false otherwise.
   247 	 * @return null|false Null if not served and a HEAD request, false otherwise.
   229 	 */
   248 	 */
   230 	public function serve_request( $path = null ) {
   249 	public function serve_request( $path = null ) {
   231 		$content_type = isset( $_GET['_jsonp'] ) ? 'application/javascript' : 'application/json';
   250 		/* @var WP_User|null $current_user */
       
   251 		global $current_user;
       
   252 
       
   253 		if ( $current_user instanceof WP_User && ! $current_user->exists() ) {
       
   254 			/*
       
   255 			 * If there is no current user authenticated via other means, clear
       
   256 			 * the cached lack of user, so that an authenticate check can set it
       
   257 			 * properly.
       
   258 			 *
       
   259 			 * This is done because for authentications such as Application
       
   260 			 * Passwords, we don't want it to be accepted unless the current HTTP
       
   261 			 * request is a REST API request, which can't always be identified early
       
   262 			 * enough in evaluation.
       
   263 			 */
       
   264 			$current_user = null;
       
   265 		}
       
   266 
       
   267 		/**
       
   268 		 * Filters whether JSONP is enabled for the REST API.
       
   269 		 *
       
   270 		 * @since 4.4.0
       
   271 		 *
       
   272 		 * @param bool $jsonp_enabled Whether JSONP is enabled. Default true.
       
   273 		 */
       
   274 		$jsonp_enabled = apply_filters( 'rest_jsonp_enabled', true );
       
   275 
       
   276 		$jsonp_callback = false;
       
   277 		if ( isset( $_GET['_jsonp'] ) ) {
       
   278 			$jsonp_callback = $_GET['_jsonp'];
       
   279 		}
       
   280 
       
   281 		$content_type = ( $jsonp_callback && $jsonp_enabled ) ? 'application/javascript' : 'application/json';
   232 		$this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) );
   282 		$this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) );
   233 		$this->send_header( 'X-Robots-Tag', 'noindex' );
   283 		$this->send_header( 'X-Robots-Tag', 'noindex' );
   234 
   284 
   235 		$api_root = get_rest_url();
   285 		$api_root = get_rest_url();
   236 		if ( ! empty( $api_root ) ) {
   286 		if ( ! empty( $api_root ) ) {
   244 		 */
   294 		 */
   245 		$this->send_header( 'X-Content-Type-Options', 'nosniff' );
   295 		$this->send_header( 'X-Content-Type-Options', 'nosniff' );
   246 		$expose_headers = array( 'X-WP-Total', 'X-WP-TotalPages', 'Link' );
   296 		$expose_headers = array( 'X-WP-Total', 'X-WP-TotalPages', 'Link' );
   247 
   297 
   248 		/**
   298 		/**
   249 		 * Filters the list of response headers that are exposed to CORS requests.
   299 		 * Filters the list of response headers that are exposed to REST API CORS requests.
   250 		 *
   300 		 *
   251 		 * @since 5.5.0
   301 		 * @since 5.5.0
   252 		 *
   302 		 *
   253 		 * @param string[] $expose_headers The list of headers to expose.
   303 		 * @param string[] $expose_headers The list of response headers to expose.
   254 		 */
   304 		 */
   255 		$expose_headers = apply_filters( 'rest_exposed_cors_headers', $expose_headers );
   305 		$expose_headers = apply_filters( 'rest_exposed_cors_headers', $expose_headers );
   256 
   306 
   257 		$this->send_header( 'Access-Control-Expose-Headers', implode( ', ', $expose_headers ) );
   307 		$this->send_header( 'Access-Control-Expose-Headers', implode( ', ', $expose_headers ) );
   258 
   308 
   263 			'Content-MD5',
   313 			'Content-MD5',
   264 			'Content-Type',
   314 			'Content-Type',
   265 		);
   315 		);
   266 
   316 
   267 		/**
   317 		/**
   268 		 * Filters the list of request headers that are allowed for CORS requests.
   318 		 * Filters the list of request headers that are allowed for REST API CORS requests.
   269 		 *
   319 		 *
   270 		 * The allowed headers are passed to the browser to specify which
   320 		 * The allowed headers are passed to the browser to specify which
   271 		 * headers can be passed to the REST API. By default, we allow the
   321 		 * headers can be passed to the REST API. By default, we allow the
   272 		 * Content-* headers needed to upload files to the media endpoints.
   322 		 * Content-* headers needed to upload files to the media endpoints.
   273 		 * As well as the Authorization and Nonce headers for allowing authentication.
   323 		 * As well as the Authorization and Nonce headers for allowing authentication.
   274 		 *
   324 		 *
   275 		 * @since 5.5.0
   325 		 * @since 5.5.0
   276 		 *
   326 		 *
   277 		 * @param string[] $allow_headers The list of headers to allow.
   327 		 * @param string[] $allow_headers The list of request headers to allow.
   278 		 */
   328 		 */
   279 		$allow_headers = apply_filters( 'rest_allowed_cors_headers', $allow_headers );
   329 		$allow_headers = apply_filters( 'rest_allowed_cors_headers', $allow_headers );
   280 
   330 
   281 		$this->send_header( 'Access-Control-Allow-Headers', implode( ', ', $allow_headers ) );
   331 		$this->send_header( 'Access-Control-Allow-Headers', implode( ', ', $allow_headers ) );
   282 
   332 
   283 		/**
   333 		/**
   284 		 * Send nocache headers on authenticated requests.
   334 		 * Filters whether to send nocache headers on a REST API request.
   285 		 *
   335 		 *
   286 		 * @since 4.4.0
   336 		 * @since 4.4.0
   287 		 *
   337 		 *
   288 		 * @param bool $rest_send_nocache_headers Whether to send no-cache headers.
   338 		 * @param bool $rest_send_nocache_headers Whether to send no-cache headers.
   289 		 */
   339 		 */
   301 		/**
   351 		/**
   302 		 * Filters whether the REST API is enabled.
   352 		 * Filters whether the REST API is enabled.
   303 		 *
   353 		 *
   304 		 * @since 4.4.0
   354 		 * @since 4.4.0
   305 		 * @deprecated 4.7.0 Use the {@see 'rest_authentication_errors'} filter to
   355 		 * @deprecated 4.7.0 Use the {@see 'rest_authentication_errors'} filter to
   306 		 *                   restrict access to the API.
   356 		 *                   restrict access to the REST API.
   307 		 *
   357 		 *
   308 		 * @param bool $rest_enabled Whether the REST API is enabled. Default true.
   358 		 * @param bool $rest_enabled Whether the REST API is enabled. Default true.
   309 		 */
   359 		 */
   310 		apply_filters_deprecated(
   360 		apply_filters_deprecated(
   311 			'rest_enabled',
   361 			'rest_enabled',
   317 				__( 'The REST API can no longer be completely disabled, the %s filter can be used to restrict access to the API, instead.' ),
   367 				__( 'The REST API can no longer be completely disabled, the %s filter can be used to restrict access to the API, instead.' ),
   318 				'rest_authentication_errors'
   368 				'rest_authentication_errors'
   319 			)
   369 			)
   320 		);
   370 		);
   321 
   371 
   322 		/**
   372 		if ( $jsonp_callback ) {
   323 		 * Filters whether jsonp is enabled.
       
   324 		 *
       
   325 		 * @since 4.4.0
       
   326 		 *
       
   327 		 * @param bool $jsonp_enabled Whether jsonp is enabled. Default true.
       
   328 		 */
       
   329 		$jsonp_enabled = apply_filters( 'rest_jsonp_enabled', true );
       
   330 
       
   331 		$jsonp_callback = null;
       
   332 
       
   333 		if ( isset( $_GET['_jsonp'] ) ) {
       
   334 			if ( ! $jsonp_enabled ) {
   373 			if ( ! $jsonp_enabled ) {
   335 				echo $this->json_error( 'rest_callback_disabled', __( 'JSONP support is disabled on this site.' ), 400 );
   374 				echo $this->json_error( 'rest_callback_disabled', __( 'JSONP support is disabled on this site.' ), 400 );
   336 				return false;
   375 				return false;
   337 			}
   376 			}
   338 
   377 
   339 			$jsonp_callback = $_GET['_jsonp'];
       
   340 			if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) {
   378 			if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) {
   341 				echo $this->json_error( 'rest_callback_invalid', __( 'Invalid JSONP callback function.' ), 400 );
   379 				echo $this->json_error( 'rest_callback_invalid', __( 'Invalid JSONP callback function.' ), 400 );
   342 				return false;
   380 				return false;
   343 			}
   381 			}
   344 		}
   382 		}
   383 		if ( is_wp_error( $result ) ) {
   421 		if ( is_wp_error( $result ) ) {
   384 			$result = $this->error_to_response( $result );
   422 			$result = $this->error_to_response( $result );
   385 		}
   423 		}
   386 
   424 
   387 		/**
   425 		/**
   388 		 * Filters the API response.
   426 		 * Filters the REST API response.
   389 		 *
   427 		 *
   390 		 * Allows modification of the response before returning.
   428 		 * Allows modification of the response before returning.
   391 		 *
   429 		 *
   392 		 * @since 4.4.0
   430 		 * @since 4.4.0
   393 		 * @since 4.5.0 Applied to embedded responses.
   431 		 * @since 4.5.0 Applied to embedded responses.
   394 		 *
   432 		 *
   395 		 * @param WP_HTTP_Response $result  Result to send to the client. Usually a WP_REST_Response.
   433 		 * @param WP_HTTP_Response $result  Result to send to the client. Usually a `WP_REST_Response`.
   396 		 * @param WP_REST_Server   $this    Server instance.
   434 		 * @param WP_REST_Server   $server  Server instance.
   397 		 * @param WP_REST_Request  $request Request used to generate the response.
   435 		 * @param WP_REST_Request  $request Request used to generate the response.
   398 		 */
   436 		 */
   399 		$result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $request );
   437 		$result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $request );
   400 
   438 
   401 		// Wrap the response in an envelope if asked for.
   439 		// Wrap the response in an envelope if asked for.
   409 
   447 
   410 		$code = $result->get_status();
   448 		$code = $result->get_status();
   411 		$this->set_status( $code );
   449 		$this->set_status( $code );
   412 
   450 
   413 		/**
   451 		/**
   414 		 * Filters whether the request has already been served.
   452 		 * Filters whether the REST API request has already been served.
   415 		 *
   453 		 *
   416 		 * Allow sending the request manually - by returning true, the API result
   454 		 * Allow sending the request manually - by returning true, the API result
   417 		 * will not be sent to the client.
   455 		 * will not be sent to the client.
   418 		 *
   456 		 *
   419 		 * @since 4.4.0
   457 		 * @since 4.4.0
   420 		 *
   458 		 *
   421 		 * @param bool             $served  Whether the request has already been served.
   459 		 * @param bool             $served  Whether the request has already been served.
   422 		 *                                           Default false.
   460 		 *                                           Default false.
   423 		 * @param WP_HTTP_Response $result  Result to send to the client. Usually a WP_REST_Response.
   461 		 * @param WP_HTTP_Response $result  Result to send to the client. Usually a `WP_REST_Response`.
   424 		 * @param WP_REST_Request  $request Request used to generate the response.
   462 		 * @param WP_REST_Request  $request Request used to generate the response.
   425 		 * @param WP_REST_Server   $this    Server instance.
   463 		 * @param WP_REST_Server   $server  Server instance.
   426 		 */
   464 		 */
   427 		$served = apply_filters( 'rest_pre_serve_request', false, $result, $request, $this );
   465 		$served = apply_filters( 'rest_pre_serve_request', false, $result, $request, $this );
   428 
   466 
   429 		if ( ! $served ) {
   467 		if ( ! $served ) {
   430 			if ( 'HEAD' === $request->get_method() ) {
   468 			if ( 'HEAD' === $request->get_method() ) {
   434 			// Embed links inside the request.
   472 			// Embed links inside the request.
   435 			$embed  = isset( $_GET['_embed'] ) ? rest_parse_embed_param( $_GET['_embed'] ) : false;
   473 			$embed  = isset( $_GET['_embed'] ) ? rest_parse_embed_param( $_GET['_embed'] ) : false;
   436 			$result = $this->response_to_data( $result, $embed );
   474 			$result = $this->response_to_data( $result, $embed );
   437 
   475 
   438 			/**
   476 			/**
   439 			 * Filters the API response.
   477 			 * Filters the REST API response.
   440 			 *
   478 			 *
   441 			 * Allows modification of the response data after inserting
   479 			 * Allows modification of the response data after inserting
   442 			 * embedded data (if any) and before echoing the response data.
   480 			 * embedded data (if any) and before echoing the response data.
   443 			 *
   481 			 *
   444 			 * @since 4.8.1
   482 			 * @since 4.8.1
   445 			 *
   483 			 *
   446 			 * @param array            $result  Response data to send to the client.
   484 			 * @param array            $result  Response data to send to the client.
   447 			 * @param WP_REST_Server   $this    Server instance.
   485 			 * @param WP_REST_Server   $server  Server instance.
   448 			 * @param WP_REST_Request  $request Request used to generate the response.
   486 			 * @param WP_REST_Request  $request Request used to generate the response.
   449 			 */
   487 			 */
   450 			$result = apply_filters( 'rest_pre_echo_response', $result, $this, $request );
   488 			$result = apply_filters( 'rest_pre_echo_response', $result, $this, $request );
   451 
   489 
   452 			// The 204 response shouldn't have a body.
   490 			// The 204 response shouldn't have a body.
   464 					$json_error_message,
   502 					$json_error_message,
   465 					array( 'status' => 500 )
   503 					array( 'status' => 500 )
   466 				);
   504 				);
   467 
   505 
   468 				$result = $this->error_to_response( $json_error_obj );
   506 				$result = $this->error_to_response( $json_error_obj );
   469 				$result = wp_json_encode( $result->data[0] );
   507 				$result = wp_json_encode( $result->data );
   470 			}
   508 			}
   471 
   509 
   472 			if ( $jsonp_callback ) {
   510 			if ( $jsonp_callback ) {
   473 				// Prepend '/**/' to mitigate possible JSONP Flash attacks.
   511 				// Prepend '/**/' to mitigate possible JSONP Flash attacks.
   474 				// https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
   512 				// https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
   491 	 * @param bool|string[]    $embed    Whether to embed all links, a filtered list of link relations, or no links.
   529 	 * @param bool|string[]    $embed    Whether to embed all links, a filtered list of link relations, or no links.
   492 	 * @return array {
   530 	 * @return array {
   493 	 *     Data with sub-requests embedded.
   531 	 *     Data with sub-requests embedded.
   494 	 *
   532 	 *
   495 	 *     @type array $_links    Links.
   533 	 *     @type array $_links    Links.
   496 	 *     @type array $_embedded Embeddeds.
   534 	 *     @type array $_embedded Embedded objects.
   497 	 * }
   535 	 * }
   498 	 */
   536 	 */
   499 	public function response_to_data( $response, $embed ) {
   537 	public function response_to_data( $response, $embed ) {
   500 		$data  = $response->get_data();
   538 		$data  = $response->get_data();
   501 		$links = self::get_compact_response_links( $response );
   539 		$links = self::get_compact_response_links( $response );
   615 	 * @param bool|string[] $embed Whether to embed all links or a filtered list of link relations.
   653 	 * @param bool|string[] $embed Whether to embed all links or a filtered list of link relations.
   616 	 * @return array {
   654 	 * @return array {
   617 	 *     Data with sub-requests embedded.
   655 	 *     Data with sub-requests embedded.
   618 	 *
   656 	 *
   619 	 *     @type array $_links    Links.
   657 	 *     @type array $_links    Links.
   620 	 *     @type array $_embedded Embeddeds.
   658 	 *     @type array $_embedded Embedded objects.
   621 	 * }
   659 	 * }
   622 	 */
   660 	 */
   623 	protected function embed_links( $data, $embed = true ) {
   661 	protected function embed_links( $data, $embed = true ) {
   624 		if ( empty( $data['_links'] ) ) {
   662 		if ( empty( $data['_links'] ) ) {
   625 			return $data;
   663 			return $data;
   702 			'status'  => $response->get_status(),
   740 			'status'  => $response->get_status(),
   703 			'headers' => $response->get_headers(),
   741 			'headers' => $response->get_headers(),
   704 		);
   742 		);
   705 
   743 
   706 		/**
   744 		/**
   707 		 * Filters the enveloped form of a response.
   745 		 * Filters the enveloped form of a REST API response.
   708 		 *
   746 		 *
   709 		 * @since 4.4.0
   747 		 * @since 4.4.0
   710 		 *
   748 		 *
   711 		 * @param array            $envelope Envelope data.
   749 		 * @param array            $envelope {
       
   750 		 *     Envelope data.
       
   751 		 *
       
   752 		 *     @type array $body    Response data.
       
   753 		 *     @type int   $status  The 3-digit HTTP status code.
       
   754 		 *     @type array $headers Map of header name to header value.
       
   755 		 * }
   712 		 * @param WP_REST_Response $response Original response data.
   756 		 * @param WP_REST_Response $response Original response data.
   713 		 */
   757 		 */
   714 		$envelope = apply_filters( 'rest_envelope_response', $envelope, $response );
   758 		$envelope = apply_filters( 'rest_envelope_response', $envelope, $response );
   715 
   759 
   716 		// Ensure it's still a response and return.
   760 		// Ensure it's still a response and return.
   791 		if ( $namespace ) {
   835 		if ( $namespace ) {
   792 			$endpoints = wp_list_filter( $endpoints, array( 'namespace' => $namespace ) );
   836 			$endpoints = wp_list_filter( $endpoints, array( 'namespace' => $namespace ) );
   793 		}
   837 		}
   794 
   838 
   795 		/**
   839 		/**
   796 		 * Filters the array of available endpoints.
   840 		 * Filters the array of available REST API endpoints.
   797 		 *
   841 		 *
   798 		 * @since 4.4.0
   842 		 * @since 4.4.0
   799 		 *
   843 		 *
   800 		 * @param array $endpoints The available endpoints. An array of matching regex patterns, each mapped
   844 		 * @param array $endpoints The available endpoints. An array of matching regex patterns, each mapped
   801 		 *                         to an array of callbacks for the endpoint. These take the format
   845 		 *                         to an array of callbacks for the endpoint. These take the format
   891 	 * @param WP_REST_Request $request Request to attempt dispatching.
   935 	 * @param WP_REST_Request $request Request to attempt dispatching.
   892 	 * @return WP_REST_Response Response returned by the callback.
   936 	 * @return WP_REST_Response Response returned by the callback.
   893 	 */
   937 	 */
   894 	public function dispatch( $request ) {
   938 	public function dispatch( $request ) {
   895 		/**
   939 		/**
   896 		 * Filters the pre-calculated result of a REST dispatch request.
   940 		 * Filters the pre-calculated result of a REST API dispatch request.
   897 		 *
   941 		 *
   898 		 * Allow hijacking the request before dispatching by returning a non-empty. The returned value
   942 		 * Allow hijacking the request before dispatching by returning a non-empty. The returned value
   899 		 * will be used to serve the request instead.
   943 		 * will be used to serve the request instead.
   900 		 *
   944 		 *
   901 		 * @since 4.4.0
   945 		 * @since 4.4.0
   902 		 *
   946 		 *
   903 		 * @param mixed           $result  Response to replace the requested version with. Can be anything
   947 		 * @param mixed           $result  Response to replace the requested version with. Can be anything
   904 		 *                                 a normal endpoint can return, or null to not hijack the request.
   948 		 *                                 a normal endpoint can return, or null to not hijack the request.
   905 		 * @param WP_REST_Server  $this    Server instance.
   949 		 * @param WP_REST_Server  $server  Server instance.
   906 		 * @param WP_REST_Request $request Request used to generate the response.
   950 		 * @param WP_REST_Request $request Request used to generate the response.
   907 		 */
   951 		 */
   908 		$result = apply_filters( 'rest_pre_dispatch', null, $this, $request );
   952 		$result = apply_filters( 'rest_pre_dispatch', null, $this, $request );
   909 
   953 
   910 		if ( ! empty( $result ) ) {
   954 		if ( ! empty( $result ) ) {
   911 			return $result;
   955 			return $result;
   912 		}
   956 		}
   913 
   957 
       
   958 		$error   = null;
       
   959 		$matched = $this->match_request_to_handler( $request );
       
   960 
       
   961 		if ( is_wp_error( $matched ) ) {
       
   962 			return $this->error_to_response( $matched );
       
   963 		}
       
   964 
       
   965 		list( $route, $handler ) = $matched;
       
   966 
       
   967 		if ( ! is_callable( $handler['callback'] ) ) {
       
   968 			$error = new WP_Error(
       
   969 				'rest_invalid_handler',
       
   970 				__( 'The handler for the route is invalid.' ),
       
   971 				array( 'status' => 500 )
       
   972 			);
       
   973 		}
       
   974 
       
   975 		if ( ! is_wp_error( $error ) ) {
       
   976 			$check_required = $request->has_valid_params();
       
   977 			if ( is_wp_error( $check_required ) ) {
       
   978 				$error = $check_required;
       
   979 			} else {
       
   980 				$check_sanitized = $request->sanitize_params();
       
   981 				if ( is_wp_error( $check_sanitized ) ) {
       
   982 					$error = $check_sanitized;
       
   983 				}
       
   984 			}
       
   985 		}
       
   986 
       
   987 		return $this->respond_to_request( $request, $route, $handler, $error );
       
   988 	}
       
   989 
       
   990 	/**
       
   991 	 * Matches a request object to its handler.
       
   992 	 *
       
   993 	 * @access private
       
   994 	 * @since 5.6.0
       
   995 	 *
       
   996 	 * @param WP_REST_Request $request The request object.
       
   997 	 * @return array|WP_Error The route and request handler on success or a WP_Error instance if no handler was found.
       
   998 	 */
       
   999 	protected function match_request_to_handler( $request ) {
   914 		$method = $request->get_method();
  1000 		$method = $request->get_method();
   915 		$path   = $request->get_route();
  1001 		$path   = $request->get_route();
   916 
  1002 
   917 		$with_namespace = array();
  1003 		$with_namespace = array();
   918 
  1004 
   955 				if ( empty( $handler['methods'][ $checked_method ] ) ) {
  1041 				if ( empty( $handler['methods'][ $checked_method ] ) ) {
   956 					continue;
  1042 					continue;
   957 				}
  1043 				}
   958 
  1044 
   959 				if ( ! is_callable( $callback ) ) {
  1045 				if ( ! is_callable( $callback ) ) {
   960 					$response = new WP_Error(
  1046 					return array( $route, $handler );
   961 						'rest_invalid_handler',
  1047 				}
   962 						__( 'The handler for the route is invalid' ),
  1048 
   963 						array( 'status' => 500 )
  1049 				$request->set_url_params( $args );
   964 					);
  1050 				$request->set_attributes( $handler );
   965 				}
  1051 
   966 
  1052 				$defaults = array();
   967 				if ( ! is_wp_error( $response ) ) {
  1053 
   968 					// Remove the redundant preg_match argument.
  1054 				foreach ( $handler['args'] as $arg => $options ) {
   969 					unset( $args[0] );
  1055 					if ( isset( $options['default'] ) ) {
   970 
  1056 						$defaults[ $arg ] = $options['default'];
   971 					$request->set_url_params( $args );
       
   972 					$request->set_attributes( $handler );
       
   973 
       
   974 					$defaults = array();
       
   975 
       
   976 					foreach ( $handler['args'] as $arg => $options ) {
       
   977 						if ( isset( $options['default'] ) ) {
       
   978 							$defaults[ $arg ] = $options['default'];
       
   979 						}
       
   980 					}
  1057 					}
   981 
  1058 				}
   982 					$request->set_default_params( $defaults );
  1059 
   983 
  1060 				$request->set_default_params( $defaults );
   984 					$check_required = $request->has_valid_params();
  1061 
   985 					if ( is_wp_error( $check_required ) ) {
  1062 				return array( $route, $handler );
   986 						$response = $check_required;
  1063 			}
   987 					} else {
  1064 		}
   988 						$check_sanitized = $request->sanitize_params();
  1065 
   989 						if ( is_wp_error( $check_sanitized ) ) {
  1066 		return new WP_Error(
   990 							$response = $check_sanitized;
  1067 			'rest_no_route',
   991 						}
  1068 			__( 'No route was found matching the URL and request method.' ),
   992 					}
  1069 			array( 'status' => 404 )
   993 				}
       
   994 
       
   995 				/**
       
   996 				 * Filters the response before executing any REST API callbacks.
       
   997 				 *
       
   998 				 * Allows plugins to perform additional validation after a
       
   999 				 * request is initialized and matched to a registered route,
       
  1000 				 * but before it is executed.
       
  1001 				 *
       
  1002 				 * Note that this filter will not be called for requests that
       
  1003 				 * fail to authenticate or match to a registered route.
       
  1004 				 *
       
  1005 				 * @since 4.7.0
       
  1006 				 *
       
  1007 				 * @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.
       
  1008 				 * @param array                                            $handler  Route handler used for the request.
       
  1009 				 * @param WP_REST_Request                                  $request  Request used to generate the response.
       
  1010 				 */
       
  1011 				$response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request );
       
  1012 
       
  1013 				if ( ! is_wp_error( $response ) ) {
       
  1014 					// Check permission specified on the route.
       
  1015 					if ( ! empty( $handler['permission_callback'] ) ) {
       
  1016 						$permission = call_user_func( $handler['permission_callback'], $request );
       
  1017 
       
  1018 						if ( is_wp_error( $permission ) ) {
       
  1019 							$response = $permission;
       
  1020 						} elseif ( false === $permission || null === $permission ) {
       
  1021 							$response = new WP_Error(
       
  1022 								'rest_forbidden',
       
  1023 								__( 'Sorry, you are not allowed to do that.' ),
       
  1024 								array( 'status' => rest_authorization_required_code() )
       
  1025 							);
       
  1026 						}
       
  1027 					}
       
  1028 				}
       
  1029 
       
  1030 				if ( ! is_wp_error( $response ) ) {
       
  1031 					/**
       
  1032 					 * Filters the REST dispatch request result.
       
  1033 					 *
       
  1034 					 * Allow plugins to override dispatching the request.
       
  1035 					 *
       
  1036 					 * @since 4.4.0
       
  1037 					 * @since 4.5.0 Added `$route` and `$handler` parameters.
       
  1038 					 *
       
  1039 					 * @param mixed           $dispatch_result Dispatch result, will be used if not empty.
       
  1040 					 * @param WP_REST_Request $request         Request used to generate the response.
       
  1041 					 * @param string          $route           Route matched for the request.
       
  1042 					 * @param array           $handler         Route handler used for the request.
       
  1043 					 */
       
  1044 					$dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler );
       
  1045 
       
  1046 					// Allow plugins to halt the request via this filter.
       
  1047 					if ( null !== $dispatch_result ) {
       
  1048 						$response = $dispatch_result;
       
  1049 					} else {
       
  1050 						$response = call_user_func( $callback, $request );
       
  1051 					}
       
  1052 				}
       
  1053 
       
  1054 				/**
       
  1055 				 * Filters the response immediately after executing any REST API
       
  1056 				 * callbacks.
       
  1057 				 *
       
  1058 				 * Allows plugins to perform any needed cleanup, for example,
       
  1059 				 * to undo changes made during the {@see 'rest_request_before_callbacks'}
       
  1060 				 * filter.
       
  1061 				 *
       
  1062 				 * Note that this filter will not be called for requests that
       
  1063 				 * fail to authenticate or match to a registered route.
       
  1064 				 *
       
  1065 				 * Note that an endpoint's `permission_callback` can still be
       
  1066 				 * called after this filter - see `rest_send_allow_header()`.
       
  1067 				 *
       
  1068 				 * @since 4.7.0
       
  1069 				 *
       
  1070 				 * @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.
       
  1071 				 * @param array                                            $handler  Route handler used for the request.
       
  1072 				 * @param WP_REST_Request                                  $request  Request used to generate the response.
       
  1073 				 */
       
  1074 				$response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request );
       
  1075 
       
  1076 				if ( is_wp_error( $response ) ) {
       
  1077 					$response = $this->error_to_response( $response );
       
  1078 				} else {
       
  1079 					$response = rest_ensure_response( $response );
       
  1080 				}
       
  1081 
       
  1082 				$response->set_matched_route( $route );
       
  1083 				$response->set_matched_handler( $handler );
       
  1084 
       
  1085 				return $response;
       
  1086 			}
       
  1087 		}
       
  1088 
       
  1089 		return $this->error_to_response(
       
  1090 			new WP_Error(
       
  1091 				'rest_no_route',
       
  1092 				__( 'No route was found matching the URL and request method' ),
       
  1093 				array( 'status' => 404 )
       
  1094 			)
       
  1095 		);
  1070 		);
       
  1071 	}
       
  1072 
       
  1073 	/**
       
  1074 	 * Dispatches the request to the callback handler.
       
  1075 	 *
       
  1076 	 * @access private
       
  1077 	 * @since 5.6.0
       
  1078 	 *
       
  1079 	 * @param WP_REST_Request $request  The request object.
       
  1080 	 * @param array           $handler  The matched route handler.
       
  1081 	 * @param string          $route    The matched route regex.
       
  1082 	 * @param WP_Error|null   $response The current error object if any.
       
  1083 	 * @return WP_REST_Response
       
  1084 	 */
       
  1085 	protected function respond_to_request( $request, $route, $handler, $response ) {
       
  1086 		/**
       
  1087 		 * Filters the response before executing any REST API callbacks.
       
  1088 		 *
       
  1089 		 * Allows plugins to perform additional validation after a
       
  1090 		 * request is initialized and matched to a registered route,
       
  1091 		 * but before it is executed.
       
  1092 		 *
       
  1093 		 * Note that this filter will not be called for requests that
       
  1094 		 * fail to authenticate or match to a registered route.
       
  1095 		 *
       
  1096 		 * @since 4.7.0
       
  1097 		 *
       
  1098 		 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
       
  1099 		 *                                                                   Usually a WP_REST_Response or WP_Error.
       
  1100 		 * @param array                                            $handler  Route handler used for the request.
       
  1101 		 * @param WP_REST_Request                                  $request  Request used to generate the response.
       
  1102 		 */
       
  1103 		$response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request );
       
  1104 
       
  1105 		// Check permission specified on the route.
       
  1106 		if ( ! is_wp_error( $response ) && ! empty( $handler['permission_callback'] ) ) {
       
  1107 			$permission = call_user_func( $handler['permission_callback'], $request );
       
  1108 
       
  1109 			if ( is_wp_error( $permission ) ) {
       
  1110 				$response = $permission;
       
  1111 			} elseif ( false === $permission || null === $permission ) {
       
  1112 				$response = new WP_Error(
       
  1113 					'rest_forbidden',
       
  1114 					__( 'Sorry, you are not allowed to do that.' ),
       
  1115 					array( 'status' => rest_authorization_required_code() )
       
  1116 				);
       
  1117 			}
       
  1118 		}
       
  1119 
       
  1120 		if ( ! is_wp_error( $response ) ) {
       
  1121 			/**
       
  1122 			 * Filters the REST API dispatch request result.
       
  1123 			 *
       
  1124 			 * Allow plugins to override dispatching the request.
       
  1125 			 *
       
  1126 			 * @since 4.4.0
       
  1127 			 * @since 4.5.0 Added `$route` and `$handler` parameters.
       
  1128 			 *
       
  1129 			 * @param mixed           $dispatch_result Dispatch result, will be used if not empty.
       
  1130 			 * @param WP_REST_Request $request         Request used to generate the response.
       
  1131 			 * @param string          $route           Route matched for the request.
       
  1132 			 * @param array           $handler         Route handler used for the request.
       
  1133 			 */
       
  1134 			$dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler );
       
  1135 
       
  1136 			// Allow plugins to halt the request via this filter.
       
  1137 			if ( null !== $dispatch_result ) {
       
  1138 				$response = $dispatch_result;
       
  1139 			} else {
       
  1140 				$response = call_user_func( $handler['callback'], $request );
       
  1141 			}
       
  1142 		}
       
  1143 
       
  1144 		/**
       
  1145 		 * Filters the response immediately after executing any REST API
       
  1146 		 * callbacks.
       
  1147 		 *
       
  1148 		 * Allows plugins to perform any needed cleanup, for example,
       
  1149 		 * to undo changes made during the {@see 'rest_request_before_callbacks'}
       
  1150 		 * filter.
       
  1151 		 *
       
  1152 		 * Note that this filter will not be called for requests that
       
  1153 		 * fail to authenticate or match to a registered route.
       
  1154 		 *
       
  1155 		 * Note that an endpoint's `permission_callback` can still be
       
  1156 		 * called after this filter - see `rest_send_allow_header()`.
       
  1157 		 *
       
  1158 		 * @since 4.7.0
       
  1159 		 *
       
  1160 		 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
       
  1161 		 *                                                                   Usually a WP_REST_Response or WP_Error.
       
  1162 		 * @param array                                            $handler  Route handler used for the request.
       
  1163 		 * @param WP_REST_Request                                  $request  Request used to generate the response.
       
  1164 		 */
       
  1165 		$response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request );
       
  1166 
       
  1167 		if ( is_wp_error( $response ) ) {
       
  1168 			$response = $this->error_to_response( $response );
       
  1169 		} else {
       
  1170 			$response = rest_ensure_response( $response );
       
  1171 		}
       
  1172 
       
  1173 		$response->set_matched_route( $route );
       
  1174 		$response->set_matched_handler( $handler );
       
  1175 
       
  1176 		return $response;
  1096 	}
  1177 	}
  1097 
  1178 
  1098 	/**
  1179 	/**
  1099 	 * Returns if an error occurred during most recent JSON encode/decode.
  1180 	 * Returns if an error occurred during most recent JSON encode/decode.
  1100 	 *
  1181 	 *
  1101 	 * Strings to be translated will be in format like
  1182 	 * Strings to be translated will be in format like
  1102 	 * "Encoding error: Maximum stack depth exceeded".
  1183 	 * "Encoding error: Maximum stack depth exceeded".
  1103 	 *
  1184 	 *
  1104 	 * @since 4.4.0
  1185 	 * @since 4.4.0
  1105 	 *
  1186 	 *
  1106 	 * @return bool|string Boolean false or string error message.
  1187 	 * @return false|string Boolean false or string error message.
  1107 	 */
  1188 	 */
  1108 	protected function get_json_last_error() {
  1189 	protected function get_json_last_error() {
  1109 		$last_error_code = json_last_error();
  1190 		$last_error_code = json_last_error();
  1110 
  1191 
  1111 		if ( JSON_ERROR_NONE === $last_error_code || empty( $last_error_code ) ) {
  1192 		if ( JSON_ERROR_NONE === $last_error_code || empty( $last_error_code ) ) {
  1142 			'authentication'  => array(),
  1223 			'authentication'  => array(),
  1143 			'routes'          => $this->get_data_for_routes( $this->get_routes(), $request['context'] ),
  1224 			'routes'          => $this->get_data_for_routes( $this->get_routes(), $request['context'] ),
  1144 		);
  1225 		);
  1145 
  1226 
  1146 		$response = new WP_REST_Response( $available );
  1227 		$response = new WP_REST_Response( $available );
  1147 
  1228 		$response->add_link( 'help', 'https://developer.wordpress.org/rest-api/' );
  1148 		$response->add_link( 'help', 'http://v2.wp-api.org/' );
  1229 		$this->add_active_theme_link_to_index( $response );
       
  1230 		$this->add_site_logo_to_index( $response );
  1149 
  1231 
  1150 		/**
  1232 		/**
  1151 		 * Filters the API root index data.
  1233 		 * Filters the REST API root index data.
  1152 		 *
  1234 		 *
  1153 		 * This contains the data describing the API. This includes information
  1235 		 * This contains the data describing the API. This includes information
  1154 		 * about supported authentication schemes, supported namespaces, routes
  1236 		 * about supported authentication schemes, supported namespaces, routes
  1155 		 * available on the API, and a small amount of data about the site.
  1237 		 * available on the API, and a small amount of data about the site.
  1156 		 *
  1238 		 *
  1157 		 * @since 4.4.0
  1239 		 * @since 4.4.0
  1158 		 *
  1240 		 *
  1159 		 * @param WP_REST_Response $response Response data.
  1241 		 * @param WP_REST_Response $response Response data.
  1160 		 */
  1242 		 */
  1161 		return apply_filters( 'rest_index', $response );
  1243 		return apply_filters( 'rest_index', $response );
       
  1244 	}
       
  1245 
       
  1246 	/**
       
  1247 	 * Adds a link to the active theme for users who have proper permissions.
       
  1248 	 *
       
  1249 	 * @since 5.7.0
       
  1250 	 *
       
  1251 	 * @param WP_REST_Response $response REST API response.
       
  1252 	 */
       
  1253 	protected function add_active_theme_link_to_index( WP_REST_Response $response ) {
       
  1254 		$should_add = current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' );
       
  1255 
       
  1256 		if ( ! $should_add && current_user_can( 'edit_posts' ) ) {
       
  1257 			$should_add = true;
       
  1258 		}
       
  1259 
       
  1260 		if ( ! $should_add ) {
       
  1261 			foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
       
  1262 				if ( current_user_can( $post_type->cap->edit_posts ) ) {
       
  1263 					$should_add = true;
       
  1264 					break;
       
  1265 				}
       
  1266 			}
       
  1267 		}
       
  1268 
       
  1269 		if ( $should_add ) {
       
  1270 			$theme = wp_get_theme();
       
  1271 			$response->add_link( 'https://api.w.org/active-theme', rest_url( 'wp/v2/themes/' . $theme->get_stylesheet() ) );
       
  1272 		}
       
  1273 	}
       
  1274 
       
  1275 	/**
       
  1276 	 * Exposes the site logo through the WordPress REST API.
       
  1277 	 * This is used for fetching this information when user has no rights
       
  1278 	 * to update settings.
       
  1279 	 *
       
  1280 	 * @since 5.8.0
       
  1281 	 *
       
  1282 	 * @param WP_REST_Response $response REST API response.
       
  1283 	 */
       
  1284 	protected function add_site_logo_to_index( WP_REST_Response $response ) {
       
  1285 		$site_logo_id                = get_theme_mod( 'custom_logo' );
       
  1286 		$response->data['site_logo'] = $site_logo_id;
       
  1287 		if ( $site_logo_id ) {
       
  1288 			$response->add_link(
       
  1289 				'https://api.w.org/featuredmedia',
       
  1290 				rest_url( 'wp/v2/media/' . $site_logo_id ),
       
  1291 				array(
       
  1292 					'embeddable' => true,
       
  1293 				)
       
  1294 			);
       
  1295 		}
  1162 	}
  1296 	}
  1163 
  1297 
  1164 	/**
  1298 	/**
  1165 	 * Retrieves the index for a namespace.
  1299 	 * Retrieves the index for a namespace.
  1166 	 *
  1300 	 *
  1192 
  1326 
  1193 		// Link to the root index.
  1327 		// Link to the root index.
  1194 		$response->add_link( 'up', rest_url( '/' ) );
  1328 		$response->add_link( 'up', rest_url( '/' ) );
  1195 
  1329 
  1196 		/**
  1330 		/**
  1197 		 * Filters the namespace index data.
  1331 		 * Filters the REST API namespace index data.
  1198 		 *
  1332 		 *
  1199 		 * This typically is just the route data for the namespace, but you can
  1333 		 * This typically is just the route data for the namespace, but you can
  1200 		 * add any data you'd like here.
  1334 		 * add any data you'd like here.
  1201 		 *
  1335 		 *
  1202 		 * @since 4.4.0
  1336 		 * @since 4.4.0
  1225 			if ( empty( $data ) ) {
  1359 			if ( empty( $data ) ) {
  1226 				continue;
  1360 				continue;
  1227 			}
  1361 			}
  1228 
  1362 
  1229 			/**
  1363 			/**
  1230 			 * Filters the REST endpoint data.
  1364 			 * Filters the REST API endpoint data.
  1231 			 *
  1365 			 *
  1232 			 * @since 4.4.0
  1366 			 * @since 4.4.0
  1233 			 *
  1367 			 *
  1234 			 * @param WP_REST_Request $request Request data. The namespace is passed as the 'namespace' parameter.
  1368 			 * @param WP_REST_Request $request Request data. The namespace is passed as the 'namespace' parameter.
  1235 			 */
  1369 			 */
  1236 			$available[ $route ] = apply_filters( 'rest_endpoints_description', $data );
  1370 			$available[ $route ] = apply_filters( 'rest_endpoints_description', $data );
  1237 		}
  1371 		}
  1238 
  1372 
  1239 		/**
  1373 		/**
  1240 		 * Filters the publicly-visible data for routes.
  1374 		 * Filters the publicly-visible data for REST API routes.
  1241 		 *
  1375 		 *
  1242 		 * This data is exposed on indexes and can be used by clients or
  1376 		 * This data is exposed on indexes and can be used by clients or
  1243 		 * developers to investigate the site and find out how to use it. It
  1377 		 * developers to investigate the site and find out how to use it. It
  1244 		 * acts as a form of self-documentation.
  1378 		 * acts as a form of self-documentation.
  1245 		 *
  1379 		 *
  1278 			if ( isset( $options['schema'] ) && 'help' === $context ) {
  1412 			if ( isset( $options['schema'] ) && 'help' === $context ) {
  1279 				$data['schema'] = call_user_func( $options['schema'] );
  1413 				$data['schema'] = call_user_func( $options['schema'] );
  1280 			}
  1414 			}
  1281 		}
  1415 		}
  1282 
  1416 
       
  1417 		$allowed_schema_keywords = array_flip( rest_get_allowed_schema_keywords() );
       
  1418 
  1283 		$route = preg_replace( '#\(\?P<(\w+?)>.*?\)#', '{$1}', $route );
  1419 		$route = preg_replace( '#\(\?P<(\w+?)>.*?\)#', '{$1}', $route );
  1284 
  1420 
  1285 		foreach ( $callbacks as $callback ) {
  1421 		foreach ( $callbacks as $callback ) {
  1286 			// Skip to the next route if any callback is hidden.
  1422 			// Skip to the next route if any callback is hidden.
  1287 			if ( empty( $callback['show_in_index'] ) ) {
  1423 			if ( empty( $callback['show_in_index'] ) ) {
  1295 
  1431 
  1296 			if ( isset( $callback['args'] ) ) {
  1432 			if ( isset( $callback['args'] ) ) {
  1297 				$endpoint_data['args'] = array();
  1433 				$endpoint_data['args'] = array();
  1298 
  1434 
  1299 				foreach ( $callback['args'] as $key => $opts ) {
  1435 				foreach ( $callback['args'] as $key => $opts ) {
  1300 					$arg_data = array(
  1436 					$arg_data             = array_intersect_key( $opts, $allowed_schema_keywords );
  1301 						'required' => ! empty( $opts['required'] ),
  1437 					$arg_data['required'] = ! empty( $opts['required'] );
  1302 					);
  1438 
  1303 					if ( isset( $opts['default'] ) ) {
       
  1304 						$arg_data['default'] = $opts['default'];
       
  1305 					}
       
  1306 					if ( isset( $opts['enum'] ) ) {
       
  1307 						$arg_data['enum'] = $opts['enum'];
       
  1308 					}
       
  1309 					if ( isset( $opts['description'] ) ) {
       
  1310 						$arg_data['description'] = $opts['description'];
       
  1311 					}
       
  1312 					if ( isset( $opts['type'] ) ) {
       
  1313 						$arg_data['type'] = $opts['type'];
       
  1314 					}
       
  1315 					if ( isset( $opts['items'] ) ) {
       
  1316 						$arg_data['items'] = $opts['items'];
       
  1317 					}
       
  1318 					$endpoint_data['args'][ $key ] = $arg_data;
  1439 					$endpoint_data['args'][ $key ] = $arg_data;
  1319 				}
  1440 				}
  1320 			}
  1441 			}
  1321 
  1442 
  1322 			$data['endpoints'][] = $endpoint_data;
  1443 			$data['endpoints'][] = $endpoint_data;
  1337 			// No methods supported, hide the route.
  1458 			// No methods supported, hide the route.
  1338 			return null;
  1459 			return null;
  1339 		}
  1460 		}
  1340 
  1461 
  1341 		return $data;
  1462 		return $data;
       
  1463 	}
       
  1464 
       
  1465 	/**
       
  1466 	 * Gets the maximum number of requests that can be included in a batch.
       
  1467 	 *
       
  1468 	 * @since 5.6.0
       
  1469 	 *
       
  1470 	 * @return int The maximum requests.
       
  1471 	 */
       
  1472 	protected function get_max_batch_size() {
       
  1473 		/**
       
  1474 		 * Filters the maximum number of REST API requests that can be included in a batch.
       
  1475 		 *
       
  1476 		 * @since 5.6.0
       
  1477 		 *
       
  1478 		 * @param int $max_size The maximum size.
       
  1479 		 */
       
  1480 		return apply_filters( 'rest_get_max_batch_size', 25 );
       
  1481 	}
       
  1482 
       
  1483 	/**
       
  1484 	 * Serves the batch/v1 request.
       
  1485 	 *
       
  1486 	 * @since 5.6.0
       
  1487 	 *
       
  1488 	 * @param WP_REST_Request $batch_request The batch request object.
       
  1489 	 * @return WP_REST_Response The generated response object.
       
  1490 	 */
       
  1491 	public function serve_batch_request_v1( WP_REST_Request $batch_request ) {
       
  1492 		$requests = array();
       
  1493 
       
  1494 		foreach ( $batch_request['requests'] as $args ) {
       
  1495 			$parsed_url = wp_parse_url( $args['path'] );
       
  1496 
       
  1497 			if ( false === $parsed_url ) {
       
  1498 				$requests[] = new WP_Error( 'parse_path_failed', __( 'Could not parse the path.' ), array( 'status' => 400 ) );
       
  1499 
       
  1500 				continue;
       
  1501 			}
       
  1502 
       
  1503 			$single_request = new WP_REST_Request( isset( $args['method'] ) ? $args['method'] : 'POST', $parsed_url['path'] );
       
  1504 
       
  1505 			if ( ! empty( $parsed_url['query'] ) ) {
       
  1506 				$query_args = null; // Satisfy linter.
       
  1507 				wp_parse_str( $parsed_url['query'], $query_args );
       
  1508 				$single_request->set_query_params( $query_args );
       
  1509 			}
       
  1510 
       
  1511 			if ( ! empty( $args['body'] ) ) {
       
  1512 				$single_request->set_body_params( $args['body'] );
       
  1513 			}
       
  1514 
       
  1515 			if ( ! empty( $args['headers'] ) ) {
       
  1516 				$single_request->set_headers( $args['headers'] );
       
  1517 			}
       
  1518 
       
  1519 			$requests[] = $single_request;
       
  1520 		}
       
  1521 
       
  1522 		$matches    = array();
       
  1523 		$validation = array();
       
  1524 		$has_error  = false;
       
  1525 
       
  1526 		foreach ( $requests as $single_request ) {
       
  1527 			$match     = $this->match_request_to_handler( $single_request );
       
  1528 			$matches[] = $match;
       
  1529 			$error     = null;
       
  1530 
       
  1531 			if ( is_wp_error( $match ) ) {
       
  1532 				$error = $match;
       
  1533 			}
       
  1534 
       
  1535 			if ( ! $error ) {
       
  1536 				list( $route, $handler ) = $match;
       
  1537 
       
  1538 				if ( isset( $handler['allow_batch'] ) ) {
       
  1539 					$allow_batch = $handler['allow_batch'];
       
  1540 				} else {
       
  1541 					$route_options = $this->get_route_options( $route );
       
  1542 					$allow_batch   = isset( $route_options['allow_batch'] ) ? $route_options['allow_batch'] : false;
       
  1543 				}
       
  1544 
       
  1545 				if ( ! is_array( $allow_batch ) || empty( $allow_batch['v1'] ) ) {
       
  1546 					$error = new WP_Error(
       
  1547 						'rest_batch_not_allowed',
       
  1548 						__( 'The requested route does not support batch requests.' ),
       
  1549 						array( 'status' => 400 )
       
  1550 					);
       
  1551 				}
       
  1552 			}
       
  1553 
       
  1554 			if ( ! $error ) {
       
  1555 				$check_required = $single_request->has_valid_params();
       
  1556 				if ( is_wp_error( $check_required ) ) {
       
  1557 					$error = $check_required;
       
  1558 				}
       
  1559 			}
       
  1560 
       
  1561 			if ( ! $error ) {
       
  1562 				$check_sanitized = $single_request->sanitize_params();
       
  1563 				if ( is_wp_error( $check_sanitized ) ) {
       
  1564 					$error = $check_sanitized;
       
  1565 				}
       
  1566 			}
       
  1567 
       
  1568 			if ( $error ) {
       
  1569 				$has_error    = true;
       
  1570 				$validation[] = $error;
       
  1571 			} else {
       
  1572 				$validation[] = true;
       
  1573 			}
       
  1574 		}
       
  1575 
       
  1576 		$responses = array();
       
  1577 
       
  1578 		if ( $has_error && 'require-all-validate' === $batch_request['validation'] ) {
       
  1579 			foreach ( $validation as $valid ) {
       
  1580 				if ( is_wp_error( $valid ) ) {
       
  1581 					$responses[] = $this->envelope_response( $this->error_to_response( $valid ), false )->get_data();
       
  1582 				} else {
       
  1583 					$responses[] = null;
       
  1584 				}
       
  1585 			}
       
  1586 
       
  1587 			return new WP_REST_Response(
       
  1588 				array(
       
  1589 					'failed'    => 'validation',
       
  1590 					'responses' => $responses,
       
  1591 				),
       
  1592 				WP_Http::MULTI_STATUS
       
  1593 			);
       
  1594 		}
       
  1595 
       
  1596 		foreach ( $requests as $i => $single_request ) {
       
  1597 			$clean_request = clone $single_request;
       
  1598 			$clean_request->set_url_params( array() );
       
  1599 			$clean_request->set_attributes( array() );
       
  1600 			$clean_request->set_default_params( array() );
       
  1601 
       
  1602 			/** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */
       
  1603 			$result = apply_filters( 'rest_pre_dispatch', null, $this, $clean_request );
       
  1604 
       
  1605 			if ( empty( $result ) ) {
       
  1606 				$match = $matches[ $i ];
       
  1607 				$error = null;
       
  1608 
       
  1609 				if ( is_wp_error( $validation[ $i ] ) ) {
       
  1610 					$error = $validation[ $i ];
       
  1611 				}
       
  1612 
       
  1613 				if ( is_wp_error( $match ) ) {
       
  1614 					$result = $this->error_to_response( $match );
       
  1615 				} else {
       
  1616 					list( $route, $handler ) = $match;
       
  1617 
       
  1618 					if ( ! $error && ! is_callable( $handler['callback'] ) ) {
       
  1619 						$error = new WP_Error(
       
  1620 							'rest_invalid_handler',
       
  1621 							__( 'The handler for the route is invalid' ),
       
  1622 							array( 'status' => 500 )
       
  1623 						);
       
  1624 					}
       
  1625 
       
  1626 					$result = $this->respond_to_request( $single_request, $route, $handler, $error );
       
  1627 				}
       
  1628 			}
       
  1629 
       
  1630 			/** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */
       
  1631 			$result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $single_request );
       
  1632 
       
  1633 			$responses[] = $this->envelope_response( $result, false )->get_data();
       
  1634 		}
       
  1635 
       
  1636 		return new WP_REST_Response( array( 'responses' => $responses ), WP_Http::MULTI_STATUS );
  1342 	}
  1637 	}
  1343 
  1638 
  1344 	/**
  1639 	/**
  1345 	 * Sends an HTTP status code.
  1640 	 * Sends an HTTP status code.
  1346 	 *
  1641 	 *