[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> class-wp-scripts.php (source)

   1  <?php
   2  /**
   3   * Dependencies API: WP_Scripts class
   4   *
   5   * @since 2.6.0
   6   *
   7   * @package WordPress
   8   * @subpackage Dependencies
   9   */
  10  
  11  /**
  12   * Core class used to register scripts.
  13   *
  14   * @since 2.1.0
  15   *
  16   * @see WP_Dependencies
  17   */
  18  class WP_Scripts extends WP_Dependencies {
  19      /**
  20       * Base URL for scripts.
  21       *
  22       * Full URL with trailing slash.
  23       *
  24       * @since 2.6.0
  25       * @see wp_default_scripts()
  26       * @var string|null
  27       */
  28      public $base_url;
  29  
  30      /**
  31       * URL of the content directory.
  32       *
  33       * @since 2.8.0
  34       * @see wp_default_scripts()
  35       * @var string|null
  36       */
  37      public $content_url;
  38  
  39      /**
  40       * Default version string for scripts.
  41       *
  42       * @since 2.6.0
  43       * @see wp_default_scripts()
  44       * @var string|null
  45       */
  46      public $default_version;
  47  
  48      /**
  49       * Holds handles of scripts which are enqueued in footer.
  50       *
  51       * @since 2.8.0
  52       * @var string[]
  53       */
  54      public $in_footer = array();
  55  
  56      /**
  57       * Holds a list of script handles which will be concatenated.
  58       *
  59       * @since 2.8.0
  60       * @var string
  61       */
  62      public $concat = '';
  63  
  64      /**
  65       * Holds a string which contains script handles and their version.
  66       *
  67       * @since 2.8.0
  68       * @deprecated 3.4.0
  69       * @var string
  70       */
  71      public $concat_version = '';
  72  
  73      /**
  74       * Whether to perform concatenation.
  75       *
  76       * @since 2.8.0
  77       * @var bool
  78       */
  79      public $do_concat = false;
  80  
  81      /**
  82       * Holds HTML markup of scripts and additional data if concatenation
  83       * is enabled.
  84       *
  85       * @since 2.8.0
  86       * @var string
  87       */
  88      public $print_html = '';
  89  
  90      /**
  91       * Holds inline code if concatenation is enabled.
  92       *
  93       * @since 2.8.0
  94       * @var string
  95       */
  96      public $print_code = '';
  97  
  98      /**
  99       * Holds a list of script handles which are not in the default directory
 100       * if concatenation is enabled.
 101       *
 102       * Unused in core.
 103       *
 104       * @since 2.8.0
 105       * @var string
 106       */
 107      public $ext_handles = '';
 108  
 109      /**
 110       * Holds a string which contains handles and versions of scripts which
 111       * are not in the default directory if concatenation is enabled.
 112       *
 113       * Unused in core.
 114       *
 115       * @since 2.8.0
 116       * @var string
 117       */
 118      public $ext_version = '';
 119  
 120      /**
 121       * List of default directories.
 122       *
 123       * @since 2.8.0
 124       * @see wp_default_scripts()
 125       * @var string[]|null
 126       */
 127      public $default_dirs;
 128  
 129      /**
 130       * Holds a mapping of dependents (as handles) for a given script handle.
 131       * Used to optimize recursive dependency tree checks.
 132       *
 133       * @since 6.3.0
 134       * @var array<string, string[]>
 135       */
 136      private $dependents_map = array();
 137  
 138      /**
 139       * Holds a reference to the delayed (non-blocking) script loading strategies.
 140       * Used by methods that validate loading strategies.
 141       *
 142       * @since 6.3.0
 143       * @var string[]
 144       */
 145      private $delayed_strategies = array( 'defer', 'async' );
 146  
 147      /**
 148       * Constructor.
 149       *
 150       * @since 2.6.0
 151       */
 152  	public function __construct() {
 153          $this->init();
 154          add_action( 'init', array( $this, 'init' ), 0 );
 155      }
 156  
 157      /**
 158       * Initialize the class.
 159       *
 160       * @since 3.4.0
 161       */
 162  	public function init() {
 163          /**
 164           * Fires when the WP_Scripts instance is initialized.
 165           *
 166           * @since 2.6.0
 167           *
 168           * @param WP_Scripts $wp_scripts WP_Scripts instance (passed by reference).
 169           */
 170          do_action_ref_array( 'wp_default_scripts', array( &$this ) );
 171      }
 172  
 173      /**
 174       * Prints scripts.
 175       *
 176       * Prints the scripts passed to it or the print queue. Also prints all necessary dependencies.
 177       *
 178       * @since 2.1.0
 179       * @since 2.8.0 Added the `$group` parameter.
 180       *
 181       * @param string|string[]|false $handles Optional. Scripts to be printed: queue (false),
 182       *                                       single script (string), or multiple scripts (array of strings).
 183       *                                       Default false.
 184       * @param int|false             $group   Optional. Group level: level (int), no groups (false).
 185       *                                       Default false.
 186       * @return string[] Handles of scripts that have been printed.
 187       */
 188  	public function print_scripts( $handles = false, $group = false ) {
 189          return $this->do_items( $handles, $group );
 190      }
 191  
 192      /**
 193       * Prints extra scripts of a registered script.
 194       *
 195       * @since 2.1.0
 196       * @since 2.8.0 Added the `$display` parameter.
 197       * @deprecated 3.3.0
 198       *
 199       * @see print_extra_script()
 200       *
 201       * @param string $handle  The script's registered handle.
 202       * @param bool   $display Optional. Whether to print the extra script
 203       *                        instead of just returning it. Default true.
 204       * @return bool|string|null Null if no data exists, extra scripts if `$display` is true,
 205       *                          true otherwise.
 206       */
 207  	public function print_scripts_l10n( $handle, $display = true ) {
 208          _deprecated_function( __FUNCTION__, '3.3.0', 'WP_Scripts::print_extra_script()' );
 209          return $this->print_extra_script( $handle, $display );
 210      }
 211  
 212      /**
 213       * Prints extra scripts of a registered script.
 214       *
 215       * @since 3.3.0
 216       *
 217       * @param string $handle  The script's registered handle.
 218       * @param bool   $display Optional. Whether to print the extra script
 219       *                        instead of just returning it. Default true.
 220       * @return bool|string|null Null if no data exists, extra scripts if `$display` is true,
 221       *                          true otherwise.
 222       */
 223  	public function print_extra_script( $handle, $display = true ) {
 224          $output = $this->get_data( $handle, 'data' );
 225          if ( ! $output ) {
 226              return null;
 227          }
 228  
 229          /*
 230           * Do not print a sourceURL comment if concatenation is enabled.
 231           *
 232           * Extra scripts may be concatenated into a single script.
 233           * The line-based sourceURL comments may break concatenated scripts
 234           * and do not make sense when multiple scripts are joined together.
 235           */
 236          if ( ! $this->do_concat ) {
 237              $output .= sprintf(
 238                  "\n//# sourceURL=%s",
 239                  rawurlencode( "{$handle}-js-extra" )
 240              );
 241          }
 242  
 243          if ( ! $display ) {
 244              return $output;
 245          }
 246  
 247          wp_print_inline_script_tag( $output, array( 'id' => "{$handle}-js-extra" ) );
 248  
 249          return true;
 250      }
 251  
 252      /**
 253       * Checks whether all dependents of a given handle are in the footer.
 254       *
 255       * If there are no dependents, this is considered the same as if all dependents were in the footer.
 256       *
 257       * @since 6.4.0
 258       *
 259       * @param string $handle Script handle.
 260       * @return bool Whether all dependents are in the footer.
 261       */
 262  	private function are_all_dependents_in_footer( $handle ) {
 263          foreach ( $this->get_dependents( $handle ) as $dep ) {
 264              if ( isset( $this->groups[ $dep ] ) && 0 === $this->groups[ $dep ] ) {
 265                  return false;
 266              }
 267          }
 268          return true;
 269      }
 270  
 271      /**
 272       * Processes a script dependency.
 273       *
 274       * @since 2.6.0
 275       * @since 2.8.0 Added the `$group` parameter.
 276       *
 277       * @see WP_Dependencies::do_item()
 278       *
 279       * @param string    $handle The script's registered handle.
 280       * @param int|false $group  Optional. Group level: level (int), no groups (false).
 281       *                          Default false.
 282       * @return bool True on success, false on failure.
 283       */
 284  	public function do_item( $handle, $group = false ) {
 285          if ( ! parent::do_item( $handle ) ) {
 286              return false;
 287          }
 288  
 289          if ( 0 === $group && $this->groups[ $handle ] > 0 ) {
 290              $this->in_footer[] = $handle;
 291              return false;
 292          }
 293  
 294          if ( false === $group && in_array( $handle, $this->in_footer, true ) ) {
 295              $this->in_footer = array_diff( $this->in_footer, (array) $handle );
 296          }
 297  
 298          $obj = $this->registered[ $handle ];
 299          if ( $obj->extra['conditional'] ?? false ) {
 300              return false;
 301          }
 302  
 303          if ( null === $obj->ver ) {
 304              $ver = '';
 305          } else {
 306              $ver = $obj->ver ? $obj->ver : $this->default_version;
 307          }
 308  
 309          if ( isset( $this->args[ $handle ] ) ) {
 310              $ver = $ver ? $ver . '&amp;' . $this->args[ $handle ] : $this->args[ $handle ];
 311          }
 312  
 313          $src               = $obj->src;
 314          $strategy          = $this->get_eligible_loading_strategy( $handle );
 315          $intended_strategy = (string) $this->get_data( $handle, 'strategy' );
 316  
 317          if ( ! $this->is_delayed_strategy( $intended_strategy ) ) {
 318              $intended_strategy = '';
 319          }
 320  
 321          /*
 322           * Move this script to the footer if:
 323           * 1. The script is in the header group.
 324           * 2. The current output is the header.
 325           * 3. The intended strategy is delayed.
 326           * 4. The actual strategy is not delayed.
 327           * 5. All dependent scripts are in the footer.
 328           */
 329          if (
 330              0 === $group &&
 331              0 === $this->groups[ $handle ] &&
 332              $intended_strategy &&
 333              ! $this->is_delayed_strategy( $strategy ) &&
 334              $this->are_all_dependents_in_footer( $handle )
 335          ) {
 336              $this->in_footer[] = $handle;
 337              return false;
 338          }
 339  
 340          $before_script = $this->get_inline_script_tag( $handle, 'before' );
 341          $after_script  = $this->get_inline_script_tag( $handle, 'after' );
 342  
 343          if ( $before_script || $after_script ) {
 344              $inline_script_tag = $before_script . $after_script;
 345          } else {
 346              $inline_script_tag = '';
 347          }
 348  
 349          /*
 350           * Prevent concatenation of scripts if the text domain is defined
 351           * to ensure the dependency order is respected.
 352           */
 353          $translations_stop_concat = ! empty( $obj->textdomain );
 354  
 355          $translations = $this->print_translations( $handle, false );
 356          if ( $translations ) {
 357              /*
 358               * The sourceURL comment is not included by WP_Scripts::print_translations()
 359               * when `$display` is `false` to prevent issues where the script tag contents are used
 360               * by extenders for other purposes, for example concatenated with other script content.
 361               *
 362               * Include the sourceURL comment here as it would be when printed directly.
 363               */
 364              $source_url    = rawurlencode( "{$handle}-js-translations" );
 365              $translations .= "\n//# sourceURL={$source_url}";
 366              $translations  = wp_get_inline_script_tag( $translations, array( 'id' => "{$handle}-js-translations" ) );
 367          }
 368  
 369          if ( $this->do_concat ) {
 370              /**
 371               * Filters the script loader source.
 372               *
 373               * @since 2.2.0
 374               *
 375               * @param string $src    Script loader source path.
 376               * @param string $handle Script handle.
 377               */
 378              $filtered_src = apply_filters( 'script_loader_src', $src, $handle );
 379  
 380              if (
 381                  is_string( $filtered_src )
 382                  && $this->in_default_dir( $filtered_src )
 383                  && ( $before_script || $after_script || $translations_stop_concat || $this->is_delayed_strategy( $strategy ) )
 384              ) {
 385                  $this->do_concat = false;
 386  
 387                  // Have to print the so-far concatenated scripts right away to maintain the right order.
 388                  _print_scripts();
 389                  $this->reset();
 390              } elseif ( $this->in_default_dir( $filtered_src ) ) {
 391                  $this->print_code     .= $this->print_extra_script( $handle, false );
 392                  $this->concat         .= "$handle,";
 393                  $this->concat_version .= "$handle$ver";
 394                  return true;
 395              } else {
 396                  $this->ext_handles .= "$handle,";
 397                  $this->ext_version .= "$handle$ver";
 398              }
 399          }
 400  
 401          $this->print_extra_script( $handle );
 402  
 403          // A single item may alias a set of items, by having dependencies, but no source.
 404          if ( ! $src ) {
 405              if ( $inline_script_tag ) {
 406                  if ( $this->do_concat ) {
 407                      $this->print_html .= $inline_script_tag;
 408                  } else {
 409                      echo $inline_script_tag;
 410                  }
 411              }
 412  
 413              return true;
 414          }
 415  
 416          if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $this->content_url && str_starts_with( $src, $this->content_url ) ) ) {
 417              $src = $this->base_url . $src;
 418          }
 419  
 420          $ver_to_add = '';
 421          if ( empty( $obj->ver ) && null !== $obj->ver && is_string( $this->default_version ) ) {
 422              $ver_to_add = $this->default_version;
 423          } elseif ( is_scalar( $obj->ver ) ) {
 424              $ver_to_add = (string) $obj->ver;
 425          }
 426  
 427          $added_args = (string) ( $this->args[ $handle ] ?? '' );
 428  
 429          if ( '' !== $ver_to_add || '' !== $added_args ) {
 430              $fragment = strstr( $src, '#' );
 431              if ( false !== $fragment ) {
 432                  $src = substr( $src, 0, -strlen( $fragment ) );
 433              }
 434  
 435              if ( '' !== $ver_to_add ) {
 436                  $src .= ( str_contains( $src, '?' ) ? '&' : '?' ) . 'ver=' . rawurlencode( $ver_to_add );
 437              }
 438              if ( '' !== $added_args ) {
 439                  $src .= ( str_contains( $src, '?' ) ? '&' : '?' ) . $added_args;
 440              }
 441  
 442              if ( false !== $fragment ) {
 443                  $src .= $fragment;
 444              }
 445          }
 446  
 447          /** This filter is documented in wp-includes/class-wp-scripts.php */
 448          $src = esc_url_raw( apply_filters( 'script_loader_src', $src, $handle ) );
 449  
 450          if ( ! $src ) {
 451              return true;
 452          }
 453  
 454          $attr = array(
 455              'src' => $src,
 456              'id'  => "{$handle}-js",
 457          );
 458          if ( $strategy ) {
 459              $attr[ $strategy ] = true;
 460          }
 461          if ( $intended_strategy ) {
 462              $attr['data-wp-strategy'] = $intended_strategy;
 463          }
 464  
 465          // Determine fetchpriority.
 466          $original_fetchpriority = $obj->extra['fetchpriority'] ?? null;
 467          if ( null === $original_fetchpriority || ! $this->is_valid_fetchpriority( $original_fetchpriority ) ) {
 468              $original_fetchpriority = 'auto';
 469          }
 470          $actual_fetchpriority = $this->get_highest_fetchpriority_with_dependents( $handle );
 471          if ( null === $actual_fetchpriority ) {
 472              // If null, it's likely this script was not explicitly enqueued, so in this case use the original priority.
 473              $actual_fetchpriority = $original_fetchpriority;
 474          }
 475          if ( is_string( $actual_fetchpriority ) && 'auto' !== $actual_fetchpriority ) {
 476              $attr['fetchpriority'] = $actual_fetchpriority;
 477          }
 478  
 479          if ( $original_fetchpriority !== $actual_fetchpriority ) {
 480              $attr['data-wp-fetchpriority'] = $original_fetchpriority;
 481          }
 482  
 483          $tag  = $translations . $before_script;
 484          $tag .= wp_get_script_tag( $attr );
 485          $tag .= $after_script;
 486  
 487          /**
 488           * Filters the HTML script tag of an enqueued script.
 489           *
 490           * @since 4.1.0
 491           *
 492           * @param string $tag    The `<script>` tag for the enqueued script.
 493           * @param string $handle The script's registered handle.
 494           * @param string $src    The script's source URL.
 495           */
 496          $tag = apply_filters( 'script_loader_tag', $tag, $handle, $src );
 497  
 498          if ( $this->do_concat ) {
 499              $this->print_html .= $tag;
 500          } else {
 501              echo $tag;
 502          }
 503  
 504          return true;
 505      }
 506  
 507      /**
 508       * Adds extra code to a registered script.
 509       *
 510       * @since 4.5.0
 511       *
 512       * @param string $handle   Name of the script to add the inline script to.
 513       *                         Must be lowercase.
 514       * @param string $data     String containing the JavaScript to be added.
 515       * @param string $position Optional. Whether to add the inline script
 516       *                         before the handle or after. Default 'after'.
 517       * @return bool True on success, false on failure.
 518       */
 519  	public function add_inline_script( $handle, $data, $position = 'after' ) {
 520          if ( ! $data ) {
 521              return false;
 522          }
 523  
 524          if ( 'after' !== $position ) {
 525              $position = 'before';
 526          }
 527  
 528          $script   = (array) $this->get_data( $handle, $position );
 529          $script[] = $data;
 530  
 531          return $this->add_data( $handle, $position, $script );
 532      }
 533  
 534      /**
 535       * Prints inline scripts registered for a specific handle.
 536       *
 537       * @since 4.5.0
 538       * @deprecated 6.3.0 Use methods get_inline_script_tag() or get_inline_script_data() instead.
 539       *
 540       * @param string $handle   Name of the script to print inline scripts for.
 541       *                         Must be lowercase.
 542       * @param string $position Optional. Whether to add the inline script
 543       *                         before the handle or after. Default 'after'.
 544       * @param bool   $display  Optional. Whether to print the script tag
 545       *                         instead of just returning the script data. Default true.
 546       * @return string|false Script data on success, false otherwise.
 547       */
 548  	public function print_inline_script( $handle, $position = 'after', $display = true ) {
 549          _deprecated_function( __METHOD__, '6.3.0', 'WP_Scripts::get_inline_script_data() or WP_Scripts::get_inline_script_tag()' );
 550  
 551          $output = $this->get_inline_script_data( $handle, $position );
 552          if ( empty( $output ) ) {
 553              return false;
 554          }
 555  
 556          if ( $display ) {
 557              echo $this->get_inline_script_tag( $handle, $position );
 558          }
 559          return $output;
 560      }
 561  
 562      /**
 563       * Gets data for inline scripts registered for a specific handle.
 564       *
 565       * @since 6.3.0
 566       *
 567       * @param string $handle   Name of the script to get data for.
 568       *                         Must be lowercase.
 569       * @param string $position Optional. Whether to add the inline script
 570       *                         before the handle or after. Default 'after'.
 571       * @return string Inline script, which may be empty string.
 572       */
 573  	public function get_inline_script_data( $handle, $position = 'after' ) {
 574          $data = $this->get_data( $handle, $position );
 575          if ( empty( $data ) || ! is_array( $data ) ) {
 576              return '';
 577          }
 578  
 579          /*
 580           * Print sourceURL comment regardless of concatenation.
 581           *
 582           * Inline scripts prevent scripts from being concatenated, so
 583           * sourceURL comments are safe to print for inline scripts.
 584           */
 585          $data[] = sprintf(
 586              '//# sourceURL=%s',
 587              rawurlencode( "{$handle}-js-{$position}" )
 588          );
 589  
 590          return trim( implode( "\n", $data ), "\n" );
 591      }
 592  
 593      /**
 594       * Gets tags for inline scripts registered for a specific handle.
 595       *
 596       * @since 6.3.0
 597       *
 598       * @param string $handle   Name of the script to get associated inline script tag for.
 599       *                         Must be lowercase.
 600       * @param string $position Optional. Whether to get tag for inline
 601       *                         scripts in the before or after position. Default 'after'.
 602       * @return string Inline script, which may be empty string.
 603       */
 604  	public function get_inline_script_tag( $handle, $position = 'after' ) {
 605          $js = $this->get_inline_script_data( $handle, $position );
 606          if ( empty( $js ) ) {
 607              return '';
 608          }
 609  
 610          $id = "{$handle}-js-{$position}";
 611  
 612          return wp_get_inline_script_tag( $js, compact( 'id' ) );
 613      }
 614  
 615      /**
 616       * Localizes a script, only if the script has already been added.
 617       *
 618       * @since 2.1.0
 619       *
 620       * @param string               $handle      Name of the script to attach data to.
 621       * @param string               $object_name Name of the variable that will contain the data.
 622       * @param array<string, mixed> $l10n        Array of data to localize.
 623       * @return bool True on success, false on failure.
 624       */
 625  	public function localize( $handle, $object_name, $l10n ) {
 626          if ( 'jquery' === $handle ) {
 627              $handle = 'jquery-core';
 628          }
 629  
 630          if ( is_array( $l10n ) && isset( $l10n['l10n_print_after'] ) ) { // back compat, preserve the code in 'l10n_print_after' if present.
 631              $after = $l10n['l10n_print_after'];
 632              unset( $l10n['l10n_print_after'] );
 633          }
 634  
 635          if ( ! is_array( $l10n ) ) {
 636              _doing_it_wrong(
 637                  __METHOD__,
 638                  sprintf(
 639                      /* translators: 1: $l10n, 2: wp_add_inline_script() */
 640                      __( 'The %1$s parameter must be an array. To pass arbitrary data to scripts, use the %2$s function instead.' ),
 641                      '<code>$l10n</code>',
 642                      '<code>wp_add_inline_script()</code>'
 643                  ),
 644                  '5.7.0'
 645              );
 646  
 647              if ( false === $l10n ) {
 648                  // This should really not be needed, but is necessary for backward compatibility.
 649                  $l10n = array( $l10n );
 650              }
 651          }
 652  
 653          if ( is_string( $l10n ) ) {
 654              $l10n = html_entity_decode( $l10n, ENT_QUOTES, 'UTF-8' );
 655          } elseif ( is_array( $l10n ) ) {
 656              foreach ( $l10n as $key => $value ) {
 657                  if ( ! is_scalar( $value ) ) {
 658                      continue;
 659                  }
 660  
 661                  $l10n[ $key ] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' );
 662              }
 663          }
 664  
 665          $script = "var $object_name = " . wp_json_encode( $l10n, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) . ';';
 666  
 667          if ( ! empty( $after ) ) {
 668              $script .= "\n$after;";
 669          }
 670  
 671          $data = $this->get_data( $handle, 'data' );
 672  
 673          if ( ! empty( $data ) ) {
 674              $script = "$data\n$script";
 675          }
 676  
 677          return $this->add_data( $handle, 'data', $script );
 678      }
 679  
 680      /**
 681       * Sets handle group.
 682       *
 683       * @since 2.8.0
 684       *
 685       * @see WP_Dependencies::set_group()
 686       *
 687       * @param string    $handle    Name of the item. Should be unique.
 688       * @param bool      $recursion Internal flag that calling function was called recursively.
 689       * @param int|false $group     Optional. Group level: level (int), no groups (false).
 690       *                             Default false.
 691       * @return bool Not already in the group or a lower group.
 692       */
 693  	public function set_group( $handle, $recursion, $group = false ) {
 694          if ( isset( $this->registered[ $handle ]->args ) && 1 === $this->registered[ $handle ]->args ) {
 695              $calculated_group = 1;
 696          } else {
 697              $calculated_group = (int) $this->get_data( $handle, 'group' );
 698          }
 699  
 700          if ( false !== $group && $calculated_group > $group ) {
 701              $calculated_group = $group;
 702          }
 703  
 704          return parent::set_group( $handle, $recursion, $calculated_group );
 705      }
 706  
 707      /**
 708       * Sets a translation textdomain.
 709       *
 710       * @since 5.0.0
 711       * @since 5.1.0 The `$domain` parameter was made optional.
 712       *
 713       * @param string $handle Name of the script to register a translation domain to.
 714       * @param string $domain Optional. Text domain. Default 'default'.
 715       * @param string $path   Optional. The full file path to the directory containing translation files.
 716       * @return bool True if the text domain was registered, false if not.
 717       */
 718  	public function set_translations( $handle, $domain = 'default', $path = '' ) {
 719          if ( ! isset( $this->registered[ $handle ] ) ) {
 720              return false;
 721          }
 722  
 723          /** @var \_WP_Dependency $obj */
 724          $obj = $this->registered[ $handle ];
 725  
 726          if ( ! in_array( 'wp-i18n', $obj->deps, true ) ) {
 727              $obj->deps[] = 'wp-i18n';
 728          }
 729  
 730          return $obj->set_translations( $domain, $path );
 731      }
 732  
 733      /**
 734       * Prints translations set for a specific handle.
 735       *
 736       * @since 5.0.0
 737       *
 738       * @param string $handle  Name of the script to add the inline script to.
 739       *                        Must be lowercase.
 740       * @param bool   $display Optional. Whether to print the script
 741       *                        instead of just returning it. Default true.
 742       * @return string|false Script on success, false otherwise.
 743       */
 744  	public function print_translations( $handle, $display = true ) {
 745          if ( ! isset( $this->registered[ $handle ] ) || empty( $this->registered[ $handle ]->textdomain ) ) {
 746              return false;
 747          }
 748  
 749          $domain = $this->registered[ $handle ]->textdomain;
 750          $path   = '';
 751  
 752          if ( isset( $this->registered[ $handle ]->translations_path ) ) {
 753              $path = $this->registered[ $handle ]->translations_path;
 754          }
 755  
 756          $json_translations = load_script_textdomain( $handle, $domain, $path );
 757  
 758          if ( ! $json_translations ) {
 759              return false;
 760          }
 761  
 762          $output = <<<JS
 763  ( function( domain, translations ) {
 764      var localeData = translations.locale_data[ domain ] || translations.locale_data.messages;
 765      localeData[""].domain = domain;
 766      wp.i18n.setLocaleData( localeData, domain );
 767  } )( "{$domain}", {$json_translations} );
 768  JS;
 769  
 770          if ( $display ) {
 771              $source_url = rawurlencode( "{$handle}-js-translations" );
 772              $output    .= "\n//# sourceURL={$source_url}";
 773              wp_print_inline_script_tag( $output, array( 'id' => "{$handle}-js-translations" ) );
 774          }
 775  
 776          return $output;
 777      }
 778  
 779      /**
 780       * Determines script dependencies.
 781       *
 782       * @since 2.1.0
 783       *
 784       * @see WP_Dependencies::all_deps()
 785       *
 786       * @param string|string[] $handles   Item handle (string) or item handles (array of strings).
 787       * @param bool            $recursion Optional. Internal flag that function is calling itself.
 788       *                                   Default false.
 789       * @param int|false       $group     Optional. Group level: level (int), no groups (false).
 790       *                                   Default false.
 791       * @return bool True on success, false on failure.
 792       */
 793  	public function all_deps( $handles, $recursion = false, $group = false ) {
 794          $result = parent::all_deps( $handles, $recursion, $group );
 795          if ( ! $recursion ) {
 796              /**
 797               * Filters the list of script dependencies left to print.
 798               *
 799               * @since 2.3.0
 800               *
 801               * @param string[] $to_do An array of script dependency handles.
 802               */
 803              $this->to_do = apply_filters( 'print_scripts_array', $this->to_do );
 804          }
 805          return $result;
 806      }
 807  
 808      /**
 809       * Processes items and dependencies for the head group.
 810       *
 811       * @since 2.8.0
 812       *
 813       * @see WP_Dependencies::do_items()
 814       *
 815       * @return string[] Handles of items that have been processed.
 816       */
 817  	public function do_head_items() {
 818          $this->do_items( false, 0 );
 819          return $this->done;
 820      }
 821  
 822      /**
 823       * Processes items and dependencies for the footer group.
 824       *
 825       * @since 2.8.0
 826       *
 827       * @see WP_Dependencies::do_items()
 828       *
 829       * @return string[] Handles of items that have been processed.
 830       */
 831  	public function do_footer_items() {
 832          $this->do_items( false, 1 );
 833          return $this->done;
 834      }
 835  
 836      /**
 837       * Whether a handle's source is in a default directory.
 838       *
 839       * @since 2.8.0
 840       *
 841       * @param string $src The source of the enqueued script.
 842       * @return bool True if found, false if not.
 843       */
 844  	public function in_default_dir( $src ) {
 845          if ( ! $this->default_dirs ) {
 846              return true;
 847          }
 848  
 849          if ( str_starts_with( $src, '/' . WPINC . '/js/l10n' ) ) {
 850              return false;
 851          }
 852  
 853          foreach ( (array) $this->default_dirs as $test ) {
 854              if ( str_starts_with( $src, $test ) ) {
 855                  return true;
 856              }
 857          }
 858          return false;
 859      }
 860  
 861      /**
 862       * This overrides the add_data method from WP_Dependencies, to support normalizing of $args.
 863       *
 864       * @since 6.3.0
 865       *
 866       * @param string $handle Name of the item. Should be unique.
 867       * @param string $key    The data key.
 868       * @param mixed  $value  The data value.
 869       * @return bool True on success, false on failure.
 870       */
 871  	public function add_data( $handle, $key, $value ) {
 872          if ( ! isset( $this->registered[ $handle ] ) ) {
 873              return false;
 874          }
 875  
 876          if ( 'conditional' === $key ) {
 877              // If a dependency is declared by a conditional script, remove it.
 878              $this->registered[ $handle ]->deps = array();
 879          }
 880  
 881          if ( 'strategy' === $key ) {
 882              if ( ! empty( $value ) && ! $this->is_delayed_strategy( $value ) ) {
 883                  _doing_it_wrong(
 884                      __METHOD__,
 885                      sprintf(
 886                          /* translators: 1: $strategy, 2: $handle */
 887                          __( 'Invalid strategy `%1$s` defined for `%2$s` during script registration.' ),
 888                          is_string( $value ) ? $value : gettype( $value ),
 889                          $handle
 890                      ),
 891                      '6.3.0'
 892                  );
 893                  return false;
 894              } elseif ( ! $this->registered[ $handle ]->src && $this->is_delayed_strategy( $value ) ) {
 895                  _doing_it_wrong(
 896                      __METHOD__,
 897                      sprintf(
 898                          /* translators: 1: $strategy, 2: $handle */
 899                          __( 'Cannot supply a strategy `%1$s` for script `%2$s` because it is an alias (it lacks a `src` value).' ),
 900                          is_string( $value ) ? $value : gettype( $value ),
 901                          $handle
 902                      ),
 903                      '6.3.0'
 904                  );
 905                  return false;
 906              }
 907          } elseif ( 'fetchpriority' === $key ) {
 908              if ( empty( $value ) ) {
 909                  $value = 'auto';
 910              }
 911              if ( ! $this->is_valid_fetchpriority( $value ) ) {
 912                  _doing_it_wrong(
 913                      __METHOD__,
 914                      sprintf(
 915                          /* translators: 1: $fetchpriority, 2: $handle */
 916                          __( 'Invalid fetchpriority `%1$s` defined for `%2$s` during script registration.' ),
 917                          is_string( $value ) ? $value : gettype( $value ),
 918                          $handle
 919                      ),
 920                      '6.9.0'
 921                  );
 922                  return false;
 923              } elseif ( ! $this->registered[ $handle ]->src ) {
 924                  _doing_it_wrong(
 925                      __METHOD__,
 926                      sprintf(
 927                          /* translators: 1: $fetchpriority, 2: $handle */
 928                          __( 'Cannot supply a fetchpriority `%1$s` for script `%2$s` because it is an alias (it lacks a `src` value).' ),
 929                          is_string( $value ) ? $value : gettype( $value ),
 930                          $handle
 931                      ),
 932                      '6.9.0'
 933                  );
 934                  return false;
 935              }
 936          } elseif ( 'module_dependencies' === $key ) {
 937              if ( ! is_array( $value ) ) {
 938                  _doing_it_wrong(
 939                      __METHOD__,
 940                      sprintf(
 941                          /* translators: 1: 'module_dependencies', 2: Script handle. */
 942                          __( 'The value for "%1$s" must be an array for the "%2$s" script.' ),
 943                          'module_dependencies',
 944                          $handle
 945                      ),
 946                      '7.0.0'
 947                  );
 948                  return false;
 949              }
 950  
 951              $sanitized_value = array();
 952              $has_invalid_ids = false;
 953              foreach ( $value as $module ) {
 954                  if (
 955                      is_string( $module ) ||
 956                      ( is_array( $module ) && isset( $module['id'] ) && is_string( $module['id'] ) )
 957                  ) {
 958                      $sanitized_value[] = $module;
 959                  } else {
 960                      $has_invalid_ids = true;
 961                  }
 962              }
 963  
 964              if ( $has_invalid_ids ) {
 965                  _doing_it_wrong(
 966                      __METHOD__,
 967                      sprintf(
 968                          /* translators: 1: Script handle, 2: 'module_dependencies' */
 969                          __( 'The script handle "%1$s" has one or more of its script module dependencies ("%2$s") which are invalid.' ),
 970                          $handle,
 971                          'module_dependencies'
 972                      ),
 973                      '7.0.0'
 974                  );
 975              }
 976  
 977              $value = $sanitized_value;
 978          }
 979          return parent::add_data( $handle, $key, $value );
 980      }
 981  
 982      /**
 983       * Gets all dependents of a script.
 984       *
 985       * This is not recursive.
 986       *
 987       * @since 6.3.0
 988       *
 989       * @param string $handle The script handle.
 990       * @return string[] Script handles.
 991       */
 992  	private function get_dependents( $handle ) {
 993          // Check if dependents map for the handle in question is present. If so, use it.
 994          if ( isset( $this->dependents_map[ $handle ] ) ) {
 995              return $this->dependents_map[ $handle ];
 996          }
 997  
 998          $dependents = array();
 999  
1000          // Iterate over all registered scripts, finding dependents of the script passed to this method.
1001          foreach ( $this->registered as $registered_handle => $args ) {
1002              if ( in_array( $handle, $args->deps, true ) ) {
1003                  $dependents[] = $registered_handle;
1004              }
1005          }
1006  
1007          // Add the handles dependents to the map to ease future lookups.
1008          $this->dependents_map[ $handle ] = $dependents;
1009  
1010          return $dependents;
1011      }
1012  
1013      /**
1014       * Checks if the strategy passed is a valid delayed (non-blocking) strategy.
1015       *
1016       * @since 6.3.0
1017       *
1018       * @param string|mixed $strategy The strategy to check.
1019       * @return bool True if $strategy is one of the delayed strategies, otherwise false.
1020       */
1021  	private function is_delayed_strategy( $strategy ): bool {
1022          return in_array(
1023              $strategy,
1024              $this->delayed_strategies,
1025              true
1026          );
1027      }
1028  
1029      /**
1030       * Checks if the provided fetchpriority is valid.
1031       *
1032       * @since 6.9.0
1033       *
1034       * @param string|mixed $priority Fetch priority.
1035       * @return bool Whether valid fetchpriority.
1036       */
1037  	private function is_valid_fetchpriority( $priority ): bool {
1038          return in_array( $priority, array( 'auto', 'low', 'high' ), true );
1039      }
1040  
1041      /**
1042       * Gets the best eligible loading strategy for a script.
1043       *
1044       * @since 6.3.0
1045       *
1046       * @param string $handle The script handle.
1047       * @return string The best eligible loading strategy.
1048       */
1049  	private function get_eligible_loading_strategy( $handle ) {
1050          $intended_strategy = (string) $this->get_data( $handle, 'strategy' );
1051  
1052          // Bail early if there is no intended strategy.
1053          if ( ! $intended_strategy ) {
1054              return '';
1055          }
1056  
1057          /*
1058           * If the intended strategy is 'defer', limit the initial list of eligible
1059           * strategies, since 'async' can fallback to 'defer', but not vice-versa.
1060           */
1061          $initial_strategy = ( 'defer' === $intended_strategy ) ? array( 'defer' ) : null;
1062  
1063          $eligible_strategies = $this->filter_eligible_strategies( $handle, $initial_strategy );
1064  
1065          // Return early once we know the eligible strategy is blocking.
1066          if ( empty( $eligible_strategies ) ) {
1067              return '';
1068          }
1069  
1070          return in_array( 'async', $eligible_strategies, true ) ? 'async' : 'defer';
1071      }
1072  
1073      /**
1074       * Filter the list of eligible loading strategies for a script.
1075       *
1076       * @since 6.3.0
1077       *
1078       * @param string                  $handle              The script handle.
1079       * @param string[]|null           $eligible_strategies Optional. The list of strategies to filter. Default null.
1080       * @param array<string, true>     $checked             Optional. An array of already checked script handles, used to avoid recursive loops.
1081       * @param array<string, string[]> $stored_results      Optional. An array of already computed eligible loading strategies by handle, used to increase performance in large dependency lists.
1082       * @return string[] A list of eligible loading strategies that could be used.
1083       */
1084  	private function filter_eligible_strategies( $handle, $eligible_strategies = null, $checked = array(), array &$stored_results = array() ) {
1085          if ( isset( $stored_results[ $handle ] ) ) {
1086              return $stored_results[ $handle ];
1087          }
1088  
1089          // If no strategies are being passed, all strategies are eligible.
1090          if ( null === $eligible_strategies ) {
1091              $eligible_strategies = $this->delayed_strategies;
1092          }
1093  
1094          // If this handle was already checked, return early.
1095          if ( isset( $checked[ $handle ] ) ) {
1096              return $eligible_strategies;
1097          }
1098  
1099          // Mark this handle as checked.
1100          $checked[ $handle ] = true;
1101  
1102          // If this handle isn't registered, don't filter anything and return.
1103          if ( ! isset( $this->registered[ $handle ] ) ) {
1104              return $eligible_strategies;
1105          }
1106  
1107          // If the handle is not enqueued, don't filter anything and return.
1108          if ( ! $this->query( $handle, 'enqueued' ) ) {
1109              return $eligible_strategies;
1110          }
1111  
1112          $is_alias          = (bool) ! $this->registered[ $handle ]->src;
1113          $intended_strategy = $this->get_data( $handle, 'strategy' );
1114  
1115          // For non-alias handles, an empty intended strategy filters all strategies.
1116          if ( ! $is_alias && empty( $intended_strategy ) ) {
1117              return array();
1118          }
1119  
1120          // Handles with inline scripts attached in the 'after' position cannot be delayed.
1121          if ( $this->has_inline_script( $handle, 'after' ) ) {
1122              return array();
1123          }
1124  
1125          // If the intended strategy is 'defer', filter out 'async'.
1126          if ( 'defer' === $intended_strategy ) {
1127              $eligible_strategies = array( 'defer' );
1128          }
1129  
1130          $dependents = $this->get_dependents( $handle );
1131  
1132          // Recursively filter eligible strategies for dependents.
1133          foreach ( $dependents as $dependent ) {
1134              // Bail early once we know the eligible strategy is blocking.
1135              if ( empty( $eligible_strategies ) ) {
1136                  return array();
1137              }
1138  
1139              $eligible_strategies = $this->filter_eligible_strategies( $dependent, $eligible_strategies, $checked, $stored_results );
1140          }
1141          $stored_results[ $handle ] = $eligible_strategies;
1142          return $eligible_strategies;
1143      }
1144  
1145      /**
1146       * Gets the highest fetch priority for a given script and all of its dependent scripts.
1147       *
1148       * @since 6.9.0
1149       * @see self::filter_eligible_strategies()
1150       * @see WP_Script_Modules::get_highest_fetchpriority()
1151       *
1152       * @param string                $handle         Script module ID.
1153       * @param array<string, true>   $checked        Optional. An array of already checked script handles, used to avoid recursive loops.
1154       * @param array<string, string> $stored_results Optional. An array of already computed max priority by handle, used to increase performance in large dependency lists.
1155       * @return string|null Highest fetch priority for the script and its dependents.
1156       */
1157  	private function get_highest_fetchpriority_with_dependents( string $handle, array $checked = array(), array &$stored_results = array() ): ?string {
1158          if ( isset( $stored_results[ $handle ] ) ) {
1159              return $stored_results[ $handle ];
1160          }
1161  
1162          // If there is a recursive dependency, return early.
1163          if ( isset( $checked[ $handle ] ) ) {
1164              return null;
1165          }
1166  
1167          // Mark this handle as checked to guard against infinite recursion.
1168          $checked[ $handle ] = true;
1169  
1170          // Abort if the script is not enqueued or a dependency of an enqueued script.
1171          if ( ! $this->query( $handle, 'enqueued' ) ) {
1172              return null;
1173          }
1174  
1175          $fetchpriority = $this->get_data( $handle, 'fetchpriority' );
1176          if ( ! $this->is_valid_fetchpriority( $fetchpriority ) ) {
1177              $fetchpriority = 'auto';
1178          }
1179  
1180          static $priorities   = array(
1181              'low',
1182              'auto',
1183              'high',
1184          );
1185          $high_priority_index = count( $priorities ) - 1;
1186  
1187          $highest_priority_index = (int) array_search( $fetchpriority, $priorities, true );
1188          if ( $highest_priority_index !== $high_priority_index ) {
1189              foreach ( $this->get_dependents( $handle ) as $dependent_handle ) {
1190                  $dependent_priority = $this->get_highest_fetchpriority_with_dependents( $dependent_handle, $checked, $stored_results );
1191                  if ( is_string( $dependent_priority ) ) {
1192                      $highest_priority_index = max(
1193                          $highest_priority_index,
1194                          (int) array_search( $dependent_priority, $priorities, true )
1195                      );
1196                      if ( $highest_priority_index === $high_priority_index ) {
1197                          break;
1198                      }
1199                  }
1200              }
1201          }
1202          $stored_results[ $handle ] = $priorities[ $highest_priority_index ];
1203          return $priorities[ $highest_priority_index ];
1204      }
1205  
1206      /**
1207       * Gets data for inline scripts registered for a specific handle.
1208       *
1209       * @since 6.3.0
1210       *
1211       * @param string $handle   Name of the script to get data for. Must be lowercase.
1212       * @param string $position The position of the inline script.
1213       * @return bool Whether the handle has an inline script (either before or after).
1214       */
1215  	private function has_inline_script( $handle, $position = null ) {
1216          if ( $position && in_array( $position, array( 'before', 'after' ), true ) ) {
1217              return (bool) $this->get_data( $handle, $position );
1218          }
1219  
1220          return (bool) ( $this->get_data( $handle, 'before' ) || $this->get_data( $handle, 'after' ) );
1221      }
1222  
1223      /**
1224       * Resets class properties.
1225       *
1226       * @since 2.8.0
1227       */
1228  	public function reset() {
1229          $this->do_concat      = false;
1230          $this->print_code     = '';
1231          $this->concat         = '';
1232          $this->concat_version = '';
1233          $this->print_html     = '';
1234          $this->ext_version    = '';
1235          $this->ext_handles    = '';
1236      }
1237  
1238      /**
1239       * Gets a script-specific dependency warning message.
1240       *
1241       * @since 6.9.1
1242       *
1243       * @param string   $handle                     Script handle with missing dependencies.
1244       * @param string[] $missing_dependency_handles Missing dependency handles.
1245       * @return string Formatted, localized warning message.
1246       */
1247  	protected function get_dependency_warning_message( $handle, $missing_dependency_handles ) {
1248          return sprintf(
1249              /* translators: 1: Script handle, 2: List of missing dependency handles. */
1250              __( 'The script with the handle "%1$s" was enqueued with dependencies that are not registered: %2$s.' ),
1251              $handle,
1252              implode( wp_get_list_item_separator(), $missing_dependency_handles )
1253          );
1254      }
1255  }


Generated : Mon Jun 15 08:20:09 2026 Cross-referenced by PHPXref