From ef6973b7f0898dbf7125d02a3545977096f10620 Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Sat, 28 Dec 2019 21:18:03 +0000 Subject: [PATCH] Bootstrap/Load: Make handling the `/favicon.ico` requests more flexible. Previously, `wp_favicon_request()` was introduced in [13205] to avoid a performance hit of serving a full 404 page on every favicon request. While working as intended, that implementation did not provide a way for theme or plugin authors to manage the behavior of favicon requests. This changeset implements the following logic (only applied if WordPress is installed in the root directory): * If there is a Site Icon set in Customizer, redirect `/favicon.ico` requests to that icon. * Otherwise, use the WordPress logo as a default icon. * If a physical `/favicon.ico` file exists, do nothing, let the server handle the request. Handling `/favicon.ico` is now more consistent with handling `/robots.txt` requests. New functions and hooks: * Introduce `is_favicon()` conditional tag to complement `is_robots()`. * Introduce `do_favicon` action to complement `do_robots` and use it in template loader. * Introduce `do_favicon()` function, hooked to the above action by default, to complement `do_robots()`. * Introduce `do_faviconico` action to complement `do_robotstxt`, for plugins to override the default behavior. * Mark `wp_favicon_request()` as deprecated in favor of `do_favicon()`. Props jonoaldersonwp, birgire, joostdevalk, mukesh27, SergeyBiryukov. Fixes #47398. git-svn-id: https://develop.svn.wordpress.org/trunk@47018 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/canonical.php | 6 ++--- src/wp-includes/class-wp-query.php | 28 ++++++++++++++++++-- src/wp-includes/class-wp-rewrite.php | 9 ++++--- src/wp-includes/class-wp.php | 6 ++--- src/wp-includes/default-filters.php | 1 + src/wp-includes/functions.php | 17 ++++++++++++ src/wp-includes/load.php | 1 + src/wp-includes/query.php | 22 ++++++++++++++- src/wp-includes/template-loader.php | 8 ++++++ src/wp-includes/version.php | 2 +- src/wp-settings.php | 3 --- tests/phpunit/includes/abstract-testcase.php | 1 + tests/phpunit/tests/query/vars.php | 1 + 13 files changed, 89 insertions(+), 16 deletions(-) diff --git a/src/wp-includes/canonical.php b/src/wp-includes/canonical.php index 557e36acd9..ecacb59a60 100644 --- a/src/wp-includes/canonical.php +++ b/src/wp-includes/canonical.php @@ -19,8 +19,8 @@ * * Prevents redirection for feeds, trackbacks, searches, and * admin URLs. Does not redirect on non-pretty-permalink-supporting IIS 7+, - * page/post previews, WP admin, Trackbacks, robots.txt, searches, or on POST - * requests. + * page/post previews, WP admin, Trackbacks, robots.txt, favicon.ico, searches, + * or on POST requests. * * Will also attempt to find the correct link when a user enters a URL that does * not exist based on exact WordPress query. Will instead try to parse the URL @@ -56,7 +56,7 @@ function redirect_canonical( $requested_url = null, $do_redirect = true ) { } } - if ( is_trackback() || is_search() || is_admin() || is_preview() || is_robots() || ( $is_IIS && ! iis7_supports_permalinks() ) ) { + if ( is_trackback() || is_search() || is_admin() || is_preview() || is_robots() || is_favicon() || ( $is_IIS && ! iis7_supports_permalinks() ) ) { return; } diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 9d7d1a5be7..658e66bb9c 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -390,6 +390,14 @@ class WP_Query { */ public $is_robots = false; + /** + * Signifies whether the current query is for the favicon.ico file. + * + * @since 5.4.0 + * @var bool + */ + public $is_favicon = false; + /** * Signifies whether the current query is for the page_for_posts page. * @@ -478,6 +486,7 @@ class WP_Query { $this->is_attachment = false; $this->is_singular = false; $this->is_robots = false; + $this->is_favicon = false; $this->is_posts_page = false; $this->is_post_type_archive = false; } @@ -744,6 +753,8 @@ class WP_Query { if ( ! empty( $qv['robots'] ) ) { $this->is_robots = true; + } elseif ( ! empty( $qv['favicon'] ) ) { + $this->is_favicon = true; } if ( ! is_scalar( $qv['p'] ) || $qv['p'] < 0 ) { @@ -957,7 +968,9 @@ class WP_Query { $this->is_comment_feed = true; } - if ( ! ( $this->is_singular || $this->is_archive || $this->is_search || $this->is_feed || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_robots ) ) { + if ( ! ( $this->is_singular || $this->is_archive || $this->is_search || $this->is_feed + || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) + || $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_robots || $this->is_favicon ) ) { $this->is_home = true; } @@ -4007,7 +4020,7 @@ class WP_Query { } /** - * Is the query for the robots file? + * Is the query for the robots.txt file? * * @since 3.1.0 * @@ -4017,6 +4030,17 @@ class WP_Query { return (bool) $this->is_robots; } + /** + * Is the query for the favicon.ico file? + * + * @since 5.4.0 + * + * @return bool + */ + public function is_favicon() { + return (bool) $this->is_favicon; + } + /** * Is the query for a search? * diff --git a/src/wp-includes/class-wp-rewrite.php b/src/wp-includes/class-wp-rewrite.php index 6fe36f7591..16d688ee48 100644 --- a/src/wp-includes/class-wp-rewrite.php +++ b/src/wp-includes/class-wp-rewrite.php @@ -1259,10 +1259,13 @@ class WP_Rewrite { return $rewrite; } - // robots.txt -only if installed at the root + // robots.txt -- only if installed at the root $home_path = parse_url( home_url() ); $robots_rewrite = ( empty( $home_path['path'] ) || '/' == $home_path['path'] ) ? array( 'robots\.txt$' => $this->index . '?robots=1' ) : array(); + // favicon.ico -- only if installed at the root + $favicon_rewrite = ( empty( $home_path['path'] ) || '/' == $home_path['path'] ) ? array( 'favicon\.ico$' => $this->index . '?favicon=1' ) : array(); + // Old feed and service files. $deprecated_files = array( '.*wp-(atom|rdf|rss|rss2|feed|commentsrss2)\.php$' => $this->index . '?feed=old', @@ -1419,9 +1422,9 @@ class WP_Rewrite { // Put them together. if ( $this->use_verbose_page_rules ) { - $this->rules = array_merge( $this->extra_rules_top, $robots_rewrite, $deprecated_files, $registration_pages, $root_rewrite, $comments_rewrite, $search_rewrite, $author_rewrite, $date_rewrite, $page_rewrite, $post_rewrite, $this->extra_rules ); + $this->rules = array_merge( $this->extra_rules_top, $robots_rewrite, $favicon_rewrite, $deprecated_files, $registration_pages, $root_rewrite, $comments_rewrite, $search_rewrite, $author_rewrite, $date_rewrite, $page_rewrite, $post_rewrite, $this->extra_rules ); } else { - $this->rules = array_merge( $this->extra_rules_top, $robots_rewrite, $deprecated_files, $registration_pages, $root_rewrite, $comments_rewrite, $search_rewrite, $author_rewrite, $date_rewrite, $post_rewrite, $page_rewrite, $this->extra_rules ); + $this->rules = array_merge( $this->extra_rules_top, $robots_rewrite, $favicon_rewrite, $deprecated_files, $registration_pages, $root_rewrite, $comments_rewrite, $search_rewrite, $author_rewrite, $date_rewrite, $post_rewrite, $page_rewrite, $this->extra_rules ); } /** diff --git a/src/wp-includes/class-wp.php b/src/wp-includes/class-wp.php index 6dbc7d4794..508fae2f03 100644 --- a/src/wp-includes/class-wp.php +++ b/src/wp-includes/class-wp.php @@ -14,7 +14,7 @@ class WP { * @since 2.0.0 * @var string[] */ - public $public_query_vars = array( 'm', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order', 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'name', 'category_name', 'tag', 'feed', 'author_name', 'pagename', 'page_id', 'error', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term', 'cpage', 'post_type', 'embed' ); + public $public_query_vars = array( 'm', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order', 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'name', 'category_name', 'tag', 'feed', 'author_name', 'pagename', 'page_id', 'error', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'favicon', 'taxonomy', 'term', 'cpage', 'post_type', 'embed' ); /** * Private query variables. @@ -659,8 +659,8 @@ class WP { return; } - // Never 404 for the admin, robots, or if we found posts. - if ( is_admin() || is_robots() || $wp_query->posts ) { + // Never 404 for the admin, robots, favicon, or if we found posts. + if ( is_admin() || is_robots() || is_favicon() || $wp_query->posts ) { $success = true; if ( is_singular() ) { diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 50e66c15ef..a2b1296f5c 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -338,6 +338,7 @@ add_action( 'do_feed_rss2', 'do_feed_rss2', 10, 1 ); add_action( 'do_feed_atom', 'do_feed_atom', 10, 1 ); add_action( 'do_pings', 'do_all_pings', 10, 0 ); add_action( 'do_robots', 'do_robots' ); +add_action( 'do_favicon', 'do_favicon' ); add_action( 'set_comment_cookies', 'wp_set_comment_cookies', 10, 3 ); add_action( 'sanitize_comment_cookies', 'sanitize_comment_cookies' ); add_action( 'admin_print_scripts', 'print_emoji_detection_script' ); diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 9c6d60de0e..12e4d0efe3 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -1636,6 +1636,23 @@ function do_robots() { echo apply_filters( 'robots_txt', $output, $public ); } +/** + * Display the favicon.ico file content. + * + * @since 5.4.0 + */ +function do_favicon() { + /** + * Fires when serving the favicon.ico file. + * + * @since 5.4.0 + */ + do_action( 'do_faviconico' ); + + wp_redirect( get_site_icon_url( 32, admin_url( 'images/w-logo-blue.png' ) ) ); + exit; +} + /** * Determines whether WordPress is already installed. * diff --git a/src/wp-includes/load.php b/src/wp-includes/load.php index 52d965018c..e413803e48 100644 --- a/src/wp-includes/load.php +++ b/src/wp-includes/load.php @@ -160,6 +160,7 @@ function wp_check_php_mysql_versions() { * Instead, send the headers for a zero-length favicon and bail. * * @since 3.0.0 + * @deprecated 5.4.0 Deprecated in favor of do_favicon(). */ function wp_favicon_request() { if ( '/favicon.ico' == $_SERVER['REQUEST_URI'] ) { diff --git a/src/wp-includes/query.php b/src/wp-includes/query.php index ae2ff97c46..24b97990b0 100644 --- a/src/wp-includes/query.php +++ b/src/wp-includes/query.php @@ -623,7 +623,7 @@ function is_preview() { } /** - * Is the query for the robots file? + * Is the query for the robots.txt file? * * @since 2.1.0 * @@ -642,6 +642,26 @@ function is_robots() { return $wp_query->is_robots(); } +/** + * Is the query for the favicon.ico file? + * + * @since 5.4.0 + * + * @global WP_Query $wp_query WordPress Query object. + * + * @return bool + */ +function is_favicon() { + global $wp_query; + + if ( ! isset( $wp_query ) ) { + _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' ); + return false; + } + + return $wp_query->is_favicon(); +} + /** * Determines whether the query is for a search. * diff --git a/src/wp-includes/template-loader.php b/src/wp-includes/template-loader.php index 42519a22ad..8aa1ebeb67 100644 --- a/src/wp-includes/template-loader.php +++ b/src/wp-includes/template-loader.php @@ -36,6 +36,14 @@ if ( is_robots() ) { */ do_action( 'do_robots' ); return; +} elseif ( is_favicon() ) { + /** + * Fired when the template loader determines a favicon.ico request. + * + * @since 5.4.0 + */ + do_action( 'do_favicon' ); + return; } elseif ( is_feed() ) { do_feed(); return; diff --git a/src/wp-includes/version.php b/src/wp-includes/version.php index e2db14cc04..b3deb5ad92 100644 --- a/src/wp-includes/version.php +++ b/src/wp-includes/version.php @@ -20,7 +20,7 @@ $wp_version = '5.4-alpha-46582-src'; * * @global int $wp_db_version */ -$wp_db_version = 45805; +$wp_db_version = 47018; /** * Holds the TinyMCE version diff --git a/src/wp-settings.php b/src/wp-settings.php index a4d9a78316..ea273df86b 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -63,9 +63,6 @@ wp_unregister_GLOBALS(); // Standardize $_SERVER variables across setups. wp_fix_server_vars(); -// Check if we have received a request due to missing favicon.ico -wp_favicon_request(); - // Check if we're in maintenance mode. wp_maintenance(); diff --git a/tests/phpunit/includes/abstract-testcase.php b/tests/phpunit/includes/abstract-testcase.php index a15a3ea15d..75525d55d0 100644 --- a/tests/phpunit/includes/abstract-testcase.php +++ b/tests/phpunit/includes/abstract-testcase.php @@ -962,6 +962,7 @@ abstract class WP_UnitTestCase_Base extends PHPUnit_Framework_TestCase { 'is_posts_page', 'is_preview', 'is_robots', + 'is_favicon', 'is_search', 'is_single', 'is_singular', diff --git a/tests/phpunit/tests/query/vars.php b/tests/phpunit/tests/query/vars.php index 705d3852b9..f644061da4 100644 --- a/tests/phpunit/tests/query/vars.php +++ b/tests/phpunit/tests/query/vars.php @@ -60,6 +60,7 @@ class Tests_Query_Vars extends WP_UnitTestCase { 'subpost_id', 'preview', 'robots', + 'favicon', 'taxonomy', 'term', 'cpage',