From f255ebfa08cd8ba0cb77b1b5559b9dcb28fc628d Mon Sep 17 00:00:00 2001 From: Infinite-Null Date: Mon, 20 Apr 2026 20:42:03 +0530 Subject: [PATCH 1/7] Tests: Add unit tests for WP_Object_Cache::stats to handle serializable and non-serializable data --- src/wp-includes/class-wp-object-cache.php | 6 ++- tests/phpunit/tests/cache.php | 52 +++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-object-cache.php b/src/wp-includes/class-wp-object-cache.php index cda63e66d49ef..9b086f78bdae2 100644 --- a/src/wp-includes/class-wp-object-cache.php +++ b/src/wp-includes/class-wp-object-cache.php @@ -637,7 +637,11 @@ public function stats() { echo '

'; echo ''; } diff --git a/tests/phpunit/tests/cache.php b/tests/phpunit/tests/cache.php index 1f345652b1fd9..03b8fcc7912d4 100644 --- a/tests/phpunit/tests/cache.php +++ b/tests/phpunit/tests/cache.php @@ -491,4 +491,56 @@ public function test_wp_cache_delete_multiple() { $this->assertSame( $expected, $found ); } + + /** + * Tests that stats() outputs cache group information for serializable data. + * + * @ticket 21650 + * + * @covers WP_Object_Cache::stats + */ + public function test_stats_with_serializable_data() { + $this->cache->set( 'key1', 'value1', 'test-group' ); + $this->cache->set( 'key2', array( 'a', 'b', 'c' ), 'test-group' ); + + ob_start(); + $this->cache->stats(); + $output = ob_get_clean(); + + $this->assertStringContainsString( 'test-group', $output, 'stats() output should contain the cache group name.' ); + $this->assertStringContainsString( '
  • ', $output, 'stats() output should contain list items.' ); + } + + /** + * Tests that stats() does not fatal error when a cache group contains a + * non-serializable object such as SimpleXMLElement, and falls back to + * print_r() for size estimation. + * + * @ticket 21650 + * + * @covers WP_Object_Cache::stats + */ + public function test_stats_with_non_serializable_simplexml_data() { + if ( ! class_exists( 'SimpleXMLElement' ) ) { + $this->markTestSkipped( 'SimpleXMLElement class is not available.' ); + } + + $xml_object = new SimpleXMLElement( 'value' ); + + // Directly inject the SimpleXMLElement into the cache storage to bypass + // any object-clone logic in set(), simulating a real-world scenario where + // a non-serializable object ends up in the cache. + $cache_property = new ReflectionProperty( $this->cache, 'cache' ); + $cache_data = $cache_property->getValue( $this->cache ); + $cache_data['xml-group']['item1'] = $xml_object; + $cache_property->setValue( $this->cache, $cache_data ); + + // stats() should not throw a fatal error or exception. + ob_start(); + $this->cache->stats(); + $output = ob_get_clean(); + + $this->assertStringContainsString( 'xml-group', $output, 'stats() output should contain the group name even for non-serializable data.' ); + $this->assertStringContainsString( '
  • ', $output, 'stats() output should contain a list item for the non-serializable group.' ); + } } From 608603fdcdbb6d3693a2ad365ed4900e5b1db9dc Mon Sep 17 00:00:00 2001 From: Infinite-Null Date: Mon, 20 Apr 2026 21:13:47 +0530 Subject: [PATCH 2/7] Fix: Adjust indentation for ReflectionProperty assignment in non-serializable object test --- tests/phpunit/tests/cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/cache.php b/tests/phpunit/tests/cache.php index 03b8fcc7912d4..caf79eec5de56 100644 --- a/tests/phpunit/tests/cache.php +++ b/tests/phpunit/tests/cache.php @@ -530,7 +530,7 @@ public function test_stats_with_non_serializable_simplexml_data() { // Directly inject the SimpleXMLElement into the cache storage to bypass // any object-clone logic in set(), simulating a real-world scenario where // a non-serializable object ends up in the cache. - $cache_property = new ReflectionProperty( $this->cache, 'cache' ); + $cache_property = new ReflectionProperty( $this->cache, 'cache' ); $cache_data = $cache_property->getValue( $this->cache ); $cache_data['xml-group']['item1'] = $xml_object; $cache_property->setValue( $this->cache, $cache_data ); From 28bb960877f3088448db40e800c9a3104c5d9d70 Mon Sep 17 00:00:00 2001 From: Infinite-Null Date: Mon, 20 Apr 2026 21:19:25 +0530 Subject: [PATCH 3/7] Fix: Correct spacing in exception handling for cache group output --- src/wp-includes/class-wp-object-cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-object-cache.php b/src/wp-includes/class-wp-object-cache.php index 9b086f78bdae2..3bf977d4dc8ce 100644 --- a/src/wp-includes/class-wp-object-cache.php +++ b/src/wp-includes/class-wp-object-cache.php @@ -639,7 +639,7 @@ public function stats() { foreach ( $this->cache as $group => $cache ) { try { echo '
  • Group: ' . esc_html( $group ) . ' - ( ' . number_format( strlen( serialize( $cache ) ) / KB_IN_BYTES, 2 ) . 'k )
  • '; - } catch( Exception $e ) { + } catch ( Exception $e ) { echo '
  • Group: ' . esc_html( $group ) . ' - ( ' . number_format( strlen( print_r( $cache, true ) ) / KB_IN_BYTES, 2 ) . 'k )
  • '; } } From dea96719781d60e2f4dcf6b844209cb8b5627c8f Mon Sep 17 00:00:00 2001 From: Infinite-Null Date: Tue, 21 Apr 2026 20:27:13 +0530 Subject: [PATCH 4/7] Fix: Update test to handle non-serializable objects in cache --- tests/phpunit/tests/cache.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/cache.php b/tests/phpunit/tests/cache.php index caf79eec5de56..5f0ed6561f763 100644 --- a/tests/phpunit/tests/cache.php +++ b/tests/phpunit/tests/cache.php @@ -530,7 +530,8 @@ public function test_stats_with_non_serializable_simplexml_data() { // Directly inject the SimpleXMLElement into the cache storage to bypass // any object-clone logic in set(), simulating a real-world scenario where // a non-serializable object ends up in the cache. - $cache_property = new ReflectionProperty( $this->cache, 'cache' ); + $cache_property = new ReflectionProperty( $this->cache, 'cache' ); + $cache_property->setAccessible( true ); $cache_data = $cache_property->getValue( $this->cache ); $cache_data['xml-group']['item1'] = $xml_object; $cache_property->setValue( $this->cache, $cache_data ); From 1241f5a67b4d919766e5a7d27857f8817b2b462c Mon Sep 17 00:00:00 2001 From: Infinite-Null Date: Tue, 21 Apr 2026 21:29:46 +0530 Subject: [PATCH 5/7] Fix: Skip tests for stats with serializable and non-serializable data when using external object cache --- tests/phpunit/tests/cache.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/phpunit/tests/cache.php b/tests/phpunit/tests/cache.php index 5f0ed6561f763..f378bf224a3a3 100644 --- a/tests/phpunit/tests/cache.php +++ b/tests/phpunit/tests/cache.php @@ -500,6 +500,10 @@ public function test_wp_cache_delete_multiple() { * @covers WP_Object_Cache::stats */ public function test_stats_with_serializable_data() { + if ( wp_using_ext_object_cache() ) { + $this->markTestSkipped( 'This test requires that an external object cache is not in use.' ); + } + $this->cache->set( 'key1', 'value1', 'test-group' ); $this->cache->set( 'key2', array( 'a', 'b', 'c' ), 'test-group' ); @@ -521,6 +525,10 @@ public function test_stats_with_serializable_data() { * @covers WP_Object_Cache::stats */ public function test_stats_with_non_serializable_simplexml_data() { + if ( wp_using_ext_object_cache() ) { + $this->markTestSkipped( 'This test requires that an external object cache is not in use.' ); + } + if ( ! class_exists( 'SimpleXMLElement' ) ) { $this->markTestSkipped( 'SimpleXMLElement class is not available.' ); } From 8175e94daf70b905b793e9da5dd64e5b93f423fb Mon Sep 17 00:00:00 2001 From: Infinite-Null Date: Tue, 21 Apr 2026 21:59:51 +0530 Subject: [PATCH 6/7] Fix: Remove accessibility modification for cache property in non-serializable object test --- tests/phpunit/tests/cache.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/tests/cache.php b/tests/phpunit/tests/cache.php index f378bf224a3a3..f9a590bce5350 100644 --- a/tests/phpunit/tests/cache.php +++ b/tests/phpunit/tests/cache.php @@ -539,7 +539,6 @@ public function test_stats_with_non_serializable_simplexml_data() { // any object-clone logic in set(), simulating a real-world scenario where // a non-serializable object ends up in the cache. $cache_property = new ReflectionProperty( $this->cache, 'cache' ); - $cache_property->setAccessible( true ); $cache_data = $cache_property->getValue( $this->cache ); $cache_data['xml-group']['item1'] = $xml_object; $cache_property->setValue( $this->cache, $cache_data ); From 49438a995f2c3959ed03f67a06650ebde618616e Mon Sep 17 00:00:00 2001 From: Infinite-Null Date: Tue, 21 Apr 2026 22:52:10 +0530 Subject: [PATCH 7/7] Fix: Adjust accessibility of cache property for PHP versions below 8.5 --- tests/phpunit/tests/cache.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/phpunit/tests/cache.php b/tests/phpunit/tests/cache.php index f9a590bce5350..81d9e6888c56c 100644 --- a/tests/phpunit/tests/cache.php +++ b/tests/phpunit/tests/cache.php @@ -539,6 +539,9 @@ public function test_stats_with_non_serializable_simplexml_data() { // any object-clone logic in set(), simulating a real-world scenario where // a non-serializable object ends up in the cache. $cache_property = new ReflectionProperty( $this->cache, 'cache' ); + if ( PHP_VERSION_ID < 80500 ) { + $cache_property->setAccessible( true ); + } $cache_data = $cache_property->getValue( $this->cache ); $cache_data['xml-group']['item1'] = $xml_object; $cache_property->setValue( $this->cache, $cache_data );