Commit 169672a256b for woocommerce
commit 169672a256b54c47a20726ab06fe9d40bd4ec321
Author: Vladimir Reznichenko <kalessil@gmail.com>
Date: Tue Sep 22 06:18:34 2026 +0200
[Performance] Enable optimized variations price hashing for new stores by default (#68910)
diff --git a/plugins/woocommerce/changelog/performance-66883-disable-legacy-variable-products-price-hashing b/plugins/woocommerce/changelog/performance-66883-disable-legacy-variable-products-price-hashing
new file mode 100644
index 00000000000..6d73a064deb
--- /dev/null
+++ b/plugins/woocommerce/changelog/performance-66883-disable-legacy-variable-products-price-hashing
@@ -0,0 +1,4 @@
+Significance: minor
+Type: performance
+
+Enabled optimized variations price hashing for new stores by default.
diff --git a/plugins/woocommerce/includes/class-wc-install.php b/plugins/woocommerce/includes/class-wc-install.php
index b72653019d5..d4b6233ce15 100644
--- a/plugins/woocommerce/includes/class-wc-install.php
+++ b/plugins/woocommerce/includes/class-wc-install.php
@@ -362,6 +362,7 @@ class WC_Install {
),
'11.3.0' => array(
'wc_update_1130_repair_hpos_order_dates_from_posts',
+ 'wc_update_1130_set_legacy_variation_price_hash_option',
),
);
@@ -404,6 +405,7 @@ class WC_Install {
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_newly_installed', array( __CLASS__, 'disable_legacy_variation_price_hash_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' ) );
@@ -1376,6 +1378,17 @@ class WC_Install {
$feature_controller->change_feature_enable( ProductCacheController::FEATURE_NAME, true );
}
+ /**
+ * Disable the legacy variations price hash algorithm for new stores.
+ *
+ * @since 11.3.0
+ *
+ * @return void
+ */
+ public static function disable_legacy_variation_price_hash_for_newly_installed(): void {
+ add_option( 'woocommerce_use_legacy_get_variations_price_hash', 'no', '', true );
+ }
+
/**
* Enable email improvements by default for existing shops if conditions are met.
*
diff --git a/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
index 8d11ae54915..c771eda9880 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
@@ -669,13 +669,13 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
);
}
- $filter_names = array( 'woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price' );
+ $filter_names = array( 'woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price' );
+ $use_legacy_algorithm = 'yes' === get_option( 'woocommerce_use_legacy_get_variations_price_hash', 'yes' );
/**
* Filters whether to use the legacy callback serialization algorithm.
*
- * By default, WooCommerce will use the legacy algorithm to get the callback signatures
- * for variation price hash calculation. That algorithm includes the callback array as it
+ * The legacy algorithm for variation price hash calculation includes the callback array as it
* comes from $wp_filter in the hashed data, which is then JSON encoded. For callbacks that
* are class methods, JSON encoding captures the object's PUBLIC property values only;
* private and protected properties are not captured. Note that dynamically created
@@ -690,13 +690,15 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
*
* IMPORTANT: see also the documentation for the 'woocommerce_variation_prices_price' filter.
*
- * @since 10.5.0
+ * @since 10.5.0 the hook is introduced and all stores use the legacy algorith by default.
+ * @since 11.3.0 the new stores use optimized algorithm by default.
*
- * @param bool $use_legacy True to use the legacy algorithm (default), false to use CallbackUtil
- * @param WC_Product $product The product object.
- * @param bool $for_display If taxes should be calculated or not.
+ * @param bool $use_legacy_algorithm True to use the legacy algorithm, false to use CallbackUtil.
+ * @param WC_Product $product The product object.
+ * @param bool $for_display If taxes should be calculated or not.
+ * @return bool
*/
- $use_legacy_algorithm = apply_filters( 'woocommerce_use_legacy_get_variations_price_hash', true, $product, $for_display );
+ $use_legacy_algorithm = (bool) apply_filters( 'woocommerce_use_legacy_get_variations_price_hash', $use_legacy_algorithm, $product, $for_display );
if ( $use_legacy_algorithm ) {
global $wp_filter;
diff --git a/plugins/woocommerce/includes/wc-update-functions.php b/plugins/woocommerce/includes/wc-update-functions.php
index 43b066bd32a..b7d19919e03 100644
--- a/plugins/woocommerce/includes/wc-update-functions.php
+++ b/plugins/woocommerce/includes/wc-update-functions.php
@@ -4171,3 +4171,14 @@ function wc_update_1130_repair_hpos_order_dates_from_posts() {
return false;
}
+
+/**
+ * Persist the legacy variation price hash option for existing stores so get_option returns an explicit value.
+ *
+ * @return void
+ */
+function wc_update_1130_set_legacy_variation_price_hash_option() {
+ if ( false === get_option( 'woocommerce_use_legacy_get_variations_price_hash' ) ) {
+ add_option( 'woocommerce_use_legacy_get_variations_price_hash', 'yes', '', true );
+ }
+}
diff --git a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php
index 31ff1ca3a1d..284d1e6215d 100644
--- a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php
+++ b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare( strict_types = 1 );
use Automattic\WooCommerce\Enums\ProductStatus;
use Automattic\WooCommerce\Enums\ProductStockStatus;
@@ -773,10 +773,25 @@ class WC_Product_Variable_Data_Store_CPT_Test extends WC_Unit_Test_Case {
}
/**
- * @testdox get_price_hash includes callback signatures via CallbackUtil when the legacy algorithm is disabled.
+ * Data provider for falsy values that should disable the legacy algorithm.
*/
- public function test_get_price_hash_uses_callback_util_when_legacy_algorithm_is_disabled(): void {
- add_filter( 'woocommerce_use_legacy_get_variations_price_hash', '__return_false' );
+ public function woocommerce_use_legacy_get_variations_price_hash_disabled_values_provider(): array {
+ return array(
+ 'false' => array( false ),
+ 'zero' => array( 0 ),
+ 'empty_string' => array( '' ),
+ 'empty_array' => array( array() ),
+ );
+ }
+
+ /**
+ * @testdox get_price_hash includes callback signatures via CallbackUtil when the filter returns $falsy_value.
+ * @dataProvider woocommerce_use_legacy_get_variations_price_hash_disabled_values_provider
+ * @param mixed $falsy_value The falsy value to inject via the filter.
+ */
+ public function test_get_price_hash_uses_callback_util_when_legacy_algorithm_is_disabled( $falsy_value ): void {
+ $filter = fn() => $falsy_value;
+ add_filter( 'woocommerce_use_legacy_get_variations_price_hash', $filter );
$product = WC_Helper_Product::create_variation_product();
$extended_data_store = $this->get_data_store_with_public_get_price_hash();
@@ -793,7 +808,7 @@ class WC_Product_Variable_Data_Store_CPT_Test extends WC_Unit_Test_Case {
$this->assertNotSame( $hash_without_callback, $hash_with_callback );
remove_filter( 'woocommerce_variation_prices_price', $callback );
- remove_filter( 'woocommerce_use_legacy_get_variations_price_hash', '__return_false' );
+ remove_filter( 'woocommerce_use_legacy_get_variations_price_hash', $filter );
$product->delete();
}
@@ -2212,4 +2227,36 @@ class WC_Product_Variable_Data_Store_CPT_Test extends WC_Unit_Test_Case {
$this->assertSame( self::$product_id, $logger->errors[0][1]['product_id'] );
$this->assertSame( array(), $result, 'A failed read defers every variation rather than promoting it.' );
}
+
+ /**
+ * @testdox get_price_hash respects the woocommerce_use_legacy_get_variations_price_hash option.
+ */
+ public function test_get_price_hash_respects_legacy_price_hash_option(): void {
+ $product = WC_Helper_Product::create_variation_product();
+
+ $captured = null;
+ $capture_filter = function ( $use_legacy ) use ( &$captured ) {
+ $captured = $use_legacy;
+ return $use_legacy;
+ };
+ add_filter( 'woocommerce_use_legacy_get_variations_price_hash', $capture_filter );
+
+ // Option 'yes' → legacy enabled → filter receives true.
+ update_option( 'woocommerce_use_legacy_get_variations_price_hash', 'yes' );
+ $this->get_data_store_with_public_get_price_hash()->get_price_hash( $product, false );
+ $this->assertTrue( $captured, 'Option "yes" must dispatch true (legacy enabled) into the filter.' );
+
+ // Option 'no' → legacy disabled → filter receives false.
+ update_option( 'woocommerce_use_legacy_get_variations_price_hash', 'no' );
+ $this->get_data_store_with_public_get_price_hash()->get_price_hash( $product, false );
+ $this->assertFalse( $captured, 'Option "no" must dispatch false (legacy disabled) into the filter.' );
+
+ delete_option( 'woocommerce_use_legacy_get_variations_price_hash' );
+ $this->get_data_store_with_public_get_price_hash()->get_price_hash( $product, false );
+ $this->assertTrue( $captured, 'Missing option must dispatch true (legacy enabled) into the filter.' );
+
+ remove_filter( 'woocommerce_use_legacy_get_variations_price_hash', $capture_filter );
+
+ $product->delete();
+ }
}
diff --git a/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php b/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php
index d83e01e5576..3a7c144b8f0 100644
--- a/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php
+++ b/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php
@@ -1024,4 +1024,23 @@ class WC_Update_Functions_Test extends \WC_Unit_Test_Case {
$this->assertSame( array( 'First@Example.com', 'Second@Example.com' ), $emails, 'No row should be rewritten after the write fails' );
}
+
+ /**
+ * @testdox wc_update_1130_set_legacy_variation_price_hash_option sets the option when absent and skips when already set.
+ */
+ public function test_wc_update_1130_set_legacy_variation_price_hash_option(): void {
+ include_once WC_ABSPATH . 'includes/wc-update-functions.php';
+
+ // When the option does not exist, the migration must create it with 'yes'.
+ delete_option( 'woocommerce_use_legacy_get_variations_price_hash' );
+ wc_update_1130_set_legacy_variation_price_hash_option();
+ $this->assertSame( 'yes', get_option( 'woocommerce_use_legacy_get_variations_price_hash' ), 'Migration must set "yes" for existing stores.' );
+
+ // When the option already exists (e.g. new store set to 'no'), the migration must not overwrite it.
+ update_option( 'woocommerce_use_legacy_get_variations_price_hash', 'no' );
+ wc_update_1130_set_legacy_variation_price_hash_option();
+ $this->assertSame( 'no', get_option( 'woocommerce_use_legacy_get_variations_price_hash' ), 'Migration must not overwrite an existing option.' );
+
+ delete_option( 'woocommerce_use_legacy_get_variations_price_hash' );
+ }
}