Skip to content

Script Loader: Preserve duplicate query variables in enqueued script and style URLs.#11164

Closed
westonruter wants to merge 8 commits intoWordPress:trunkfrom
westonruter:trac-64372-bugfix
Closed

Script Loader: Preserve duplicate query variables in enqueued script and style URLs.#11164
westonruter wants to merge 8 commits intoWordPress:trunkfrom
westonruter:trac-64372-bugfix

Conversation

@westonruter
Copy link
Member

@westonruter westonruter commented Mar 5, 2026

Previously, using add_query_arg() to append versions or handle-specific query arguments would strip 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.

Regression tests are added to verify preservation of duplicate query variables and fragments using WP_HTML_Tag_Processor for precise attribute verification.

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

Follow-up to r61397.

Props @nichita231.
See #64372.

This issue would have been impacted quite a few themes and plugins:

Trac ticket: https://core.trac.wordpress.org/ticket/64372

Use of AI Tools

I iterated with Gemini CLI on adding tests to reproduce the issue, and then to iterated back and forth on changes. Then I had Gemini draft a commit message and commit.

Prompts
  1. Initial instruction regarding add_query_arg() eliminating repeated query vars in WP_Styles and WP_Scripts, with a request for a better solution and unit tests.

  2. Don't add a new test file. Just add to the existing files which were previously modified in c4d8047. Add the new tests in the appropriate spot, probably right after the tests added in that commit to those two files

  3. You can supply --group=64372 to just one call to npm run test:php

  4. As opposed to using assertStringContainsString, it would be better to assert on the entire URL being the same. Let's provide 'https://example.com/' URLs for each script and style and add assetions to assertSame for the entire string

  5. Let's actually compare URLs. Extract the href/src from the tag via the WP_HTML_Tag_Processor and then compare the attribute value with the expected URL

  6. Let's also add assertTrue() checks to the next_tag calls

  7. ok, now you provided an abbreviated "$frag" variable. Let's avoid this abbreviation. use $fragment

  8. None of the tests provide any URLs with fragments

  9. Wouldn't it make sense to skip setting $fragment to an empy string if strstr() returns false, and then to only append $fragment onto $src if it is a string?

  10. for the tests, the phpdoc descriptions should all use 3rd singular person verbs to start (e.g. "Tests")

  11. I see there are failures now when I run pm run test:php -- --group=scripts

  12. I think you should rather change the URLs back to what they had been beforec c4d8047. YOu can see that I had changed the URLs to deal with the encoding change

  13. That doesn't look relevant. I mean literally to undo the fixture changes I had made in c4d8047.

  14. Your sed commands keep failing.

  15. ok, now based on all of the changes we've made together, and the changes which are staged in git, write a commit message and commit

  16. This code doesn't seem ideal:

    $added_args = isset( $this->args[ $handle ] ) ? (string) $this->args[ $handle ] : ''

    It should rather be:

    $added_args = (string) ( $this->args[ $handle ] ?? '' );
  17. Add a new commit

  18. There is redundancy in this code:

    	$added_ver = '';
    	if ( empty( $obj->ver ) && null !== $obj->ver && is_string( $this->default_version ) ) {
    		$added_ver = 'ver=' . rawurlencode( $this->default_version );
    	} elseif ( is_scalar( $obj->ver ) ) {
    		$added_ver = 'ver=' . rawurlencode( (string) $obj->ver );
    	}

    I think it should set $added_ver to $this->default_version or (string) $obj->ver and then below if $added_ver is not '', then append 'ver=' . rawurlencode( $added_ver )

  19. You can simplify the commit message without the additional references or component name. Refer to the previous commit as an exmaple

  20. This looks strange: ( '' !== $ver_to_add || $added_args). Why compare with empty string for one but not the other. Can't they both just be truthy checks since they are both guaranteed to be strings?


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

…and style URLs.

Previously, using 'add_query_arg()' to append versions or handle-specific query arguments would strip 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.

Regression tests are added to verify preservation of duplicate query variables and fragments using 'WP_HTML_Tag_Processor' for precise attribute verification.

Follow-up to r61397.
See #64372.

Co-authored-by: gemini-cli <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@github-actions
Copy link

github-actions bot commented Mar 5, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props westonruter, jonsurrell.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

westonruter and others added 3 commits March 4, 2026 17:38
Refactors the assignment of '' in 'WP_Styles::_css_href()' and 'WP_Scripts::do_item()' to use the more concise null coalescing operator.

