Make WordPress Core

Opened 7 weeks ago

Last modified 7 weeks ago

#64357 assigned defect (bug)

get_terms() no longer returns terms when "include" or "exclude" is set to 'all' or an empty value in WP 6.9

Reported by: ibachal's profile ibachal Owned by: johnjamesjacoby's profile johnjamesjacoby
Milestone: Awaiting Review Priority: normal
Severity: major Version: 6.9
Component: Taxonomy Keywords:
Focuses: Cc:

Description

After updating to WordPress 6.9, I noticed a regression in how get_terms() handles the include and exclude arguments.

Previously (in WP 6.8 and earlier), the following usages worked without issue:

get_terms( [
    'taxonomy' => 'category',
    'include'  => 'all',
    'exclude'  => 'all',
] );

get_terms( [
    'taxonomy' => 'category',
    'exclude'  => array(),
] );

Both returned the full list of terms, just as if include/exclude were not set.

Since updating to WordPress 6.9, the same code now returns no terms at all.

Expected Behavior

When include = 'all' or an empty value, WordPress should treat it the same as omitting the argument and return all terms.

This is the behavior in WP 6.8 and earlier.

Actual Behavior in WP 6.9

get_terms() returns an empty array when include or exclude is set to 'all' or empty.

This breaks existing codebases and plugins that rely on passing 'all' or empty values dynamically.

Attachments (1)

64357.diff (1.8 KB) - added by iflairwebtechnologies 7 weeks ago.

Download all attachments as: .zip

Change History (7)

#1 @westonruter
7 weeks ago

This might be related to [61048] to fix #47719.

@johnjamesjacoby Can you verify?

#2 @iflairwebtechnologies
7 weeks ago

  • Keywords has-patch added

Summary:
This patch fixes a regression introduced in WordPress 6.9 where get_terms() fails to return any terms when the include or exclude arguments are passed as an empty value ([], , null) or when 'include' => 'all' / 'exclude' => 'all' is used. These values should result in no filtering, but in WP 6.9 they incorrectly produce no results.

Issue:
Previously, an empty include or exclude argument behaved as a no-op.
After WP 6.9 changes in WP_Term_Query::get_sql_for_clause(), empty term sets were treated as invalid and returned self::$no_results. This broke several common usages including:

  • Calling get_terms( [ 'include' => [] ] )
  • Passing an empty string via REST or shortcode
  • Plugins using 'include' => 'all' to indicate no filtering
  • Core internals expecting empty arrays to behave as wildcard matches

What this patch does:

  • Restores pre-6.9 behavior
  • Ensures empty / invalid / "all" includes and excludes do NOT filter terms
  • Prevents IN () and NOT IN () SQL from being generated
  • Normalizes terms before generating SQL
  • Maintains backward compatibility for all existing plugins, REST API, and shortcode usage

#4 @westonruter
7 weeks ago

  • Keywords has-patch removed

@westonruter commented on PR #10604:


7 weeks ago
#5

The issue has not been verified, so a PR seems premature.

There are many PHPCS issues with this PR and what appear to be unrelated changes.

#6 @johnjamesjacoby
7 weeks ago

  • Owner set to johnjamesjacoby
  • Status changed from new to assigned

Since updating to WordPress 6.9, the same code now returns no terms at all.

@westonruter's comment here correctly identified the relevant (and intended) change in 6.9.

When include = 'all' or an empty value, WordPress should treat it the same as omitting the argument and return all terms.

Using all like this in the Comment/Post/User queries also returns none, because all is not a known/handled/special value for the include or exclude argument keys in any WordPress core Query class, and using it like this ticket suggests never intentionally did anything unique.

For Terms, it was resulting in this kinda-weird code being run:

$include = wp_parse_id_list( array( 'all' ) );

...which would become...

$inclusions = array( 0 );

...and this too is kinda weird again – and I propose is "invalid" – in the sense that no Term/User/Post/Comment in the database could ever have an ID of 0, so including or excluding it doesn't (shouldn't) do anything.


As far as I can tell, and in my testing, the experience of passing a literal empty array() value for either include or exclude appears to be unchanged? array() is also the default value for both include and exclude and the same empty() check happens in both 6.9 and 6.8 versions (and earlier).


This breaks existing codebases and plugins that rely on passing 'all' or empty values dynamically.

Agree that this change can appear to be somewhat breaking, but it is also intentionally fixing an unbound database query bug, so I'm admittedly bias towards not reverting without semi-significant breakage reported.

Let's dig into which plugins might be relying on that all method, see what their plans are, and collaborate as needed here?

Last edited 7 weeks ago by johnjamesjacoby (previous) (diff)
Note: See TracTickets for help on using tickets.