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 https://github.com/WordPress/wordpress-develop/pull/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: http://core.svn.wordpress.org/trunk@60512 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
@@ -997,12 +997,17 @@ JS;
|
||||
*
|
||||
* @since 6.3.0
|
||||
*
|
||||
* @param string $handle The script handle.
|
||||
* @param string[]|null $eligible_strategies Optional. The list of strategies to filter. Default null.
|
||||
* @param array<string, true> $checked Optional. An array of already checked script handles, used to avoid recursive loops.
|
||||
* @param string $handle The script handle.
|
||||
* @param string[]|null $eligible_strategies Optional. The list of strategies to filter. Default null.
|
||||
* @param array<string, true> $checked Optional. An array of already checked script handles, used to avoid recursive loops.
|
||||
* @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.
|
||||
* @return string[] A list of eligible loading strategies that could be used.
|
||||
*/
|
||||
private function filter_eligible_strategies( $handle, $eligible_strategies = null, $checked = array() ) {
|
||||
private function filter_eligible_strategies( $handle, $eligible_strategies = null, $checked = array(), array &$stored_results = array() ) {
|
||||
if ( isset( $stored_results[ $handle ] ) ) {
|
||||
return $stored_results[ $handle ];
|
||||
}
|
||||
|
||||
// If no strategies are being passed, all strategies are eligible.
|
||||
if ( null === $eligible_strategies ) {
|
||||
$eligible_strategies = $this->delayed_strategies;
|
||||
@@ -1053,9 +1058,9 @@ JS;
|
||||
return array();
|
||||
}
|
||||
|
||||
$eligible_strategies = $this->filter_eligible_strategies( $dependent, $eligible_strategies, $checked );
|
||||
$eligible_strategies = $this->filter_eligible_strategies( $dependent, $eligible_strategies, $checked, $stored_results );
|
||||
}
|
||||
|
||||
$stored_results[ $handle ] = $eligible_strategies;
|
||||
return $eligible_strategies;
|
||||
}
|
||||
|
||||
@@ -1066,11 +1071,16 @@ JS;
|
||||
* @see self::filter_eligible_strategies()
|
||||
* @see WP_Script_Modules::get_highest_fetchpriority_with_dependents()
|
||||
*
|
||||
* @param string $handle Script module ID.
|
||||
* @param array<string, true> $checked Optional. An array of already checked script handles, used to avoid recursive loops.
|
||||
* @param string $handle Script module ID.
|
||||
* @param array<string, true> $checked Optional. An array of already checked script handles, used to avoid recursive loops.
|
||||
* @param array<string, string> $stored_results Optional. An array of already computed max priority by handle, used to increase performance in large dependency lists.
|
||||
* @return string|null Highest fetch priority for the script and its dependents.
|
||||
*/
|
||||
private function get_highest_fetchpriority_with_dependents( string $handle, array $checked = array() ): ?string {
|
||||
private function get_highest_fetchpriority_with_dependents( string $handle, array $checked = array(), array &$stored_results = array() ): ?string {
|
||||
if ( isset( $stored_results[ $handle ] ) ) {
|
||||
return $stored_results[ $handle ];
|
||||
}
|
||||
|
||||
// If there is a recursive dependency, return early.
|
||||
if ( isset( $checked[ $handle ] ) ) {
|
||||
return null;
|
||||
@@ -1099,7 +1109,7 @@ JS;
|
||||
$highest_priority_index = (int) array_search( $fetchpriority, $priorities, true );
|
||||
if ( $highest_priority_index !== $high_priority_index ) {
|
||||
foreach ( $this->get_dependents( $handle ) as $dependent_handle ) {
|
||||
$dependent_priority = $this->get_highest_fetchpriority_with_dependents( $dependent_handle, $checked );
|
||||
$dependent_priority = $this->get_highest_fetchpriority_with_dependents( $dependent_handle, $checked, $stored_results );
|
||||
if ( is_string( $dependent_priority ) ) {
|
||||
$highest_priority_index = max(
|
||||
$highest_priority_index,
|
||||
@@ -1111,7 +1121,7 @@ JS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$stored_results[ $handle ] = $priorities[ $highest_priority_index ]; // @phpstan-ignore parameterByRef.type (We know the index is valid and that this will be a string.)
|
||||
return $priorities[ $highest_priority_index ];
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* @global string $wp_version
|
||||
*/
|
||||
$wp_version = '6.9-beta3-61175';
|
||||
$wp_version = '6.9-beta3-61176';
|
||||
|
||||
/**
|
||||
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
||||
|
||||
Reference in New Issue
Block a user