From 94eeb1f102212add7a0efb7822529d3277598deb Mon Sep 17 00:00:00 2001 From: wppoland Date: Sun, 13 Sep 2026 09:38:18 +0200 Subject: [PATCH] Posts, Taxonomy: Skip wp_set_post_categories() on update when post_category is not provided --- src/wp-includes/post.php | 2 +- tests/phpunit/tests/post/wpInsertPost.php | 69 +++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 5edc8a50f0299..d9cafd452ff8d 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -5158,7 +5158,7 @@ function wp_insert_post( $postarr, $wp_error = false, $fire_after_hooks = true ) clean_post_cache( $post_id ); } - if ( is_object_in_taxonomy( $post_type, 'category' ) ) { + if ( is_object_in_taxonomy( $post_type, 'category' ) && ( ! $update || isset( $postarr['post_category'] ) ) ) { wp_set_post_categories( $post_id, $post_category ); } diff --git a/tests/phpunit/tests/post/wpInsertPost.php b/tests/phpunit/tests/post/wpInsertPost.php index a6512f42cbf05..34c2e7d5e49cd 100644 --- a/tests/phpunit/tests/post/wpInsertPost.php +++ b/tests/phpunit/tests/post/wpInsertPost.php @@ -1548,4 +1548,73 @@ public function test_scheduled_post_with_a_past_date_should_be_published() { $this->assertSame( 'future', get_post_status( $post_id ) ); } + + /** + * Verify that updating a post without `post_category` skips the `wp_set_post_categories()` call. + * + * `wp_set_post_categories()` triggers term-query and cache work even when its argument is the + * post's existing category list, so calling it on every update is a measurable regression. + * + * @ticket 59354 + * @covers ::wp_insert_post + */ + public function test_update_post_without_post_category_does_not_call_wp_set_post_categories() { + $post_id = self::factory()->post->create( + array( + 'post_title' => 'Original', + 'post_category' => array( 1 ), + ) + ); + + $category_set_calls = 0; + $callback = static function ( $object_id, $terms, $tt_ids, $taxonomy ) use ( &$category_set_calls ) { + if ( 'category' === $taxonomy ) { + ++$category_set_calls; + } + }; + + add_action( 'set_object_terms', $callback, 10, 4 ); + + wp_insert_post( + array( + 'ID' => $post_id, + 'post_title' => 'Updated Title Only', + ) + ); + + remove_action( 'set_object_terms', $callback ); + + $this->assertSame( 0, $category_set_calls, 'wp_set_post_categories() should not run on update without post_category.' ); + } + + /** + * Updating a post without `post_category` must preserve its existing categories. + * + * @ticket 59354 + * @covers ::wp_insert_post + */ + public function test_update_post_without_post_category_preserves_existing_categories() { + $category_id = self::factory()->category->create( array( 'name' => 'Stays Assigned' ) ); + + $post_id = self::factory()->post->create( + array( + 'post_title' => 'Original', + 'post_category' => array( $category_id ), + ) + ); + + $categories_before = wp_get_post_categories( $post_id ); + + wp_insert_post( + array( + 'ID' => $post_id, + 'post_title' => 'Updated Title Only', + ) + ); + + $categories_after = wp_get_post_categories( $post_id ); + + $this->assertSame( $categories_before, $categories_after, 'Existing categories should be preserved when post_category is not passed.' ); + $this->assertContains( $category_id, $categories_after, 'The originally-assigned category should still be set.' ); + } }