[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Class 'WP_Speculation_Rules'. 4 * 5 * @package WordPress 6 * @subpackage Speculative Loading 7 * @since 6.8.0 8 */ 9 10 /** 11 * Class representing a set of speculation rules. 12 * 13 * @since 6.8.0 14 * @access private 15 */ 16 final class WP_Speculation_Rules implements JsonSerializable { 17 18 /** 19 * Stored rules, as a map of `$mode => $rules` pairs. 20 * 21 * Every `$rules` value is a map of `$id => $rule` pairs. 22 * 23 * @since 6.8.0 24 * @var array<string, array<string, mixed>> 25 */ 26 private $rules_by_mode = array(); 27 28 /** 29 * The allowed speculation rules modes as a map, used for validation. 30 * 31 * @since 6.8.0 32 * @var array<string, bool> 33 */ 34 private static $mode_allowlist = array( 35 'prefetch' => true, 36 'prerender' => true, 37 ); 38 39 /** 40 * The allowed speculation rules eagerness levels as a map, used for validation. 41 * 42 * @since 6.8.0 43 * @var array<string, bool> 44 */ 45 private static $eagerness_allowlist = array( 46 'immediate' => true, 47 'eager' => true, 48 'moderate' => true, 49 'conservative' => true, 50 ); 51 52 /** 53 * The allowed speculation rules sources as a map, used for validation. 54 * 55 * @since 6.8.0 56 * @var array<string, bool> 57 */ 58 private static $source_allowlist = array( 59 'list' => true, 60 'document' => true, 61 ); 62 63 /** 64 * Adds a speculation rule to the speculation rules to consider. 65 * 66 * @since 6.8.0 67 * 68 * @param string $mode Speculative loading mode. Either 'prefetch' or 'prerender'. 69 * @param string $id Unique string identifier for the speculation rule. 70 * @param array<string, mixed> $rule Associative array of rule arguments. 71 * @return bool True on success, false if invalid parameters are provided. 72 */ 73 public function add_rule( string $mode, string $id, array $rule ): bool { 74 if ( ! self::is_valid_mode( $mode ) ) { 75 _doing_it_wrong( 76 __METHOD__, 77 sprintf( 78 /* translators: %s: invalid mode value */ 79 __( 'The value "%s" is not a valid speculation rules mode.' ), 80 esc_html( $mode ) 81 ), 82 '6.8.0' 83 ); 84 return false; 85 } 86 87 if ( ! $this->is_valid_id( $id ) ) { 88 _doing_it_wrong( 89 __METHOD__, 90 sprintf( 91 /* translators: %s: invalid ID value */ 92 __( 'The value "%s" is not a valid ID for a speculation rule.' ), 93 esc_html( $id ) 94 ), 95 '6.8.0' 96 ); 97 return false; 98 } 99 100 if ( $this->has_rule( $mode, $id ) ) { 101 _doing_it_wrong( 102 __METHOD__, 103 sprintf( 104 /* translators: %s: invalid ID value */ 105 __( 'A speculation rule with ID "%s" already exists.' ), 106 esc_html( $id ) 107 ), 108 '6.8.0' 109 ); 110 return false; 111 } 112 113 /* 114 * Perform some basic speculation rule validation. 115 * Every rule must have either a 'where' key or a 'urls' key, but not both. 116 * The presence of a 'where' key implies a 'source' of 'document', while the presence of a 'urls' key implies 117 * a 'source' of 'list'. 118 */ 119 if ( 120 ( ! isset( $rule['where'] ) && ! isset( $rule['urls'] ) ) || 121 ( isset( $rule['where'] ) && isset( $rule['urls'] ) ) 122 ) { 123 _doing_it_wrong( 124 __METHOD__, 125 sprintf( 126 /* translators: 1: allowed key, 2: alternative allowed key */ 127 __( 'A speculation rule must include either a "%1$s" key or a "%2$s" key, but not both.' ), 128 'where', 129 'urls' 130 ), 131 '6.8.0' 132 ); 133 return false; 134 } 135 if ( isset( $rule['source'] ) ) { 136 if ( ! self::is_valid_source( $rule['source'] ) ) { 137 _doing_it_wrong( 138 __METHOD__, 139 sprintf( 140 /* translators: %s: invalid source value */ 141 __( 'The value "%s" is not a valid source for a speculation rule.' ), 142 esc_html( $rule['source'] ) 143 ), 144 '6.8.0' 145 ); 146 return false; 147 } 148 149 if ( 'list' === $rule['source'] && isset( $rule['where'] ) ) { 150 _doing_it_wrong( 151 __METHOD__, 152 sprintf( 153 /* translators: 1: source value, 2: forbidden key */ 154 __( 'A speculation rule of source "%1$s" must not include a "%2$s" key.' ), 155 'list', 156 'where' 157 ), 158 '6.8.0' 159 ); 160 return false; 161 } 162 163 if ( 'document' === $rule['source'] && isset( $rule['urls'] ) ) { 164 _doing_it_wrong( 165 __METHOD__, 166 sprintf( 167 /* translators: 1: source value, 2: forbidden key */ 168 __( 'A speculation rule of source "%1$s" must not include a "%2$s" key.' ), 169 'document', 170 'urls' 171 ), 172 '6.8.0' 173 ); 174 return false; 175 } 176 } 177 178 // If there is an 'eagerness' key specified, make sure it's valid. 179 if ( isset( $rule['eagerness'] ) ) { 180 if ( ! self::is_valid_eagerness( $rule['eagerness'] ) ) { 181 _doing_it_wrong( 182 __METHOD__, 183 sprintf( 184 /* translators: %s: invalid eagerness value */ 185 __( 'The value "%s" is not a valid eagerness for a speculation rule.' ), 186 esc_html( $rule['eagerness'] ) 187 ), 188 '6.8.0' 189 ); 190 return false; 191 } 192 193 if ( isset( $rule['where'] ) && 'immediate' === $rule['eagerness'] ) { 194 _doing_it_wrong( 195 __METHOD__, 196 sprintf( 197 /* translators: %s: forbidden eagerness value */ 198 __( 'The eagerness value "%s" is forbidden for document-level speculation rules.' ), 199 'immediate' 200 ), 201 '6.8.0' 202 ); 203 return false; 204 } 205 } 206 207 if ( ! isset( $this->rules_by_mode[ $mode ] ) ) { 208 $this->rules_by_mode[ $mode ] = array(); 209 } 210 211 $this->rules_by_mode[ $mode ][ $id ] = $rule; 212 return true; 213 } 214 215 /** 216 * Checks whether a speculation rule for the given mode and ID already exists. 217 * 218 * @since 6.8.0 219 * 220 * @param string $mode Speculative loading mode. Either 'prefetch' or 'prerender'. 221 * @param string $id Unique string identifier for the speculation rule. 222 * @return bool True if the rule already exists, false otherwise. 223 */ 224 public function has_rule( string $mode, string $id ): bool { 225 return isset( $this->rules_by_mode[ $mode ][ $id ] ); 226 } 227 228 /** 229 * Returns the speculation rules data ready to be JSON-encoded. 230 * 231 * @since 6.8.0 232 * 233 * @return array<string, array<string, mixed>> Speculation rules data. 234 */ 235 #[ReturnTypeWillChange] 236 public function jsonSerialize() { 237 // Strip the IDs for JSON output, since they are not relevant for the Speculation Rules API. 238 return array_map( 239 static function ( array $rules ) { 240 return array_values( $rules ); 241 }, 242 array_filter( $this->rules_by_mode ) 243 ); 244 } 245 246 /** 247 * Checks whether the given ID is valid. 248 * 249 * @since 6.8.0 250 * 251 * @param string $id Unique string identifier for the speculation rule. 252 * @return bool True if the ID is valid, false otherwise. 253 */ 254 private function is_valid_id( string $id ): bool { 255 return (bool) preg_match( '/^[a-z][a-z0-9_-]+$/', $id ); 256 } 257 258 /** 259 * Checks whether the given speculation rules mode is valid. 260 * 261 * @since 6.8.0 262 * 263 * @param string $mode Speculation rules mode. 264 * @return bool True if valid, false otherwise. 265 */ 266 public static function is_valid_mode( string $mode ): bool { 267 return isset( self::$mode_allowlist[ $mode ] ); 268 } 269 270 /** 271 * Checks whether the given speculation rules eagerness is valid. 272 * 273 * @since 6.8.0 274 * 275 * @param string $eagerness Speculation rules eagerness. 276 * @return bool True if valid, false otherwise. 277 */ 278 public static function is_valid_eagerness( string $eagerness ): bool { 279 return isset( self::$eagerness_allowlist[ $eagerness ] ); 280 } 281 282 /** 283 * Checks whether the given speculation rules source is valid. 284 * 285 * @since 6.8.0 286 * 287 * @param string $source Speculation rules source. 288 * @return bool True if valid, false otherwise. 289 */ 290 public static function is_valid_source( string $source ): bool { 291 return isset( self::$source_allowlist[ $source ] ); 292 } 293 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Fri Feb 21 08:20:01 2025 | Cross-referenced by PHPXref |