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