Provide mechanism for host to change default Speculative Loading eagerness and mode#12514
Provide mechanism for host to change default Speculative Loading eagerness and mode#12514westonruter wants to merge 6 commits into
Conversation
…oading mode/eagerness
This fixes the last PHPStan level 10 error in the file: > Parameter #1 $callback of function array_map expects (callable(mixed): mixed)|null, Closure(string): string given.
…erness Fix two problems with the speculative loading default overrides: - `$_ENV` is only populated when `variables_order` includes `E`, which is not the case for the `php.ini-production` and `php.ini-development` files shipped with PHP. The overrides were therefore silently ignored on a stock install even when the environment variable was in fact set. Read them with `getenv()` instead, and let a constant of the same name take precedence, consistent with how `wp_get_environment_type()` resolves `WP_ENVIRONMENT_TYPE`. This also gives hosts the `wp-config.php` route. - `immediate` passed `is_valid_eagerness()` and so could be supplied as the default eagerness, bypassing the guard that keeps it out of the document-level rules that WordPress generates. `WP_Speculation_Rules::add_rule()` then rejected the main rule, leaving the site with no speculation rules at all. Move the default resolution into `wp_get_speculation_rules_default_configuration()` so that the overrides only change what `auto` resolves to. An explicit mode or eagerness supplied via the `wp_speculation_rules_configuration` filter continues to take precedence over a host default. Add unit tests covering both, including the constant/environment variable precedence and the rejection of `immediate`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
There was a problem hiding this comment.
Pull request overview
Adds host-level overrides for what speculative loading configuration auto resolves to (mode/eagerness), via environment variables or constants, and hardens validation against non-string filter input. This supports opt-in host experimentation with different defaults without requiring an mu-plugin.
Changes:
- Introduces
wp_get_speculation_rules_default_configuration()andwp_get_speculative_loading_override()to resolve Core defaults with optional host overrides. - Expands
WP_Speculation_Rules::is_valid_mode()/::is_valid_eagerness()to acceptmixedand safely returnfalsefor invalid types. - Adds PHPUnit coverage for override behavior and precedence, and updates existing tests to account for overridden defaults.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/wp-includes/speculative-loading.php |
Adds default-resolution and override helpers; uses resolved defaults when sanitizing configuration; filters non-string exclude paths. |
src/wp-includes/class-wp-speculation-rules.php |
Makes validation methods accept mixed to avoid TypeError from invalid filter returns. |
tests/phpunit/tests/speculative-loading/wpGetSpeculationRulesDefaultConfiguration.php |
New unit tests for env/constant override resolution and precedence. |
tests/phpunit/tests/speculative-loading/wpGetSpeculationRulesConfiguration.php |
Adds tests ensuring auto resolves to overridden defaults and filter precedence remains intact. |
tests/phpunit/tests/speculative-loading/wpGetSpeculationRules.php |
Adds coverage ensuring overridden defaults affect rules output and immediate cannot be forced as default. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Fetch the value from a constant, which overrides the environment variable. | ||
| if ( defined( $name ) ) { | ||
| $has_constant = constant( $name ); | ||
| if ( is_string( $has_constant ) ) { | ||
| $value = $has_constant; | ||
| } | ||
| } |
There was a problem hiding this comment.
I don't think we need to return null empty here.
| class Tests_Speculative_Loading_wpGetSpeculationRulesDefaultConfiguration extends WP_UnitTestCase { | ||
|
|
||
| public function tear_down(): void { | ||
| putenv( 'WP_SPECULATIVE_LOADING_DEFAULT_MODE' ); | ||
| putenv( 'WP_SPECULATIVE_LOADING_DEFAULT_EAGERNESS' ); | ||
|
|
||
| parent::tear_down(); | ||
| } |
| public function tear_down(): void { | ||
| putenv( 'WP_SPECULATIVE_LOADING_DEFAULT_MODE' ); | ||
| putenv( 'WP_SPECULATIVE_LOADING_DEFAULT_EAGERNESS' ); | ||
|
|
||
| parent::tear_down(); |
| public function tear_down(): void { | ||
| putenv( 'WP_SPECULATIVE_LOADING_DEFAULT_MODE' ); | ||
| putenv( 'WP_SPECULATIVE_LOADING_DEFAULT_EAGERNESS' ); | ||
|
|
||
| parent::tear_down(); |
There was a problem hiding this comment.
There wouldn't be any present. This seems overkill.
There was a problem hiding this comment.
But if disablePingsForEnvironment.php is doing it, I guess it wouldn't hurt.
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
Provides a mechanism for hosting providers to change the default speculative loading
modeandeagernessthat theautovalue resolves to, without having to ship an mu-plugin.This is the first step proposed in comment:26 of Core-64066: give hosts an opt-in so they can enable a
moderatedefault and report back on the resulting TTFB/LCP improvements and the increase in server load from wasted speculations. If that data is favorable, a later change can consider using the page cache and persistent object cache Site Health results as the heuristic for defaulting tomoderate, with these overrides then serving as the opt-out.Approach
Two overrides are introduced, each settable as either an environment variable or a constant:
WP_SPECULATIVE_LOADING_DEFAULT_MODE—prefetch(default) orprerender.WP_SPECULATIVE_LOADING_DEFAULT_EAGERNESS—conservative(default),moderate, oreager.Resolution follows
wp_get_environment_type(): the environment variable is read withgetenv(), and a constant of the same name takes precedence over it. Supporting the constant means a host can set this fromwp-config.php, which is how hosts already injectWP_CACHEand friends. An invalid or unrecognized value falls back to the Core default.These only change what
autoresolves to. A plugin that supplies an explicitmodeoreagernessthrough thewp_speculation_rules_configurationfilter still takes precedence over a host's default, so the Speculative Loading plugin continues to win.An
eagernessofimmediateis deliberately not accepted, since WordPress does not permit it for the document-level rules it generates —WP_Speculation_Rules::add_rule()rejects such a rule, which would leave the site with no speculation rules at all.Also widens
WP_Speculation_Rules::is_valid_mode()and::is_valid_eagerness()to acceptmixedrather thanstring. Both are validation methods, so accepting an arbitrary value and returningfalseis the more useful contract; it also hardens the filter path, where a plugin returning e.g. an array formodewould previously have caused aTypeError.Testing instructions
"eagerness": "conservative"and aprefetchrule.define( 'WP_SPECULATIVE_LOADING_DEFAULT_EAGERNESS', 'moderate' );towp-config.php)."eagerness": "moderate".bogus, orimmediate) and confirm the rules fall back toconservativeand that speculation rules are still output.moderate, add a filter returning an explicit'eagerness' => 'conservative'and confirm the filter wins.Unit tests cover the environment variable and constant handling, the precedence of the constant over the environment variable, invalid/empty values, the rejection of
immediate, and that an explicit filter value still beats a host default:Trac ticket: https://core.trac.wordpress.org/ticket/65624
Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 4.8
Used for: Reviewing the initial implementation, which surfaced two defects that I have since fixed — the overrides were originally read from
$_ENV, which is not populated under thevariables_ordervalue that PHP's shippedphp.inifiles use, so they were silently ignored on a stock install; andimmediatewas accepted as a default eagerness, which caused the main document-level rule to be rejected and left the page with no speculation rules. Also used to draft the accompanying unit tests. I have reviewed and take responsibility for all of the above.This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.