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


Generated : Wed Oct 22 08:20:04 2025 Cross-referenced by PHPXref