Commit ebba561f1de for woocommerce
commit ebba561f1de36d4e4000236d010f0ce834535b13
Author: Vladimir Reznichenko <kalessil@gmail.com>
Date: Mon Oct 26 10:14:48 2026 +0100
[Performance] Micro-optimizations package to compress woo core execution time (final batch) (#70220)
diff --git a/plugins/woocommerce/changelog/performance-woo-startup-hotpath-micro-optimiations-p3 b/plugins/woocommerce/changelog/performance-woo-startup-hotpath-micro-optimiations-p3
new file mode 100644
index 00000000000..fc532606b13
--- /dev/null
+++ b/plugins/woocommerce/changelog/performance-woo-startup-hotpath-micro-optimiations-p3
@@ -0,0 +1,4 @@
+Significance: minor
+Type: performance
+
+Micro-optimizations package to compress Woo core initialization execution time (final batch).
diff --git a/plugins/woocommerce/includes/class-wc-cache-helper.php b/plugins/woocommerce/includes/class-wc-cache-helper.php
index 6e7b84c307b..20f481e34e7 100644
--- a/plugins/woocommerce/includes/class-wc-cache-helper.php
+++ b/plugins/woocommerce/includes/class-wc-cache-helper.php
@@ -53,11 +53,11 @@ class WC_Cache_Helper {
// Optimization note: this is the earliest stage at which we can bulk-prime pages data. As a result, we
// prime data for all pages used in is_page checks across the core, even if only a subset is required here.
- $pages = array( 'cart', 'checkout', 'coming_soon', 'myaccount', 'shop', 'terms' );
+ $pages = array( 'cart', 'checkout', 'coming_soon', 'myaccount', 'refund_returns', 'shop', 'terms' );
$woocommerce_page_options = array_map( static fn ( string $page ) => sprintf( 'woocommerce_%s_page_id', $page ), $pages );
wp_prime_option_caches( $woocommerce_page_options );
$woocommerce_page_ids = array_combine( $pages, array_map( static fn ( string $page ) => wc_get_page_id( $page ), $pages ) );
- _prime_post_caches( array_values( array_filter( $woocommerce_page_ids ) ), false, false );
+ _prime_post_caches( array_values( array_filter( $woocommerce_page_ids, static fn( $page_id ) => $page_id > 0 ) ), false, false );
$page_ids = array_filter( array( $woocommerce_page_ids['cart'], $woocommerce_page_ids['checkout'], $woocommerce_page_ids['myaccount'] ) );
if ( ! is_page( $page_ids ) ) {
diff --git a/plugins/woocommerce/includes/class-wc-query.php b/plugins/woocommerce/includes/class-wc-query.php
index acea738d142..262c680c3d4 100644
--- a/plugins/woocommerce/includes/class-wc-query.php
+++ b/plugins/woocommerce/includes/class-wc-query.php
@@ -608,6 +608,15 @@ class WC_Query {
* @param WP_Query $q Query instance.
*/
public function product_query( $q ) {
+ // Prime caches to reduce future queries.
+ wp_prime_option_caches(
+ array(
+ 'woocommerce_default_catalog_orderby',
+ 'woocommerce_catalog_columns',
+ 'woocommerce_catalog_rows',
+ )
+ );
+
if ( ! is_feed() ) {
$ordering = $this->get_catalog_ordering_args();
$q->set( 'orderby', $ordering['orderby'] );
diff --git a/plugins/woocommerce/includes/wc-product-functions.php b/plugins/woocommerce/includes/wc-product-functions.php
index 2a75cd65db9..a910f5f5b60 100644
--- a/plugins/woocommerce/includes/wc-product-functions.php
+++ b/plugins/woocommerce/includes/wc-product-functions.php
@@ -1473,7 +1473,6 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array(
// We want to query related posts if they are not cached, or we don't have enough.
if ( false === $related_posts || count( $related_posts ) < $limit ) {
-
$cats_array = apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $product_id ) ? apply_filters( 'woocommerce_get_related_product_cat_terms', wc_get_product_term_ids( $product_id, 'product_cat' ), $product_id ) : array();
$tags_array = apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $product_id ) ? apply_filters( 'woocommerce_get_related_product_tag_terms', wc_get_product_term_ids( $product_id, 'product_tag' ), $product_id ) : array();
@@ -1485,13 +1484,16 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array(
$related_posts = $data_store->get_related_products( $cats_array, $tags_array, $exclude_ids, $limit + 10, $product_id );
}
+ $original_transient = $transient;
if ( $transient && is_array( $transient ) ) {
$transient[ $query_args ] = $related_posts;
} else {
$transient = array( $query_args => $related_posts );
}
- set_transient( $transient_name, $transient, DAY_IN_SECONDS );
+ if ( $original_transient !== $transient ) {
+ set_transient( $transient_name, $transient, DAY_IN_SECONDS );
+ }
}
$related_posts = apply_filters(
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 1a9f9d68b36..c224f21eb28 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -47001,12 +47001,6 @@ parameters:
count: 1
path: src/Blocks/Assets/Api.php
- -
- message: '#^Method Automattic\\WooCommerce\\Blocks\\Assets\\Api\:\:get_file_version\(\) should return string but returns int\<0, max\>\|false\.$#'
- identifier: return.type
- count: 1
- path: src/Blocks/Assets/Api.php
-
-
message: '#^Method Automattic\\WooCommerce\\Blocks\\Assets\\Api\:\:register_script\(\) has no return type specified\.$#'
identifier: missingType.return
diff --git a/plugins/woocommerce/src/Blocks/Assets/Api.php b/plugins/woocommerce/src/Blocks/Assets/Api.php
index 435d5930e7b..2735221fc30 100644
--- a/plugins/woocommerce/src/Blocks/Assets/Api.php
+++ b/plugins/woocommerce/src/Blocks/Assets/Api.php
@@ -99,8 +99,9 @@ class Api {
* @return string The cache buster value to use for the given file.
*/
protected function get_file_version( $file ) {
- if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG && file_exists( $this->package->get_path() . $file ) ) {
- return filemtime( $this->package->get_path( trim( $file, '/' ) ) );
+ if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
+ $mtime = @filemtime( $this->package->get_path( $file ) ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- performance optimization
+ return $mtime ? (string) $mtime : $this->wc_version;
}
return $this->wc_version;
}
diff --git a/plugins/woocommerce/src/Blocks/AssetsController.php b/plugins/woocommerce/src/Blocks/AssetsController.php
index f2c1e2bd811..1b8823b704d 100644
--- a/plugins/woocommerce/src/Blocks/AssetsController.php
+++ b/plugins/woocommerce/src/Blocks/AssetsController.php
@@ -550,7 +550,7 @@ final class AssetsController {
foreach ( $wp_scripts->registered as $handle => $script ) {
// scripts that are loaded in the footer has extra->group = 1.
- if ( array_intersect( $known_packages, $script->deps ) && ! isset( $script->extra['group'] ) ) {
+ if ( ! isset( $script->extra['group'] ) && ! empty( $script->deps ) && array_intersect( $known_packages, $script->deps ) ) {
// Append the script to footer.
$wp_scripts->add_data( $handle, 'group', 1 );
// Show a warning.
@@ -564,7 +564,6 @@ final class AssetsController {
$error_handle,
sprintf( 'console.warn( "%s" );', $error_message )
);
-
}
}
}
diff --git a/plugins/woocommerce/src/Blocks/BlockTemplatesRegistry.php b/plugins/woocommerce/src/Blocks/BlockTemplatesRegistry.php
index 2a289e44aaf..9e218a9e20a 100644
--- a/plugins/woocommerce/src/Blocks/BlockTemplatesRegistry.php
+++ b/plugins/woocommerce/src/Blocks/BlockTemplatesRegistry.php
@@ -114,18 +114,18 @@ class BlockTemplatesRegistry {
);
if ( wp_is_block_theme() ) {
$product_types = wc_get_product_types();
- if ( count( $product_types ) > 0 ) {
+ if ( ! empty( $product_types ) ) {
add_filter( 'default_wp_template_part_areas', array( $this, 'register_add_to_cart_with_options_template_part_area' ), 10, 1 );
- if ( array_key_exists( ProductType::SIMPLE, $product_types ) ) {
+ if ( isset( $product_types[ ProductType::SIMPLE ] ) ) {
$template_parts[ SimpleProductAddToCartWithOptionsTemplate::SLUG ] = new SimpleProductAddToCartWithOptionsTemplate();
}
- if ( array_key_exists( ProductType::EXTERNAL, $product_types ) ) {
+ if ( isset( $product_types[ ProductType::EXTERNAL ] ) ) {
$template_parts[ ExternalProductAddToCartWithOptionsTemplate::SLUG ] = new ExternalProductAddToCartWithOptionsTemplate();
}
- if ( array_key_exists( ProductType::VARIABLE, $product_types ) ) {
+ if ( isset( $product_types[ ProductType::VARIABLE ] ) ) {
$template_parts[ VariableProductAddToCartWithOptionsTemplate::SLUG ] = new VariableProductAddToCartWithOptionsTemplate();
}
- if ( array_key_exists( ProductType::GROUPED, $product_types ) ) {
+ if ( isset( $product_types[ ProductType::GROUPED ] ) ) {
$template_parts[ GroupedProductAddToCartWithOptionsTemplate::SLUG ] = new GroupedProductAddToCartWithOptionsTemplate();
}
}
diff --git a/plugins/woocommerce/src/Blocks/DependencyDetection.php b/plugins/woocommerce/src/Blocks/DependencyDetection.php
index 324731ace2c..ffbe7f6c8fa 100644
--- a/plugins/woocommerce/src/Blocks/DependencyDetection.php
+++ b/plugins/woocommerce/src/Blocks/DependencyDetection.php
@@ -232,18 +232,16 @@ final class DependencyDetection {
*/
private function is_woocommerce_script( string $url ): bool {
// Get the WooCommerce plugin URL (accounts for custom plugin directories).
- $wc_plugin_url = \plugins_url( '/', WC_PLUGIN_FILE );
+ static $wc_plugin_url = null;
+ $wc_plugin_url = $wc_plugin_url ?? \plugins_url( '/', WC_PLUGIN_FILE );
// Check if the URL starts with the WooCommerce plugin URL and is in a known subdirectory.
if ( strpos( $url, $wc_plugin_url ) !== 0 ) {
return false;
}
- // Get the path after the WooCommerce plugin URL.
- $relative_path = substr( $url, strlen( $wc_plugin_url ) );
-
- // Check if it's in one of the known WooCommerce asset directories.
- return (bool) preg_match( '#^(client|assets|build|vendor)/#', $relative_path );
+ // Check if it's in one of the known WooCommerce asset directories (requires normalization to relative path).
+ return (bool) preg_match( '#^(client|assets|build|vendor)/#', substr( $url, strlen( $wc_plugin_url ) ) );
}
/**
diff --git a/plugins/woocommerce/src/Blocks/Payments/Api.php b/plugins/woocommerce/src/Blocks/Payments/Api.php
index fa7da034bc4..478d5ff06e0 100644
--- a/plugins/woocommerce/src/Blocks/Payments/Api.php
+++ b/plugins/woocommerce/src/Blocks/Payments/Api.php
@@ -130,25 +130,19 @@ class Api {
// Preload option caches to minimize future queries for options that do not yet exist or are not set to autoload.
wp_prime_option_caches(
array(
- 'woocommerce_cheque_settings',
- 'woocommerce_paypal_settings',
+ 'woocommerce_bacs_settings',
'woocommerce_bacs_accounts',
+ 'woocommerce_cheque_settings',
'woocommerce_cod_settings',
+ 'woocommerce_paypal_settings',
)
);
- $payment_method_registry->register(
- Package::container()->get( Cheque::class )
- );
- $payment_method_registry->register(
- Package::container()->get( PayPal::class )
- );
- $payment_method_registry->register(
- Package::container()->get( BankTransfer::class )
- );
- $payment_method_registry->register(
- Package::container()->get( CashOnDelivery::class )
- );
+ $container = Package::container();
+ $payment_method_registry->register( $container->get( Cheque::class ) );
+ $payment_method_registry->register( $container->get( PayPal::class ) );
+ $payment_method_registry->register( $container->get( BankTransfer::class ) );
+ $payment_method_registry->register( $container->get( CashOnDelivery::class ) );
}
/**
diff --git a/plugins/woocommerce/src/Blocks/Utils/BlockTemplateUtils.php b/plugins/woocommerce/src/Blocks/Utils/BlockTemplateUtils.php
index 8308faffda3..74e7eeed7e6 100644
--- a/plugins/woocommerce/src/Blocks/Utils/BlockTemplateUtils.php
+++ b/plugins/woocommerce/src/Blocks/Utils/BlockTemplateUtils.php
@@ -187,17 +187,12 @@ class BlockTemplateUtils {
$template->origin = 'plugin';
}
- /*
- * Run the block hooks algorithm introduced in WP 6.4 on the template content.
- */
- if ( function_exists( 'inject_ignored_hooked_blocks_metadata_attributes' ) ) {
- $hooked_blocks = get_hooked_blocks();
- if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
- $before_block_visitor = make_before_block_visitor( $hooked_blocks, $template );
- $after_block_visitor = make_after_block_visitor( $hooked_blocks, $template );
- $blocks = parse_blocks( $template->content );
- $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
- }
+ $hooked_blocks = get_hooked_blocks();
+ if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
+ $before_block_visitor = make_before_block_visitor( $hooked_blocks, $template );
+ $after_block_visitor = make_after_block_visitor( $hooked_blocks, $template );
+ $blocks = parse_blocks( $template->content );
+ $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
}
return $template;
@@ -752,11 +747,11 @@ class BlockTemplateUtils {
$saved_templates = $request_level_cache[ $template_type ][ $theme ];
if ( ! empty( $saved_templates ) && is_array( $slugs ) && array() !== $slugs ) {
$slugs = array_map( 'sanitize_title', $slugs );
- $saved_templates = array_filter( $saved_templates, fn( $template ) => in_array( $template->post_name, $slugs, true ) );
+ $saved_templates = array_filter( $saved_templates, static fn( $template ) => in_array( $template->post_name, $slugs, true ) );
}
if ( ! empty( $saved_templates ) ) {
- $block_templates = array_map( fn( $template ) => self::build_template_result_from_post( $template ), $saved_templates );
- $block_templates = array_values( array_filter( $block_templates, fn( $template ) => $template instanceof \WP_Block_Template ) );
+ $block_templates = array_map( static fn( $template ) => self::build_template_result_from_post( $template ), $saved_templates );
+ $block_templates = array_values( array_filter( $block_templates, static fn( $template ) => $template instanceof \WP_Block_Template ) );
return $block_templates;
}
diff --git a/plugins/woocommerce/src/Packages.php b/plugins/woocommerce/src/Packages.php
index 7f65303265d..51f0dce790e 100644
--- a/plugins/woocommerce/src/Packages.php
+++ b/plugins/woocommerce/src/Packages.php
@@ -125,6 +125,14 @@ class Packages {
protected static function get_enabled_packages() {
$enabled_packages = array();
+ // Preload option caches to minimize future queries for options that do not yet exist or are not set to autoload.
+ wp_prime_option_caches(
+ array_map(
+ static fn( $name ) => 'wc_feature_' . str_replace( '-', '_', $name ) . '_enabled',
+ array_keys( self::$merged_packages )
+ )
+ );
+
foreach ( self::$merged_packages as $merged_package_name => $package_class ) {
$option = 'wc_feature_' . str_replace( '-', '_', $merged_package_name ) . '_enabled';
diff --git a/plugins/woocommerce/src/Utilities/FeaturesUtil.php b/plugins/woocommerce/src/Utilities/FeaturesUtil.php
index f8a10ccb3b5..6a8a11590b2 100644
--- a/plugins/woocommerce/src/Utilities/FeaturesUtil.php
+++ b/plugins/woocommerce/src/Utilities/FeaturesUtil.php
@@ -39,39 +39,21 @@ class FeaturesUtil {
* @return bool True if the feature is enabled, false if not or if the feature doesn't exist.
*/
public static function feature_is_enabled( string $feature_id ): bool {
- $features_controller = wc_get_container()->get( FeaturesController::class );
- $feature = $features_controller->get_feature_definition( $feature_id );
+ static $features_controller = null;
+ $features_controller = $features_controller ?? wc_get_container()->get( FeaturesController::class );
- // Log deprecation notice for deprecated features (only from the public API).
- if ( ! empty( $feature['deprecated_since'] ) ) {
- self::log_deprecated_feature_usage( $feature_id, $feature['deprecated_since'] );
+ static $checked_deprecations = array();
+ if ( ! isset( $checked_deprecations[ $feature_id ] ) ) {
+ $checked_deprecations[ $feature_id ] = true;
+ $definition = $features_controller->get_feature_definition( $feature_id );
+ if ( ! empty( $definition['deprecated_since'] ) ) {
+ wc_deprecated_function( "FeaturesUtil::feature_is_enabled('{$feature_id}')", $definition['deprecated_since'] );
+ }
}
return $features_controller->feature_is_enabled( $feature_id );
}
- /**
- * Log usage of a deprecated feature.
- *
- * This method ensures logging only happens once per request to avoid spam.
- *
- * @param string $feature_id The feature id being checked.
- * @param string $deprecated_since The version since which the feature is deprecated.
- */
- private static function log_deprecated_feature_usage( string $feature_id, string $deprecated_since ): void {
- static $logged = array();
-
- if ( isset( $logged[ $feature_id ] ) ) {
- return;
- }
- $logged[ $feature_id ] = true;
-
- wc_deprecated_function(
- "FeaturesUtil::feature_is_enabled('{$feature_id}')",
- $deprecated_since
- );
- }
-
/**
* Declare (in)compatibility with a given feature for a given plugin.
*