[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> class-wp-speculation-rules.php (source)

   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  }


Generated : Fri Feb 21 08:20:01 2025 Cross-referenced by PHPXref