[ 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 * @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 . '&' . $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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Wed Oct 22 08:20:04 2025 | Cross-referenced by PHPXref |