From accbfc4e96fd60f1ee2dc487ba9bc848acbbb675 Mon Sep 17 00:00:00 2001 From: bogdanungureanu Date: Thu, 20 Aug 2026 14:55:19 +0300 Subject: [PATCH 1/3] Taxonomy: Guard against non-object values in the terms cache. A truthy non-object in the `terms` cache group skipped the database lookup and fataled in `get_object_vars()`, taking down any `get_terms()` call that reached it. Fixes #65915. --- src/wp-includes/class-wp-term.php | 2 +- tests/phpunit/tests/term/wpTerm.php | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-term.php b/src/wp-includes/class-wp-term.php index 0cefa3097b393..b1402671f67e3 100644 --- a/src/wp-includes/class-wp-term.php +++ b/src/wp-includes/class-wp-term.php @@ -124,7 +124,7 @@ public static function get_instance( $term_id, $taxonomy = null ) { $_term = wp_cache_get( $term_id, 'terms' ); // If there isn't a cached version, hit the database. - if ( ! $_term || ( $taxonomy && $taxonomy !== $_term->taxonomy ) ) { + if ( ! is_object( $_term ) || ( $taxonomy && $taxonomy !== $_term->taxonomy ) ) { // Any term found in the cache is not a match, so don't use it. $_term = false; diff --git a/tests/phpunit/tests/term/wpTerm.php b/tests/phpunit/tests/term/wpTerm.php index e640cf8120732..00381a0b222f1 100644 --- a/tests/phpunit/tests/term/wpTerm.php +++ b/tests/phpunit/tests/term/wpTerm.php @@ -89,4 +89,33 @@ public function test_get_instance_should_respect_taxonomy_when_term_id_is_found_ $found = WP_Term::get_instance( self::$term_id, 'wptests_tax2' ); $this->assertFalse( $found ); } + + /** + * @ticket 65915 + * + * @dataProvider data_get_instance_should_ignore_non_object_cached_values + * + * @param mixed $cached_value A non-object value stored in the 'terms' cache group. + */ + public function test_get_instance_should_ignore_non_object_cached_values( $cached_value ) { + wp_cache_set( self::$term_id, $cached_value, 'terms' ); + + $found = WP_Term::get_instance( self::$term_id ); + + $this->assertInstanceOf( 'WP_Term', $found ); + $this->assertSame( self::$term_id, $found->term_id ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_get_instance_should_ignore_non_object_cached_values() { + return array( + 'an array' => array( array( 'term_id' => 12345 ) ), + 'an integer' => array( 12345 ), + 'a string' => array( 'a term' ), + ); + } } From 0238035ad1295fbda705a018879b3a2b26ef958c Mon Sep 17 00:00:00 2001 From: Bogdan Ungureanu Date: Fri, 21 Aug 2026 13:04:17 +0300 Subject: [PATCH 2/3] Apply suggestions from code review Validate if the taxonomy exists on the object to avoid a potential PHP Warning/notice and update the docblocks with PHPStan definitions Co-authored-by: Weston Ruter --- src/wp-includes/class-wp-term.php | 2 +- tests/phpunit/tests/term/wpTerm.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/class-wp-term.php b/src/wp-includes/class-wp-term.php index b1402671f67e3..8d0f243f01e98 100644 --- a/src/wp-includes/class-wp-term.php +++ b/src/wp-includes/class-wp-term.php @@ -124,7 +124,7 @@ public static function get_instance( $term_id, $taxonomy = null ) { $_term = wp_cache_get( $term_id, 'terms' ); // If there isn't a cached version, hit the database. - if ( ! is_object( $_term ) || ( $taxonomy && $taxonomy !== $_term->taxonomy ) ) { + if ( ! is_object( $_term ) || ! isset( $_term->taxonomy ) || ( $taxonomy && $taxonomy !== $_term->taxonomy ) ) { // Any term found in the cache is not a match, so don't use it. $_term = false; diff --git a/tests/phpunit/tests/term/wpTerm.php b/tests/phpunit/tests/term/wpTerm.php index 00381a0b222f1..8e40bd071ceae 100644 --- a/tests/phpunit/tests/term/wpTerm.php +++ b/tests/phpunit/tests/term/wpTerm.php @@ -102,16 +102,16 @@ public function test_get_instance_should_ignore_non_object_cached_values( $cache $found = WP_Term::get_instance( self::$term_id ); - $this->assertInstanceOf( 'WP_Term', $found ); + $this->assertInstanceOf( WP_Term::class, $found ); $this->assertSame( self::$term_id, $found->term_id ); } /** * Data provider. * - * @return array[] + * @return array */ - public function data_get_instance_should_ignore_non_object_cached_values() { + public function data_get_instance_should_ignore_non_object_cached_values(): array { return array( 'an array' => array( array( 'term_id' => 12345 ) ), 'an integer' => array( 12345 ), From a407149f430b2ba57d6d3de5571650a66fdac30b Mon Sep 17 00:00:00 2001 From: bogdanungureanu Date: Fri, 21 Aug 2026 13:26:33 +0300 Subject: [PATCH 3/3] Update the test to validate the empty taxonomy scenario --- tests/phpunit/tests/term/wpTerm.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/phpunit/tests/term/wpTerm.php b/tests/phpunit/tests/term/wpTerm.php index 8e40bd071ceae..d67f0bd086345 100644 --- a/tests/phpunit/tests/term/wpTerm.php +++ b/tests/phpunit/tests/term/wpTerm.php @@ -93,17 +93,18 @@ public function test_get_instance_should_respect_taxonomy_when_term_id_is_found_ /** * @ticket 65915 * - * @dataProvider data_get_instance_should_ignore_non_object_cached_values + * @dataProvider data_get_instance_should_ignore_unusable_cached_values * - * @param mixed $cached_value A non-object value stored in the 'terms' cache group. + * @param mixed $cached_value A cached value that is not a usable term object. */ - public function test_get_instance_should_ignore_non_object_cached_values( $cached_value ) { + public function test_get_instance_should_ignore_unusable_cached_values( $cached_value ) { wp_cache_set( self::$term_id, $cached_value, 'terms' ); $found = WP_Term::get_instance( self::$term_id ); $this->assertInstanceOf( WP_Term::class, $found ); $this->assertSame( self::$term_id, $found->term_id ); + $this->assertSame( 'wptests_tax', $found->taxonomy ); } /** @@ -111,11 +112,13 @@ public function test_get_instance_should_ignore_non_object_cached_values( $cache * * @return array */ - public function data_get_instance_should_ignore_non_object_cached_values(): array { + public function data_get_instance_should_ignore_unusable_cached_values(): array { return array( - 'an array' => array( array( 'term_id' => 12345 ) ), - 'an integer' => array( 12345 ), - 'a string' => array( 'a term' ), + 'an array' => array( array( 'term_id' => 12345 ) ), + 'an integer' => array( 12345 ), + 'a string' => array( 'a term' ), + 'an object with no taxonomy' => array( (object) array( 'term_id' => 12345 ) ), + 'an object with a null taxonomy' => array( (object) array( 'taxonomy' => null ) ), ); } }