Co-authored-by: gemini-cli <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-cli <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-cli <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@github-actions
Copy link

github-actions bot commented Mar 5, 2026

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates WordPress’s script/style URL building to avoid add_query_arg() re-parsing query strings (which drops duplicate query variables), preserving the original query string (including duplicates) and ensuring new parameters are inserted before any #fragment.

Changes:

  • Refactors WP_Styles::_css_href() and WP_Scripts::do_item() to append ver and handle args directly onto the URL string (preserving duplicate query vars).
  • Improves fragment handling by ensuring appended query parameters are added before #anchor while retaining the fragment.
  • Adds regression tests asserting preservation of duplicate query vars and fragments via WP_HTML_Tag_Processor, plus updates expected markup fixtures affected by the encoding behavior change.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/wp-includes/class-wp-styles.php Builds style href without add_query_arg() to preserve duplicate query vars and handle fragments correctly.
src/wp-includes/class-wp-scripts.php Builds script src without add_query_arg() to preserve duplicate query vars and handle fragments correctly.
tests/phpunit/tests/dependencies/styles.php Adds regression tests validating duplicate query var + fragment preservation for enqueued styles.
tests/phpunit/tests/dependencies/scripts.php Updates fixture expectations impacted by URL encoding behavior and adds regression tests for scripts.
Comments suppressed due to low confidence (2)

src/wp-includes/class-wp-styles.php:420

  • $added_args is cast to string, but the subsequent truthy checks (if ( $ver_to_add || $added_args ) / if ( $added_args )) will treat the valid query string value '0' as false and skip appending it. This is a behavior change vs the previous parse_str()/add_query_arg() path, which would still include a ?0 handle arg.

Use explicit empty-string checks (e.g. '' !== $added_args) so ?0 is preserved, while still skipping the default empty string.

		$added_args = (string) ( $this->args[ $handle ] ?? '' );

		if ( $ver_to_add || $added_args ) {
			$fragment = strstr( $src, '#' );

src/wp-includes/class-wp-scripts.php:430

  • $added_args is a string, but the truthy checks (if ( $ver_to_add || $added_args ) / if ( $added_args )) will treat '0' as false and skip appending it. With a handle like foo?0, the 0 arg would now be dropped, whereas the prior parse_str()/add_query_arg() flow would preserve it.

Consider using explicit empty-string checks (e.g. '' !== $added_args) so ?0 is preserved.

		$added_args = (string) ( $this->args[ $handle ] ?? '' );

		if ( $ver_to_add || $added_args ) {
			$fragment = strstr( $src, '#' );

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-authored-by: gemini-cli <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@westonruter
Copy link
Member Author

Fix tested successfully: #10608 (comment)

westonruter and others added 2 commits March 5, 2026 08:25
Consolidates redundant duplicate query variable and fragment tests into consolidated methods with associative data providers. Adds native PHP type hints for parameters and return values, along with PHPStan array shape documentation. Also applies standard WordPress code formatting.

Co-authored-by: gemini-cli <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Updates regression tests to include coverage for 'false' version strings, which trigger the default WordPress version. Also refines parameter type hints to accurately reflect allowed 'string|bool|null' values.

Co-authored-by: gemini-cli <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Copy link
Member

@sirreal sirreal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very interesting issue. The fix seems appropriate.

@westonruter westonruter removed the request for review from peterwilsoncc March 10, 2026 21:23
pento pushed a commit that referenced this pull request Mar 11, 2026
…s 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 #11164

Follow-up to r61397, r61358.

Props westonruter, jonsurrell.
Fixes #64372.


git-svn-id: https://develop.svn.wordpress.org/trunk@61927 602fd350-edb4-49c9-b593-d223f7449a82
@github-actions
Copy link

A commit was made that fixes the Trac ticket referenced in the description of this pull request.

SVN changeset: 61927
GitHub commit: b8b74d5

This PR will be closed, but please confirm the accuracy of this and reopen if there is more work to be done.

@github-actions github-actions bot closed this Mar 11, 2026
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Mar 11, 2026
…s 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 WordPress/wordpress-develop#11164

Follow-up to r61397, r61358.

Props westonruter, jonsurrell.
Fixes #64372.

Built from https://develop.svn.wordpress.org/trunk@61927


git-svn-id: http://core.svn.wordpress.org/trunk@61209 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants