| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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|void Void 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|void Void 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; 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 . '&' . $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 $query_args = array(); 421 if ( empty( $obj->ver ) && null !== $obj->ver && is_string( $this->default_version ) ) { 422 $query_args['ver'] = $this->default_version; 423 } elseif ( is_scalar( $obj->ver ) ) { 424 $query_args['ver'] = (string) $obj->ver; 425 } 426 if ( isset( $this->args[ $handle ] ) ) { 427 parse_str( $this->args[ $handle ], $parsed_args ); 428 if ( $parsed_args ) { 429 $query_args = array_merge( $query_args, $parsed_args ); 430 } 431 } 432 $src = add_query_arg( rawurlencode_deep( $query_args ), $src ); 433 434 /** This filter is documented in wp-includes/class-wp-scripts.php */ 435 $src = esc_url_raw( apply_filters( 'script_loader_src', $src, $handle ) ); 436 437 if ( ! $src ) { 438 return true; 439 } 440 441 $attr = array( 442 'src' => $src, 443 'id' => "{$handle}-js", 444 ); 445 if ( $strategy ) { 446 $attr[ $strategy ] = true; 447 } 448 if ( $intended_strategy ) { 449 $attr['data-wp-strategy'] = $intended_strategy; 450 } 451 452 // Determine fetchpriority. 453 $original_fetchpriority = $obj->extra['fetchpriority'] ?? null; 454 if ( null === $original_fetchpriority || ! $this->is_valid_fetchpriority( $original_fetchpriority ) ) { 455 $original_fetchpriority = 'auto'; 456 } 457 $actual_fetchpriority = $this->get_highest_fetchpriority_with_dependents( $handle ); 458 if ( null === $actual_fetchpriority ) { 459 // If null, it's likely this script was not explicitly enqueued, so in this case use the original priority. 460 $actual_fetchpriority = $original_fetchpriority; 461 } 462 if ( is_string( $actual_fetchpriority ) && 'auto' !== $actual_fetchpriority ) { 463 $attr['fetchpriority'] = $actual_fetchpriority; 464 } 465 466 if ( $original_fetchpriority !== $actual_fetchpriority ) { 467 $attr['data-wp-fetchpriority'] = $original_fetchpriority; 468 } 469 470 $tag = $translations . $before_script; 471 $tag .= wp_get_script_tag( $attr ); 472 $tag .= $after_script; 473 474 /** 475 * Filters the HTML script tag of an enqueued script. 476 * 477 * @since 4.1.0 478 * 479 * @param string $tag The `<script>` tag for the enqueued script. 480 * @param string $handle The script's registered handle. 481 * @param string $src The script's source URL. 482 */ 483 $tag = apply_filters( 'script_loader_tag', $tag, $handle, $src ); 484 485 if ( $this->do_concat ) { 486 $this->print_html .= $tag; 487 } else { 488 echo $tag; 489 } 490 491 return true; 492 } 493 494 /** 495 * Adds extra code to a registered script. 496 * 497 * @since 4.5.0 498 * 499 * @param string $handle Name of the script to add the inline script to. 500 * Must be lowercase. 501 * @param string $data String containing the JavaScript to be added. 502 * @param string $position Optional. Whether to add the inline script 503 * before the handle or after. Default 'after'. 504 * @return bool True on success, false on failure. 505 */ 506 public function add_inline_script( $handle, $data, $position = 'after' ) { 507 if ( ! $data ) { 508 return false; 509 } 510 511 if ( 'after' !== $position ) { 512 $position = 'before'; 513 } 514 515 $script = (array) $this->get_data( $handle, $position ); 516 $script[] = $data; 517 518 return $this->add_data( $handle, $position, $script ); 519 } 520 521 /** 522 * Prints inline scripts registered for a specific handle. 523 * 524 * @since 4.5.0 525 * @deprecated 6.3.0 Use methods get_inline_script_tag() or get_inline_script_data() instead. 526 * 527 * @param string $handle Name of the script to print inline scripts for. 528 * Must be lowercase. 529 * @param string $position Optional. Whether to add the inline script 530 * before the handle or after. Default 'after'. 531 * @param bool $display Optional. Whether to print the script tag 532 * instead of just returning the script data. Default true. 533 * @return string|false Script data on success, false otherwise. 534 */ 535 public function print_inline_script( $handle, $position = 'after', $display = true ) { 536 _deprecated_function( __METHOD__, '6.3.0', 'WP_Scripts::get_inline_script_data() or WP_Scripts::get_inline_script_tag()' ); 537 538 $output = $this->get_inline_script_data( $handle, $position ); 539 if ( empty( $output ) ) { 540 return false; 541 } 542 543 if ( $display ) { 544 echo $this->get_inline_script_tag( $handle, $position ); 545 } 546 return $output; 547 } 548 549 /** 550 * Gets data for inline scripts registered for a specific handle. 551 * 552 * @since 6.3.0 553 * 554 * @param string $handle Name of the script to get data for. 555 * Must be lowercase. 556 * @param string $position Optional. Whether to add the inline script 557 * before the handle or after. Default 'after'. 558 * @return string Inline script, which may be empty string. 559 */ 560 public function get_inline_script_data( $handle, $position = 'after' ) { 561 $data = $this->get_data( $handle, $position ); 562 if ( empty( $data ) || ! is_array( $data ) ) { 563 return ''; 564 } 565 566 /* 567 * Print sourceURL comment regardless of concatenation. 568 * 569 * Inline scripts prevent scripts from being concatenated, so 570 * sourceURL comments are safe to print for inline scripts. 571 */ 572 $data[] = sprintf( 573 '//# sourceURL=%s', 574 rawurlencode( "{$handle}-js-{$position}" ) 575 ); 576 577 return trim( implode( "\n", $data ), "\n" ); 578 } 579 580 /** 581 * Gets tags for inline scripts registered for a specific handle. 582 * 583 * @since 6.3.0 584 * 585 * @param string $handle Name of the script to get associated inline script tag for. 586 * Must be lowercase. 587 * @param string $position Optional. Whether to get tag for inline 588 * scripts in the before or after position. Default 'after'. 589 * @return string Inline script, which may be empty string. 590 */ 591 public function get_inline_script_tag( $handle, $position = 'after' ) { 592 $js = $this->get_inline_script_data( $handle, $position ); 593 if ( empty( $js ) ) { 594 return ''; 595 } 596 597 $id = "{$handle}-js-{$position}"; 598 599 return wp_get_inline_script_tag( $js, compact( 'id' ) ); 600 } 601 602 /** 603 * Localizes a script, only if the script has already been added. 604 * 605 * @since 2.1.0 606 * 607 * @param string $handle Name of the script to attach data to. 608 * @param string $object_name Name of the variable that will contain the data. 609 * @param array<string, mixed> $l10n Array of data to localize. 610 * @return bool True on success, false on failure. 611 */ 612 public function localize( $handle, $object_name, $l10n ) { 613 if ( 'jquery' === $handle ) { 614 $handle = 'jquery-core'; 615 } 616 617 if ( is_array( $l10n ) && isset( $l10n['l10n_print_after'] ) ) { // back compat, preserve the code in 'l10n_print_after' if present. 618 $after = $l10n['l10n_print_after']; 619 unset( $l10n['l10n_print_after'] ); 620 } 621 622 if ( ! is_array( $l10n ) ) { 623 _doing_it_wrong( 624 __METHOD__, 625 sprintf( 626 /* translators: 1: $l10n, 2: wp_add_inline_script() */ 627 __( 'The %1$s parameter must be an array. To pass arbitrary data to scripts, use the %2$s function instead.' ), 628 '<code>$l10n</code>', 629 '<code>wp_add_inline_script()</code>' 630 ), 631 '5.7.0' 632 ); 633 634 if ( false === $l10n ) { 635 // This should really not be needed, but is necessary for backward compatibility. 636 $l10n = array( $l10n ); 637 } 638 } 639 640 if ( is_string( $l10n ) ) { 641 $l10n = html_entity_decode( $l10n, ENT_QUOTES, 'UTF-8' ); 642 } elseif ( is_array( $l10n ) ) { 643 foreach ( $l10n as $key => $value ) { 644 if ( ! is_scalar( $value ) ) { 645 continue; 646 } 647 648 $l10n[ $key ] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' ); 649 } 650 } 651 652 $script = "var $object_name = " . wp_json_encode( $l10n, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) . ';'; 653 654 if ( ! empty( $after ) ) { 655 $script .= "\n$after;"; 656 } 657 658 $data = $this->get_data( $handle, 'data' ); 659 660 if ( ! empty( $data ) ) { 661 $script = "$data\n$script"; 662 } 663 664 return $this->add_data( $handle, 'data', $script ); 665 } 666 667 /** 668 * Sets handle group. 669 * 670 * @since 2.8.0 671 * 672 * @see WP_Dependencies::set_group() 673 * 674 * @param string $handle Name of the item. Should be unique. 675 * @param bool $recursion Internal flag that calling function was called recursively. 676 * @param int|false $group Optional. Group level: level (int), no groups (false). 677 * Default false. 678 * @return bool Not already in the group or a lower group. 679 */ 680 public function set_group( $handle, $recursion, $group = false ) { 681 if ( isset( $this->registered[ $handle ]->args ) && 1 === $this->registered[ $handle ]->args ) { 682 $calculated_group = 1; 683 } else { 684 $calculated_group = (int) $this->get_data( $handle, 'group' ); 685 } 686 687 if ( false !== $group && $calculated_group > $group ) { 688 $calculated_group = $group; 689 } 690 691 return parent::set_group( $handle, $recursion, $calculated_group ); 692 } 693 694 /** 695 * Sets a translation textdomain. 696 * 697 * @since 5.0.0 698 * @since 5.1.0 The `$domain` parameter was made optional. 699 * 700 * @param string $handle Name of the script to register a translation domain to. 701 * @param string $domain Optional. Text domain. Default 'default'. 702 * @param string $path Optional. The full file path to the directory containing translation files. 703 * @return bool True if the text domain was registered, false if not. 704 */ 705 public function set_translations( $handle, $domain = 'default', $path = '' ) { 706 if ( ! isset( $this->registered[ $handle ] ) ) { 707 return false; 708 } 709 710 /** @var \_WP_Dependency $obj */ 711 $obj = $this->registered[ $handle ]; 712 713 if ( ! in_array( 'wp-i18n', $obj->deps, true ) ) { 714 $obj->deps[] = 'wp-i18n'; 715 } 716 717 return $obj->set_translations( $domain, $path ); 718 } 719 720 /** 721 * Prints translations set for a specific handle. 722 * 723 * @since 5.0.0 724 * 725 * @param string $handle Name of the script to add the inline script to. 726 * Must be lowercase. 727 * @param bool $display Optional. Whether to print the script 728 * instead of just returning it. Default true. 729 * @return string|false Script on success, false otherwise. 730 */ 731 public function print_translations( $handle, $display = true ) { 732 if ( ! isset( $this->registered[ $handle ] ) || empty( $this->registered[ $handle ]->textdomain ) ) { 733 return false; 734 } 735 736 $domain = $this->registered[ $handle ]->textdomain; 737 $path = ''; 738 739 if ( isset( $this->registered[ $handle ]->translations_path ) ) { 740 $path = $this->registered[ $handle ]->translations_path; 741 } 742 743 $json_translations = load_script_textdomain( $handle, $domain, $path ); 744 745 if ( ! $json_translations ) { 746 return false; 747 } 748 749 $output = <<<JS 750 ( function( domain, translations ) { 751 var localeData = translations.locale_data[ domain ] || translations.locale_data.messages; 752 localeData[""].domain = domain; 753 wp.i18n.setLocaleData( localeData, domain ); 754 } )( "{$domain}", {$json_translations} ); 755 JS; 756 757 if ( $display ) { 758 $source_url = rawurlencode( "{$handle}-js-translations" ); 759 $output .= "\n//# sourceURL={$source_url}"; 760 wp_print_inline_script_tag( $output, array( 'id' => "{$handle}-js-translations" ) ); 761 } 762 763 return $output; 764 } 765 766 /** 767 * Determines script dependencies. 768 * 769 * @since 2.1.0 770 * 771 * @see WP_Dependencies::all_deps() 772 * 773 * @param string|string[] $handles Item handle (string) or item handles (array of strings). 774 * @param bool $recursion Optional. Internal flag that function is calling itself. 775 * Default false. 776 * @param int|false $group Optional. Group level: level (int), no groups (false). 777 * Default false. 778 * @return bool True on success, false on failure. 779 */ 780 public function all_deps( $handles, $recursion = false, $group = false ) { 781 $result = parent::all_deps( $handles, $recursion, $group ); 782 if ( ! $recursion ) { 783 /** 784 * Filters the list of script dependencies left to print. 785 * 786 * @since 2.3.0 787 * 788 * @param string[] $to_do An array of script dependency handles. 789 */ 790 $this->to_do = apply_filters( 'print_scripts_array', $this->to_do ); 791 } 792 return $result; 793 } 794 795 /** 796 * Processes items and dependencies for the head group. 797 * 798 * @since 2.8.0 799 * 800 * @see WP_Dependencies::do_items() 801 * 802 * @return string[] Handles of items that have been processed. 803 */ 804 public function do_head_items() { 805 $this->do_items( false, 0 ); 806 return $this->done; 807 } 808 809 /** 810 * Processes items and dependencies for the footer group. 811 * 812 * @since 2.8.0 813 * 814 * @see WP_Dependencies::do_items() 815 * 816 * @return string[] Handles of items that have been processed. 817 */ 818 public function do_footer_items() { 819 $this->do_items( false, 1 ); 820 return $this->done; 821 } 822 823 /** 824 * Whether a handle's source is in a default directory. 825 * 826 * @since 2.8.0 827 * 828 * @param string $src The source of the enqueued script. 829 * @return bool True if found, false if not. 830 */ 831 public function in_default_dir( $src ) { 832 if ( ! $this->default_dirs ) { 833 return true; 834 } 835 836 if ( str_starts_with( $src, '/' . WPINC . '/js/l10n' ) ) { 837 return false; 838 } 839 840 foreach ( (array) $this->default_dirs as $test ) { 841 if ( str_starts_with( $src, $test ) ) { 842 return true; 843 } 844 } 845 return false; 846 } 847 848 /** 849 * This overrides the add_data method from WP_Dependencies, to support normalizing of $args. 850 * 851 * @since 6.3.0 852 * 853 * @param string $handle Name of the item. Should be unique. 854 * @param string $key The data key. 855 * @param mixed $value The data value. 856 * @return bool True on success, false on failure. 857 */ 858 public function add_data( $handle, $key, $value ) { 859 if ( ! isset( $this->registered[ $handle ] ) ) { 860 return false; 861 } 862 863 if ( 'conditional' === $key ) { 864 // If a dependency is declared by a conditional script, remove it. 865 $this->registered[ $handle ]->deps = array(); 866 } 867 868 if ( 'strategy' === $key ) { 869 if ( ! empty( $value ) && ! $this->is_delayed_strategy( $value ) ) { 870 _doing_it_wrong( 871 __METHOD__, 872 sprintf( 873 /* translators: 1: $strategy, 2: $handle */ 874 __( 'Invalid strategy `%1$s` defined for `%2$s` during script registration.' ), 875 $value, 876 $handle 877 ), 878 '6.3.0' 879 ); 880 return false; 881 } elseif ( ! $this->registered[ $handle ]->src && $this->is_delayed_strategy( $value ) ) { 882 _doing_it_wrong( 883 __METHOD__, 884 sprintf( 885 /* translators: 1: $strategy, 2: $handle */ 886 __( 'Cannot supply a strategy `%1$s` for script `%2$s` because it is an alias (it lacks a `src` value).' ), 887 $value, 888 $handle 889 ), 890 '6.3.0' 891 ); 892 return false; 893 } 894 } elseif ( 'fetchpriority' === $key ) { 895 if ( empty( $value ) ) { 896 $value = 'auto'; 897 } 898 if ( ! $this->is_valid_fetchpriority( $value ) ) { 899 _doing_it_wrong( 900 __METHOD__, 901 sprintf( 902 /* translators: 1: $fetchpriority, 2: $handle */ 903 __( 'Invalid fetchpriority `%1$s` defined for `%2$s` during script registration.' ), 904 is_string( $value ) ? $value : gettype( $value ), 905 $handle 906 ), 907 '6.9.0' 908 ); 909 return false; 910 } elseif ( ! $this->registered[ $handle ]->src ) { 911 _doing_it_wrong( 912 __METHOD__, 913 sprintf( 914 /* translators: 1: $fetchpriority, 2: $handle */ 915 __( 'Cannot supply a fetchpriority `%1$s` for script `%2$s` because it is an alias (it lacks a `src` value).' ), 916 is_string( $value ) ? $value : gettype( $value ), 917 $handle 918 ), 919 '6.9.0' 920 ); 921 return false; 922 } 923 } 924 return parent::add_data( $handle, $key, $value ); 925 } 926 927 /** 928 * Gets all dependents of a script. 929 * 930 * This is not recursive. 931 * 932 * @since 6.3.0 933 * 934 * @param string $handle The script handle. 935 * @return string[] Script handles. 936 */ 937 private function get_dependents( $handle ) { 938 // Check if dependents map for the handle in question is present. If so, use it. 939 if ( isset( $this->dependents_map[ $handle ] ) ) { 940 return $this->dependents_map[ $handle ]; 941 } 942 943 $dependents = array(); 944 945 // Iterate over all registered scripts, finding dependents of the script passed to this method. 946 foreach ( $this->registered as $registered_handle => $args ) { 947 if ( in_array( $handle, $args->deps, true ) ) { 948 $dependents[] = $registered_handle; 949 } 950 } 951 952 // Add the handles dependents to the map to ease future lookups. 953 $this->dependents_map[ $handle ] = $dependents; 954 955 return $dependents; 956 } 957 958 /** 959 * Checks if the strategy passed is a valid delayed (non-blocking) strategy. 960 * 961 * @since 6.3.0 962 * 963 * @param string|mixed $strategy The strategy to check. 964 * @return bool True if $strategy is one of the delayed strategies, otherwise false. 965 */ 966 private function is_delayed_strategy( $strategy ): bool { 967 return in_array( 968 $strategy, 969 $this->delayed_strategies, 970 true 971 ); 972 } 973 974 /** 975 * Checks if the provided fetchpriority is valid. 976 * 977 * @since 6.9.0 978 * 979 * @param string|mixed $priority Fetch priority. 980 * @return bool Whether valid fetchpriority. 981 */ 982 private function is_valid_fetchpriority( $priority ): bool { 983 return in_array( $priority, array( 'auto', 'low', 'high' ), true ); 984 } 985 986 /** 987 * Gets the best eligible loading strategy for a script. 988 * 989 * @since 6.3.0 990 * 991 * @param string $handle The script handle. 992 * @return string The best eligible loading strategy. 993 */ 994 private function get_eligible_loading_strategy( $handle ) { 995 $intended_strategy = (string) $this->get_data( $handle, 'strategy' ); 996 997 // Bail early if there is no intended strategy. 998 if ( ! $intended_strategy ) { 999 return ''; 1000 } 1001 1002 /* 1003 * If the intended strategy is 'defer', limit the initial list of eligible 1004 * strategies, since 'async' can fallback to 'defer', but not vice-versa. 1005 */ 1006 $initial_strategy = ( 'defer' === $intended_strategy ) ? array( 'defer' ) : null; 1007 1008 $eligible_strategies = $this->filter_eligible_strategies( $handle, $initial_strategy ); 1009 1010 // Return early once we know the eligible strategy is blocking. 1011 if ( empty( $eligible_strategies ) ) { 1012 return ''; 1013 } 1014 1015 return in_array( 'async', $eligible_strategies, true ) ? 'async' : 'defer'; 1016 } 1017 1018 /** 1019 * Filter the list of eligible loading strategies for a script. 1020 * 1021 * @since 6.3.0 1022 * 1023 * @param string $handle The script handle. 1024 * @param string[]|null $eligible_strategies Optional. The list of strategies to filter. Default null. 1025 * @param array<string, true> $checked Optional. An array of already checked script handles, used to avoid recursive loops. 1026 * @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. 1027 * @return string[] A list of eligible loading strategies that could be used. 1028 */ 1029 private function filter_eligible_strategies( $handle, $eligible_strategies = null, $checked = array(), array &$stored_results = array() ) { 1030 if ( isset( $stored_results[ $handle ] ) ) { 1031 return $stored_results[ $handle ]; 1032 } 1033 1034 // If no strategies are being passed, all strategies are eligible. 1035 if ( null === $eligible_strategies ) { 1036 $eligible_strategies = $this->delayed_strategies; 1037 } 1038 1039 // If this handle was already checked, return early. 1040 if ( isset( $checked[ $handle ] ) ) { 1041 return $eligible_strategies; 1042 } 1043 1044 // Mark this handle as checked. 1045 $checked[ $handle ] = true; 1046 1047 // If this handle isn't registered, don't filter anything and return. 1048 if ( ! isset( $this->registered[ $handle ] ) ) { 1049 return $eligible_strategies; 1050 } 1051 1052 // If the handle is not enqueued, don't filter anything and return. 1053 if ( ! $this->query( $handle, 'enqueued' ) ) { 1054 return $eligible_strategies; 1055 } 1056 1057 $is_alias = (bool) ! $this->registered[ $handle ]->src; 1058 $intended_strategy = $this->get_data( $handle, 'strategy' ); 1059 1060 // For non-alias handles, an empty intended strategy filters all strategies. 1061 if ( ! $is_alias && empty( $intended_strategy ) ) { 1062 return array(); 1063 } 1064 1065 // Handles with inline scripts attached in the 'after' position cannot be delayed. 1066 if ( $this->has_inline_script( $handle, 'after' ) ) { 1067 return array(); 1068 } 1069 1070 // If the intended strategy is 'defer', filter out 'async'. 1071 if ( 'defer' === $intended_strategy ) { 1072 $eligible_strategies = array( 'defer' ); 1073 } 1074 1075 $dependents = $this->get_dependents( $handle ); 1076 1077 // Recursively filter eligible strategies for dependents. 1078 foreach ( $dependents as $dependent ) { 1079 // Bail early once we know the eligible strategy is blocking. 1080 if ( empty( $eligible_strategies ) ) { 1081 return array(); 1082 } 1083 1084 $eligible_strategies = $this->filter_eligible_strategies( $dependent, $eligible_strategies, $checked, $stored_results ); 1085 } 1086 $stored_results[ $handle ] = $eligible_strategies; 1087 return $eligible_strategies; 1088 } 1089 1090 /** 1091 * Gets the highest fetch priority for a given script and all of its dependent scripts. 1092 * 1093 * @since 6.9.0 1094 * @see self::filter_eligible_strategies() 1095 * @see WP_Script_Modules::get_highest_fetchpriority() 1096 * 1097 * @param string $handle Script module ID. 1098 * @param array<string, true> $checked Optional. An array of already checked script handles, used to avoid recursive loops. 1099 * @param array<string, string> $stored_results Optional. An array of already computed max priority by handle, used to increase performance in large dependency lists. 1100 * @return string|null Highest fetch priority for the script and its dependents. 1101 */ 1102 private function get_highest_fetchpriority_with_dependents( string $handle, array $checked = array(), array &$stored_results = array() ): ?string { 1103 if ( isset( $stored_results[ $handle ] ) ) { 1104 return $stored_results[ $handle ]; 1105 } 1106 1107 // If there is a recursive dependency, return early. 1108 if ( isset( $checked[ $handle ] ) ) { 1109 return null; 1110 } 1111 1112 // Mark this handle as checked to guard against infinite recursion. 1113 $checked[ $handle ] = true; 1114 1115 // Abort if the script is not enqueued or a dependency of an enqueued script. 1116 if ( ! $this->query( $handle, 'enqueued' ) ) { 1117 return null; 1118 } 1119 1120 $fetchpriority = $this->get_data( $handle, 'fetchpriority' ); 1121 if ( ! $this->is_valid_fetchpriority( $fetchpriority ) ) { 1122 $fetchpriority = 'auto'; 1123 } 1124 1125 static $priorities = array( 1126 'low', 1127 'auto', 1128 'high', 1129 ); 1130 $high_priority_index = count( $priorities ) - 1; 1131 1132 $highest_priority_index = (int) array_search( $fetchpriority, $priorities, true ); 1133 if ( $highest_priority_index !== $high_priority_index ) { 1134 foreach ( $this->get_dependents( $handle ) as $dependent_handle ) { 1135 $dependent_priority = $this->get_highest_fetchpriority_with_dependents( $dependent_handle, $checked, $stored_results ); 1136 if ( is_string( $dependent_priority ) ) { 1137 $highest_priority_index = max( 1138 $highest_priority_index, 1139 (int) array_search( $dependent_priority, $priorities, true ) 1140 ); 1141 if ( $highest_priority_index === $high_priority_index ) { 1142 break; 1143 } 1144 } 1145 } 1146 } 1147 $stored_results[ $handle ] = $priorities[ $highest_priority_index ]; // @phpstan-ignore parameterByRef.type (We know the index is valid and that this will be a string.) 1148 return $priorities[ $highest_priority_index ]; 1149 } 1150 1151 /** 1152 * Gets data for inline scripts registered for a specific handle. 1153 * 1154 * @since 6.3.0 1155 * 1156 * @param string $handle Name of the script to get data for. Must be lowercase. 1157 * @param string $position The position of the inline script. 1158 * @return bool Whether the handle has an inline script (either before or after). 1159 */ 1160 private function has_inline_script( $handle, $position = null ) { 1161 if ( $position && in_array( $position, array( 'before', 'after' ), true ) ) { 1162 return (bool) $this->get_data( $handle, $position ); 1163 } 1164 1165 return (bool) ( $this->get_data( $handle, 'before' ) || $this->get_data( $handle, 'after' ) ); 1166 } 1167 1168 /** 1169 * Resets class properties. 1170 * 1171 * @since 2.8.0 1172 */ 1173 public function reset() { 1174 $this->do_concat = false; 1175 $this->print_code = ''; 1176 $this->concat = ''; 1177 $this->concat_version = ''; 1178 $this->print_html = ''; 1179 $this->ext_version = ''; 1180 $this->ext_handles = ''; 1181 } 1182 1183 /** 1184 * Gets a script-specific dependency warning message. 1185 * 1186 * @since 6.9.1 1187 * 1188 * @param string $handle Script handle with missing dependencies. 1189 * @param string[] $missing_dependency_handles Missing dependency handles. 1190 * @return string Formatted, localized warning message. 1191 */ 1192 protected function get_dependency_warning_message( $handle, $missing_dependency_handles ) { 1193 return sprintf( 1194 /* translators: 1: Script handle, 2: Comma-separated list of missing dependency handles. */ 1195 __( 'The script with the handle "%1$s" was enqueued with dependencies that are not registered: %2$s.' ), 1196 $handle, 1197 implode( ', ', $missing_dependency_handles ) 1198 ); 1199 } 1200 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Tue May 5 08:20:14 2026 | Cross-referenced by PHPXref |