Commit 7f77a18b6e9 for woocommerce
commit 7f77a18b6e981b99569f49d9debb84b3d02edd5d
Author: Michael Pretty <prettyboymp@users.noreply.github.com>
Date: Tue Jun 2 20:04:32 2026 -0400
Enable product object caching by default for new installs (#65445)
* Enable product object caching by default for new installs
* Run unit and e2e tests with product object caching enabled
Force product_instance_caching on in the PHPUnit bootstrap and the e2e site
setup so both suites exercise the cache-on path that new installs now get by
default. This surfaces any core code that bypasses the product CRUD/cache
interfaces (raw SQL or direct postmeta writes without invalidation) as a test
failure instead of a silent production bug.
* Flush object cache in download and COGS tests for cache-on runs
With product object caching enabled by default in the test bootstrap, these
tests need a fresh product read after changing state that the cached object
wouldn't otherwise reflect: disabling an Approved Download Directory rule (the
download enabled state is computed at product load) and registering a
woocommerce_load_product_cogs_value filter after the product was already saved
into the cache. Flush the object cache so wc_get_product() re-reads via the data
store. Behavior is unchanged when the feature is off.
* Update @since to 11.0.0 for product caching new-install handler
diff --git a/plugins/woocommerce/changelog/enable-product-object-caching-new-installs b/plugins/woocommerce/changelog/enable-product-object-caching-new-installs
new file mode 100644
index 00000000000..36d3ef59bd3
--- /dev/null
+++ b/plugins/woocommerce/changelog/enable-product-object-caching-new-installs
@@ -0,0 +1,4 @@
+Significance: minor
+Type: tweak
+
+Enable the product object caching feature by default for newly installed stores. Existing stores are unaffected and keep the opt-in default.
diff --git a/plugins/woocommerce/includes/class-wc-install.php b/plugins/woocommerce/includes/class-wc-install.php
index f2a9ab9d378..c290f9ec4ef 100644
--- a/plugins/woocommerce/includes/class-wc-install.php
+++ b/plugins/woocommerce/includes/class-wc-install.php
@@ -10,6 +10,7 @@ use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Admin\API\Reports\Orders\Stats\DataStore;
use Automattic\WooCommerce\Enums\ProductType;
use Automattic\WooCommerce\Internal\Admin\EmailImprovements\EmailImprovements;
+use Automattic\WooCommerce\Internal\Caches\ProductCacheController;
use Automattic\WooCommerce\Internal\TransientFiles\TransientFilesEngine;
use Automattic\WooCommerce\Internal\DataStores\Orders\{ CustomOrdersTableController, DataSynchronizer, OrdersTableDataStore };
use Automattic\WooCommerce\Internal\DataStores\StockNotifications\StockNotificationsDataStore;
@@ -374,6 +375,7 @@ class WC_Install {
add_action( 'woocommerce_newly_installed', array( __CLASS__, 'enable_email_improvements_for_newly_installed' ), 20 );
add_action( 'woocommerce_newly_installed', array( __CLASS__, 'enable_customer_stock_notifications_signups' ), 20 );
add_action( 'woocommerce_newly_installed', array( __CLASS__, 'enable_analytics_scheduled_import' ), 20 );
+ add_action( 'woocommerce_newly_installed', array( __CLASS__, 'enable_product_instance_caching_for_newly_installed' ), 20 );
add_action( 'woocommerce_updated', array( __CLASS__, 'enable_email_improvements_for_existing_merchants' ), 20 );
add_action( 'woocommerce_run_update_callback', array( __CLASS__, 'run_update_callback' ) );
add_action( 'woocommerce_update_db_to_current_version', array( __CLASS__, 'update_db_version' ) );
@@ -1308,6 +1310,21 @@ class WC_Install {
add_option( 'woocommerce_analytics_scheduled_import', 'yes' );
}
+ /**
+ * Enable product object caching by default for new shops.
+ *
+ * Only newly installed stores get the feature enabled here; existing installs keep the
+ * opt-in default so their behavior is unchanged on upgrade.
+ *
+ * @since 11.0.0
+ *
+ * @return void
+ */
+ public static function enable_product_instance_caching_for_newly_installed(): void {
+ $feature_controller = wc_get_container()->get( FeaturesController::class );
+ $feature_controller->change_feature_enable( ProductCacheController::FEATURE_NAME, true );
+ }
+
/**
* Enable email improvements by default for existing shops if conditions are met.
*
diff --git a/plugins/woocommerce/tests/e2e-pw/fixtures/site.setup.ts b/plugins/woocommerce/tests/e2e-pw/fixtures/site.setup.ts
index f2d36892de0..bed5b3c6e1f 100644
--- a/plugins/woocommerce/tests/e2e-pw/fixtures/site.setup.ts
+++ b/plugins/woocommerce/tests/e2e-pw/fixtures/site.setup.ts
@@ -1,6 +1,7 @@
/**
* External dependencies
*/
+import { request } from '@playwright/test';
import { WC_API_PATH } from '@woocommerce/e2e-utils-playwright';
/**
@@ -9,6 +10,7 @@ import { WC_API_PATH } from '@woocommerce/e2e-utils-playwright';
import { test as setup } from './fixtures';
import { setComingSoon } from '../utils/coming-soon';
import { skipOnboardingWizard } from '../utils/onboarding';
+import { setOption } from '../utils/options';
setup( 'setup site', async ( { baseURL, restApi } ) => {
await setup.step( 'configure HPOS', async () => {
@@ -64,6 +66,19 @@ setup( 'setup site', async ( { baseURL, restApi } ) => {
);
} );
+ await setup.step( 'enable product object caching', async () => {
+ // Always run e2e with product object caching on (the new-install default), so any
+ // flow that bypasses the product CRUD/cache interfaces surfaces as a failure. Set
+ // explicitly rather than relying on the new-install default, so the state is
+ // deterministic regardless of how the test site's DB was provisioned.
+ await setOption(
+ request,
+ baseURL,
+ 'woocommerce_feature_product_instance_caching_enabled',
+ 'yes'
+ );
+ } );
+
await setup.step( 'disable coming soon', async () => {
await setComingSoon( { baseURL, enabled: 'no' } );
} );
diff --git a/plugins/woocommerce/tests/legacy/bootstrap.php b/plugins/woocommerce/tests/legacy/bootstrap.php
index c7f5f5bf562..6928f4214ca 100644
--- a/plugins/woocommerce/tests/legacy/bootstrap.php
+++ b/plugins/woocommerce/tests/legacy/bootstrap.php
@@ -284,6 +284,13 @@ class WC_Unit_Tests_Bootstrap {
WC_Install::install();
+ // Run the test suite with product object caching enabled (the new-install default).
+ // This ensures tests exercise the cache-on path and fail loudly if any code bypasses
+ // the product CRUD/cache interfaces (e.g. raw SQL or direct postmeta writes without
+ // invalidation). install_wc() runs on `setup_theme`, before `init`, so the option is
+ // set in time for ProductCacheController::on_init() to register its invalidation hooks.
+ update_option( 'woocommerce_feature_product_instance_caching_enabled', 'yes' );
+
// Reload capabilities after install, see https://core.trac.wordpress.org/ticket/28374.
if ( version_compare( $GLOBALS['wp_version'], '4.7', '<' ) ) {
$GLOBALS['wp_roles']->reinit();
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/install.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/install.php
index 3dd003f872c..6dacbcf264c 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/install.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/install.php
@@ -237,6 +237,22 @@ class WC_Admin_Tests_Install extends WP_UnitTestCase {
$this->assertEquals( 'no', get_option( 'woocommerce_analytics_scheduled_import' ) );
}
+ /**
+ * Test product object caching is enabled by default for new installations.
+ *
+ * @return void
+ */
+ public function test_enable_product_instance_caching_for_new_installation() {
+ // Ensure the feature option doesn't exist before testing.
+ delete_option( 'woocommerce_feature_product_instance_caching_enabled' );
+
+ // Call the method to enable the feature for new installs.
+ WC_Install::enable_product_instance_caching_for_newly_installed();
+
+ // Verify the feature option was set to 'yes'.
+ $this->assertEquals( 'yes', get_option( 'woocommerce_feature_product_instance_caching_enabled' ) );
+ }
+
/**
* Test migrate_options();
* @return void
diff --git a/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-product-test.php b/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-product-test.php
index 7d7c250975a..4344030499f 100644
--- a/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-product-test.php
+++ b/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-product-test.php
@@ -78,6 +78,11 @@ class WC_Abstract_Product_Test extends WC_Unit_Test_Case {
);
$this->download_directories->disable_by_id( $this->download_directories->get_by_url( 'https://new.supplier/' )->get_id() );
+
+ // Approved Download Directory rule changes don't invalidate the product object cache, so
+ // flush to force a fresh read that reflects the updated rules.
+ wp_cache_flush();
+
$product_downloads = wc_get_product( $this->product->get_id() )->get_downloads();
$this->assertCount(
@@ -92,6 +97,7 @@ class WC_Abstract_Product_Test extends WC_Unit_Test_Case {
);
$this->download_directories->set_mode( Download_Directories::MODE_DISABLED );
+ wp_cache_flush();
$this->assertCount(
2,
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-download-handler-tests.php b/plugins/woocommerce/tests/php/includes/class-wc-download-handler-tests.php
index 80f681b0383..b11e00a976c 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-download-handler-tests.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-download-handler-tests.php
@@ -112,6 +112,11 @@ class WC_Download_Handler_Tests extends \WC_Unit_Test_Case {
// And now with one of the approved directory rules disabled...
$approved_directories->disable_by_id( $approved_directory_rule_id );
+
+ // Approved Download Directory rule changes don't invalidate the product object cache, so
+ // flush to force a fresh read that reflects the updated rules.
+ wp_cache_flush();
+
$_GET['key'] = $download_keys[1];
$wp_die_happened = false;
diff --git a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php
index a33051f623e..27c688253fa 100644
--- a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php
+++ b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php
@@ -299,6 +299,10 @@ class WC_Product_Data_Store_CPT_Test extends WC_Unit_Test_Case {
add_filter( 'woocommerce_load_product_cogs_value', fn( $value, $product ) => $value + $product->get_id(), 10, 2 );
+ // The save above populates the product object cache; flush so the re-read goes through the
+ // data store and applies the load filter registered after the save.
+ wp_cache_flush();
+
$product = wc_get_product( $product->get_id() );
$this->assertEquals( 12.34 + $product->get_id(), $product->get_cogs_value() );
}