Commit e69c9a7b66f for woocommerce
commit e69c9a7b66fda406aa5a5254c1c6023b937d0384
Author: Michal Iwanow <4765119+mcliwanow@users.noreply.github.com>
Date: Tue Oct 13 11:34:38 2026 +0200
Stop the Extensions menu update count from running an update check (#69454)
* Count Extensions menu updates from cached data without an update check
get_updates_count() now reads the products in the cached update data
even when it is stale or expired, instead of calling get_update_data(),
which made a blocking update-check request once the cache was older than
12 hours. A new update-check result also resets the cached count, so a
count taken from stale data doesn't stick for 12 hours after a refresh.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add changelog entry for the Extensions menu update count fix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Remove the changelog entry for the Extensions menu update count fix
The fix follows up on #69160, which hasn't been released yet, so its
changelog entry already covers it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
diff --git a/plugins/woocommerce/includes/admin/helper/class-wc-helper-updater.php b/plugins/woocommerce/includes/admin/helper/class-wc-helper-updater.php
index f121a51637a..5db885a2189 100644
--- a/plugins/woocommerce/includes/admin/helper/class-wc-helper-updater.php
+++ b/plugins/woocommerce/includes/admin/helper/class-wc-helper-updater.php
@@ -1186,6 +1186,7 @@ class WC_Helper_Updater {
}
set_transient( $cache_key, $data, WEEK_IN_SECONDS );
+ delete_transient( '_woocommerce_helper_updates_count' );
return $data['products'];
}
@@ -1201,17 +1202,13 @@ class WC_Helper_Updater {
return $count;
}
- // This runs often, so it only counts from cached data; a cache that's stale or expired by a refresh triggers one update check here.
+ // This runs on every admin page, so it counts from the cached update data, even when stale, and never runs an update check.
if ( ! get_transient( '_woocommerce_helper_subscriptions' ) ) {
return 0;
}
- if ( ! get_transient( '_woocommerce_helper_updates' ) ) {
- return 0;
- }
-
$count = 0;
- $update_data = self::get_update_data();
+ $update_data = self::get_cached_products( get_transient( '_woocommerce_helper_updates' ), '' );
if ( empty( $update_data ) ) {
set_transient( $cache_key, $count, 12 * HOUR_IN_SECONDS );
@@ -1220,7 +1217,7 @@ class WC_Helper_Updater {
// Scan local plugins.
foreach ( WC_Helper::get_local_woo_plugins() as $plugin ) {
- if ( empty( $update_data[ $plugin['_product_id'] ] ) ) {
+ if ( ! isset( $update_data[ $plugin['_product_id'] ]['version'] ) ) {
continue;
}
@@ -1235,7 +1232,7 @@ class WC_Helper_Updater {
// Scan local themes.
foreach ( WC_Helper::get_local_woo_themes() as $theme ) {
- if ( empty( $update_data[ $theme['_product_id'] ] ) ) {
+ if ( ! isset( $update_data[ $theme['_product_id'] ]['version'] ) ) {
continue;
}
diff --git a/plugins/woocommerce/tests/php/includes/admin/helper/class-wc-helper-updater-test.php b/plugins/woocommerce/tests/php/includes/admin/helper/class-wc-helper-updater-test.php
index 4f7698b4291..92145b3e24c 100644
--- a/plugins/woocommerce/tests/php/includes/admin/helper/class-wc-helper-updater-test.php
+++ b/plugins/woocommerce/tests/php/includes/admin/helper/class-wc-helper-updater-test.php
@@ -753,6 +753,77 @@ class WC_Helper_Updater_Test extends WC_Unit_Test_Case {
$this->assertFalse( get_transient( '_woocommerce_helper_updates_count' ), 'Count transient should be cleared after upgrade' );
}
+ /**
+ * @testdox The updates count should come from cached data an update check wouldn't reuse, without making the check.
+ */
+ public function test_updates_count_reads_stale_cache_without_a_request(): void {
+ $plugin_file = $this->mock_local_woo_plugin();
+ update_option( 'active_plugins', array( $plugin_file ) );
+ $this->set_subscriptions( array( $this->subscription() ) );
+ set_transient(
+ '_woocommerce_helper_updates',
+ array(
+ 'hash' => 'a-stale-hash',
+ 'updated' => time() - 13 * HOUR_IN_SECONDS,
+ 'products' => array( 123 => array( 'version' => '2.0.0' ) ),
+ 'errors' => array(),
+ ),
+ WEEK_IN_SECONDS
+ );
+
+ $requests = 0;
+ $http_mock = static function () use ( &$requests ) {
+ ++$requests;
+ return new WP_Error( 'http_request_failed', 'cURL error 28: Operation timed out' );
+ };
+ add_filter( 'pre_http_request', $http_mock );
+
+ try {
+ $count = WC_Helper_Updater::get_updates_count();
+ } finally {
+ remove_filter( 'pre_http_request', $http_mock );
+ }
+
+ $this->assertSame( 1, $count, 'The active plugin behind the cached version should be counted' );
+ $this->assertSame( 0, $requests, 'Counting updates should not call WooCommerce.com' );
+ }
+
+ /**
+ * @testdox The updates count should be 0 when the cached update data is malformed.
+ */
+ public function test_updates_count_is_zero_for_malformed_cached_data(): void {
+ $plugin_file = $this->mock_local_woo_plugin();
+ update_option( 'active_plugins', array( $plugin_file ) );
+ $this->set_subscriptions( array( $this->subscription() ) );
+ set_transient( '_woocommerce_helper_updates', array( 'products' => 'not-an-array' ), WEEK_IN_SECONDS );
+
+ $this->assertSame( 0, WC_Helper_Updater::get_updates_count() );
+ }
+
+ /**
+ * @testdox A successful update check should reset the cached updates count.
+ */
+ public function test_update_check_resets_updates_count(): void {
+ set_transient( '_woocommerce_helper_updates_count', 5, 12 * HOUR_IN_SECONDS );
+ add_filter( 'pre_http_request', array( $this, 'mock_helper_api_response' ), 10, 3 );
+
+ try {
+ $this->call_update_check(
+ array(
+ 123 => array(
+ 'product_id' => 123,
+ 'file_id' => 'abc123',
+ ),
+ )
+ );
+ } finally {
+ remove_filter( 'pre_http_request', array( $this, 'mock_helper_api_response' ) );
+ }
+
+ $this->assertNotFalse( get_transient( '_woocommerce_helper_updates' ), 'The update check should cache its result' );
+ $this->assertFalse( get_transient( '_woocommerce_helper_updates_count' ), 'A new cache should reset the count computed from the old one' );
+ }
+
/**
* Test should_use_cached_update_data returns false when data is not an array.
*/