Skip to content

Commit 0f55f41

Browse files
committed
Sitemaps: Add the is_sitemap() conditional tag.
Introduce `is_sitemap()` as a conditional tag, mirroring the existing `is_robots()` and `is_favicon()` tags, and add the corresponding `$is_sitemap` property to `WP_Query`. The flag is set whenever the `sitemap` query variable is present, and a sitemap request is now excluded from being treated as the home page. Developed in WordPress#12142. Follow-up to r47018, r48072. Props masteradhoc, nimeshatxecurify, westonruter, mukesh27, madtownlems, ravanh, cybr, swissspidy. See #51117. Fixes #51542, #51543. git-svn-id: https://develop.svn.wordpress.org/trunk@62664 602fd350-edb4-49c9-b593-d223f7449a82
1 parent fa7d8d3 commit 0f55f41

4 files changed

Lines changed: 274 additions & 1 deletion

File tree

src/wp-includes/class-wp-query.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,13 @@ class WP_Query {
410410
*/
411411
public $is_favicon = false;
412412

413+
/**
414+
* Signifies whether the current query is for a sitemap.
415+
*
416+
* @since 7.1.0
417+
*/
418+
public bool $is_sitemap = false;
419+
413420
/**
414421
* Signifies whether the current query is for the page_for_posts page.
415422
*
@@ -519,6 +526,7 @@ private function init_query_flags() {
519526
$this->is_singular = false;
520527
$this->is_robots = false;
521528
$this->is_favicon = false;
529+
$this->is_sitemap = false;
522530
$this->is_posts_page = false;
523531
$this->is_post_type_archive = false;
524532
}
@@ -817,6 +825,8 @@ public function parse_query( $query = '' ) {
817825
$this->is_robots = true;
818826
} elseif ( ! empty( $query_vars['favicon'] ) ) {
819827
$this->is_favicon = true;
828+
} elseif ( ! empty( $query_vars['sitemap'] ) ) {
829+
$this->is_sitemap = true;
820830
}
821831

822832
if ( ! is_scalar( $query_vars['p'] ) || (int) $query_vars['p'] < 0 ) {
@@ -1040,7 +1050,7 @@ public function parse_query( $query = '' ) {
10401050

10411051
if ( ! ( $this->is_singular || $this->is_archive || $this->is_search || $this->is_feed
10421052
|| ( wp_is_serving_rest_request() && $this->is_main_query() )
1043-
|| $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_robots || $this->is_favicon ) ) {
1053+
|| $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_robots || $this->is_favicon || $this->is_sitemap ) ) {
10441054
$this->is_home = true;
10451055
}
10461056

@@ -4642,6 +4652,17 @@ public function is_favicon() {
46424652
return (bool) $this->is_favicon;
46434653
}
46444654

4655+
/**
4656+
* Determines whether the query is for a sitemap.
4657+
*
4658+
* @since 7.1.0
4659+
*
4660+
* @return bool Whether the query is for a sitemap.
4661+
*/
4662+
public function is_sitemap(): bool {
4663+
return $this->is_sitemap;
4664+
}
4665+
46454666
/**
46464667
* Determines whether the query is for a search.
46474668
*

src/wp-includes/query.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,26 @@ function is_favicon() {
680680
return $wp_query->is_favicon();
681681
}
682682

683+
/**
684+
* Is the query for a sitemap?
685+
*
686+
* @since 7.1.0
687+
*
688+
* @global WP_Query $wp_query WordPress Query object.
689+
*
690+
* @return bool Whether the query is for a sitemap.
691+
*/
692+
function is_sitemap(): bool {
693+
global $wp_query;
694+
695+
if ( ! isset( $wp_query ) ) {
696+
_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '7.1.0' );
697+
return false;
698+
}
699+
700+
return $wp_query->is_sitemap();
701+
}
702+
683703
/**
684704
* Determines whether the query is for a search.
685705
*

tests/phpunit/includes/abstract-testcase.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,7 @@ public function assertQueryTrue( ...$prop ) {
11871187
'is_preview',
11881188
'is_robots',
11891189
'is_favicon',
1190+
'is_sitemap',
11901191
'is_search',
11911192
'is_single',
11921193
'is_singular',
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
<?php
2+
3+
/**
4+
* Tests for the is_sitemap() conditional tag and the WP_Query::$is_sitemap property.
5+
*
6+
* This exercises both query.php and class-wp-query.php: query vars are fed through
7+
* WP_Query, then the effects on the wp_query object are tested.
8+
*
9+
* @group query
10+
* @group sitemaps
11+
*/
12+
class Tests_Query_IsSitemap extends WP_UnitTestCase {
13+
14+
/**
15+
* Set up the shared fixture.
16+
*
17+
* Published posts are required so that a sitemap request does not turn into a
18+
* 404 in WP::handle_404(), which would reset the is_sitemap flag via set_404().
19+
*
20+
* @param WP_UnitTest_Factory $factory Factory instance.
21+
*/
22+
public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ): void {
23+
$factory->post->create_many( 3 );
24+
}
25+
26+
public function set_up() {
27+
parent::set_up();
28+
29+
$this->set_permalink_structure( '/%year%/%monthnum%/%day%/%postname%/' );
30+
31+
create_initial_taxonomies();
32+
}
33+
34+
/**
35+
* The property defaults to false on a freshly initialized query.
36+
*
37+
* @ticket 51543
38+
*
39+
* @covers WP_Query::is_sitemap
40+
*/
41+
public function test_is_sitemap_defaults_to_false(): void {
42+
$query = new WP_Query();
43+
44+
$this->assertFalse( $query->is_sitemap, 'The $is_sitemap property should default to false.' );
45+
$this->assertFalse( $query->is_sitemap(), 'WP_Query::is_sitemap() should return false by default.' );
46+
}
47+
48+
/**
49+
* The property gets reset when initialized.
50+
*
51+
* @ticket 51543
52+
*
53+
* @covers WP_Query::init
54+
* @covers WP_Query::init_query_flags
55+
*/
56+
public function test_is_sitemap_gets_reset_to_false(): void {
57+
$query = new WP_Query();
58+
59+
$query->is_sitemap = true;
60+
$query->init();
61+
$this->assertFalse( $query->is_sitemap, 'The $is_sitemap property should initialize as false.' );
62+
}
63+
64+
/**
65+
* The flag is set when the "sitemap" query var is present (sitemap index route).
66+
*
67+
* @ticket 51543
68+
*
69+
* @covers WP_Query::parse_query
70+
* @covers WP_Query::is_sitemap
71+
*/
72+
public function test_is_sitemap_true_for_sitemap_index(): void {
73+
$query = new WP_Query( array( 'sitemap' => 'index' ) );
74+
75+
$this->assertTrue( $query->is_sitemap, 'The $is_sitemap property should be true for a sitemap query.' );
76+
$this->assertTrue( $query->is_sitemap(), 'WP_Query::is_sitemap() should return true for a sitemap query.' );
77+
}
78+
79+
/**
80+
* The flag is set for a sitemap subtype route (e.g. wp-sitemap-posts-post-1.xml).
81+
*
82+
* @ticket 51543
83+
*
84+
* @covers WP_Query::parse_query
85+
* @covers WP_Query::is_sitemap
86+
*/
87+
public function test_is_sitemap_true_for_sitemap_subtype(): void {
88+
$query = new WP_Query(
89+
array(
90+
'sitemap' => 'posts',
91+
'sitemap-subtype' => 'post',
92+
'paged' => 1,
93+
)
94+
);
95+
96+
$this->assertTrue( $query->is_sitemap(), 'WP_Query::is_sitemap() should return true for a sitemap subtype query.' );
97+
}
98+
99+
/**
100+
* An empty "sitemap" query var must not set the flag.
101+
*
102+
* @ticket 51543
103+
*
104+
* @covers WP_Query::parse_query
105+
*/
106+
public function test_is_sitemap_false_for_empty_sitemap_var(): void {
107+
$query = new WP_Query( array( 'sitemap' => '' ) );
108+
109+
$this->assertFalse( $query->is_sitemap(), 'An empty "sitemap" query var should not set is_sitemap.' );
110+
}
111+
112+
/**
113+
* The sitemap stylesheet route uses the "sitemap-stylesheet" query var, which must
114+
* not flag the query as a sitemap.
115+
*
116+
* @ticket 51543
117+
*
118+
* @covers WP_Query::parse_query
119+
*/
120+
public function test_is_sitemap_false_for_stylesheet_route(): void {
121+
$query = new WP_Query( array( 'sitemap-stylesheet' => 'sitemap' ) );
122+
123+
$this->assertFalse( $query->is_sitemap(), 'The sitemap stylesheet route should not flag the query as a sitemap.' );
124+
}
125+
126+
/**
127+
* is_robots takes precedence over is_sitemap in the parse_query branch.
128+
*
129+
* @ticket 51543
130+
*
131+
* @covers WP_Query::parse_query
132+
*/
133+
public function test_robots_takes_precedence_over_sitemap(): void {
134+
$query = new WP_Query(
135+
array(
136+
'robots' => true,
137+
'sitemap' => 'index',
138+
)
139+
);
140+
141+
$this->assertTrue( $query->is_robots(), 'is_robots() should be true when the robots query var is set.' );
142+
$this->assertFalse( $query->is_sitemap(), 'is_sitemap() should be false when is_robots() takes precedence.' );
143+
}
144+
145+
/**
146+
* A regular query is never flagged as a sitemap.
147+
*
148+
* @ticket 51543
149+
*
150+
* @covers WP_Query::is_sitemap
151+
*/
152+
public function test_is_sitemap_false_for_regular_query(): void {
153+
$post_id = self::factory()->post->create();
154+
155+
$query = new WP_Query( array( 'p' => $post_id ) );
156+
157+
$this->assertFalse( $query->is_sitemap(), 'A regular post query should not be flagged as a sitemap.' );
158+
}
159+
160+
/**
161+
* A sitemap query must not also be treated as the home/front page.
162+
*
163+
* This is the practical motivation for the conditional tag: distinguishing a
164+
* sitemap request from the home page (see #51542).
165+
*
166+
* @ticket 51543
167+
*
168+
* @covers WP_Query::parse_query
169+
*/
170+
public function test_sitemap_query_is_not_home(): void {
171+
$query = new WP_Query( array( 'sitemap' => 'index' ) );
172+
173+
$this->assertTrue( $query->is_sitemap(), 'The sitemap query should be flagged as a sitemap.' );
174+
$this->assertFalse( $query->is_home(), 'A sitemap query should not be treated as the home page.' );
175+
$this->assertFalse( $query->is_front_page(), 'A sitemap query should not be treated as the front page.' );
176+
}
177+
178+
/**
179+
* The global is_sitemap() conditional tag reflects the main query.
180+
*
181+
* @ticket 51543
182+
*
183+
* @covers ::is_sitemap
184+
*/
185+
public function test_global_is_sitemap_reflects_main_query(): void {
186+
// Prevent WP_Sitemaps from rendering and calling exit during go_to().
187+
remove_action( 'template_redirect', array( wp_sitemaps_get_server(), 'render_sitemaps' ) );
188+
189+
$this->go_to( home_url( '/?sitemap=index' ) );
190+
191+
$this->assertTrue( is_sitemap(), 'is_sitemap() should be true on a sitemap request.' );
192+
193+
// is_sitemap should be the only conditional that is true for a sitemap request.
194+
$this->assertQueryTrue( 'is_sitemap' );
195+
}
196+
197+
/**
198+
* The global is_sitemap() conditional tag is false for a non-sitemap request.
199+
*
200+
* @ticket 51543
201+
*
202+
* @covers ::is_sitemap
203+
*/
204+
public function test_global_is_sitemap_false_on_home(): void {
205+
$this->go_to( home_url( '/' ) );
206+
207+
$this->assertFalse( is_sitemap(), 'is_sitemap() should be false on the home page.' );
208+
$this->assertTrue( is_home(), 'is_home() should be true on the home page.' );
209+
}
210+
211+
/**
212+
* The global is_sitemap() returns false and triggers a notice when the query
213+
* has not yet run.
214+
*
215+
* @ticket 51543
216+
*
217+
* @covers ::is_sitemap
218+
*
219+
* @expectedIncorrectUsage is_sitemap
220+
*/
221+
public function test_global_is_sitemap_before_query_is_run(): void {
222+
$wp_query_temp = $GLOBALS['wp_query'];
223+
unset( $GLOBALS['wp_query'] );
224+
225+
$result = is_sitemap();
226+
227+
$GLOBALS['wp_query'] = $wp_query_temp;
228+
229+
$this->assertFalse( $result, 'is_sitemap() should return false before the query is run.' );
230+
}
231+
}

0 commit comments

Comments
 (0)