Changeset 64589
- Timestamp:
- 10/20/2026 05:49:16 AM (25 hours ago)
- Location:
- trunk
- Files:
-
- 3 edited
-
src/wp-includes/post.php (modified) (3 diffs)
-
tests/phpunit/tests/post/wpCountAttachments.php (modified) (1 diff)
-
tests/phpunit/tests/post/wpCountPosts.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/post.php
r64583 r64589 3545 3545 * Filters the post counts before the query is run. 3546 3546 * 3547 * Returning a non-null value short-circuits wp_count_posts(), skipping both3548 * the object cache lookup and the database query. This allows the counts to3549 * be served from another source, such as a value pre-computed in the3550 * background, when an exact real-time count is not required.3551 * 3552 * Any registered post status missing from the filtered value areadded3547 * Returning a stdClass object short-circuits wp_count_posts(), skipping 3548 * both the object cache lookup and the database query. This allows the 3549 * counts to be served from another source, such as a value pre-computed in 3550 * the background, when an exact real-time count is not required. 3551 * 3552 * Any registered post status missing from the filtered value is added 3553 3553 * to the result and set to zero. 3554 3554 * … … 3566 3566 $counts = apply_filters( 'pre_wp_count_posts', null, $type, $perm ); 3567 3567 3568 if ( null !== $counts ) {3569 $counts = (object)$counts;3568 if ( $counts instanceof stdClass ) { 3569 $counts = clone $counts; 3570 3570 3571 3571 foreach ( get_post_stati() as $status ) { … … 3669 3669 function wp_count_attachments( $mime_type = '' ) { 3670 3670 global $wpdb; 3671 3672 /** 3673 * Filters the attachment counts before the query is run. 3674 * 3675 * Returning a stdClass object short-circuits wp_count_attachments(), 3676 * skipping both the object cache lookup and the database queries. This 3677 * allows the counts to be served from another source, such as a value 3678 * pre-computed in the background, when an exact real-time count is not 3679 * required. 3680 * 3681 * If the filtered value does not contain a trash count, one is added to the 3682 * result and set to zero. Missing MIME types are not added, as they depend on 3683 * the attachments that exist. The filtered value should therefore contain a 3684 * count for every MIME type matching `$mime_type` that has attachments, 3685 * including types first uploaded after the value was computed, as callers 3686 * may read the count for any MIME type that exists. It should also contain 3687 * only MIME types matching `$mime_type`, with a trash count limited to 3688 * attachments of those types, as the default query does. 3689 * 3690 * The returned value is not cached and passed through the 3691 * {@see 'wp_count_attachments'} filter. 3692 * 3693 * @since 7.2.0 3694 * 3695 * @param stdClass|null $counts An object containing the attachment counts by 3696 * mime type, or null to run the default query. 3697 * Default null. 3698 * @param string|string[] $mime_type Array or comma-separated list of MIME patterns. 3699 */ 3700 $counts = apply_filters( 'pre_wp_count_attachments', null, $mime_type ); 3701 3702 if ( $counts instanceof stdClass ) { 3703 $counts = clone $counts; 3704 3705 if ( ! isset( $counts->trash ) ) { 3706 $counts->trash = 0; 3707 } 3708 3709 /** This filter is documented in wp-includes/post.php */ 3710 return apply_filters( 'wp_count_attachments', $counts, $mime_type ); 3711 } 3671 3712 3672 3713 $cache_key = sprintf( -
trunk/tests/phpunit/tests/post/wpCountAttachments.php
r64071 r64589 32 32 $this->assertEquals( $expected, $actual ); 33 33 } 34 35 /** 36 * @ticket 66291 37 */ 38 public function test_pre_wp_count_attachments_short_circuits_query() { 39 self::factory()->post->create_many( 40 3, 41 array( 42 'post_type' => 'attachment', 43 'post_mime_type' => 'image/jpeg', 44 ) 45 ); 46 47 $mime_type = 'image/jpeg'; 48 $mock_count = 42; 49 add_filter( 50 'pre_wp_count_attachments', 51 static function () use ( $mime_type, $mock_count ) { 52 return (object) array( $mime_type => $mock_count ); 53 } 54 ); 55 56 $start_num_queries = get_num_queries(); 57 $counts = wp_count_attachments(); 58 59 $this->assertSame( 0, get_num_queries() - $start_num_queries, 'No database query should run when the filter returns a value.' ); 60 $this->assertObjectHasProperty( $mime_type, $counts ); 61 $this->assertSame( $mock_count, $counts->$mime_type, 'Count is expected to be filtered value.' ); 62 $this->assertObjectHasProperty( 'trash', $counts ); 63 $this->assertSame( 0, $counts->trash, 'Expected trash to be set to zero.' ); 64 } 65 66 /** 67 * @ticket 66291 68 */ 69 public function test_pre_wp_count_attachments_takes_precedence_over_cached_counts() { 70 self::factory()->post->create_many( 71 3, 72 array( 73 'post_type' => 'attachment', 74 'post_mime_type' => 'image/jpeg', 75 ) 76 ); 77 78 wp_count_attachments(); 79 $this->assertNotFalse( wp_cache_get( 'attachments', 'counts' ), 'Expected the counts to be cached before the filter is added.' ); 80 81 $mime_type = 'image/jpeg'; 82 $mock_count = 42; 83 add_filter( 84 'pre_wp_count_attachments', 85 static function () use ( $mime_type, $mock_count ) { 86 return (object) array( $mime_type => $mock_count ); 87 } 88 ); 89 90 $this->assertSame( $mock_count, wp_count_attachments()->$mime_type, 'The filtered value should be returned instead of the cached counts.' ); 91 } 92 93 /** 94 * @ticket 66291 95 */ 96 public function test_pre_wp_count_attachments_null_runs_default_query() { 97 self::factory()->post->create_many( 98 3, 99 array( 100 'post_type' => 'attachment', 101 'post_mime_type' => 'image/jpeg', 102 ) 103 ); 104 105 add_filter( 'pre_wp_count_attachments', '__return_null' ); 106 $start_num_queries = get_num_queries(); 107 108 $this->assertSame( '3', wp_count_attachments()->{'image/jpeg'}, 'Attachment count is expected to be set.' ); 109 $this->assertGreaterThan( 0, get_num_queries() - $start_num_queries, 'wp_count_attachments() is expected to trigger database queries.' ); 110 } 111 112 /** 113 * @ticket 66291 114 * 115 * @dataProvider data_non_stdclass_values 116 * 117 * @param mixed $value Filtered value. 118 */ 119 public function test_pre_wp_count_attachments_non_stdclass_runs_default_query( $value ) { 120 self::factory()->post->create_many( 121 3, 122 array( 123 'post_type' => 'attachment', 124 'post_mime_type' => 'image/jpeg', 125 ) 126 ); 127 128 add_filter( 129 'pre_wp_count_attachments', 130 static function () use ( $value ) { 131 return $value; 132 } 133 ); 134 $start_num_queries = get_num_queries(); 135 136 $this->assertSame( '3', wp_count_attachments()->{'image/jpeg'}, 'A filtered value that is not a stdClass object is expected to be ignored.' ); 137 $this->assertGreaterThan( 0, get_num_queries() - $start_num_queries, 'wp_count_attachments() is expected to trigger database queries.' ); 138 } 139 140 /** 141 * Data provider for {@see self::test_pre_wp_count_attachments_non_stdclass_runs_default_query()}. 142 * 143 * @return array<non-falsy-string, array{ 0: mixed }> 144 */ 145 public function data_non_stdclass_values(): array { 146 return array( 147 'array' => array( array( 'image/jpeg' => 42 ) ), 148 'false' => array( false ), 149 'ArrayObject' => array( new ArrayObject( array( 'image/jpeg' => 42 ) ) ), 150 ); 151 } 152 153 /** 154 * @ticket 66291 155 */ 156 public function test_pre_wp_count_attachments_receives_mime_type() { 157 $filter = new MockAction(); 158 add_filter( 'pre_wp_count_attachments', array( $filter, 'filter' ), 10, 2 ); 159 160 wp_count_attachments( array( 'image/jpeg', 'image/png' ) ); 161 162 $args = $filter->get_args(); 163 $this->assertSame( array( null, array( 'image/jpeg', 'image/png' ) ), $args[0] ); 164 } 165 166 /** 167 * @ticket 66291 168 */ 169 public function test_pre_wp_count_attachments_keeps_filtered_trash_count() { 170 $trash_count = 7; 171 add_filter( 172 'pre_wp_count_attachments', 173 static function () use ( $trash_count ) { 174 return (object) array( 'trash' => $trash_count ); 175 } 176 ); 177 178 $this->assertSame( $trash_count, wp_count_attachments()->trash, 'Expected trash count to be the filtered value.' ); 179 } 180 181 /** 182 * @ticket 66291 183 */ 184 public function test_pre_wp_count_attachments_value_is_passed_to_wp_count_attachments_filter() { 185 add_filter( 186 'pre_wp_count_attachments', 187 static function () { 188 return (object) array( 'image/jpeg' => 5 ); 189 } 190 ); 191 $filter = new MockAction(); 192 add_filter( 'wp_count_attachments', array( $filter, 'filter' ), 10, 2 ); 193 194 wp_count_attachments( 'image' ); 195 196 $args = $filter->get_args(); 197 $this->assertSame( 1, $filter->get_call_count(), 'Expected the wp_count_attachments filter to run once.' ); 198 $this->assertEquals( 199 array( 200 (object) array( 201 'image/jpeg' => 5, 202 'trash' => 0, 203 ), 204 'image', 205 ), 206 $args[0], 207 'Expected the wp_count_attachments filter to receive the filtered counts and MIME type.' 208 ); 209 } 210 211 /** 212 * @ticket 66291 213 */ 214 public function test_pre_wp_count_attachments_does_not_modify_filtered_object() { 215 $stored_counts = (object) array( 'image/jpeg' => 5 ); 216 add_filter( 217 'pre_wp_count_attachments', 218 static function () use ( $stored_counts ) { 219 return $stored_counts; 220 } 221 ); 222 223 $counts = wp_count_attachments(); 224 225 $this->assertSame( 0, $counts->trash, 'Expected trash to be set to zero.' ); 226 $this->assertEquals( (object) array( 'image/jpeg' => 5 ), $stored_counts, 'The object returned by the filter should not be modified.' ); 227 } 228 229 /** 230 * @ticket 66291 231 */ 232 public function test_pre_wp_count_attachments_result_is_not_cached() { 233 self::factory()->post->create_many( 234 3, 235 array( 236 'post_type' => 'attachment', 237 'post_mime_type' => 'image/jpeg', 238 ) 239 ); 240 241 $filter = static function () { 242 return (object) array( 'image/jpeg' => 99 ); 243 }; 244 add_filter( 'pre_wp_count_attachments', $filter ); 245 246 $this->assertSame( 99, wp_count_attachments( 'image/jpeg' )->{'image/jpeg'}, 'Expected the filtered count.' ); 247 $this->assertFalse( wp_cache_get( 'attachments:image_jpeg', 'counts' ), 'Expected the filtered counts not to be cached.' ); 248 249 remove_filter( 'pre_wp_count_attachments', $filter ); 250 $start_num_queries = get_num_queries(); 251 252 $this->assertSame( '3', wp_count_attachments( 'image/jpeg' )->{'image/jpeg'}, 'Expected the real count once the filter is removed.' ); 253 $this->assertGreaterThan( 0, get_num_queries() - $start_num_queries, 'Expected database queries since nothing should have been cached.' ); 254 } 34 255 } -
trunk/tests/phpunit/tests/post/wpCountPosts.php
r64245 r64589 45 45 /** 46 46 * @ticket 66098 47 * 48 * @dataProvider data_non_stdclass_values 49 * 50 * @param mixed $value Filtered value. 51 */ 52 public function test_pre_wp_count_posts_non_stdclass_runs_default_query( $value ) { 53 self::factory()->post->create_many( 3 ); 54 55 add_filter( 56 'pre_wp_count_posts', 57 static function () use ( $value ) { 58 return $value; 59 } 60 ); 61 $start_num_queries = get_num_queries(); 62 63 $this->assertSame( '3', wp_count_posts()->publish, 'A filtered value that is not a stdClass object is expected to be ignored.' ); 64 $this->assertGreaterThan( 0, get_num_queries() - $start_num_queries, 'wp_count_posts() is expected to trigger database queries.' ); 65 } 66 67 /** 68 * Data provider for {@see self::test_pre_wp_count_posts_non_stdclass_runs_default_query()}. 69 * 70 * @return array<non-falsy-string, array{ 0: mixed }> 71 */ 72 public function data_non_stdclass_values(): array { 73 return array( 74 'array' => array( array( 'publish' => 42 ) ), 75 'false' => array( false ), 76 'ArrayObject' => array( new ArrayObject( array( 'publish' => 42 ) ) ), 77 ); 78 } 79 80 /** 81 * @ticket 66098 47 82 */ 48 83 public function test_pre_wp_count_posts_receives_type_and_perm() { … … 106 141 * @ticket 66098 107 142 */ 143 public function test_pre_wp_count_posts_value_is_passed_to_wp_count_posts_filter() { 144 add_filter( 145 'pre_wp_count_posts', 146 static function () { 147 return (object) array( 'publish' => 5 ); 148 } 149 ); 150 $filter = new MockAction(); 151 add_filter( 'wp_count_posts', array( $filter, 'filter' ), 10, 3 ); 152 153 wp_count_posts( 'page', 'readable' ); 154 155 $expected_counts = (object) array_merge( 156 array_fill_keys( get_post_stati(), 0 ), 157 array( 'publish' => 5 ) 158 ); 159 160 $args = $filter->get_args(); 161 $this->assertSame( 1, $filter->get_call_count(), 'Expected the wp_count_posts filter to run once.' ); 162 $this->assertEquals( array( $expected_counts, 'page', 'readable' ), $args[0], 'Expected the wp_count_posts filter to receive the filtered counts, type, and permission.' ); 163 } 164 165 /** 166 * @ticket 66098 167 */ 168 public function test_pre_wp_count_posts_does_not_modify_filtered_object() { 169 $stored_counts = (object) array( 'publish' => 5 ); 170 add_filter( 171 'pre_wp_count_posts', 172 static function () use ( $stored_counts ) { 173 return $stored_counts; 174 } 175 ); 176 177 $counts = wp_count_posts(); 178 179 $this->assertSame( 0, $counts->draft, 'A status missing from the filtered value should default to 0.' ); 180 $this->assertEquals( (object) array( 'publish' => 5 ), $stored_counts, 'The object returned by the filter should not be modified.' ); 181 } 182 183 /** 184 * @ticket 66098 185 */ 108 186 public function test_pre_wp_count_posts_result_is_not_cached() { 109 add_filter( 110 'pre_wp_count_posts', 111 static function () { 112 return (object) array( 'publish' => 99 ); 113 } 114 ); 115 116 wp_count_posts(); 117 118 $this->assertFalse( wp_cache_get( _count_posts_cache_key( 'post' ), 'counts' ) ); 187 self::factory()->post->create_many( 3 ); 188 189 $filter = static function () { 190 return (object) array( 'publish' => 99 ); 191 }; 192 add_filter( 'pre_wp_count_posts', $filter ); 193 194 $this->assertSame( 99, wp_count_posts()->publish, 'Expected the filtered count.' ); 195 $this->assertFalse( wp_cache_get( _count_posts_cache_key( 'post' ), 'counts' ), 'Expected the filtered counts not to be cached.' ); 196 197 remove_filter( 'pre_wp_count_posts', $filter ); 198 $start_num_queries = get_num_queries(); 199 200 $this->assertSame( '3', wp_count_posts()->publish, 'Expected the real count once the filter is removed.' ); 201 $this->assertGreaterThan( 0, get_num_queries() - $start_num_queries, 'Expected database queries since nothing should have been cached.' ); 119 202 } 120 203 }
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)