Make WordPress Core


Ignore:
Timestamp:
03/11/2026 01:30:24 AM (2 weeks ago)
Author:
westonruter
Message:

Script Loader: Preserve duplicate URL query params in enqueued scripts and styles.

Previously in r61397, add_query_arg() was used to append versions or handle-specific query arguments. This resulted in stripping any existing duplicate query variables in the source URL (common in Google Fonts URLs). This change refactors WP_Styles::_css_href() and WP_Scripts::do_item() to manually append these parameters to the URL string. This ensures all original query variables are preserved exactly as provided. It also improves fragment handling by ensuring query parameters are inserted before any '#' anchor while maintaining the anchor's presence.

The URL encoding changes in tests/phpunit/tests/dependencies/scripts.php are reversions of what had previously been done in r61397.

Developed in https://github.com/WordPress/wordpress-develop/pull/11164

Follow-up to r61397, r61358.

Props westonruter, jonsurrell.
Fixes #64372.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/dependencies/scripts.php

    r61611 r61927  
    632632//# sourceURL=blocking-not-async-without-dependency-js-before
    633633</script>
    634 <script src='https://example.com/external.js?script_event_log=blocking-not-async-without-dependency%3A+script' id='blocking-not-async-without-dependency-js'></script>
     634<script src='https://example.com/external.js?script_event_log=blocking-not-async-without-dependency:%20script' id='blocking-not-async-without-dependency-js'></script>
    635635<script id="blocking-not-async-without-dependency-js-after">
    636636scriptEventLog.push( "blocking-not-async-without-dependency: after inline" )
     
    641641//# sourceURL=async-with-blocking-dependency-js-before
    642642</script>
    643 <script src='https://example.com/external.js?script_event_log=async-with-blocking-dependency%3A+script' id='async-with-blocking-dependency-js' data-wp-strategy='async'></script>
     643<script src='https://example.com/external.js?script_event_log=async-with-blocking-dependency:%20script' id='async-with-blocking-dependency-js' data-wp-strategy='async'></script>
    644644<script id="async-with-blocking-dependency-js-after">
    645645scriptEventLog.push( "async-with-blocking-dependency: after inline" )
     
    673673//# sourceURL=async-no-dependency-js-before
    674674</script>
    675 <script src='https://example.com/external.js?script_event_log=async-no-dependency%3A+script' id='async-no-dependency-js' data-wp-strategy='async'></script>
     675<script src='https://example.com/external.js?script_event_log=async-no-dependency:%20script' id='async-no-dependency-js' data-wp-strategy='async'></script>
    676676<script id="async-no-dependency-js-after">
    677677scriptEventLog.push( "async-no-dependency: after inline" )
     
    682682//# sourceURL=async-one-async-dependency-js-before
    683683</script>
    684 <script src='https://example.com/external.js?script_event_log=async-one-async-dependency%3A+script' id='async-one-async-dependency-js' data-wp-strategy='async'></script>
     684<script src='https://example.com/external.js?script_event_log=async-one-async-dependency:%20script' id='async-one-async-dependency-js' data-wp-strategy='async'></script>
    685685<script id="async-one-async-dependency-js-after">
    686686scriptEventLog.push( "async-one-async-dependency: after inline" )
     
    691691//# sourceURL=async-two-async-dependencies-js-before
    692692</script>
    693 <script src='https://example.com/external.js?script_event_log=async-two-async-dependencies%3A+script' id='async-two-async-dependencies-js' data-wp-strategy='async'></script>
     693<script src='https://example.com/external.js?script_event_log=async-two-async-dependencies:%20script' id='async-two-async-dependencies-js' data-wp-strategy='async'></script>
    694694<script id="async-two-async-dependencies-js-after">
    695695scriptEventLog.push( "async-two-async-dependencies: after inline" )
     
    715715//# sourceURL=async-with-blocking-dependent-js-before
    716716</script>
    717 <script src='https://example.com/external.js?script_event_log=async-with-blocking-dependent%3A+script' id='async-with-blocking-dependent-js' data-wp-strategy='async'></script>
     717<script src='https://example.com/external.js?script_event_log=async-with-blocking-dependent:%20script' id='async-with-blocking-dependent-js' data-wp-strategy='async'></script>
    718718<script id="async-with-blocking-dependent-js-after">
    719719scriptEventLog.push( "async-with-blocking-dependent: after inline" )
     
    724724//# sourceURL=blocking-dependent-of-async-js-before
    725725</script>
    726 <script src='https://example.com/external.js?script_event_log=blocking-dependent-of-async%3A+script' id='blocking-dependent-of-async-js'></script>
     726<script src='https://example.com/external.js?script_event_log=blocking-dependent-of-async:%20script' id='blocking-dependent-of-async-js'></script>
    727727<script id="blocking-dependent-of-async-js-after">
    728728scriptEventLog.push( "blocking-dependent-of-async: after inline" )
     
    748748//# sourceURL=async-with-defer-dependent-js-before
    749749</script>
    750 <script src='https://example.com/external.js?script_event_log=async-with-defer-dependent%3A+script' id='async-with-defer-dependent-js' data-wp-strategy='async'></script>
     750<script src='https://example.com/external.js?script_event_log=async-with-defer-dependent:%20script' id='async-with-defer-dependent-js' data-wp-strategy='async'></script>
    751751<script id="async-with-defer-dependent-js-after">
    752752scriptEventLog.push( "async-with-defer-dependent: after inline" )
     
    757757//# sourceURL=defer-dependent-of-async-js-before
    758758</script>
    759 <script src='https://example.com/external.js?script_event_log=defer-dependent-of-async%3A+script' id='defer-dependent-of-async-js' data-wp-strategy='defer'></script>
     759<script src='https://example.com/external.js?script_event_log=defer-dependent-of-async:%20script' id='defer-dependent-of-async-js' data-wp-strategy='defer'></script>
    760760<script id="defer-dependent-of-async-js-after">
    761761scriptEventLog.push( "defer-dependent-of-async: after inline" )
     
    792792//# sourceURL=defer-dependent-of-blocking-bundle-of-none-js-before
    793793</script>
    794 <script src='https://example.com/external.js?script_event_log=defer-dependent-of-blocking-bundle-of-none%3A+script' id='defer-dependent-of-blocking-bundle-of-none-js' data-wp-strategy='defer'></script>
     794<script src='https://example.com/external.js?script_event_log=defer-dependent-of-blocking-bundle-of-none:%20script' id='defer-dependent-of-blocking-bundle-of-none-js' data-wp-strategy='defer'></script>
    795795<script id="defer-dependent-of-blocking-bundle-of-none-js-after">
    796796scriptEventLog.push( "defer-dependent-of-blocking-bundle-of-none: after inline" )
     
    822822//# sourceURL=blocking-bundle-member-one-js-before
    823823</script>
    824 <script src='https://example.com/external.js?script_event_log=blocking-bundle-member-one%3A+script' id='blocking-bundle-member-one-js'></script>
     824<script src='https://example.com/external.js?script_event_log=blocking-bundle-member-one:%20script' id='blocking-bundle-member-one-js'></script>
    825825<script id="blocking-bundle-member-one-js-after">
    826826scriptEventLog.push( "blocking-bundle-member-one: after inline" )
     
    831831//# sourceURL=blocking-bundle-member-two-js-before
    832832</script>
    833 <script src='https://example.com/external.js?script_event_log=blocking-bundle-member-two%3A+script' id='blocking-bundle-member-two-js'></script>
     833<script src='https://example.com/external.js?script_event_log=blocking-bundle-member-two:%20script' id='blocking-bundle-member-two-js'></script>
    834834<script id="blocking-bundle-member-two-js-after">
    835835scriptEventLog.push( "blocking-bundle-member-two: after inline" )
     
    840840//# sourceURL=defer-dependent-of-blocking-bundle-of-two-js-before
    841841</script>
    842 <script src='https://example.com/external.js?script_event_log=defer-dependent-of-blocking-bundle-of-two%3A+script' id='defer-dependent-of-blocking-bundle-of-two-js' data-wp-strategy='defer'></script>
     842<script src='https://example.com/external.js?script_event_log=defer-dependent-of-blocking-bundle-of-two:%20script' id='defer-dependent-of-blocking-bundle-of-two-js' data-wp-strategy='defer'></script>
    843843<script id="defer-dependent-of-blocking-bundle-of-two-js-after">
    844844scriptEventLog.push( "defer-dependent-of-blocking-bundle-of-two: after inline" )
     
    877877//# sourceURL=defer-dependent-of-defer-bundle-of-none-js-before
    878878</script>
    879 <script src='https://example.com/external.js?script_event_log=defer-dependent-of-defer-bundle-of-none%3A+script' id='defer-dependent-of-defer-bundle-of-none-js' data-wp-strategy='defer'></script>
     879<script src='https://example.com/external.js?script_event_log=defer-dependent-of-defer-bundle-of-none:%20script' id='defer-dependent-of-defer-bundle-of-none-js' data-wp-strategy='defer'></script>
    880880<script id="defer-dependent-of-defer-bundle-of-none-js-after">
    881881scriptEventLog.push( "defer-dependent-of-defer-bundle-of-none: after inline" )
     
    904904//# sourceURL=blocking-dependency-with-defer-following-dependency-js-before
    905905</script>
    906 <script src='https://example.com/external.js?script_event_log=blocking-dependency-with-defer-following-dependency%3A+script' id='blocking-dependency-with-defer-following-dependency-js'></script>
     906<script src='https://example.com/external.js?script_event_log=blocking-dependency-with-defer-following-dependency:%20script' id='blocking-dependency-with-defer-following-dependency-js'></script>
    907907<script id="blocking-dependency-with-defer-following-dependency-js-after">
    908908scriptEventLog.push( "blocking-dependency-with-defer-following-dependency: after inline" )
     
    913913//# sourceURL=defer-dependency-with-blocking-preceding-dependency-js-before
    914914</script>
    915 <script src='https://example.com/external.js?script_event_log=defer-dependency-with-blocking-preceding-dependency%3A+script' id='defer-dependency-with-blocking-preceding-dependency-js' data-wp-strategy='defer'></script>
     915<script src='https://example.com/external.js?script_event_log=defer-dependency-with-blocking-preceding-dependency:%20script' id='defer-dependency-with-blocking-preceding-dependency-js' data-wp-strategy='defer'></script>
    916916<script id="defer-dependency-with-blocking-preceding-dependency-js-after">
    917917scriptEventLog.push( "defer-dependency-with-blocking-preceding-dependency: after inline" )
     
    922922//# sourceURL=defer-dependent-of-blocking-and-defer-dependencies-js-before
    923923</script>
    924 <script src='https://example.com/external.js?script_event_log=defer-dependent-of-blocking-and-defer-dependencies%3A+script' id='defer-dependent-of-blocking-and-defer-dependencies-js' data-wp-strategy='defer'></script>
     924<script src='https://example.com/external.js?script_event_log=defer-dependent-of-blocking-and-defer-dependencies:%20script' id='defer-dependent-of-blocking-and-defer-dependencies-js' data-wp-strategy='defer'></script>
    925925<script id="defer-dependent-of-blocking-and-defer-dependencies-js-after">
    926926scriptEventLog.push( "defer-dependent-of-blocking-and-defer-dependencies: after inline" )
     
    949949//# sourceURL=defer-dependency-with-blocking-following-dependency-js-before
    950950</script>
    951 <script src='https://example.com/external.js?script_event_log=defer-dependency-with-blocking-following-dependency%3A+script' id='defer-dependency-with-blocking-following-dependency-js' data-wp-strategy='defer'></script>
     951<script src='https://example.com/external.js?script_event_log=defer-dependency-with-blocking-following-dependency:%20script' id='defer-dependency-with-blocking-following-dependency-js' data-wp-strategy='defer'></script>
    952952<script id="defer-dependency-with-blocking-following-dependency-js-after">
    953953scriptEventLog.push( "defer-dependency-with-blocking-following-dependency: after inline" )
     
    958958//# sourceURL=blocking-dependency-with-defer-preceding-dependency-js-before
    959959</script>
    960 <script src='https://example.com/external.js?script_event_log=blocking-dependency-with-defer-preceding-dependency%3A+script' id='blocking-dependency-with-defer-preceding-dependency-js'></script>
     960<script src='https://example.com/external.js?script_event_log=blocking-dependency-with-defer-preceding-dependency:%20script' id='blocking-dependency-with-defer-preceding-dependency-js'></script>
    961961<script id="blocking-dependency-with-defer-preceding-dependency-js-after">
    962962scriptEventLog.push( "blocking-dependency-with-defer-preceding-dependency: after inline" )
     
    967967//# sourceURL=defer-dependent-of-defer-and-blocking-dependencies-js-before
    968968</script>
    969 <script src='https://example.com/external.js?script_event_log=defer-dependent-of-defer-and-blocking-dependencies%3A+script' id='defer-dependent-of-defer-and-blocking-dependencies-js' data-wp-strategy='defer'></script>
     969<script src='https://example.com/external.js?script_event_log=defer-dependent-of-defer-and-blocking-dependencies:%20script' id='defer-dependent-of-defer-and-blocking-dependencies-js' data-wp-strategy='defer'></script>
    970970<script id="defer-dependent-of-defer-and-blocking-dependencies-js-after">
    971971scriptEventLog.push( "defer-dependent-of-defer-and-blocking-dependencies: after inline" )
     
    991991//# sourceURL=defer-with-async-dependent-js-before
    992992</script>
    993 <script src='https://example.com/external.js?script_event_log=defer-with-async-dependent%3A+script' id='defer-with-async-dependent-js' data-wp-strategy='defer'></script>
     993<script src='https://example.com/external.js?script_event_log=defer-with-async-dependent:%20script' id='defer-with-async-dependent-js' data-wp-strategy='defer'></script>
    994994<script id="defer-with-async-dependent-js-after">
    995995scriptEventLog.push( "defer-with-async-dependent: after inline" )
     
    10001000//# sourceURL=async-dependent-of-defer-js-before
    10011001</script>
    1002 <script src='https://example.com/external.js?script_event_log=async-dependent-of-defer%3A+script' id='async-dependent-of-defer-js' data-wp-strategy='async'></script>
     1002<script src='https://example.com/external.js?script_event_log=async-dependent-of-defer:%20script' id='async-dependent-of-defer-js' data-wp-strategy='async'></script>
    10031003<script id="async-dependent-of-defer-js-after">
    10041004scriptEventLog.push( "async-dependent-of-defer: after inline" )
     
    10201020//# sourceURL=defer-with-before-inline-js-before
    10211021</script>
    1022 <script src='https://example.com/external.js?script_event_log=defer-with-before-inline%3A+script' id='defer-with-before-inline-js' defer data-wp-strategy='defer'></script>
     1022<script src='https://example.com/external.js?script_event_log=defer-with-before-inline:%20script' id='defer-with-before-inline-js' defer data-wp-strategy='defer'></script>
    10231023HTML
    10241024                ,
     
    10321032                },
    10331033                'expected_markup' => <<<HTML
    1034 <script src='https://example.com/external.js?script_event_log=defer-with-after-inline%3A+script' id='defer-with-after-inline-js' data-wp-strategy='defer'></script>
     1034<script src='https://example.com/external.js?script_event_log=defer-with-after-inline:%20script' id='defer-with-after-inline-js' data-wp-strategy='defer'></script>
    10351035<script id="defer-with-after-inline-js-after">
    10361036scriptEventLog.push( "defer-with-after-inline: after inline" )
     
    10761076                },
    10771077                'expected_markup' => <<<HTML
    1078 <script src='https://example.com/external.js?script_event_log=inner-bundle-member-one%3A+script' id='inner-bundle-member-one-js' data-wp-strategy='defer'></script>
    1079 <script src='https://example.com/external.js?script_event_log=inner-bundle-member-two%3A+script' id='inner-bundle-member-two-js' data-wp-strategy='defer'></script>
    1080 <script src='https://example.com/external.js?script_event_log=outer-bundle-leaf-member%3A+script' id='outer-bundle-leaf-member-js'></script>
     1078<script src='https://example.com/external.js?script_event_log=inner-bundle-member-one:%20script' id='inner-bundle-member-one-js' data-wp-strategy='defer'></script>
     1079<script src='https://example.com/external.js?script_event_log=inner-bundle-member-two:%20script' id='inner-bundle-member-two-js' data-wp-strategy='defer'></script>
     1080<script src='https://example.com/external.js?script_event_log=outer-bundle-leaf-member:%20script' id='outer-bundle-leaf-member-js'></script>
    10811081<script id="defer-dependent-of-nested-aliases-js-before">
    10821082scriptEventLog.push( "defer-dependent-of-nested-aliases: before inline" )
    10831083//# sourceURL=defer-dependent-of-nested-aliases-js-before
    10841084</script>
    1085 <script src='https://example.com/external.js?script_event_log=defer-dependent-of-nested-aliases%3A+script' id='defer-dependent-of-nested-aliases-js' data-wp-strategy='defer'></script>
     1085<script src='https://example.com/external.js?script_event_log=defer-dependent-of-nested-aliases:%20script' id='defer-dependent-of-nested-aliases-js' data-wp-strategy='defer'></script>
    10861086<script id="defer-dependent-of-nested-aliases-js-after">
    10871087scriptEventLog.push( "defer-dependent-of-nested-aliases: after inline" )
     
    11051105                },
    11061106                'expected_markup' => <<<HTML
    1107 <script src='https://example.com/external.js?script_event_log=async1%3A+script' id='async1-js' defer data-wp-strategy='async'></script>
    1108 <script src='https://example.com/external.js?script_event_log=async2%3A+script' id='async2-js' defer data-wp-strategy='async'></script>
    1109 <script src='https://example.com/external.js?script_event_log=defer-dependent-of-async-aliases%3A+script' id='defer-dependent-of-async-aliases-js' defer data-wp-strategy='defer'></script>
     1107<script src='https://example.com/external.js?script_event_log=async1:%20script' id='async1-js' defer data-wp-strategy='async'></script>
     1108<script src='https://example.com/external.js?script_event_log=async2:%20script' id='async2-js' defer data-wp-strategy='async'></script>
     1109<script src='https://example.com/external.js?script_event_log=defer-dependent-of-async-aliases:%20script' id='defer-dependent-of-async-aliases-js' defer data-wp-strategy='defer'></script>
    11101110HTML
    11111111                ,
     
    41944194
    41954195    /**
     4196     * Tests that duplicate query vars and fragments are preserved in scripts.
     4197     *
     4198     * @ticket 64372
     4199     *
     4200     * @dataProvider data_duplicate_query_vars_and_fragments_preserved_in_scripts
     4201     *
     4202     * @param string           $src          The script's source URL.
     4203     * @param string|bool|null $ver          The script's version.
     4204     * @param string           $expected_url The expected URL.
     4205     * @param string           $handle       Optional. The script's registered handle. Default 'test-script'.
     4206     */
     4207    public function test_duplicate_query_vars_and_fragments_preserved_in_scripts( string $src, $ver, string $expected_url, string $handle = 'test-script' ): void {
     4208        wp_enqueue_script( $handle, $src, array(), $ver );
     4209        $output    = get_echo( 'wp_print_scripts' );
     4210        $processor = new WP_HTML_Tag_Processor( $output );
     4211
     4212        $this->assertTrue( $processor->next_tag( 'script' ) );
     4213        $this->assertSame( $expected_url, $processor->get_attribute( 'src' ) );
     4214    }
     4215
     4216    /**
     4217     * Data provider for test_duplicate_query_vars_and_fragments_preserved_in_scripts.
     4218     *
     4219     * @return array<string, array{src: string, ver: string|bool|null, expected_url: string, handle?: string}> Data provider.
     4220     */
     4221    public function data_duplicate_query_vars_and_fragments_preserved_in_scripts(): array {
     4222        $ver = get_bloginfo( 'version' );
     4223
     4224        return array(
     4225            'duplicate query vars'                => array(
     4226                'src'          => 'https://example.com/script.js?arg=1&arg=2',
     4227                'ver'          => '1.0',
     4228                'expected_url' => 'https://example.com/script.js?arg=1&arg=2&ver=1.0',
     4229            ),
     4230            'duplicate query vars, null version'  => array(
     4231                'src'          => 'https://example.com/script.js?arg=1&arg=2',
     4232                'ver'          => null,
     4233                'expected_url' => 'https://example.com/script.js?arg=1&arg=2',
     4234            ),
     4235            'duplicate query vars, false version' => array(
     4236                'src'          => 'https://example.com/script.js?arg=1&arg=2',
     4237                'ver'          => false,
     4238                'expected_url' => "https://example.com/script.js?arg=1&arg=2&ver=$ver",
     4239            ),
     4240            'duplicate query vars in handle'      => array(
     4241                'src'          => 'https://example.com/test-script.js',
     4242                'ver'          => '1.0',
     4243                'expected_url' => 'https://example.com/test-script.js?ver=1.0&a=1&a=2',
     4244                'handle'       => 'test-script?a=1&a=2',
     4245            ),
     4246            'duplicate query vars and fragments'  => array(
     4247                'src'          => 'https://example.com/script.js?arg=1&arg=2#anchor',
     4248                'ver'          => '1.0',
     4249                'expected_url' => 'https://example.com/script.js?arg=1&arg=2&ver=1.0#anchor',
     4250            ),
     4251            'zero query var in handle'            => array(
     4252                'src'          => 'https://example.com/test-script.js',
     4253                'ver'          => '1.0',
     4254                'expected_url' => 'https://example.com/test-script.js?ver=1.0&0',
     4255                'handle'       => 'test-script?0',
     4256            ),
     4257        );
     4258    }
     4259
     4260    /**
    41964261     * Data provider for:
    41974262     * - test_varying_versions_added_to_handle_args_enqueued_scripts
Note: See TracChangeset for help on using the changeset viewer.