From fc434a0c77d77b0e6f8d661d205bbaf0f29d63c5 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 11 Feb 2020 03:20:05 +0000 Subject: [PATCH] REST API: Match REST API routes on namespace before performing regex checks. Rule out groups of API endpoints by simple namespace string comparison to reduce the number of regex checks necessary when matching a route. Props TimothyBlynJacobs. Fixes #48530. git-svn-id: https://develop.svn.wordpress.org/trunk@47260 602fd350-edb4-49c9-b593-d223f7449a82 --- .../rest-api/class-wp-rest-server.php | 26 ++++++++++++++++--- tests/phpunit/tests/rest-api/rest-server.php | 11 ++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) 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 acf23f4dda..efda2321b7 100644 --- a/src/wp-includes/rest-api/class-wp-rest-server.php +++ b/src/wp-includes/rest-api/class-wp-rest-server.php @@ -744,11 +744,18 @@ class WP_REST_Server { * used as the delimiter with preg_match() * * @since 4.4.0 + * @since 5.4.0 Add $namespace parameter. * + * @param string $namespace Optionally, only return routes in the given namespace. * @return array `'/path/regex' => array( $callback, $bitmask )` or * `'/path/regex' => array( array( $callback, $bitmask ), ...)`. */ - public function get_routes() { + public function get_routes( $namespace = '' ) { + $endpoints = $this->endpoints; + + if ( $namespace ) { + $endpoints = wp_list_filter( $endpoints, array( 'namespace' => $namespace ) ); + } /** * Filters the array of available endpoints. @@ -760,7 +767,7 @@ class WP_REST_Server { * `'/path/regex' => array( $callback, $bitmask )` or * `'/path/regex' => array( array( $callback, $bitmask ). */ - $endpoints = apply_filters( 'rest_endpoints', $this->endpoints ); + $endpoints = apply_filters( 'rest_endpoints', $endpoints ); // Normalise the endpoints. $defaults = array( @@ -872,7 +879,20 @@ class WP_REST_Server { $method = $request->get_method(); $path = $request->get_route(); - foreach ( $this->get_routes() as $route => $handlers ) { + $routes = array(); + + foreach ( $this->get_namespaces() as $namespace ) { + if ( 0 === strpos( ltrim( $path, '/' ), $namespace ) ) { + $routes = $this->get_routes( $namespace ); + break; + } + } + + if ( ! $routes ) { + $routes = $this->get_routes(); + } + + foreach ( $routes as $route => $handlers ) { $match = preg_match( '@^' . $route . '$@i', $path, $matches ); if ( ! $match ) { diff --git a/tests/phpunit/tests/rest-api/rest-server.php b/tests/phpunit/tests/rest-api/rest-server.php index abfdefcd0b..755f0296ea 100644 --- a/tests/phpunit/tests/rest-api/rest-server.php +++ b/tests/phpunit/tests/rest-api/rest-server.php @@ -1431,6 +1431,17 @@ class Tests_REST_Server extends WP_Test_REST_TestCase { ); } + /** + * @ticket 48530 + */ + public function test_get_routes_respects_namespace_parameter() { + $routes = rest_get_server()->get_routes( 'oembed/1.0' ); + + foreach ( $routes as $route => $handlers ) { + $this->assertStringStartsWith( '/oembed/1.0', $route ); + } + } + public function _validate_as_integer_123( $value, $request, $key ) { if ( ! is_int( $value ) ) { return new WP_Error( 'some-error', 'This is not valid!' );