diff --git a/src/wp-includes/rest-api/class-wp-rest-request.php b/src/wp-includes/rest-api/class-wp-rest-request.php index d2fbe24109..25a5e69f08 100644 --- a/src/wp-includes/rest-api/class-wp-rest-request.php +++ b/src/wp-includes/rest-api/class-wp-rest-request.php @@ -934,4 +934,48 @@ class WP_REST_Request implements ArrayAccess { unset( $this->params[ $type ][ $offset ] ); } } + + /** + * Gets a WP_REST_Request object from a full URL. + * + * @since 4.5.0 + * + * @param string $url URL with protocol, domain, path and query args. + * @return WP_REST_Request|false WP_REST_Request object on success, false on failure. + */ + public static function from_url( $url ) { + $bits = parse_url( $url ); + $query_params = array(); + + if ( ! empty( $bits['query'] ) ) { + wp_parse_str( $bits['query'], $query_params ); + } + + $api_root = rest_url(); + if ( get_option( 'permalink_structure' ) && 0 === strpos( $url, $api_root ) ) { + // Pretty permalinks on, and URL is under the API root + $api_url_part = substr( $url, strlen( untrailingslashit( $api_root ) ) ); + $route = parse_url( $api_url_part, PHP_URL_PATH ); + } elseif ( ! empty( $query_params['rest_route'] ) ) { + // ?rest_route=... set directly + $route = $query_params['rest_route']; + unset( $query_params['rest_route'] ); + } + + $request = false; + if ( ! empty( $route ) ) { + $request = new WP_REST_Request( 'GET', $route ); + $request->set_query_params( $query_params ); + } + + /** + * Filter the request generated from a URL. + * + * @since 4.5.0 + * + * @param WP_REST_Request|false $request Generated request object, or false if URL could not be parsed. + * @param string $url URL the request was generated from. + */ + return apply_filters( 'rest_request_from_url', $request, $url ); + } } diff --git a/tests/phpunit/tests/rest-api/rest-request.php b/tests/phpunit/tests/rest-api/rest-request.php index 52cfae356a..e3b8eea2e3 100644 --- a/tests/phpunit/tests/rest-api/rest-request.php +++ b/tests/phpunit/tests/rest-api/rest-request.php @@ -420,4 +420,46 @@ class Tests_REST_Request extends WP_UnitTestCase { public function _return_wp_error_on_validate_callback() { return new WP_Error( 'some-error', 'This is not valid!' ); } + + public function data_from_url() { + return array( + array( + 'permalink_structure' => '/%post_name%/', + 'original_url' => 'http://' . WP_TESTS_DOMAIN . '/wp-json/wp/v2/posts/1?foo=bar', + ), + array( + 'permalink_structure' => '', + 'original_url' => 'http://' . WP_TESTS_DOMAIN . '/?rest_route=%2Fwp%2Fv2%2Fposts%2F1&foo=bar', + ), + ); + } + + /** + * @dataProvider data_from_url + */ + public function test_from_url( $permalink_structure, $original_url ) { + update_option( 'permalink_structure', $permalink_structure ); + $url = add_query_arg( 'foo', 'bar', rest_url( '/wp/v2/posts/1' ) ); + $this->assertEquals( $original_url, $url ); + $request = WP_REST_Request::from_url( $url ); + $this->assertInstanceOf( 'WP_REST_Request', $request ); + $this->assertEquals( '/wp/v2/posts/1', $request->get_route() ); + $this->assertEqualSets( array( + 'foo' => 'bar', + ), $request->get_query_params() ); + } + + /** + * @dataProvider data_from_url + */ + public function test_from_url_invalid( $permalink_structure ) { + update_option( 'permalink_structure', $permalink_structure ); + $using_site = site_url( '/wp/v2/posts/1' ); + $request = WP_REST_Request::from_url( $using_site ); + $this->assertFalse( $request ); + + $using_home = home_url( '/wp/v2/posts/1' ) ; + $request = WP_REST_Request::from_url( $using_home ); + $this->assertFalse( $request ); + } }