How WooCommerce 10.5 Caches Product Objects to Cut Hydration Overhead
Table of Contents
What Product Object Caching Actually Solves
Every call to wc_get_product() builds a fresh WC_Product instance. WordPress already caches the raw post data, meta rows, and taxonomy terms through its own object cache layer – that part has worked for years. The expensive step happens after the data is fetched: property assignment, meta loading, type resolution, and internal state setup. That hydration process costs roughly 0.5-0.9ms per product, sometimes more when plugins attach heavy meta.
On a category page rendering 48 products, each product might get loaded two or three times during a single request – once for the main loop, once for the price filter widget, once for a related products block. That adds up to 70-140 wasted hydration cycles per page load. The database is not the bottleneck here. Object construction is.
WooCommerce 10.5 ships an experimental feature that stores fully instantiated product objects in memory for the duration of the request. Repeated wc_get_product() calls return clones from that in-memory store instead of rebuilding from scratch.
Enabling the Feature in WooCommerce 10.5
Navigate to WooCommerce > Settings > Advanced > Features and toggle “Cache Product Objects” on. The setting does not appear in earlier versions – WooCommerce 10.5 or later is required. After activation, wc_get_product() and WC_Product_Factory::get_product() check an in-memory map before hitting the datastore.
The cache is non-persistent. Nothing survives between HTTP requests, and no external service like Redis or Memcached is needed for this specific feature. Clones are returned rather than references, so modifying a product object in one part of the code does not silently alter it elsewhere.
// Both calls now return clones of the same cached instance
$product_a = wc_get_product( 42 );
$product_b = wc_get_product( 42 );
// $product_a and $product_b are separate objects with identical data
$product_a->set_price( 9.99 );
echo $product_b->get_price(); // Still returns the original price
Stores running WooCommerce All Products for Subscriptions or similar extensions that trigger extra product loading see the largest gains. Benchmarks from the WooCommerce team showed INP-sensitive pages dropping request times measurably when the same products were loaded multiple times per cycle.
How It Differs From Redis and Transient Caching
Redis and Memcached operate at the WordPress object cache layer – they persist data across requests and store serialized values in external memory stores. Product object caching in 10.5 sits one level above that. It caches the PHP object after hydration, not the raw data before it.
Think of it as two separate problems. Redis object caching reduces TTFB by eliminating repeated database queries across requests. Product object caching reduces CPU time within a single request by eliminating repeated object construction. A store benefits from running both.
Transients like wc_var_prices_, wc_product_children_, and wc_related_ handle a different concern entirely. These store computed results – variable product price ranges, child product ID lists, related product sets – in the wp_options table or in the persistent cache backend. They expire on a timer or when product data changes at the HPOS level. Product object caching does not replace transients. It complements them by avoiding the cost of rebuilding the product object that reads those transients.
Building a Custom Product Cache for Older Versions
Stores not yet on 10.5 can implement a similar pattern manually. The approach wraps wc_get_product() with an in-memory static cache and returns clones to maintain isolation.
function wrtech_get_cached_product( int $product_id ): ?WC_Product {
static $cache = [];
if ( isset( $cache[ $product_id ] ) ) {
return clone $cache[ $product_id ];
}
$product = wc_get_product( $product_id );
if ( $product instanceof WC_Product ) {
$cache[ $product_id ] = $product;
return clone $product;
}
return null;
}
This works for read-heavy operations like rendering archive pages or generating feeds. Drop it into a plugin structured with OOP patterns rather than functions.php to keep the codebase maintainable. The static variable persists for the lifetime of the PHP process, which means it resets automatically on the next request.
One caveat: if any code modifies a product object and then calls save(), the cached version becomes stale for the remainder of that request. Handle this by unsetting the cache key after a save operation.
function wrtech_invalidate_cached_product( int $product_id ): void {
// Force next retrieval to rebuild from datastore
static $cache = [];
unset( $cache[ $product_id ] );
}
add_action( 'woocommerce_after_product_object_save', function( $product ) {
wrtech_invalidate_cached_product( $product->get_id() );
});
Transient Bloat and the wp_options Problem
Stores with thousands of variable products often see the wp_options table balloon with _transient_wc_var_prices_ and _transient_wc_product_children_ rows. A catalog with 50,000 variations can generate over 100,000 transient rows. Without a persistent object cache backend, every transient lives in wp_options, turning autoload queries into a bottleneck.
The fix starts with enabling Redis or Memcached. Once a persistent backend exists, WooCommerce stores transients there instead of the database. The wp_options table shrinks, autoloaded data drops, and the database indexing overhead decreases proportionally.
WooCommerce 10.5 also improved its transient cleanup tool. The system tools panel at WooCommerce > Status > Tools now clears Product Filters cache data alongside the standard shop and product transients. Running this cleanup after bulk product imports prevents stale price ranges and incorrect stock indicators from persisting on archive pages.
# Clear all WooCommerce transients via WP-CLI
wp transient delete --all
# Or target only expired transients
wp transient delete --expired
Compatibility Concerns With Third-Party Extensions
Extensions that attach data to product instances using WeakMap or custom properties face a specific problem with cloned objects. A clone does not carry WeakMap associations from the original, and there is currently no event fired when the cache returns a clone. Extensions relying on this pattern need to reattach their data after retrieval or switch to storing supplementary information in product meta.
Plugins that call wc_get_product(), modify the returned object, and expect those modifications to persist across the request without calling save() will also break. The cache returns independent clones – mutations on one copy are invisible to the next wc_get_product() call for the same ID. This is the intended behavior and actually prevents a class of subtle bugs where unrelated code paths accidentally share mutable state.
Test extensions by enabling the feature on a staging environment, exercising every product-related workflow, and watching for unexpected price displays, missing custom fields, or schema markup errors in structured data output.
Measuring the Performance Difference
Quantifying the improvement requires profiling at the PHP level rather than relying on page-level metrics alone. Xdebug or Blackfire traces will show the reduction in WC_Product constructor calls and total time spent in WC_Product_Factory::get_product().
A practical before/after test: load a WooCommerce category page with Query Monitor active. Note the number of times wc_get_product() appears in the function log and the cumulative time. Enable product object caching, reload, and compare. Stores with heavy product widget usage or multiple product blocks per page typically see the constructor call count drop by 40-60%.
The per-request savings seem small in isolation – maybe 15-40ms on a well-optimized server. Multiply that across concurrent visitors during a sale event and the aggregate CPU reduction becomes significant. Pair product object caching with asynchronous JavaScript loading and you address both server-side and client-side bottlenecks in the same deployment cycle.
When Not to Cache Product Objects
Highly dynamic storefronts where product data changes mid-request – think real-time auction plugins or stock-aware pricing that recalculates during checkout – should evaluate the feature carefully. The cache returns the state of the product at the moment it was first loaded in that request. If business logic depends on fetching the absolute latest state multiple times within the same execution cycle, the cache can mask updates that happened between those calls.
Full page caching eliminates the need for per-request product caching on anonymous visitor traffic entirely. If Varnish or a CDN serves the category page from its own cache layer, wc_get_product() never runs for that request. Product object caching helps most on logged-in user requests, admin-ajax calls, REST API responses, and any code path that bypasses full page cache.
Често задавани въпроси
-
Does WooCommerce product object caching persist between requests?
No. The cache lives in PHP memory for the duration of a single HTTP request. It resets automatically when the request completes and does not require Redis or Memcached.
-
How do you enable product object caching in WooCommerce 10.5?
Go to WooCommerce > Settings > Advanced > Features and toggle the Cache Product Objects option. The feature is experimental and disabled by default.
-
Will product object caching break third-party WooCommerce plugins?
Plugins that modify product objects without saving or rely on WeakMap associations may behave unexpectedly. Testing on a staging site before enabling in production is strongly recommended.
-
Is product object caching the same as Redis object caching?
No. Redis caches raw data across requests in external memory. Product object caching stores fully hydrated PHP objects within a single request to avoid repeated construction overhead.
-
Can stores on older WooCommerce versions cache product objects?
Yes. A custom wrapper around wc_get_product() using a static variable can replicate the pattern. The article includes a PHP implementation that returns clones from an in-memory array.
Related Articles
If you enjoyed reading this, then please explore our other articles below:
More Articles
If you enjoyed reading this, then please explore our other articles below:




2019-2026 ©