Commit ba34ef7fa28 for woocommerce
commit ba34ef7fa28ac0047935969040d131430ffdbaf2
Author: Vladimir Reznichenko <kalessil@gmail.com>
Date: Mon Oct 12 14:44:35 2026 +0200
[Performance] Trim the number of file_exists calls during blocks initalization for better performance (#69593)
Eliminates file_exists calls aiming block manifests in Woo code during block initialization, letting WordPress core APIs handle the necessary checks. PHP caches stats internally, but overhead remains; execution time will now be clearly attributed to WordPress APIs when it decides to handle the provided block manifests, so we can more easily navigate the flame graph and benefit from future upstream optimizations.
diff --git a/plugins/woocommerce/changelog/performance-69369-blocks-registration-fewer-file-exists b/plugins/woocommerce/changelog/performance-69369-blocks-registration-fewer-file-exists
new file mode 100644
index 00000000000..5a38e166f13
--- /dev/null
+++ b/plugins/woocommerce/changelog/performance-69369-blocks-registration-fewer-file-exists
@@ -0,0 +1,4 @@
+Significance: patch
+Type: performance
+
+Trimmed the number of file_exists calls during blocks initialization for better performance.
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index db0cb0ea4f1..49c0a8ab8ca 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -48657,12 +48657,6 @@ parameters:
count: 1
path: src/Blocks/BlockTypes/AbstractBlock.php
- -
- message: '#^Parameter \#1 \$file_or_folder of function register_block_type_from_metadata expects string, string\|true given\.$#'
- identifier: argument.type
- count: 1
- path: src/Blocks/BlockTypes/AbstractBlock.php
-
-
message: '#^Parameter \#1 \$handle of function wp_enqueue_script expects string, array\|string\|null given\.$#'
identifier: argument.type
@@ -48723,12 +48717,6 @@ parameters:
count: 1
path: src/Blocks/BlockTypes/AbstractInnerBlock.php
- -
- message: '#^Parameter \#1 \$file_or_folder of function register_block_type_from_metadata expects string, bool\|string given\.$#'
- identifier: argument.type
- count: 1
- path: src/Blocks/BlockTypes/AbstractInnerBlock.php
-
-
message: '#^Parameter \#2 \$args of function register_block_type_from_metadata expects array\{api_version\?\: string, title\?\: string, category\?\: string\|null, parent\?\: array\<string\>\|null, ancestor\?\: array\<string\>\|null, allowed_blocks\?\: array\<string\>\|null, icon\?\: string\|null, description\?\: string, \.\.\.\}, array\{render_callback\: mixed, editor_style\: string\|null, style\: null, api_version\?\: int\} given\.$#'
identifier: argument.type
diff --git a/plugins/woocommerce/src/Blocks/Assets/Api.php b/plugins/woocommerce/src/Blocks/Assets/Api.php
index c87f6b746a9..c52e28a66cf 100644
--- a/plugins/woocommerce/src/Blocks/Assets/Api.php
+++ b/plugins/woocommerce/src/Blocks/Assets/Api.php
@@ -120,19 +120,37 @@ class Api {
/**
* Get the path to a block's metadata
*
+ * @deprecated 11.3.0 Use ::generate_block_metadata_path() instead.
+ *
* @param string $block_name The block to get metadata for.
* @param string $path Optional. The path to the metadata file inside the 'assets/client/blocks' folder.
*
* @return string|boolean False if metadata file is not found for the block.
*/
public function get_block_metadata_path( $block_name, $path = '' ) {
- $path_to_metadata_from_plugin_root = $this->package->get_path( 'assets/client/blocks/' . $path . $block_name . '/block.json' );
+ wc_deprecated_function( __METHOD__, '11.3.0', __CLASS__ . '::generate_block_metadata_path()' );
+
+ $path_to_metadata_from_plugin_root = $this->generate_block_metadata_path( $block_name, $path );
if ( ! file_exists( $path_to_metadata_from_plugin_root ) ) {
return false;
}
return $path_to_metadata_from_plugin_root;
}
+ /**
+ * Generate the path to a block's metadata.
+ *
+ * @since 11.3.0
+ *
+ * @param string $block_name The block to get metadata for.
+ * @param string $path Optional. The path to the metadata file inside the 'assets/client/blocks' folder.
+ *
+ * @return string
+ */
+ public function generate_block_metadata_path( $block_name, $path = '' ) {
+ return $this->package->get_path( 'assets/client/blocks/' . $path . $block_name . '/block.json' );
+ }
+
/**
* Generates a hash containing the site url, plugin version and package path.
*
diff --git a/plugins/woocommerce/src/Blocks/BlockPatterns.php b/plugins/woocommerce/src/Blocks/BlockPatterns.php
index 2acfea83826..06c84227ce9 100644
--- a/plugins/woocommerce/src/Blocks/BlockPatterns.php
+++ b/plugins/woocommerce/src/Blocks/BlockPatterns.php
@@ -94,28 +94,18 @@ class BlockPatterns {
return;
}
- $patterns = $this->get_block_patterns();
- foreach ( $patterns as $pattern ) {
+ foreach ( $this->get_block_patterns() as $pattern ) {
// The pattern list can come from a stale or malformed site transient, so make sure the source is a
// usable string before dereferencing it, to avoid a PHP warning when building the path.
if ( empty( $pattern['source'] ) || ! is_string( $pattern['source'] ) ) {
continue;
}
- $pattern_path = $this->patterns_path . '/' . $pattern['source'];
-
- // The pattern list can come from a stale cache, so confirm the file
- // still exists before registering it with a `filePath`. Without
- // inline content, core would otherwise emit an undefined-content
- // warning when it tries to load a missing file on demand. This
- // mirrors core's own `_register_theme_block_patterns()` guard.
- if ( ! file_exists( $pattern_path ) ) {
- continue;
- }
-
+ $pattern_path = $this->patterns_path . '/' . $pattern['source'];
$pattern['source'] = $pattern_path;
$pattern['filePath'] = $pattern_path;
+ // Performance note: file_exists removed — transient is version-keyed, so it re-scans on upgrade/downgrade.
$this->pattern_registry->register_block_pattern( $pattern_path, $pattern );
}
}
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AbstractBlock.php b/plugins/woocommerce/src/Blocks/BlockTypes/AbstractBlock.php
index 082bc3a0fe0..279705aee62 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AbstractBlock.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AbstractBlock.php
@@ -319,14 +319,12 @@ abstract class AbstractBlock {
$block_settings['api_version'] = intval( $this->api_version );
}
- $metadata_path = $this->asset_api->get_block_metadata_path( $this->block_name );
-
// Prefer to register with metadata if the path is set in the block's class.
- if ( ! empty( $metadata_path ) ) {
- register_block_type_from_metadata(
- $metadata_path,
- $block_settings
- );
+ $is_registered = (bool) register_block_type_from_metadata(
+ $this->asset_api->generate_block_metadata_path( $this->block_name ),
+ $block_settings
+ );
+ if ( $is_registered ) {
return;
}
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AbstractInnerBlock.php b/plugins/woocommerce/src/Blocks/BlockTypes/AbstractInnerBlock.php
index e39f3815bbf..48d20bd8871 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AbstractInnerBlock.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AbstractInnerBlock.php
@@ -30,10 +30,9 @@ abstract class AbstractInnerBlock extends AbstractBlock {
$block_settings['api_version'] = intval( $this->api_version );
}
- $metadata_path = $this->asset_api->get_block_metadata_path( $this->block_name, 'inner-blocks/' );
// Prefer to register with metadata if the path is set in the block's class.
register_block_type_from_metadata(
- $metadata_path,
+ $this->asset_api->generate_block_metadata_path( $this->block_name, 'inner-blocks/' ),
$block_settings
);
}
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockPatterns/BlockPatterns.php b/plugins/woocommerce/tests/php/src/Blocks/BlockPatterns/BlockPatterns.php
index b52d54c7238..0d6922e7b3a 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockPatterns/BlockPatterns.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockPatterns/BlockPatterns.php
@@ -139,31 +139,6 @@ class BlockPatterns extends \WP_UnitTestCase {
$this->block_patterns->register_block_patterns();
}
- /**
- * Tests that a cached pattern whose source file no longer exists on disk is
- * skipped, so core is never asked to lazily load a missing file.
- */
- public function test_cached_pattern_with_missing_file_is_skipped() {
- $pattern_data = array(
- 'version' => WOOCOMMERCE_VERSION,
- 'patterns' => array(
- array(
- 'title' => 'Mock Missing',
- 'source' => 'does-not-exist.php',
- 'content' => '',
- ),
- ),
- );
-
- set_site_transient( 'woocommerce_blocks_patterns', $pattern_data );
-
- $this->pattern_registry
- ->expects( $this->never() )
- ->method( 'register_block_pattern' );
-
- $this->block_patterns->register_block_patterns();
- }
-
/**
* Tests that a cached pattern with a missing or invalid source is skipped, so the source is never
* dereferenced (which would raise a PHP warning) when building the pattern path.