From 6178829ab0594d3a541c406502ba31e05ddd82f3 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Wed, 14 Jun 2023 23:49:36 +0000 Subject: [PATCH] Taxonomy: Prevent deprecation notices clearing terms. Prevents `wp_set_object_terms()` throwing a deprecation notice in PHP 8.1+ when passing an empty value as the second parameter to clear the terms. Props audrasjb, chouby, costdev, jrf, peterwilsoncc, prashantbhivsane, sergeybiryukov. Fixes #57923. git-svn-id: https://develop.svn.wordpress.org/trunk@55921 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/taxonomy.php | 6 +- tests/phpunit/tests/term/wpSetObjectTerms.php | 84 ++++++++++++++----- 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index 71280f0437..35031e3bef 100644 --- a/src/wp-includes/taxonomy.php +++ b/src/wp-includes/taxonomy.php @@ -2737,7 +2737,7 @@ function wp_insert_term( $term, $taxonomy, $args = array() ) { * @param int $object_id The object to relate to. * @param string|int|array $terms A single term slug, single term ID, or array of either term slugs or IDs. * Will replace all existing related terms in this taxonomy. Passing an - * empty value will remove all related terms. + * empty array will remove all related terms. * @param string $taxonomy The context in which to relate the term to the object. * @param bool $append Optional. If false will delete difference of terms. Default false. * @return array|WP_Error Term taxonomy IDs of the affected terms or WP_Error on failure. @@ -2751,7 +2751,9 @@ function wp_set_object_terms( $object_id, $terms, $taxonomy, $append = false ) { return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); } - if ( ! is_array( $terms ) ) { + if ( empty( $terms ) ) { + $terms = array(); + } elseif ( ! is_array( $terms ) ) { $terms = array( $terms ); } diff --git a/tests/phpunit/tests/term/wpSetObjectTerms.php b/tests/phpunit/tests/term/wpSetObjectTerms.php index 8032a50e79..989f7a2d74 100644 --- a/tests/phpunit/tests/term/wpSetObjectTerms.php +++ b/tests/phpunit/tests/term/wpSetObjectTerms.php @@ -4,11 +4,13 @@ * @group taxonomy */ class Tests_Term_WpSetObjectTerms extends WP_UnitTestCase { - protected $taxonomy = 'category'; + protected static $taxonomy = 'category'; protected static $post_ids = array(); + protected static $term_ids = array(); public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { self::$post_ids = $factory->post->create_many( 5 ); + self::$term_ids = $factory->term->create_many( 5, array( 'taxonomy' => self::$taxonomy ) ); } /** @@ -105,26 +107,26 @@ class Tests_Term_WpSetObjectTerms extends WP_UnitTestCase { $terms = array(); for ( $i = 0; $i < 3; $i++ ) { $term = "term_{$i}"; - $result = wp_insert_term( $term, $this->taxonomy ); + $result = wp_insert_term( $term, self::$taxonomy ); $this->assertIsArray( $result ); $term_id[ $term ] = $result['term_id']; } foreach ( $ids as $id ) { - $tt = wp_set_object_terms( $id, array_values( $term_id ), $this->taxonomy ); + $tt = wp_set_object_terms( $id, array_values( $term_id ), self::$taxonomy ); // Should return three term taxonomy IDs. $this->assertCount( 3, $tt ); } // Each term should be associated with every post. foreach ( $term_id as $term => $id ) { - $actual = get_objects_in_term( $id, $this->taxonomy ); + $actual = get_objects_in_term( $id, self::$taxonomy ); $this->assertSame( $ids, array_map( 'intval', $actual ) ); } // Each term should have a count of 5. foreach ( array_keys( $term_id ) as $term ) { - $t = get_term_by( 'name', $term, $this->taxonomy ); + $t = get_term_by( 'name', $term, self::$taxonomy ); $this->assertSame( 5, $t->count ); } } @@ -139,25 +141,25 @@ class Tests_Term_WpSetObjectTerms extends WP_UnitTestCase { ); foreach ( $ids as $id ) { - $tt = wp_set_object_terms( $id, $terms, $this->taxonomy ); + $tt = wp_set_object_terms( $id, $terms, self::$taxonomy ); // Should return three term taxonomy IDs. $this->assertCount( 3, $tt ); // Remember which term has which term_id. for ( $i = 0; $i < 3; $i++ ) { - $term = get_term_by( 'name', $terms[ $i ], $this->taxonomy ); + $term = get_term_by( 'name', $terms[ $i ], self::$taxonomy ); $term_id[ $terms[ $i ] ] = (int) $term->term_id; } } // Each term should be associated with every post. foreach ( $term_id as $term => $id ) { - $actual = get_objects_in_term( $id, $this->taxonomy ); + $actual = get_objects_in_term( $id, self::$taxonomy ); $this->assertSame( $ids, array_map( 'intval', $actual ) ); } // Each term should have a count of 5. foreach ( $terms as $term ) { - $t = get_term_by( 'name', $term, $this->taxonomy ); + $t = get_term_by( 'name', $term, self::$taxonomy ); $this->assertSame( 5, $t->count ); } } @@ -253,7 +255,7 @@ class Tests_Term_WpSetObjectTerms extends WP_UnitTestCase { $terms_1 = array(); for ( $i = 0; $i < 3; $i++ ) { $term = "term_{$i}"; - $result = wp_insert_term( $term, $this->taxonomy ); + $result = wp_insert_term( $term, self::$taxonomy ); $this->assertIsArray( $result ); $terms_1[ $i ] = $result['term_id']; } @@ -263,17 +265,17 @@ class Tests_Term_WpSetObjectTerms extends WP_UnitTestCase { $terms_2[0] = $terms_1[1]; $term = 'term'; - $result = wp_insert_term( $term, $this->taxonomy ); + $result = wp_insert_term( $term, self::$taxonomy ); $terms_2[1] = $result['term_id']; // Set the initial terms. - $tt_1 = wp_set_object_terms( $post_id, $terms_1, $this->taxonomy ); + $tt_1 = wp_set_object_terms( $post_id, $terms_1, self::$taxonomy ); $this->assertCount( 3, $tt_1 ); // Make sure they're correct. $terms = wp_get_object_terms( $post_id, - $this->taxonomy, + self::$taxonomy, array( 'fields' => 'ids', 'orderby' => 'term_id', @@ -282,13 +284,13 @@ class Tests_Term_WpSetObjectTerms extends WP_UnitTestCase { $this->assertSame( $terms_1, $terms ); // Change the terms. - $tt_2 = wp_set_object_terms( $post_id, $terms_2, $this->taxonomy ); + $tt_2 = wp_set_object_terms( $post_id, $terms_2, self::$taxonomy ); $this->assertCount( 2, $tt_2 ); // Make sure they're correct. $terms = wp_get_object_terms( $post_id, - $this->taxonomy, + self::$taxonomy, array( 'fields' => 'ids', 'orderby' => 'term_id', @@ -311,13 +313,13 @@ class Tests_Term_WpSetObjectTerms extends WP_UnitTestCase { $terms_2 = array( 'bar', 'bing' ); // Set the initial terms. - $tt_1 = wp_set_object_terms( $post_id, $terms_1, $this->taxonomy ); + $tt_1 = wp_set_object_terms( $post_id, $terms_1, self::$taxonomy ); $this->assertCount( 3, $tt_1 ); // Make sure they're correct. $terms = wp_get_object_terms( $post_id, - $this->taxonomy, + self::$taxonomy, array( 'fields' => 'names', 'orderby' => 'term_id', @@ -326,13 +328,13 @@ class Tests_Term_WpSetObjectTerms extends WP_UnitTestCase { $this->assertSame( $terms_1, $terms ); // Change the terms. - $tt_2 = wp_set_object_terms( $post_id, $terms_2, $this->taxonomy ); + $tt_2 = wp_set_object_terms( $post_id, $terms_2, self::$taxonomy ); $this->assertCount( 2, $tt_2 ); // Make sure they're correct. $terms = wp_get_object_terms( $post_id, - $this->taxonomy, + self::$taxonomy, array( 'fields' => 'names', 'orderby' => 'term_id', @@ -430,4 +432,48 @@ class Tests_Term_WpSetObjectTerms extends WP_UnitTestCase { $this->assertSame( array(), $tt_ids ); } + + /** + * Tests that empty values clear an object of all terms. + * + * @ticket 57923 + * + * @dataProvider data_empty_value_should_clear_terms + * + * @param mixed $empty_value An empty value. + */ + public function test_empty_value_should_clear_terms( $empty_value ) { + $post_id = self::$post_ids[0]; + + // Assign some terms. + wp_set_object_terms( $post_id, self::$term_ids, self::$taxonomy ); + + // Make sure the terms are set. + $terms = wp_get_object_terms( $post_id, self::$taxonomy, array( 'fields' => 'names' ) ); + $this->assertNotEmpty( $terms, 'Terms should initially be applied to post object.' ); + + // Remove terms by passing an empty value. + wp_set_object_terms( $post_id, $empty_value, self::$taxonomy ); + + // Make sure the terms have been removed. + $terms = wp_get_object_terms( $post_id, self::$taxonomy, array( 'fields' => 'names' ) ); + $this->assertEmpty( $terms, 'An empty() value should clear terms from the post object.' ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_empty_value_should_clear_terms() { + return array( + '(bool) false' => array( false ), + 'null' => array( null ), + '(int) 0' => array( 0 ), + '(float) 0.0' => array( 0.0 ), + 'empty string' => array( '' ), + '(string) 0' => array( '0' ), + 'empty array' => array( array() ), + ); + } }