[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> speculative-loading.php (source)

   1  <?php
   2  /**
   3   * Speculative loading functions.
   4   *
   5   * @package WordPress
   6   * @subpackage Speculative Loading
   7   * @since 6.8.0
   8   */
   9  
  10  /**
  11   * Returns the speculation rules configuration.
  12   *
  13   * @since 6.8.0
  14   *
  15   * @return array<string, string>|null Associative array with 'mode' and 'eagerness' keys, or null if speculative
  16   *                                    loading is disabled.
  17   */
  18  function wp_get_speculation_rules_configuration(): ?array {
  19      // By default, speculative loading is only enabled for sites with pretty permalinks when no user is logged in.
  20      if ( ! is_user_logged_in() && get_option( 'permalink_structure' ) ) {
  21          $config = array(
  22              'mode'      => 'auto',
  23              'eagerness' => 'auto',
  24          );
  25      } else {
  26          $config = null;
  27      }
  28  
  29      /**
  30       * Filters the way that speculation rules are configured.
  31       *
  32       * The Speculation Rules API is a web API that allows to automatically prefetch or prerender certain URLs on the
  33       * page, which can lead to near-instant page load times. This is also referred to as speculative loading.
  34       *
  35       * There are two aspects to the configuration:
  36       * * The "mode" (whether to "prefetch" or "prerender" URLs).
  37       * * The "eagerness" (whether to speculatively load URLs in an "eager", "moderate", or "conservative" way).
  38       *
  39       * By default, the speculation rules configuration is decided by WordPress Core ("auto"). This filter can be used
  40       * to force a certain configuration, which could for instance load URLs more or less eagerly.
  41       *
  42       * For logged-in users or for sites that are not configured to use pretty permalinks, the default value is `null`,
  43       * indicating that speculative loading is entirely disabled.
  44       *
  45       * @since 6.8.0
  46       * @see https://developer.chrome.com/docs/web-platform/prerender-pages
  47       *
  48       * @param array<string, string>|null $config Associative array with 'mode' and 'eagerness' keys, or `null`. The
  49       *                                           default value for both of the keys is 'auto'. Other possible values
  50       *                                           for 'mode' are 'prefetch' and 'prerender'. Other possible values for
  51       *                                           'eagerness' are 'eager', 'moderate', and 'conservative'. The value
  52       *                                           `null` is used to disable speculative loading entirely.
  53       */
  54      $config = apply_filters( 'wp_speculation_rules_configuration', $config );
  55  
  56      // Allow the value `null` to indicate that speculative loading is disabled.
  57      if ( null === $config ) {
  58          return null;
  59      }
  60  
  61      // Sanitize the configuration and replace 'auto' with current defaults.
  62      $default_mode      = 'prefetch';
  63      $default_eagerness = 'conservative';
  64      if ( ! is_array( $config ) ) {
  65          return array(
  66              'mode'      => $default_mode,
  67              'eagerness' => $default_eagerness,
  68          );
  69      }
  70      if (
  71          ! isset( $config['mode'] ) ||
  72          'auto' === $config['mode'] ||
  73          ! WP_Speculation_Rules::is_valid_mode( $config['mode'] )
  74      ) {
  75          $config['mode'] = $default_mode;
  76      }
  77      if (
  78          ! isset( $config['eagerness'] ) ||
  79          'auto' === $config['eagerness'] ||
  80          ! WP_Speculation_Rules::is_valid_eagerness( $config['eagerness'] ) ||
  81          // 'immediate' is a valid eagerness, but for safety WordPress does not allow it for document-level rules.
  82          'immediate' === $config['eagerness']
  83      ) {
  84          $config['eagerness'] = $default_eagerness;
  85      }
  86  
  87      return array(
  88          'mode'      => $config['mode'],
  89          'eagerness' => $config['eagerness'],
  90      );
  91  }
  92  
  93  /**
  94   * Returns the full speculation rules data based on the configuration.
  95   *
  96   * Plugins with features that rely on frontend URLs to exclude from prefetching or prerendering should use the
  97   * {@see 'wp_speculation_rules_href_exclude_paths'} filter to ensure those URL patterns are excluded.
  98   *
  99   * Additional speculation rules other than the default rule from WordPress Core can be provided by using the
 100   * {@see 'wp_load_speculation_rules'} action and amending the passed WP_Speculation_Rules object.
 101   *
 102   * @since 6.8.0
 103   * @access private
 104   *
 105   * @return WP_Speculation_Rules|null Object representing the speculation rules to use, or null if speculative loading
 106   *                                   is disabled in the current context.
 107   */
 108  function wp_get_speculation_rules(): ?WP_Speculation_Rules {
 109      $configuration = wp_get_speculation_rules_configuration();
 110      if ( null === $configuration ) {
 111          return null;
 112      }
 113  
 114      $mode      = $configuration['mode'];
 115      $eagerness = $configuration['eagerness'];
 116  
 117      $prefixer = new WP_URL_Pattern_Prefixer();
 118  
 119      $base_href_exclude_paths = array(
 120          $prefixer->prefix_path_pattern( '/wp-*.php', 'site' ),
 121          $prefixer->prefix_path_pattern( '/wp-admin/*', 'site' ),
 122          $prefixer->prefix_path_pattern( '/*', 'uploads' ),
 123          $prefixer->prefix_path_pattern( '/*', 'content' ),
 124          $prefixer->prefix_path_pattern( '/*', 'plugins' ),
 125          $prefixer->prefix_path_pattern( '/*', 'template' ),
 126          $prefixer->prefix_path_pattern( '/*', 'stylesheet' ),
 127      );
 128  
 129      /*
 130       * If pretty permalinks are enabled, exclude any URLs with query parameters.
 131       * Otherwise, exclude specifically the URLs with a `_wpnonce` query parameter or any other query parameter
 132       * containing the word `nonce`.
 133       */
 134      if ( get_option( 'permalink_structure' ) ) {
 135          $base_href_exclude_paths[] = $prefixer->prefix_path_pattern( '/*\\?(.+)', 'home' );
 136      } else {
 137          $base_href_exclude_paths[] = $prefixer->prefix_path_pattern( '/*\\?*(^|&)*nonce*=*', 'home' );
 138      }
 139  
 140      /**
 141       * Filters the paths for which speculative loading should be disabled.
 142       *
 143       * All paths should start in a forward slash, relative to the root document. The `*` can be used as a wildcard.
 144       * If the WordPress site is in a subdirectory, the exclude paths will automatically be prefixed as necessary.
 145       *
 146       * Note that WordPress always excludes certain path patterns such as `/wp-login.php` and `/wp-admin/*`, and those
 147       * cannot be modified using the filter.
 148       *
 149       * @since 6.8.0
 150       *
 151       * @param string[] $href_exclude_paths Additional path patterns to disable speculative loading for.
 152       * @param string   $mode               Mode used to apply speculative loading. Either 'prefetch' or 'prerender'.
 153       */
 154      $href_exclude_paths = (array) apply_filters( 'wp_speculation_rules_href_exclude_paths', array(), $mode );
 155  
 156      // Ensure that:
 157      // 1. There are no duplicates.
 158      // 2. The base paths cannot be removed.
 159      // 3. The array has sequential keys (i.e. array_is_list()).
 160      $href_exclude_paths = array_values(
 161          array_unique(
 162              array_merge(
 163                  $base_href_exclude_paths,
 164                  array_map(
 165                      static function ( string $href_exclude_path ) use ( $prefixer ): string {
 166                          return $prefixer->prefix_path_pattern( $href_exclude_path );
 167                      },
 168                      $href_exclude_paths
 169                  )
 170              )
 171          )
 172      );
 173  
 174      $speculation_rules = new WP_Speculation_Rules();
 175  
 176      $main_rule_conditions = array(
 177          // Include any URLs within the same site.
 178          array(
 179              'href_matches' => $prefixer->prefix_path_pattern( '/*' ),
 180          ),
 181          // Except for excluded paths.
 182          array(
 183              'not' => array(
 184                  'href_matches' => $href_exclude_paths,
 185              ),
 186          ),
 187          // Also exclude rel=nofollow links, as certain plugins use that on their links that perform an action.
 188          array(
 189              'not' => array(
 190                  'selector_matches' => 'a[rel~="nofollow"]',
 191              ),
 192          ),
 193          // Also exclude links that are explicitly marked to opt out.
 194          array(
 195              'not' => array(
 196                  'selector_matches' => ".no-{$mode}",
 197              ),
 198          ),
 199      );
 200  
 201      // If using 'prerender', also exclude links that opt-out of 'prefetch' because it's part of 'prerender'.
 202      if ( 'prerender' === $mode ) {
 203          $main_rule_conditions[] = array(
 204              'not' => array(
 205                  'selector_matches' => '.no-prefetch',
 206              ),
 207          );
 208      }
 209  
 210      $speculation_rules->add_rule(
 211          $mode,
 212          'main',
 213          array(
 214              'source'    => 'document',
 215              'where'     => array(
 216                  'and' => $main_rule_conditions,
 217              ),
 218              'eagerness' => $eagerness,
 219          )
 220      );
 221  
 222      /**
 223       * Fires when speculation rules data is loaded, allowing to amend the rules.
 224       *
 225       * @since 6.8.0
 226       *
 227       * @param WP_Speculation_Rules $speculation_rules Object representing the speculation rules to use.
 228       */
 229      do_action( 'wp_load_speculation_rules', $speculation_rules );
 230  
 231      return $speculation_rules;
 232  }
 233  
 234  /**
 235   * Prints the speculation rules.
 236   *
 237   * For browsers that do not support speculation rules yet, the `script[type="speculationrules"]` tag will be ignored.
 238   *
 239   * @since 6.8.0
 240   * @access private
 241   */
 242  function wp_print_speculation_rules(): void {
 243      $speculation_rules = wp_get_speculation_rules();
 244      if ( null === $speculation_rules ) {
 245          return;
 246      }
 247  
 248      wp_print_inline_script_tag(
 249          (string) wp_json_encode(
 250              $speculation_rules
 251          ),
 252          array( 'type' => 'speculationrules' )
 253      );
 254  }


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