wp_enqueue_scripts Deep Dive

While trying to figure out why the footer-loaded scripts in Store Locator Plus are not being output on some admin page, I went deep down the rabbit hole of the WordPress wp_enqueue_scripts function.   Here are my notes from an analysis of WordPress (5.0-alpha-42191) .

wp_enqueue_scripts

/Users/lancecleveland/vagrant-local/www/wpslp/public_html/wp-includes/functions.wp-scripts.php

This is a PHP inline function.

/**
 * Enqueue a script.
 *
 * Registers the script if $src provided (does NOT overwrite), and enqueues it.
 *
 * @see WP_Dependencies::add()
 * @see WP_Dependencies::add_data()
 * @see WP_Dependencies::enqueue()
 *
 * @since 2.1.0
 *
 * @param string           $handle    Name of the script. Should be unique.
 * @param string           $src       Full URL of the script, or path of the script relative to the WordPress root directory.
 *                                    Default empty.
 * @param array            $deps      Optional. An array of registered script handles this script depends on. Default empty array.
 * @param string|bool|null $ver       Optional. String specifying script version number, if it has one, which is added to the URL
 *                                    as a query string for cache busting purposes. If version is set to false, a version
 *                                    number is automatically added equal to current installed WordPress version.
 *                                    If set to null, no version is added.
 * @param bool             $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>.
 *                                    Default 'false'.
 */
function wp_enqueue_script( $handle, $src = '', $deps = array(), $ver = false, $in_footer = false ) {
    $wp_scripts = wp_scripts();

    _wp_scripts_maybe_doing_it_wrong( __FUNCTION__ );


    if ( $src || $in_footer ) {
        $_handle = explode( '?', $handle );

        if ( $src ) {
            $wp_scripts->add( $_handle[0], $src, $deps, $ver );
        }

        if ( $in_footer ) {
            $wp_scripts->add_data( $_handle[0], 'group', 1 );
        }
    }

    $wp_scripts->enqueue( $handle );
}

The local $wp_scripts variable is set as a copy of the global $wp_scripts variable which is an instantiation of the WP_Scripts class.

Since wp_enqueue_scripts has to be run after the WordPress init hook, the _wp_scripts_maybe_doing_it_wrong call checks on of the following actions was already fired.

did_action( 'init' ) || did_action( 'admin_enqueue_scripts' ) || did_action( 'wp_enqueue_scripts' ) || did_action( 'login_enqueue_scripts' )

Something to make a mental note of, the handle checks to see if there is a ‘?’ buried within and splits it off.    The first half of the handle, before the ? is used in WP_Scripts::add (as long as source is present) and again in WP_Scripts::add_data if in_footer is set.   My code uses a sanitized key style handle with no ‘?’.    There code does have source , the file to be loaded, and is going in footer so both are called.

Once both preparations with add/add_data are done the WP_Scripts::enqueue is called with the full handle including any ? in the source.

WP_Scripts Class

/Users/lancecleveland/vagrant-local/www/wpslp/public_html/wp-includes/class.wp-scripts.php

It extends the WP_Dependencies class.

add() method

From WP_Dependencies

add_data() method

From WP_Dependencies

enqueue() method

From WP_Dependencies

do_item() method

First checks WP_Dependencies::do_item which checks that the handle for the script to be “done” is still registered.

Down the line is then checks the do_concat property and outputs all concatenated scripts.  If do_concat is true the filter script_loader_src is called and passed the $src and $handle properties.   The same script_loader_src filter is called later in the process.    You can hook into this, as I did in my investigation, to output what scripts are being processed.

With do_concat enabled, any scripts that are marked as “in footer” get put into the WP_Scripts::print_html property.  This is NOT output after they are appended if do_concat is enabled unless _print_scripts() is called.

 

WP_Dependencies Class

/Users/lancecleveland/vagrant-local/www/wpslp/public_html/wp-includes/class.wp-dependencies.php

add() method

* Registers the item if no item of that name already exists.

What does that mean?    Since this is used as a base class for WP_Scripts, in this case it means that is registers a script if it is not already registered.

How does it know?   The handle.    In this case the part of the handle BEFORE a ? which we can assume is used only for doing things like adding parameters to a handle like a version to make sure browser caches don’t mess with it.

Anything that has called add() will get an entry in the $registered field for this class which is a named array that holds a _WP_Dependecy object.

$this->registered[$handle] = new _WP_Dependency( $handle, $src, $deps, $ver, $args );
        return true;

This keeps track of which handles are used, make sure each handle is unique, and tracks the parameters that go with that handle.

add_data

This calls _WP_Dependency::add_data() and sets the _WP_Dependency $extras field where the key is ‘group’ and the value is ‘1’ when wp_enqueue_scripts() is called with the in_footer set to true.

Looks like there is some magic sauce right here.

enqueue

This goes through the array of handles, odd since handle is a singular item for WP_Scripts but maybe other classes pass arrays here… and strips off the part after the ? before processing.

It then checks the $queue field to see if the script is already in the queue.  If not it adds the handle to the end of the queue field (an array) and if the part after the ? exists adds that to an $args field with the key = to the handle and the value = the part after the ?.

What We’ve Learned So Far

wp_enqueue_scripts() does not output anything.   It only prepares the scripts TO BE output somewhere else.

wp_enqueue_scripts must be called AFTER  init , admin_enqueue_scripts , wp_enqueue_scripts, OR login_enqueue_scripts  fired.

Investigating $wp_scripts

The global $wp_scripts appears to hold the keys to the script output magic.

The inline function print_head_scripts from /Users/lancecleveland/vagrant-local/www/wpslp/public_html/wp-includes/script-loader.php should output the non-footer admin scripts.

The inline function print_footer_scripts from /Users/lancecleveland/vagrant-local/www/wpslp/public_html/wp-includes/script-loader.php should output the footer admin scripts.   This is of particular interest for my investigation.

print_footer_scripts()

An inline WordPress function.

The WordPress filter print_footer_scripts can be used to thwart output of all footer scripts by returning false.

First – concatenate the scripts according to global script handling settings via the inline function script_concat_settings() another inline WordPress function.

Next – call WP_Scripts::do_footer_items() which calls WP_Dependencies::do_items() with parameters false and ‘1’ with 1 being the group to be processed.   In turn WP_Scripts::do_items() calls its own all_deps method  to check some things and setup the $to_do field for the queue of stuff to process.  Looping over to_do calls WP_Scripts::do_item() after making sure each script handle is only processed once.

script_concat_settings()

An inline WordPress function to concatenate and compress scripts.

The following globals impact how the scripts are man-handled before being output: $concatenate_scripts, $compress_scripts, $compress_css

If the global define CONCATENATE_SCRIPTS is set and SCRIPT_DEBUG is NOT set then the script is marked to be concatenated.

If the global defined COMPRESS_SCRIPTS is set and the get_site_option(‘can_compress_scripts‘) returns TRUE and the compression libs are turned on for PHP then the script is marked to be compressed.

compress_css works like the compress_scripts but we are not doing CSS so I’m not getting into that.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.