From 584df2a1698d39b36ad6fb3318ff14b32ea96e38 Mon Sep 17 00:00:00 2001 From: Dominik Schilling Date: Sat, 23 May 2020 14:34:38 +0000 Subject: [PATCH] Rest API: Ensure `rest_ensure_response()` upgrades `WP_HTTP_Response` to `WP_REST_Response`. An instance of `WP_HTTP_Response` doesn't ensure that the required methods used in `WP_REST_Server::dispatch()` exist, currently causing a fatal error. Props ali11007, TimothyBlynJacobs, ocean90. Fixes #49495. git-svn-id: https://develop.svn.wordpress.org/trunk@47849 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/rest-api.php | 22 ++++++++--- .../rest-api/class-wp-rest-server.php | 12 +++--- tests/phpunit/tests/rest-api.php | 37 ++++++++++++++++++- 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index 11eb2b71ad..e408522ab6 100644 --- a/src/wp-includes/rest-api.php +++ b/src/wp-includes/rest-api.php @@ -506,26 +506,36 @@ function rest_ensure_request( $request ) { /** * Ensures a REST response is a response object (for consistency). * - * This implements WP_HTTP_Response, allowing usage of `set_status`/`header`/etc + * This implements WP_REST_Response, allowing usage of `set_status`/`header`/etc * without needing to double-check the object. Will also allow WP_Error to indicate error * responses, so users should immediately check for this value. * * @since 4.4.0 * - * @param WP_HTTP_Response|WP_Error|mixed $response Response to check. - * @return WP_REST_Response|mixed If response generated an error, WP_Error, if response - * is already an instance, WP_HTTP_Response, otherwise - * returns a new WP_REST_Response instance. + * @param WP_REST_Response|WP_Error|WP_HTTP_Response|mixed $response Response to check. + * @return WP_REST_Response|WP_Error If response generated an error, WP_Error, if response + * is already an instance, WP_REST_Response, otherwise + * returns a new WP_REST_Response instance. */ function rest_ensure_response( $response ) { if ( is_wp_error( $response ) ) { return $response; } - if ( $response instanceof WP_HTTP_Response ) { + if ( $response instanceof WP_REST_Response ) { return $response; } + // While WP_HTTP_Response is the base class of WP_REST_Response, it doesn't provide + // all the required methods used in WP_REST_Server::dispatch(). + if ( $response instanceof WP_HTTP_Response ) { + return new WP_REST_Response( + $response->get_data(), + $response->get_status(), + $response->get_headers() + ); + } + return new WP_REST_Response( $response ); } diff --git a/src/wp-includes/rest-api/class-wp-rest-server.php b/src/wp-includes/rest-api/class-wp-rest-server.php index 771c3b9fbe..0ab94bbbde 100644 --- a/src/wp-includes/rest-api/class-wp-rest-server.php +++ b/src/wp-includes/rest-api/class-wp-rest-server.php @@ -969,9 +969,9 @@ class WP_REST_Server { * * @since 4.7.0 * - * @param WP_HTTP_Response|WP_Error $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. + * @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 ); @@ -1032,9 +1032,9 @@ class WP_REST_Server { * * @since 4.7.0 * - * @param WP_HTTP_Response|WP_Error $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. + * @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 ); diff --git a/tests/phpunit/tests/rest-api.php b/tests/phpunit/tests/rest-api.php index ac3274ec7a..788dc069a8 100644 --- a/tests/phpunit/tests/rest-api.php +++ b/tests/phpunit/tests/rest-api.php @@ -942,7 +942,7 @@ class Tests_REST_API extends WP_UnitTestCase { /** * @ticket 40614 */ - function test_rest_ensure_response_accepts_path_string() { + function test_rest_ensure_request_accepts_path_string() { $request = rest_ensure_request( '/wp/v2/posts' ); $this->assertInstanceOf( 'WP_REST_Request', $request ); $this->assertEquals( '/wp/v2/posts', $request->get_route() ); @@ -1315,4 +1315,39 @@ class Tests_REST_API extends WP_UnitTestCase { ), ); } + + function test_rest_ensure_response_accepts_wp_error_and_returns_wp_error() { + $response = rest_ensure_response( new WP_Error() ); + $this->assertInstanceOf( 'WP_Error', $response ); + } + + /** + * @dataProvider rest_ensure_response_data_provider + * @group test1 + * + * @param mixed $response The response passed to rest_ensure_response(). + * @param mixed $expected_data The expected data a response should include. + */ + function test_rest_ensure_response_returns_instance_of_wp_rest_response( $response, $expected_data ) { + $response_object = rest_ensure_response( $response ); + $this->assertInstanceOf( 'WP_REST_Response', $response_object ); + $this->assertSame( $expected_data, $response_object->get_data() ); + } + + /** + * Data provider for test_rest_ensure_response_returns_instance_of_wp_rest_response(). + * + * @return array + */ + function rest_ensure_response_data_provider() { + return array( + array( null, null ), + array( array( 'chocolate' => 'cookies' ), array( 'chocolate' => 'cookies' ) ), + array( 123, 123 ), + array( true, true ), + array( 'chocolate', 'chocolate' ), + array( new WP_HTTP_Response( 'http' ), 'http' ), + array( new WP_REST_Response( 'rest' ), 'rest' ), + ); + } }