PluginProbe
Speculative Loading / trunk
Speculative Loading vtrunk
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 trunk, at plugin-api.php

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