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

Commit

Permalink
Performance: Cache script data in a transient for production builds (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
mikejolley authored Jun 16, 2023
1 parent 2db3544 commit 6b9955d
Showing 1 changed file with 82 additions and 22 deletions.
104 changes: 82 additions & 22 deletions src/Assets/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ class Api {
*/
private $inline_scripts = [];

/**
* Determines if caching is enabled for script data.
*
* @var boolean
*/
private $disable_cache = false;

/**
* Stores loaded script data for the current request
*
* @var array|null
*/
private $script_data = null;

/**
* Reference to the Package instance
*
Expand All @@ -31,7 +45,9 @@ class Api {
* @param Package $package An instance of Package.
*/
public function __construct( Package $package ) {
$this->package = $package;
$this->package = $package;
$this->disable_cache = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) || ! $this->package->feature()->is_production_environment();
add_action( 'shutdown', array( $this, 'update_script_data_cache' ), 20 );
}

/**
Expand Down Expand Up @@ -76,6 +92,44 @@ public function get_block_metadata_path( $block_name, $path = '' ) {
return $path_to_metadata_from_plugin_root;
}

/**
* Initialize and load cached script data from the transient cache.
*
* @return array
*/
private function get_cached_script_data() {
if ( $this->disable_cache ) {
return [];
}

$transient_value = json_decode( (string) get_transient( 'woocommerce_blocks_asset_api_script_data' ), true );

if ( empty( $transient_value ) || empty( $transient_value['script_data'] ) || empty( $transient_value['version'] ) || $transient_value['version'] !== $this->package->get_version() ) {
return [];
}

return (array) ( $transient_value['script_data'] ?? [] );
}

/**
* Store all cached script data in the transient cache.
*/
public function update_script_data_cache() {
if ( is_null( $this->script_data ) || $this->disable_cache ) {
return;
}
set_transient(
'woocommerce_blocks_asset_api_script_data',
wp_json_encode(
array(
'script_data' => $this->script_data,
'version' => $this->package->get_version(),
)
),
DAY_IN_SECONDS * 30
);
}

/**
* Get src, version and dependencies given a script relative src.
*
Expand All @@ -85,31 +139,37 @@ public function get_block_metadata_path( $block_name, $path = '' ) {
* @return array src, version and dependencies of the script.
*/
public function get_script_data( $relative_src, $dependencies = [] ) {
$src = '';
$version = '1';

if ( $relative_src ) {
$src = $this->get_asset_url( $relative_src );
$asset_path = $this->package->get_path(
str_replace( '.js', '.asset.php', $relative_src )
if ( ! $relative_src ) {
return array(
'src' => '',
'version' => '1',
'dependencies' => $dependencies,
);
}

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

return array(
'src' => $src,
'version' => $version,
'dependencies' => $dependencies,
);
if ( empty( $this->script_data[ $relative_src ] ) ) {
$asset_path = $this->package->get_path( str_replace( '.js', '.asset.php', $relative_src ) );
// The following require is safe because we are checking if the file exists and it is not a user input.
// nosemgrep audit.php.lang.security.file.inclusion-arg.
$asset = file_exists( $asset_path ) ? require $asset_path : [];

$this->script_data[ $relative_src ] = array(
'src' => $this->get_asset_url( $relative_src ),
'version' => ! empty( $asset['version'] ) ? $asset['version'] : $this->get_file_version( $relative_src ),
'dependencies' => ! empty( $asset['dependencies'] ) ? $asset['dependencies'] : [],
);
}

// Return asset details as well as the requested dependencies array.
return [
'src' => $this->script_data[ $relative_src ]['src'],
'version' => $this->script_data[ $relative_src ]['version'],
'dependencies' => array_merge( $this->script_data[ $relative_src ]['dependencies'], $dependencies ),
];
}

/**
Expand Down

0 comments on commit 6b9955d

Please sign in to comment.