Make WordPress Core

Opened 9 years ago

Closed 9 years ago

#33281 closed defect (bug) (fixed)

get_term_by() not returning false if ID does not exist

Reported by: charlestonsw's profile charlestonsw Owned by: boonebgorges's profile boonebgorges
Milestone: 4.4 Priority: normal
Severity: normal Version: 2.3
Component: Taxonomy Keywords: has-patch
Focuses: Cc:

Description

get_term_by() should return false if a specified ID does not exist within a custom taxonomy. Instead an empty array (null) is returned.

Example:

...
$category_array = get_term_by( 'id' , $term_id , SLPlus::locationTaxonomy, ARRAY_A );
if ( $category_array === false ) {
$this->debugMP('msg',  "Tagalong term ID {$term_id} does not exist." );
}

When passing term_id value of 2, which does not exist in the terms tables, this should return false. Instead null is returned.

The issue originates in get_term() which returns null in two cases versus throwing a WP_Error.

From get_term()

...
		if ( is_object($term) )
			$term = $term->term_id;
		if ( !$term = (int) $term )
			return null;
		if ( ! $_term = wp_cache_get( $term, $taxonomy ) ) {
			$_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND t.term_id = %d LIMIT 1", $taxonomy, $term) );
			if ( ! $_term )
				return null;
...

In those two cases, one of which is exactly the "term ID does not exist in the specified taxonomy" get_term_by should return false. However the return logic does not catch the null value:

From get_term_by():

...
		$term = get_term( (int) $value, $taxonomy, $output, $filter );
		if ( is_wp_error( $term ) )
			$term = false;
		return $term;
...

This should be:

...
		$term = get_term( (int) $value, $taxonomy, $output, $filter );
		if ( is_wp_error( $term ) || is_null( $term ) )
			$term = false;
		return $term;
...

WP Version: You are using a development version (4.3-RC2-33572-src).

Attachments (2)

taxonomy.33281.diff (410 bytes) - added by charlestonsw 9 years ago.
Proposed patch.
33281.patch (956 bytes) - added by tyxla 9 years ago.
Adding a unit test of the issue; refreshing previous patch

Download all attachments as: .zip

Change History (9)

@charlestonsw
9 years ago

Proposed patch.

#1 @helen
9 years ago

  • Keywords close added
  • Version changed from trunk to 2.3

I imagine it's done this since the introduction of the function in 2.3. I'm not sure this can reasonably be changed now, given that developers may be doing strict comparisons now that this kind of change would break. The inline docs seem like they could use updating to really lay out which situation returns which value.

#2 @charlestonsw
9 years ago

The docs definitely need to be updated.

As for backward compatibility - developers would be required to check for is_null(), false, and is_wp_error() to ensure proper functionality. By forcing false the function would match the documentation and not cause the null corner case to be missed as any developer relying ONLY on null return values will have several other mainstream cases the would not be functioning properly.

In my admittedly-limited research I found dozens of plugins that incorrectly test for only false or is_wp_error(). It appears in a number of prevalent plugins including several main JetPack modules. Leaving out the is_null() test will cause errant behavior in a variety of taxonomy-related corner cases.

I would bet that far more developers have incorrectly coded for false/is_wp_error() versus developers incorrectly coding for is_null() testing only.

With NULL coming back from several possible logic branches it would be extremely difficult, and I'm guessing a very rare case, that a developer is relying on an is_null() test to branch their logic for one-of-several specific failures with this method.

Yes, updating the docs prevents future developers from making the 2-test logic issue and warns them to look for is_null() as well, but I'm thinking more issues would be fixed by returning the "assumed false if failed" than would be introduced by changing null to false.

My code has been updated to test for all 3 results and will work regardless of null or false. I'm only discussing the point for the betterment of all WordPress installs as there are definitely a number of popular plugins that are missing the null return value.

#3 @theMikeD
9 years ago

FWIW +1 to @charlestonsw fix.

#4 @boonebgorges
9 years ago

  • Keywords needs-unit-tests added; close removed
  • Milestone changed from Awaiting Review to 4.4

In the past, we've made a conscious decision that get_term_by() should return only false on failure, so that it could be used like this: if ( $term = get_term_by() ) For example, [17526] made it impossible to return a WP_Error object. See #16464.

It's true that returning null vs false is not precisely the same thing as the above, but I think it's in the same spirit. And it's very difficult to come up with a reasonable situation in which null would be distinguished from false in the context of get_term_by(). Let's switch it.

Can we get a unit test that demonstrates the issue, please?

#5 @wonderboymusic
9 years ago

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

@tyxla
9 years ago

Adding a unit test of the issue; refreshing previous patch

#6 @tyxla
9 years ago

  • Keywords has-patch added; needs-unit-tests removed

I've just added a patch with the unit test that demonstrates that case.

The patch also contains a refreshed version of the previous patch by @charlestonsw, as it needed refresh due to the recent changes in [33760].

#7 @boonebgorges
9 years ago

  • Resolution set to fixed
  • Status changed from assigned to closed

In 34246:

Failed get_term_by() lookups should always return false.

Previously, we sometimes returned null.

Props charlestonsw, tyxla.
Fixes #33281.

Note: See TracTickets for help on using tickets.