Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.

Commit 6b9955d

Browse files
authored
Performance: Cache script data in a transient for production builds (#9120)
* Cache script data in a transient for production builds * Store as json * Disable cache when script debug is on * Disable in test environment * Simplify caching structure * Put back cache buster * Fix update_script_data_cache * Merge dependencies param always—do not cache it
1 parent 2db3544 commit 6b9955d

File tree

1 file changed

+82
-22
lines changed

1 file changed

+82
-22
lines changed

src/Assets/Api.php

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@ class Api {
1818
*/
1919
private $inline_scripts = [];
2020

21+
/**
22+
* Determines if caching is enabled for script data.
23+
*
24+
* @var boolean
25+
*/
26+
private $disable_cache = false;
27+
28+
/**
29+
* Stores loaded script data for the current request
30+
*
31+
* @var array|null
32+
*/
33+
private $script_data = null;
34+
2135
/**
2236
* Reference to the Package instance
2337
*
@@ -31,7 +45,9 @@ class Api {
3145
* @param Package $package An instance of Package.
3246
*/
3347
public function __construct( Package $package ) {
34-
$this->package = $package;
48+
$this->package = $package;
49+
$this->disable_cache = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) || ! $this->package->feature()->is_production_environment();
50+
add_action( 'shutdown', array( $this, 'update_script_data_cache' ), 20 );
3551
}
3652

3753
/**
@@ -76,6 +92,44 @@ public function get_block_metadata_path( $block_name, $path = '' ) {
7692
return $path_to_metadata_from_plugin_root;
7793
}
7894

95+
/**
96+
* Initialize and load cached script data from the transient cache.
97+
*
98+
* @return array
99+
*/
100+
private function get_cached_script_data() {
101+
if ( $this->disable_cache ) {
102+
return [];
103+
}
104+
105+
$transient_value = json_decode( (string) get_transient( 'woocommerce_blocks_asset_api_script_data' ), true );
106+
107+
if ( empty( $transient_value ) || empty( $transient_value['script_data'] ) || empty( $transient_value['version'] ) || $transient_value['version'] !== $this->package->get_version() ) {
108+
return [];
109+
}
110+
111+
return (array) ( $transient_value['script_data'] ?? [] );
112+
}
113+
114+
/**
115+
* Store all cached script data in the transient cache.
116+
*/
117+
public function update_script_data_cache() {
118+
if ( is_null( $this->script_data ) || $this->disable_cache ) {
119+
return;
120+
}
121+
set_transient(
122+
'woocommerce_blocks_asset_api_script_data',
123+
wp_json_encode(
124+
array(
125+
'script_data' => $this->script_data,
126+
'version' => $this->package->get_version(),
127+
)
128+
),
129+
DAY_IN_SECONDS * 30
130+
);
131+
}
132+
79133
/**
80134
* Get src, version and dependencies given a script relative src.
81135
*
@@ -85,31 +139,37 @@ public function get_block_metadata_path( $block_name, $path = '' ) {
85139
* @return array src, version and dependencies of the script.
86140
*/
87141
public function get_script_data( $relative_src, $dependencies = [] ) {
88-
$src = '';
89-
$version = '1';
90-
91-
if ( $relative_src ) {
92-
$src = $this->get_asset_url( $relative_src );
93-
$asset_path = $this->package->get_path(
94-
str_replace( '.js', '.asset.php', $relative_src )
142+
if ( ! $relative_src ) {
143+
return array(
144+
'src' => '',
145+
'version' => '1',
146+
'dependencies' => $dependencies,
95147
);
148+
}
96149

97-
if ( file_exists( $asset_path ) ) {
98-
// The following require is safe because we are checking if the file exists and it is not a user input.
99-
// nosemgrep audit.php.lang.security.file.inclusion-arg.
100-
$asset = require $asset_path;
101-
$dependencies = isset( $asset['dependencies'] ) ? array_merge( $asset['dependencies'], $dependencies ) : $dependencies;
102-
$version = ! empty( $asset['version'] ) ? $asset['version'] : $this->get_file_version( $relative_src );
103-
} else {
104-
$version = $this->get_file_version( $relative_src );
105-
}
150+
if ( is_null( $this->script_data ) ) {
151+
$this->script_data = $this->get_cached_script_data();
106152
}
107153

108-
return array(
109-
'src' => $src,
110-
'version' => $version,
111-
'dependencies' => $dependencies,
112-
);
154+
if ( empty( $this->script_data[ $relative_src ] ) ) {
155+
$asset_path = $this->package->get_path( str_replace( '.js', '.asset.php', $relative_src ) );
156+
// The following require is safe because we are checking if the file exists and it is not a user input.
157+
// nosemgrep audit.php.lang.security.file.inclusion-arg.
158+
$asset = file_exists( $asset_path ) ? require $asset_path : [];
159+
160+
$this->script_data[ $relative_src ] = array(
161+
'src' => $this->get_asset_url( $relative_src ),
162+
'version' => ! empty( $asset['version'] ) ? $asset['version'] : $this->get_file_version( $relative_src ),
163+
'dependencies' => ! empty( $asset['dependencies'] ) ? $asset['dependencies'] : [],
164+
);
165+
}
166+
167+
// Return asset details as well as the requested dependencies array.
168+
return [
169+
'src' => $this->script_data[ $relative_src ]['src'],
170+
'version' => $this->script_data[ $relative_src ]['version'],
171+
'dependencies' => array_merge( $this->script_data[ $relative_src ]['dependencies'], $dependencies ),
172+
];
113173
}
114174

115175
/**

0 commit comments

Comments
 (0)