PluginProbe
Speculative Loading / 1.5.0
Speculative Loading v1.5.0
1.7.0 1.1.0 1.2.0 1.2.1 1.2.2 1.3.0 1.3.1 1.4.0 1.5.0 1.6.0 trunk 1.0.0 1.0.1
speculation-rules / plugin-api.php

plugin-api.php in Speculative Loading 1.5.0, at plugin-api.php

166 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin API for Speculative Loading.
4 *
5 * @package speculation-rules
6 * @since 1.0.0
7 */
8
9 // @codeCoverageIgnoreStart
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit; // Exit if accessed directly.
12 }
13 // @codeCoverageIgnoreEnd
14
15 /**
16 * Returns the speculation rules.
17 *
18 * Plugins with features that rely on frontend URLs to exclude from prefetching or prerendering should use the
19 * {@see 'plsr_speculation_rules_href_exclude_paths'} filter to ensure those URL patterns are excluded.
20 *
21 * @since 1.0.0
22 *
23 * @return non-empty-array<string, array<int, array<string, mixed>>> Associative array of speculation rules by type.
24 */
25 function plsr_get_speculation_rules(): array {
26 $option = plsr_get_stored_setting_value();
27 $mode = $option['mode'];
28 $eagerness = $option['eagerness'];
29
30 $prefixer = new PLSR_URL_Pattern_Prefixer();
31
32 $base_href_exclude_paths = array(
33 $prefixer->prefix_path_pattern( '/wp-*.php', 'site' ),
34 $prefixer->prefix_path_pattern( '/wp-admin/*', 'site' ),
35 $prefixer->prefix_path_pattern( '/*', 'uploads' ),
36 $prefixer->prefix_path_pattern( '/*', 'content' ),
37 $prefixer->prefix_path_pattern( '/*', 'plugins' ),
38 $prefixer->prefix_path_pattern( '/*', 'template' ),
39 $prefixer->prefix_path_pattern( '/*', 'stylesheet' ),
40 );
41
42 /*
43 * If pretty permalinks are enabled, exclude any URLs with query parameters.
44 * Otherwise, exclude specifically the URLs with a `_wpnonce` query parameter.
45 */
46 if ( (bool) get_option( 'permalink_structure' ) ) {
47 $base_href_exclude_paths[] = $prefixer->prefix_path_pattern( '/*\\?(.+)', 'home' );
48 } else {
49 $base_href_exclude_paths[] = $prefixer->prefix_path_pattern( '/*\\?*(^|&)_wpnonce=*', 'home' );
50 }
51
52 /**
53 * Filters the paths for which speculative prerendering should be disabled.
54 *
55 * All paths should start in a forward slash, relative to the root document. The `*` can be used as a wildcard.
56 *
57 * If the WordPress site is in a subdirectory, the exclude paths will automatically be prefixed as necessary.
58 *
59 * @since 1.0.0
60 * @since 1.1.0 The $mode parameter was added.
61 *
62 * @param string[] $href_exclude_paths Additional paths to disable speculative prerendering for. The base exclude paths,
63 * such as for wp-admin, cannot be removed.
64 * @param string $mode Mode used to apply speculative prerendering. Either 'prefetch' or 'prerender'.
65 */
66 $href_exclude_paths = (array) apply_filters( 'plsr_speculation_rules_href_exclude_paths', array(), $mode );
67
68 // Ensure that:
69 // 1. There are no duplicates.
70 // 2. The base paths cannot be removed.
71 // 3. The array has sequential keys (i.e. array_is_list()).
72 $href_exclude_paths = array_values(
73 array_unique(
74 array_merge(
75 $base_href_exclude_paths,
76 array_map(
77 static function ( string $href_exclude_path ) use ( $prefixer ): string {
78 return $prefixer->prefix_path_pattern( $href_exclude_path );
79 },
80 $href_exclude_paths
81 )
82 )
83 )
84 );
85
86 $rules = array(
87 array(
88 'source' => 'document',
89 'where' => array(
90 'and' => array(
91 // Include any URLs within the same site.
92 array(
93 'href_matches' => $prefixer->prefix_path_pattern( '/*' ),
94 ),
95 // Except for WP login and admin URLs.
96 array(
97 'not' => array(
98 'href_matches' => $href_exclude_paths,
99 ),
100 ),
101 // Also exclude rel=nofollow links, as plugins like WooCommerce use that on their add-to-cart links.
102 array(
103 'not' => array(
104 'selector_matches' => 'a[rel~="nofollow"]',
105 ),
106 ),
107 ),
108 ),
109 'eagerness' => $eagerness,
110 ),
111 );
112
113 // Allow adding a class on any links to prevent prerendering.
114 if ( 'prerender' === $mode ) {
115 $rules[0]['where']['and'][] = array(
116 'not' => array(
117 'selector_matches' => '.no-prerender',
118 ),
119 );
120 }
121
122 return array( $mode => $rules );
123 }
124
125 /**
126 * Prints the speculation rules.
127 *
128 * For browsers that do not support speculation rules yet, the `script[type="speculationrules"]` tag will be ignored.
129 *
130 * @since 1.0.0
131 */
132 function plsr_print_speculation_rules(): void {
133 // Skip speculative loading for logged-in users.
134 if ( is_user_logged_in() ) {
135 return;
136 }
137
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
161 wp_print_inline_script_tag(
162 (string) wp_json_encode( plsr_get_speculation_rules() ),
163 array( 'type' => 'speculationrules' )
164 );
165 }
166