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 '';
foreach ( $this->cache as $group => $cache ) {
- echo '- Group: ' . esc_html( $group ) . ' - ( ' . number_format( strlen( serialize( $cache ) ) / KB_IN_BYTES, 2 ) . 'k )
';
+ try {
+ echo '- Group: ' . esc_html( $group ) . ' - ( ' . number_format( strlen( serialize( $cache ) ) / KB_IN_BYTES, 2 ) . 'k )
';
+ } catch( Exception $e ) {
+ echo '- Group: ' . esc_html( $group ) . ' - ( ' . number_format( strlen( print_r( $cache, true ) ) / KB_IN_BYTES, 2 ) . 'k )
';
+ }
}
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 );