diff --git a/src/wp-includes/class-wp-rewrite.php b/src/wp-includes/class-wp-rewrite.php index 8b75fa5c36d16..5fd699f4420b5 100644 --- a/src/wp-includes/class-wp-rewrite.php +++ b/src/wp-includes/class-wp-rewrite.php @@ -1405,6 +1405,7 @@ public function rewrite_rules() { $page_rewrite = apply_filters( 'page_rewrite_rules', $page_rewrite ); // Extra permastructs. + $permastruct_rewrite = array(); foreach ( $this->extra_permastructs as $permastructname => $struct ) { if ( is_array( $struct ) ) { if ( count( $struct ) === 2 ) { @@ -1447,14 +1448,14 @@ public function rewrite_rules() { $rules = apply_filters_deprecated( 'tag_rewrite_rules', array( $rules ), '3.1.0', 'post_tag_rewrite_rules' ); } - $this->extra_rules_top = array_merge( $this->extra_rules_top, $rules ); + $permastruct_rewrite = array_merge( $permastruct_rewrite, $rules ); } // Put them together. if ( $this->use_verbose_page_rules ) { - $this->rules = array_merge( $this->extra_rules_top, $robots_rewrite, $favicon_rewrite, $sitemap_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 + $permastruct_rewrite, $robots_rewrite, $favicon_rewrite, $sitemap_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, $favicon_rewrite, $sitemap_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 + $permastruct_rewrite, $robots_rewrite, $favicon_rewrite, $sitemap_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/tests/phpunit/tests/rewrite.php b/tests/phpunit/tests/rewrite.php index 2bb7254abfcef..badb06685c5bb 100644 --- a/tests/phpunit/tests/rewrite.php +++ b/tests/phpunit/tests/rewrite.php @@ -489,6 +489,82 @@ public function test_parse_request_with_post_slug_that_clashes_with_a_trashed_pa $this->assertFalse( is_404() ); } + /** + * @ticket 14991 + */ + public function test_extra_rules_top_takes_priority_over_extra_permastructs() { + global $wp_rewrite; + + // Register a custom permastruct with a known pattern. + add_permastruct( + 'test_permastruct_14991', + '/test_permastruct_14991/%test_permastruct_14991%', + array( + 'walk_dirs' => false, + 'paged' => false, + 'feed' => false, + ) + ); + + $wp_rewrite->flush_rules(); + $rewrite_rules = $wp_rewrite->rewrite_rules(); + + // Find a pattern generated by the permastruct. + $permastruct_pattern = null; + foreach ( array_keys( $rewrite_rules ) as $pattern ) { + if ( str_contains( $pattern, 'test_permastruct_14991' ) ) { + $permastruct_pattern = $pattern; + break; + } + } + + $this->assertNotNull( $permastruct_pattern, 'Expected a rewrite rule to be generated for the permastruct.' ); + + // Add a top-priority rule with the same regex pattern. + $top_query = 'index.php?custom_override=1'; + $wp_rewrite->add_rule( $permastruct_pattern, $top_query, 'top' ); + + $wp_rewrite->flush_rules(); + $rewrite_rules = $wp_rewrite->rewrite_rules(); + + // The top rule's query should win over the permastruct's. + $this->assertSame( + $top_query, + $rewrite_rules[ $permastruct_pattern ], + 'extra_rules_top should take priority over extra_permastructs when there is a regex conflict.' + ); + } + + /** + * @ticket 14991 + */ + public function test_extra_rules_top_is_not_polluted_by_permastruct_rules() { + global $wp_rewrite; + + // Register a custom permastruct. + add_permastruct( + 'test_permastruct_14991_c', + '/test_permastruct_14991_c/%test_permastruct_14991_c%', + array( + 'walk_dirs' => false, + 'paged' => false, + 'feed' => false, + ) + ); + + $wp_rewrite->flush_rules(); + $wp_rewrite->rewrite_rules(); + + // $extra_rules_top should only contain manually-added rules, not permastruct-generated ones. + foreach ( array_keys( $wp_rewrite->extra_rules_top ) as $pattern ) { + $this->assertStringNotContainsString( + 'test_permastruct_14991_c', + $pattern, + 'Permastruct-generated rules must not be stored in $extra_rules_top.' + ); + } + } + /** * @ticket 29107 */