Skip to content

Commit e0a4b0e

Browse files
committed
Script Loader: Guard against exponential recursion during calculation of loading strategy and fetchpriority.
This addresses a performance issue in the recursive `WP_Scripts::get_highest_fetchpriority_with_dependents()` and `WP_Scripts::filter_eligible_strategies()` methods for redundant processing of shared dependencies in complex dependency graphs. To fix this, a `$stored_results` param is introduced which is passed by reference; this variable contains a cache of the calculated results for all scripts handles, so that subsequent calls for the same handle can return the cached value instead of re-computing it. Developed in WordPress/wordpress-develop#10459 Follow-up to [60704], [60931], [56033]. Props ciobanucatalin, b1ink0, westonruter, mukesh27. See #61734, #12009. Fixes #64194. Built from https://develop.svn.wordpress.org/trunk@61176 git-svn-id: https://core.svn.wordpress.org/trunk@60512 1a063a9b-81f0-0310-95a4-ce76da25c4cd
1 parent 06e8feb commit e0a4b0e

File tree

2 files changed

+22
-12
lines changed

2 files changed

+22
-12
lines changed

wp-includes/class-wp-scripts.php

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -997,12 +997,17 @@ private function get_eligible_loading_strategy( $handle ) {
997997
*
998998
* @since 6.3.0
999999
*
1000-
* @param string $handle The script handle.
1001-
* @param string[]|null $eligible_strategies Optional. The list of strategies to filter. Default null.
1002-
* @param array<string, true> $checked Optional. An array of already checked script handles, used to avoid recursive loops.
1000+
* @param string $handle The script handle.
1001+
* @param string[]|null $eligible_strategies Optional. The list of strategies to filter. Default null.
1002+
* @param array<string, true> $checked Optional. An array of already checked script handles, used to avoid recursive loops.
1003+
* @param array<string, string[]> $stored_results Optional. An array of already computed eligible loading strategies by handle, used to increase performance in large dependency lists.
10031004
* @return string[] A list of eligible loading strategies that could be used.
10041005
*/
1005-
private function filter_eligible_strategies( $handle, $eligible_strategies = null, $checked = array() ) {
1006+
private function filter_eligible_strategies( $handle, $eligible_strategies = null, $checked = array(), array &$stored_results = array() ) {
1007+
if ( isset( $stored_results[ $handle ] ) ) {
1008+
return $stored_results[ $handle ];
1009+
}
1010+
10061011
// If no strategies are being passed, all strategies are eligible.
10071012
if ( null === $eligible_strategies ) {
10081013
$eligible_strategies = $this->delayed_strategies;
@@ -1053,9 +1058,9 @@ private function filter_eligible_strategies( $handle, $eligible_strategies = nul
10531058
return array();
10541059
}
10551060

1056-
$eligible_strategies = $this->filter_eligible_strategies( $dependent, $eligible_strategies, $checked );
1061+
$eligible_strategies = $this->filter_eligible_strategies( $dependent, $eligible_strategies, $checked, $stored_results );
10571062
}
1058-
1063+
$stored_results[ $handle ] = $eligible_strategies;
10591064
return $eligible_strategies;
10601065
}
10611066

@@ -1066,11 +1071,16 @@ private function filter_eligible_strategies( $handle, $eligible_strategies = nul
10661071
* @see self::filter_eligible_strategies()
10671072
* @see WP_Script_Modules::get_highest_fetchpriority_with_dependents()
10681073
*
1069-
* @param string $handle Script module ID.
1070-
* @param array<string, true> $checked Optional. An array of already checked script handles, used to avoid recursive loops.
1074+
* @param string $handle Script module ID.
1075+
* @param array<string, true> $checked Optional. An array of already checked script handles, used to avoid recursive loops.
1076+
* @param array<string, string> $stored_results Optional. An array of already computed max priority by handle, used to increase performance in large dependency lists.
10711077
* @return string|null Highest fetch priority for the script and its dependents.
10721078
*/
1073-
private function get_highest_fetchpriority_with_dependents( string $handle, array $checked = array() ): ?string {
1079+
private function get_highest_fetchpriority_with_dependents( string $handle, array $checked = array(), array &$stored_results = array() ): ?string {
1080+
if ( isset( $stored_results[ $handle ] ) ) {
1081+
return $stored_results[ $handle ];
1082+
}
1083+
10741084
// If there is a recursive dependency, return early.
10751085
if ( isset( $checked[ $handle ] ) ) {
10761086
return null;
@@ -1099,7 +1109,7 @@ private function get_highest_fetchpriority_with_dependents( string $handle, arra
10991109
$highest_priority_index = (int) array_search( $fetchpriority, $priorities, true );
11001110
if ( $highest_priority_index !== $high_priority_index ) {
11011111
foreach ( $this->get_dependents( $handle ) as $dependent_handle ) {
1102-
$dependent_priority = $this->get_highest_fetchpriority_with_dependents( $dependent_handle, $checked );
1112+
$dependent_priority = $this->get_highest_fetchpriority_with_dependents( $dependent_handle, $checked, $stored_results );
11031113
if ( is_string( $dependent_priority ) ) {
11041114
$highest_priority_index = max(
11051115
$highest_priority_index,
@@ -1111,7 +1121,7 @@ private function get_highest_fetchpriority_with_dependents( string $handle, arra
11111121
}
11121122
}
11131123
}
1114-
1124+
$stored_results[ $handle ] = $priorities[ $highest_priority_index ]; // @phpstan-ignore parameterByRef.type (We know the index is valid and that this will be a string.)
11151125
return $priorities[ $highest_priority_index ];
11161126
}
11171127

wp-includes/version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
* @global string $wp_version
1818
*/
19-
$wp_version = '6.9-beta3-61175';
19+
$wp_version = '6.9-beta3-61176';
2020

2121
/**
2222
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.

0 commit comments

Comments
 (0)