From 63d9e57cd3b121710ad1415d8d40f020036f20c3 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Fri, 10 Nov 2023 12:31:27 -0800 Subject: [PATCH] Implement functions to check whether a REST request is being made and whether a REST endpoint request is currently being handled. --- src/wp-includes/blocks/post-excerpt.php | 3 +- src/wp-includes/class-wp-query.php | 2 +- src/wp-includes/deprecated.php | 2 +- src/wp-includes/functions.php | 33 +++++++++++++- src/wp-includes/load.php | 4 ++ src/wp-includes/rest-api.php | 43 ++++++++++++++++++- .../rest-api/class-wp-rest-server.php | 32 +++++++++++++- .../class-wp-rest-attachments-controller.php | 4 +- .../class-wp-rest-url-details-controller.php | 2 +- src/wp-includes/script-loader.php | 2 +- src/wp-includes/user.php | 1 + 11 files changed, 115 insertions(+), 13 deletions(-) diff --git a/src/wp-includes/blocks/post-excerpt.php b/src/wp-includes/blocks/post-excerpt.php index 09d6b6f003d34..c002e1b2dd754 100644 --- a/src/wp-includes/blocks/post-excerpt.php +++ b/src/wp-includes/blocks/post-excerpt.php @@ -83,8 +83,7 @@ function register_block_core_post_excerpt() { * the excerpt length block setting has no effect. * Returns 100 because 100 is the max length in the setting. */ -if ( is_admin() || - defined( 'REST_REQUEST' ) && REST_REQUEST ) { +if ( is_admin() || wp_is_rest_endpoint() ) { add_filter( 'excerpt_length', static function () { diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index a9ed269d72244..36ee031fe59cf 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -1027,7 +1027,7 @@ public function parse_query( $query = '' ) { } if ( ! ( $this->is_singular || $this->is_archive || $this->is_search || $this->is_feed - || ( defined( 'REST_REQUEST' ) && REST_REQUEST && $this->is_main_query() ) + || ( wp_is_rest_request() && $this->is_main_query() ) || $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_robots || $this->is_favicon ) ) { $this->is_home = true; } diff --git a/src/wp-includes/deprecated.php b/src/wp-includes/deprecated.php index e98cad915fd11..a10280870c385 100644 --- a/src/wp-includes/deprecated.php +++ b/src/wp-includes/deprecated.php @@ -5430,7 +5430,7 @@ function _wp_theme_json_webfonts_handler() { $settings = WP_Theme_JSON_Resolver::get_merged_data()->get_settings(); // If in the editor, add webfonts defined in variations. - if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { + if ( is_admin() || wp_is_rest_endpoint() ) { $variations = WP_Theme_JSON_Resolver::get_style_variations(); foreach ( $variations as $variation ) { // Skip if fontFamilies are not defined in the variation. diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index cb490ee1764fa..94be0399cb16f 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -3718,7 +3718,7 @@ function wp_die( $message = '', $title = '', $args = array() ) { * @param callable $callback Callback function name. */ $callback = apply_filters( 'wp_die_json_handler', '_json_wp_die_handler' ); - } elseif ( defined( 'REST_REQUEST' ) && REST_REQUEST && wp_is_jsonp_request() ) { + } elseif ( wp_is_rest_request() && wp_is_jsonp_request() ) { /** * Filters the callback for killing WordPress execution for JSONP REST requests. * @@ -4439,7 +4439,7 @@ function _wp_json_prepare_data( $data ) { * @param int $options Optional. Options to be passed to json_encode(). Default 0. */ function wp_send_json( $response, $status_code = null, $options = 0 ) { - if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { + if ( wp_is_rest_request() ) { _doing_it_wrong( __FUNCTION__, sprintf( @@ -4695,6 +4695,35 @@ function _mce_set_direction( $mce_init ) { return $mce_init; } +/** + * Determines whether the current request is a WordPress REST API request. + * + * The function relies on the 'REST_REQUEST' global. As such, it only returns true when an actual REST _request_ is + * being made. It does not return true when a REST endpoint is hit as part of another request, e.g. for preloading a + * REST response. See {@see wp_is_rest_endpoint()} for that purpose. + * + * This function must not be called until the {@see 'parse_request'} action. + * + * @since 6.5.0 + * + * @return bool True if it's a WordPress REST API request, false otherwise. + */ +function wp_is_rest_request(): bool { + if ( ! defined( 'REST_REQUEST' ) && ! did_action( 'parse_request' ) ) { + _doing_it_wrong( + __FUNCTION__, + sprintf( + /* translators: %s: parse_request */ + __( 'Detecting whether the current request is a REST API request is not possible until the %s hook.' ), + 'parse_request' + ), + '6.5.0' + ); + return false; + } + + return defined( 'REST_REQUEST' ) && REST_REQUEST; +} /** * Converts smiley code to the icon graphic file equivalent. diff --git a/src/wp-includes/load.php b/src/wp-includes/load.php index 520902cdd64ba..0dbbc187cfce2 100644 --- a/src/wp-includes/load.php +++ b/src/wp-includes/load.php @@ -598,6 +598,10 @@ function wp_debug_mode() { error_reporting( E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING | E_RECOVERABLE_ERROR ); } + /* + * The 'REST_REQUEST' check here is optimistic as the constant is most + * likely not set at this point even if it is in fact a REST request. + */ if ( defined( 'XMLRPC_REQUEST' ) || defined( 'REST_REQUEST' ) || defined( 'MS_FILES_REQUEST' ) || ( defined( 'WP_INSTALLING' ) && WP_INSTALLING ) || wp_doing_ajax() || wp_is_json_request() diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index 61e324e801445..4bdc0734e084e 100644 --- a/src/wp-includes/rest-api.php +++ b/src/wp-includes/rest-api.php @@ -209,7 +209,7 @@ function rest_api_register_rewrites() { * @since 4.4.0 */ function rest_api_default_filters() { - if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { + if ( wp_is_rest_request() ) { // Deprecated reporting. add_action( 'deprecated_function_run', 'rest_handle_deprecated_function', 10, 3 ); add_filter( 'deprecated_function_trigger_error', '__return_false' ); @@ -3389,3 +3389,44 @@ static function ( $status, $error_data ) { return new WP_REST_Response( $data, $status ); } + +/** + * Checks whether a REST API endpoint request is currently being handled. + * + * This may be a standalone REST API request, or an internal request dispatched from within a regular page load. + * + * @since 6.5.0 + * + * @global WP_REST_Server $wp_rest_server REST server instance. + * + * @return bool True if a REST endpoint request is currently being handled, false otherwise. + */ +function wp_is_rest_endpoint(): bool { + /* @var WP_REST_Server $wp_rest_server */ + global $wp_rest_server; + + /* + * Check whether this is a standalone REST request. + * This check is not using the `wp_is_rest_request()` function as it may be called before the constant is actually + * available. Since this is only one way of determining whether a REST endpoint is being handled, there is no need + * to be as strict here as in the check for whether this is an actual standalone REST request. + */ + $is_rest_endpoint = defined( 'REST_REQUEST' ) && REST_REQUEST; + if ( ! $is_rest_endpoint ) { + // Otherwise, check whether an internal REST request is currently being handled. + $is_rest_endpoint = isset( $wp_rest_server ) + && method_exists( $wp_rest_server, 'is_dispatching' ) + && $wp_rest_server->is_dispatching(); + } + + /** + * Filters whether a REST endpoint request is currently being handled. + * + * This may be a standalone REST API request, or an internal request dispatched from within a regular page load. + * + * @since 6.5.0 + * + * @param bool $is_request_endpoint Whether a REST endpoint request is currently being handled. + */ + return (bool) apply_filters( 'wp_is_rest_endpoint', $is_rest_endpoint ); +} 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 4304881b16fef..1cfed4adf4bd1 100644 --- a/src/wp-includes/rest-api/class-wp-rest-server.php +++ b/src/wp-includes/rest-api/class-wp-rest-server.php @@ -87,6 +87,14 @@ class WP_REST_Server { */ protected $embed_cache = array(); + /** + * Stores request objects that are currently being handled. + * + * @since 6.5.0 + * @var array + */ + protected $dispatching_requests = array(); + /** * Instantiates the REST server. * @@ -981,6 +989,8 @@ public function get_route_options( $route ) { * @return WP_REST_Response Response returned by the callback. */ public function dispatch( $request ) { + $this->dispatching_requests[] = $request; + /** * Filters the pre-calculated result of a REST API dispatch request. * @@ -1006,6 +1016,7 @@ public function dispatch( $request ) { $result = $this->error_to_response( $result ); } + array_pop( $this->dispatching_requests ); return $result; } @@ -1013,7 +1024,9 @@ public function dispatch( $request ) { $matched = $this->match_request_to_handler( $request ); if ( is_wp_error( $matched ) ) { - return $this->error_to_response( $matched ); + $response = $this->error_to_response( $matched ); + array_pop( $this->dispatching_requests ); + return $response; } list( $route, $handler ) = $matched; @@ -1038,7 +1051,22 @@ public function dispatch( $request ) { } } - return $this->respond_to_request( $request, $route, $handler, $error ); + $response = $this->respond_to_request( $request, $route, $handler, $error ); + array_pop( $this->dispatching_requests ); + return $response; + } + + /** + * Returns whether the REST server is currently dispatching / responding to a request. + * + * This may be a standalone REST API request, or an internal request dispatched from within a regular page load. + * + * @since 6.5.0 + * + * @return bool Whether the REST server is currently handling a request. + */ + public function is_dispatching(): bool { + return empty( $this->dispatching_requests ); } /** diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index 7367c0fc57a07..36387d8be646f 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php @@ -201,7 +201,7 @@ public function create_item( $request ) { wp_after_insert_post( $attachment, false, null ); - if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { + if ( wp_is_rest_request() ) { /* * Set a custom header with the attachment_id. * Used by the browser/client to resume creating image sub-sizes after a PHP fatal error. @@ -630,7 +630,7 @@ public function edit_media_item( $request ) { update_post_meta( $new_attachment_id, '_wp_attachment_image_alt', wp_slash( $image_alt ) ); } - if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { + if ( wp_is_rest_request() ) { /* * Set a custom header with the attachment_id. * Used by the browser/client to resume creating image sub-sizes after a PHP fatal error. diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-url-details-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-url-details-controller.php index c9ac6675d093f..7dcc4d79236a1 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-url-details-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-url-details-controller.php @@ -128,7 +128,7 @@ public function get_item_schema() { * * @since 5.9.0 * - * @param WP_REST_REQUEST $request Full details about the request. + * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error The parsed details as a response object. WP_Error if there are errors. */ public function parse_url_details( $request ) { diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 41be694281dcd..0692f62ab4f4f 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -2584,7 +2584,7 @@ function wp_should_load_block_editor_scripts_and_styles() { * @return bool Whether separate assets will be loaded. */ function wp_should_load_separate_core_block_assets() { - if ( is_admin() || is_feed() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { + if ( is_admin() || is_feed() || wp_is_rest_endpoint() ) { return false; } diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 5b9dacc20f233..dad96dea70423 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -333,6 +333,7 @@ function wp_authenticate_application_password( $input_user, $username, $password return $input_user; } + // The 'REST_REQUEST' check here may happen too early for the constant to be available. $is_api_request = ( ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ); /**