Skip to content

Commit 0ac56d3

Browse files
committed
Add Speculative Loading opt-in for authenticated users
1 parent e147007 commit 0ac56d3

File tree

4 files changed

+103
-57
lines changed

4 files changed

+103
-57
lines changed

plugins/speculation-rules/hooks.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,46 @@
1212
}
1313
// @codeCoverageIgnoreEnd
1414

15+
/**
16+
* Determines whether Speculative Loading is enabled.
17+
*
18+
* @since n.e.x.t
19+
*
20+
* @return bool Whether enabled.
21+
*/
22+
function plsr_is_speculative_loading_enabled(): bool {
23+
$option = plsr_get_stored_setting_value();
24+
return (
25+
(
26+
! is_user_logged_in()
27+
||
28+
'any' === $option['authentication']
29+
||
30+
( current_user_can( 'manage_options' ) && 'logged_out_or_admins' === $option['authentication'] )
31+
)
32+
&&
33+
(
34+
(bool) get_option( 'permalink_structure' )
35+
||
36+
/**
37+
* Filters whether speculative loading should be enabled even though the site does not use pretty permalinks.
38+
*
39+
* Since query parameters are commonly used by plugins for dynamic behavior that can change state, ideally any
40+
* such URLs are excluded from speculative loading. If the site does not use pretty permalinks though, they are
41+
* impossible to recognize. Therefore, speculative loading is disabled by default for those sites.
42+
*
43+
* For site owners of sites without pretty permalinks that are certain their site is not using such a pattern,
44+
* this filter can be used to still enable speculative loading at their own risk.
45+
*
46+
* @since 1.4.0
47+
*
48+
* @param bool $enabled Whether speculative loading is enabled even without pretty permalinks.
49+
*/
50+
(bool) apply_filters( 'plsr_enabled_without_pretty_permalinks', false )
51+
)
52+
);
53+
}
54+
1555
// Conditionally use either the WordPress Core API, or load the plugin's API implementation otherwise.
1656
if ( function_exists( 'wp_get_speculation_rules_configuration' ) ) {
1757
require_once __DIR__ . '/wp-core-api.php';

plugins/speculation-rules/plugin-api.php

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -130,34 +130,10 @@ static function ( string $href_exclude_path ) use ( $prefixer ): string {
130130
* @since 1.0.0
131131
*/
132132
function plsr_print_speculation_rules(): void {
133-
// Skip speculative loading for logged-in users.
134-
if ( is_user_logged_in() ) {
133+
if ( ! plsr_is_speculative_loading_enabled() ) {
135134
return;
136135
}
137136

138-
// Skip speculative loading for sites without pretty permalinks, unless explicitly enabled.
139-
if ( ! (bool) get_option( 'permalink_structure' ) ) {
140-
/**
141-
* Filters whether speculative loading should be enabled even though the site does not use pretty permalinks.
142-
*
143-
* Since query parameters are commonly used by plugins for dynamic behavior that can change state, ideally any
144-
* such URLs are excluded from speculative loading. If the site does not use pretty permalinks though, they are
145-
* impossible to recognize. Therefore speculative loading is disabled by default for those sites.
146-
*
147-
* For site owners of sites without pretty permalinks that are certain their site is not using such a pattern,
148-
* this filter can be used to still enable speculative loading at their own risk.
149-
*
150-
* @since 1.4.0
151-
*
152-
* @param bool $enabled Whether speculative loading is enabled even without pretty permalinks.
153-
*/
154-
$enabled = (bool) apply_filters( 'plsr_enabled_without_pretty_permalinks', false );
155-
156-
if ( ! $enabled ) {
157-
return;
158-
}
159-
}
160-
161137
wp_print_inline_script_tag(
162138
(string) wp_json_encode( plsr_get_speculation_rules() ),
163139
array( 'type' => 'speculationrules' )

plugins/speculation-rules/settings.php

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,39 @@ function plsr_get_eagerness_labels(): array {
4141
);
4242
}
4343

44+
/**
45+
* Returns the available options for the Speculative Loading authentication and their labels.
46+
*
47+
* @since n.e.x.t
48+
*
49+
* @return array{ logged_out: string, logged_out_or_admins: string, any: string } Associative array of `$authentication => $label` pairs.
50+
*/
51+
function plsr_get_authentication_labels(): array {
52+
return array(
53+
'logged_out' => _x( 'Logged out visitors only (default)', 'setting label', 'speculation-rules' ),
54+
'logged_out_or_admins' => _x( 'Administrators or logged out visitors', 'setting label', 'speculation-rules' ),
55+
'any' => _x( 'Any user (logged in or out)', 'setting label', 'speculation-rules' ),
56+
);
57+
}
58+
4459
/**
4560
* Returns the default setting value for Speculative Loading configuration.
4661
*
4762
* @since 1.0.0
4863
*
49-
* @return array{ mode: 'prerender', eagerness: 'moderate' } {
64+
* @return array{ mode: 'prerender', eagerness: 'moderate', authentication: 'logged_out' } {
5065
* Default setting value.
5166
*
52-
* @type string $mode Mode.
53-
* @type string $eagerness Eagerness.
67+
* @type string $mode Mode.
68+
* @type string $eagerness Eagerness.
69+
* @type string $authenticaiton Authentication.
5470
* }
5571
*/
5672
function plsr_get_setting_default(): array {
5773
return array(
58-
'mode' => 'prerender',
59-
'eagerness' => 'moderate',
74+
'mode' => 'prerender',
75+
'eagerness' => 'moderate',
76+
'authentication' => 'logged_out',
6077
);
6178
}
6279

@@ -65,11 +82,12 @@ function plsr_get_setting_default(): array {
6582
*
6683
* @since 1.4.0
6784
*
68-
* @return array{ mode: 'prefetch'|'prerender', eagerness: 'conservative'|'moderate'|'eager' } {
85+
* @return array{ mode: 'prefetch'|'prerender', eagerness: 'conservative'|'moderate'|'eager', authentication: 'logged_out'|'logged_out_or_admins'|'any' } {
6986
* Stored setting value.
7087
*
71-
* @type string $mode Mode.
72-
* @type string $eagerness Eagerness.
88+
* @type string $mode Mode.
89+
* @type string $eagerness Eagerness.
90+
* @type string $authenication Authentication.
7391
* }
7492
*/
7593
function plsr_get_stored_setting_value(): array {
@@ -83,11 +101,12 @@ function plsr_get_stored_setting_value(): array {
83101
* @todo Consider whether the JSON schema for the setting could be reused here.
84102
*
85103
* @param mixed $input Setting to sanitize.
86-
* @return array{ mode: 'prefetch'|'prerender', eagerness: 'conservative'|'moderate'|'eager' } {
104+
* @return array{ mode: 'prefetch'|'prerender', eagerness: 'conservative'|'moderate'|'eager', authentication: 'logged_out'|'logged_out_or_admins'|'any' } {
87105
* Sanitized setting.
88106
*
89-
* @type string $mode Mode.
90-
* @type string $eagerness Eagerness.
107+
* @type string $mode Mode.
108+
* @type string $eagerness Eagerness.
109+
* @type string $authenication Authentication.
91110
* }
92111
*/
93112
function plsr_sanitize_setting( $input ): array {
@@ -107,6 +126,9 @@ function plsr_sanitize_setting( $input ): array {
107126
if ( ! in_array( $value['eagerness'], array_keys( plsr_get_eagerness_labels() ), true ) ) {
108127
$value['eagerness'] = $default_value['eagerness'];
109128
}
129+
if ( ! in_array( $value['authentication'], array_keys( plsr_get_authentication_labels() ), true ) ) {
130+
$value['authentication'] = $default_value['authentication'];
131+
}
110132

111133
return $value;
112134
}
@@ -130,16 +152,21 @@ function plsr_register_setting(): void {
130152
'schema' => array(
131153
'type' => 'object',
132154
'properties' => array(
133-
'mode' => array(
155+
'mode' => array(
134156
'description' => __( 'Whether to prefetch or prerender URLs.', 'speculation-rules' ),
135157
'type' => 'string',
136158
'enum' => array_keys( plsr_get_mode_labels() ),
137159
),
138-
'eagerness' => array(
160+
'eagerness' => array(
139161
'description' => __( 'The eagerness setting defines the heuristics based on which the loading is triggered. "Eager" will have the minimum delay to start speculative loads, "Conservative" increases the chance that only URLs the user actually navigates to are loaded.', 'speculation-rules' ),
140162
'type' => 'string',
141163
'enum' => array_keys( plsr_get_eagerness_labels() ),
142164
),
165+
'authentication' => array(
166+
'description' => __( 'Only unauthenticated pages are typically served from cache. So in order to reduce load on the server, speculative loading is not enabled by default for logged in users. If your server can handle the additional load, you can opt in to speculative loading for all logged in users or administrator users only.', 'speculation-rules' ),
167+
'type' => 'string',
168+
'enum' => array_keys( plsr_get_authentication_labels() ),
169+
),
143170
),
144171
'additionalProperties' => false,
145172
),
@@ -174,14 +201,18 @@ static function (): void {
174201
);
175202

176203
$fields = array(
177-
'mode' => array(
204+
'mode' => array(
178205
'title' => __( 'Speculation Mode', 'speculation-rules' ),
179206
'description' => __( 'Prerendering will lead to faster load times than prefetching. However, in case of interactive content, prefetching may be a safer choice.', 'speculation-rules' ),
180207
),
181-
'eagerness' => array(
208+
'eagerness' => array(
182209
'title' => __( 'Eagerness', 'speculation-rules' ),
183210
'description' => __( 'The eagerness setting defines the heuristics based on which the loading is triggered. "Eager" will have the minimum delay to start speculative loads, "Conservative" increases the chance that only URLs the user actually navigates to are loaded.', 'speculation-rules' ),
184211
),
212+
'authentication' => array(
213+
'title' => __( 'Authentication', 'speculation-rules' ),
214+
'description' => __( 'Only unauthenticated pages are typically served from cache. So in order to reduce load on the server, speculative loading is not enabled by default for logged in users. If your server can handle the additional load, you can opt in to speculative loading for all logged in users or administrator users only.', 'speculation-rules' ),
215+
),
185216
);
186217
foreach ( $fields as $slug => $args ) {
187218
add_settings_field(
@@ -205,7 +236,7 @@ static function (): void {
205236
* @since 1.0.0
206237
* @access private
207238
*
208-
* @param array{ field: 'mode'|'eagerness', title: non-empty-string, description: non-empty-string } $args {
239+
* @param array{ field: 'mode'|'eagerness'|'authentication', title: non-empty-string, description: non-empty-string } $args {
209240
* Associative array of arguments.
210241
*
211242
* @type string $field The slug of the sub setting controlled by the field.
@@ -223,6 +254,9 @@ function plsr_render_settings_field( array $args ): void {
223254
case 'eagerness':
224255
$choices = plsr_get_eagerness_labels();
225256
break;
257+
case 'authentication':
258+
$choices = plsr_get_authentication_labels();
259+
break;
226260
default:
227261
// Invalid (and this case should never occur).
228262
return; // @codeCoverageIgnore

plugins/speculation-rules/wp-core-api.php

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,20 @@
2323
* @return array<string, string>|null Filtered $config.
2424
*/
2525
function plsr_filter_speculation_rules_configuration( $config ): ?array {
26-
/*
27-
* If speculative loading should be disabled per the WordPress Core configuration, respect that value, unless pretty
28-
* permalinks are disabled and the plugin-specific filter to opt in to the feature despite that is set to true.
29-
* This is present for backward compatibility so that usage of the plugin-specific filter does not break.
30-
*/
31-
if (
32-
null === $config &&
33-
/** This filter is documented in plugin-api.php */
34-
( (bool) get_option( 'permalink_structure' ) || ! (bool) apply_filters( 'plsr_enabled_without_pretty_permalinks', false ) )
35-
) {
36-
return null;
26+
if ( ! is_array( $config ) ) {
27+
// Because plugins do bad things.
28+
$config = null;
3729
}
3830

39-
$option = plsr_get_stored_setting_value();
40-
return array(
41-
'mode' => $option['mode'],
42-
'eagerness' => $option['eagerness'],
43-
);
31+
if ( plsr_is_speculative_loading_enabled() ) {
32+
$option = plsr_get_stored_setting_value();
33+
$config = array(
34+
'mode' => $option['mode'],
35+
'eagerness' => $option['eagerness'],
36+
);
37+
}
38+
39+
return $config;
4440
}
4541

4642
/**

0 commit comments

Comments
 (0)