Skip to content

Fix two PreGetPosts false positives in if/else branches - #910

Merged
GaryJones merged 2 commits into
developfrom
GaryJones/pre-get-posts-else
Oct 10, 2026
Merged

GaryJones merged 2 commits into
developfrom
GaryJones/pre-get-posts-else

Conversation

@GaryJones

Copy link
Copy Markdown
Contributor

This follows on from #906 and fixes two false positives in PreGetPosts, one of which #906 made worse.

Outside a conditional, the sniff only flags set() calls. Inside an if without a main query check, though, it flagged any use of the query, so reading a value was reported as modifying the main query:

add_action( 'pre_get_posts', function( $query ) {
	if ( is_admin() ) {
		$post_type = $query->get( 'post_type' ); // Was flagged.
	}
} );

#906 extended the conditional handling to elseif, so the same started happening there too. The first commit makes the sniff flag only set() calls inside conditionals, as it already did outside them. Every warning the existing tests expect is on a set() call.

The second commit deals with an else after a negated check. Only the main query reaches this else, but its set() was flagged:

add_action( 'pre_get_posts', function( $query ) {
	if ( ! $query->is_main_query() ) {
		do_log( 'x' );
	} else {
		$query->set( 'cat', '-5' ); // Was flagged.
	}
} );

A later branch of an if chain, whether else, elseif or else if, with braces or the alternative syntax, now counts as checked when an earlier condition in the chain catches every query other than the main one. That means a negated is_main_query() check on its own or combined with ||. Two cases deliberately still warn. A positive check such as if ( $query->is_main_query() ) { … } else { $query->set(); } does, because only secondary queries reach that else, and catching changes to secondary queries is what the sniff is for; the existing tests already expect that warning. A negated check combined with && does as well, because other queries can still reach the later branches.

I ran the old and new sniff over the 21 Automattic plugins I have locally and the results did not change, though this sniff only reported four issues across them.

Outside a conditional, the sniff only flags set() calls. Inside an if
without a main query check, though, it flagged any use of the query,
so reading a value with get() was reported as modifying the main
query. Since #906 the same happened inside an elseif.
A set() in the else of `if ( ! $query->is_main_query() )` was flagged,
although only the main query reaches that else. The same applied to
later elseif branches.

A later branch now counts as checked when an earlier condition in its
chain catches every query other than the main one: a negated
is_main_query() check, alone or combined with `||`. A positive check
does not count, since only secondary queries reach the branches after
it. Nor does a negated check combined with `&&`, since other queries
can still get through.
@GaryJones
GaryJones requested a review from a team as a code owner October 10, 2026 16:29
@GaryJones GaryJones added this to the 3.x milestone Oct 10, 2026
@GaryJones GaryJones self-assigned this Oct 10, 2026
@GaryJones
GaryJones merged commit 1e8a506 into develop Oct 10, 2026
26 checks passed
@GaryJones
GaryJones deleted the GaryJones/pre-get-posts-else branch October 10, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant