Make WordPress Core

Opened 2 years ago

Last modified 6 months ago

#25247 new defect (bug)

WP_Dependencies unsets a to do item when it isn't loaded

Reported by: markoheijnen Owned by:
Milestone: Future Release Priority: normal
Severity: normal Version: 3.6
Component: Script Loader Keywords: has-patch dev-feedback needs-testing
Focuses: Cc:


Currently it unsets $this->to_do no matter if do_item() return true or false. For example If you move jquery to the footer it will not load jquery at all. since it thinks jquery-core is loaded.

add_action( 'wp_default_scripts', 'move_jquery_to_footer_and_not_load_migrate' );
public function move_jquery_to_footer_and_not_load_migrate( &$scripts ) {
	if( ! is_admin() ) {
		$scripts->registered['jquery']->deps  = array( 'jquery-core' );
		$scripts->add_data( 'jquery', 'group', 1 );
		$scripts->add_data( 'jquery-core', 'group', 1 );

Attachments (7)

#25247_bugfix.diff (984 bytes) - added by nmarks 12 months ago.
Patch for #25247
#25247_bugfix_v2.diff (1.1 KB) - added by nmarks 9 months ago.
Revised patch for #25247
25247-unit-test.diff (1.1 KB) - added by nmarks 9 months ago.
Unit test for ticket #25247
25247_dependencies.diff (1.4 KB) - added by gitlost 9 months ago.
Proposed patch.
25247_tests.diff (3.4 KB) - added by gitlost 9 months ago.
Unit tests for patch.
25247_scripts.diff (2.2 KB) - added by gitlost 9 months ago.
Optional cleanup patch to WP_Scripts.
25247_tests2.diff (3.4 KB) - added by gitlost 6 months ago.
Refresh on unit tests to patch to latest version.

Download all attachments as: .zip

Change History (21)

comment:1 follow-up: @nacin2 years ago

  • Keywords needs-unit-tests added

Could you submit a unit test patch demonstrating the issue?

comment:2 @ocean9021 months ago

  • Cc ocean90 added

comment:3 @ocean9021 months ago

  • Cc azaozz added
  • Keywords needs-patch added
  • Milestone changed from Awaiting Review to Future Release
  • Version set to 3.6

This script has worked until 3.6:

 * Prints jQuery in footer on front-end.
function ds_print_jquery_in_footer( &$scripts) {
	if ( ! is_admin() )
		$scripts->add_data( 'jquery', 'group', 1 );
add_action( 'wp_default_scripts', 'ds_print_jquery_in_footer' );

With 3.6 I would set the group arg for the dependencies to 1 too, but that doesn't work.

comment:4 @nacin20 months ago

  • Component changed from General to Script Loader

@nmarks12 months ago

Patch for #25247

comment:5 @nmarks12 months ago

  • Keywords has-patch added; needs-patch removed

I added a patch for this. I found that the function was adding jquery to the 'done' array as it doesn't have an src set (src is set on jquery-core).

What's currently happening is when it is doing the footer items, jquery is already in the done array and it makes no attempt to load it.

I've added a patch that checks the current group and set group (if isset) on the item when adding to the done array in the !(has-src) conditional.

For this to work, just set the jQuery and its dependencies' group to 1 with the wp_default_scripts action.

Last edited 12 months ago by nmarks (previous) (diff)

comment:6 @nmarks11 months ago

  • Keywords dev-feedback needs-testing added

comment:7 @pauldewouters9 months ago

Tested the patch #25247_bugfix.diff on WordPress 4.0 on a site with a variety of javascript files with dependencies. jQuery is loaded in the footer as expected, in first place so all depending scripts are loaded after it, using Marko's function.
Would be great to get this merged in, as scripts should really load in the footer

@nmarks9 months ago

Revised patch for #25247

comment:8 @nmarks9 months ago

Hey fellas,

I submitted a revised patch that makes the conditional more readable by expressing some of the conditions as variables and ensured that spacing etc follows the WordPress PHP coding standards.

Last edited 9 months ago by nmarks (previous) (diff)

comment:9 in reply to: ↑ 1 @nmarks9 months ago

@nacin would you like this to be a test for WP_Scripts or WP_Dependencies?

Edit: I need to use a method from WP_Scripts to test this properly, but all the code changes are in the parent class WP_Dependencies. Should I put the test in Tests_Dependencies_Scripts or Tests_Dependencies?

Last edited 9 months ago by nmarks (previous) (diff)

@nmarks9 months ago

Unit test for ticket #25247

comment:10 @nmarks9 months ago

  • Keywords needs-unit-tests removed


Since it relies on the WP_Scripts version of the do_item method, I put the unit test in tests/dependencies/scripts.php.

I also verified that it fails without my patch and passes with my patch.

Please Note: If you are testing, I have put the unit test and the core fix in 2 separate patches so you'll need both:

[Core Patch](https://core.trac.wordpress.org/attachment/ticket/25247/%2325247_bugfix_v2.diff)
[Unit Test](https://core.trac.wordpress.org/attachment/ticket/25247/25247-unit-test.diff)

Last edited 9 months ago by nmarks (previous) (diff)

comment:11 @gitlost9 months ago

I think the no src issue is a bit of a red herring. The real problem here - as stated by the OP markoheijnen - is that the do_items() loop is removing an item from the to_do list whether or not it succeeded. An issue reported by helgatheviking on WP stackexchange (Loading a script with a dependency, is unloading another script's dependency is another symptom of the problem, which can be seen simply with this setup:

		$scripts = new WP_Scripts;
		$scripts->add( 'jquery-ui-datepicker', 'one', array(), 'v1', 1 ); 
		$scripts->add( 'wc-admin-meta-boxes', 'two', array( 'jquery-ui-datepicker' ) ); 
		$scripts->add( 'My_Plugin_Metabox', 'three', array( 'wc-admin-meta-boxes' ), 'v1', 1 ); 
		$scripts->enqueue( array( 'My_Plugin_Metabox' ) );
		$scripts->do_items( false, 0 );
		$scripts->do_items( false, 1 );

Note the mismatch in groups between 'wc-admin-meta-boxes' and 'jquery-ui-datepicker', If you run this, 'jquery-ui-datepicker' won't be outputted, it's just appended to the $in_footer array in WP_Scripts and left there, and since the array is no longer used in do_footer_items(), it gets lost.

Basically it seems that when the code was changed to do two passes of do_items() instead of one, the loop wasn't updated. The fix is simple, only remove the item from the to_do list when it's successfully added (or when the src is null).

The helgatheviking scenario also shows up another issue, in that although with the loop fix 'jquery-ui-datepicker' will get outputted, it will output in the footer, after the dependent 'wc-admin-meta-boxes' which gets outputted in the head. To fix this it would seem best to force any dependencies to the lowest common group, which can I think be achieved by setting the class $this->group when recursing in set_group().

I'll attach a patch and unit tests with these changes.

Last edited 9 months ago by gitlost (previous) (diff)

@gitlost9 months ago

Proposed patch.

@gitlost9 months ago

Unit tests for patch.

comment:12 @gitlost9 months ago

Also the following is an optional patch to WP_Scripts, removing references to the vestigal $in_footer array, and also replacing the use of item $args === 1 with add_data('group', 1) (by overriding the add() function) - having the group state in two separate variables is not the best idea and no doubt legacy again...

@gitlost9 months ago

Optional cleanup patch to WP_Scripts.

comment:13 @nmarks9 months ago

Tested gitlost's patch, looks good.

@gitlost6 months ago

Refresh on unit tests to patch to latest version.

comment:14 @gitlost6 months ago

Would like to see this bug get more attention, it's 18 months old and in a fundamental part of WP which shouldn't have bugs (and where some non-bug changes seem to be getting more consideration).

Note: See TracTickets for help on using tickets.