Skip to content

Commit

Permalink
Script Loader: Refactor Etag generation for concatenated assets.
Browse files Browse the repository at this point in the history
Move Etag HTTP header generation in `load-scripts.php` and `load-styles.php` to `WP_Dependencies`.

Introduces the method `WP_Dependencies::get_etag()` and associated unit tests.

Follow up to [57943].

Props vrajadas, martinkrcho, mukesh27.
Fixes #61485.


git-svn-id: https://develop.svn.wordpress.org/trunk@58935 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
peterwilsoncc committed Aug 25, 2024
1 parent 24e8775 commit a3520d2
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 36 deletions.
19 changes: 1 addition & 18 deletions src/wp-admin/load-scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,7 @@
wp_default_packages_vendor( $wp_scripts );
wp_default_packages_scripts( $wp_scripts );

$etag = "WP:{$wp_version};";

foreach ( $load as $handle ) {
if ( ! array_key_exists( $handle, $wp_scripts->registered ) ) {
continue;
}

$ver = $wp_scripts->registered[ $handle ]->ver ? $wp_scripts->registered[ $handle ]->ver : $wp_version;
$etag .= "{$handle}:{$ver};";
}

/*
* This is not intended to be cryptographically secure, just a fast way to get
* a fixed length string based on the script versions. As this file does not
* load the full WordPress environment, it is not possible to use the salted
* wp_hash() function.
*/
$etag = 'W/"' . md5( $etag ) . '"';
$etag = $wp_scripts->get_etag( $load );

if ( isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) && stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) === $etag ) {
header( "$protocol 304 Not Modified" );
Expand Down
19 changes: 1 addition & 18 deletions src/wp-admin/load-styles.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,7 @@
$wp_styles = new WP_Styles();
wp_default_styles( $wp_styles );

$etag = "WP:{$wp_version};";

foreach ( $load as $handle ) {
if ( ! array_key_exists( $handle, $wp_styles->registered ) ) {
continue;
}

$ver = $wp_styles->registered[ $handle ]->ver ? $wp_styles->registered[ $handle ]->ver : $wp_version;
$etag .= "{$handle}:{$ver};";
}

/*
* This is not intended to be cryptographically secure, just a fast way to get
* a fixed length string based on the script versions. As this file does not
* load the full WordPress environment, it is not possible to use the salted
* wp_hash() function.
*/
$etag = 'W/"' . md5( $etag ) . '"';
$etag = $wp_styles->get_etag( $wp_version, $load );

if ( isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) && stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) === $etag ) {
header( "$protocol 304 Not Modified" );
Expand Down
38 changes: 38 additions & 0 deletions src/wp-includes/class-wp-dependencies.php
Original file line number Diff line number Diff line change
Expand Up @@ -490,4 +490,42 @@ public function set_group( $handle, $recursion, $group ) {

return true;
}

/**
* Get etag header for cache validation.
*
* @since 6.7.0
*
* @global string $wp_version The WordPress version string.
*
* @param string[] $load Array of script or style handles to load.
* @return string Etag header.
*/
public function get_etag( $load ) {
/*
* Note: wp_get_wp_version() is not used here, as this file can be included
* via wp-admin/load-scripts.php or wp-admin/load-styles.php, in which case
* wp-includes/functions.php is not loaded.
*/
global $wp_version;

$etag = "WP:{$wp_version};";

foreach ( $load as $handle ) {
if ( ! array_key_exists( $handle, $this->registered ) ) {
continue;
}

$ver = $this->registered[ $handle ]->ver ?? $wp_version;
$etag .= "{$handle}:{$ver};";
}

/*
* This is not intended to be cryptographically secure, just a fast way to get
* a fixed length string based on the script versions. As this file does not
* load the full WordPress environment, it is not possible to use the salted
* wp_hash() function.
*/
return 'W/"' . md5( $etag ) . '"';
}
}
90 changes: 90 additions & 0 deletions tests/phpunit/tests/dependencies.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,94 @@ public function test_enqueue_before_register() {

$this->assertContains( 'one', $dep->queue );
}

/**
* Data provider for test_get_etag.
*
* @return array[]
*/
public function data_provider_get_etag() {
return array(
'should accept one dependency' => array(
'load' => array(
'abcd' => '1.0.2',
),
'hash_source_string' => 'WP:6.7;abcd:1.0.2;',
'expected' => 'W/"8145d7e3c41d5a9cc2bccba4afa861fc"',
),
'should accept empty array of dependencies' => array(
'load' => array(),
'hash_source_string' => 'WP:6.7;',
'expected' => 'W/"7ee896c19250a3d174f11469a4ad0b1e"',
),
);
}

/**
* Tests get_etag method for WP_Scripts.
*
* @ticket 58433
* @ticket 61485
*
* @covers WP_Dependencies::get_etag
*
* @dataProvider data_provider_get_etag
*
* @param array $load List of scripts to load.
* @param string $hash_source_string Hash source string.
* @param string $expected Expected etag.
*/
public function test_get_etag_scripts( $load, $hash_source_string, $expected ) {
global $wp_version;
// Modify global to avoid tests needing to change with each new version of WordPress.
$original_wp_version = $wp_version;
$wp_version = '6.7';
$instance = wp_scripts();

foreach ( $load as $handle => $ver ) {
// The src should not be empty.
wp_enqueue_script( $handle, 'https://example.org', array(), $ver );
}

$result = $instance->get_etag( array_keys( $load ) );

// Restore global prior to making assertions.
$wp_version = $original_wp_version;

$this->assertSame( $expected, $result, "Expected MD hash: $expected for $hash_source_string, but got: $result." );
}

/**
* Tests get_etag method for WP_Styles.
*
* @ticket 58433
* @ticket 61485
*
* @covers WP_Dependencies::get_etag
*
* @dataProvider data_provider_get_etag
*
* @param array $load List of styles to load.
* @param string $hash_source_string Hash source string.
* @param string $expected Expected etag.
*/
public function test_get_etag_styles( $load, $hash_source_string, $expected ) {
global $wp_version;
// Modify global to avoid tests needing to change with each new version of WordPress.
$original_wp_version = $wp_version;
$wp_version = '6.7';
$instance = wp_scripts();

foreach ( $load as $handle => $ver ) {
// The src should not be empty.
wp_enqueue_style( $handle, 'https://example.cdn', array(), $ver );
}

$result = $instance->get_etag( array_keys( $load ) );

// Restore global prior to making assertions.
$wp_version = $original_wp_version;

$this->assertSame( $expected, $result, "Expected MD hash: $expected for $hash_source_string, but got: $result." );
}
}

0 comments on commit a3520d2

Please sign in to comment.