-
-
Save adamsilverstein/ec18b67a72ff74dec12624e989e23142 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* Register scripts with a `defer` or `async` strategy in a backwards compatible manner. | |
* | |
* From WordPress 6.3 onwards, the `wp_register_script` function accepts an `$args` array that | |
* can include a `strategy` key with a value of either `async` or `defer`. | |
* | |
* This helper function handles the backwards compatibility for older versions of WordPress. When a | |
* `strategy` key is present in the `$args` array (and is either `defer` or `async`), the | |
* `script_loader_tag` filter is used to add the attribute to the script tag. | |
* | |
* Note that for older versions of WordPress, dependencies is not considered - the attribute is added unconditionally. | |
* | |
* When support for WP<6.3 is no longer required, simply replace all instances of this function with | |
* `wp_register_script()`. | |
* | |
* @see wp_register_script() | |
* | |
* @param string $handle Name of the script. Should be unique. | |
* @param string|false $src Full URL of the script, or path of the script relative to the WordPress root directory. | |
* If source is set to false, script is an alias of other scripts it depends on. | |
* @param string[] $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 array|bool $args { | |
* Optional. An array of additional script loading strategies. Default empty array. | |
* Otherwise, it may be a boolean in which case it determines whether the script is printed in the footer. Default false. | |
* | |
* @type string $strategy Optional. If provided, may be either 'defer' or 'async'. | |
* @type bool $in_footer Optional. Whether to print the script in the footer. Default 'false'. | |
* } | |
* @return bool Whether the script has been registered. True on success, false on failure. | |
*/ | |
function wpnext_register_script( $handle, $src, $deps, $ver, $args ) { | |
// If >= 6.3, re-use wrapper function signature. | |
if ( version_compare( strtok( get_bloginfo( 'version' ), '-' ), '6.3', '>=' ) ) { | |
wp_register_script( | |
$handle, | |
$src, | |
$deps, | |
$ver, | |
$args | |
); | |
} else { | |
wp_register_script( | |
$handle, | |
$src, | |
$deps, | |
$ver, | |
isset( $args['in_footer'] ) ? $args['in_footer'] : false | |
); | |
if ( isset( $args['strategy'] ) ) { | |
wp_script_add_data( $handle, 'strategy', $args['strategy'] ); | |
} | |
} | |
} | |
/** | |
* Enqueue scripts with a `defer` or `async` strategy in a backwards compatible manner. | |
* | |
* From WordPress 6.3 onwards, the `wp_enqueue_script` function accepts an `$args` array that | |
* can include a `strategy` key with a value of either `async` or `defer`. | |
* | |
* This helper function handles the backwards compatibility for older versions of WordPress. When a | |
* `strategy` key is present in the `$args` array (and is either `defer` or `async`), the | |
* `script_loader_tag` filter is used to add the attribute to the script tag. Note that | |
* for older versions of WordPress, dependency is not managed and the attribute is added unconditionally. | |
* | |
* When support for WP<6.3 is no longer required, simply replace all instances of this function with | |
* `wp_enqueue_script()`. | |
* | |
* @see wp_enqueue_script() | |
* | |
* @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 string[] $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 array|bool $args { | |
* Optional. An array of additional script loading strategies. Default empty array. | |
* Otherwise, it may be a boolean in which case it determines whether the script is printed in the footer. Default false. | |
* | |
* @type string $strategy Optional. If provided, may be either 'defer' or 'async'. | |
* @type bool $in_footer Optional. Whether to print the script in the footer. Default 'false'. | |
* } | |
*/ | |
function wpnext_enqueue_script( $handle, $src, $deps, $ver, $args ) { | |
wpnext_register_script( $handle, $src, $deps, $ver, $args ); | |
wp_enqueue_script( $handle ); | |
} | |
if ( version_compare( get_bloginfo( 'version' ), '6.3', '<' ) ) { | |
add_filter( | |
'script_loader_tag', | |
static function( $tag, $handle ) { | |
$strategy = wp_scripts()->get_data( $handle, 'strategy' ); | |
if ( in_array( $strategy, array( 'async', 'defer' ), true ) && false === strpos( $tag, $strategy) ) { | |
$tag = str_replace( '<script ', '<script ' . $strategy . ' ', $tag ); | |
} | |
return $tag; | |
}, | |
10, | |
2 | |
); | |
} |
@adamsilverstein Here's a hybrid of the two approaches: https://gist.github.com/westonruter/9694840a1cb940e66bdfb650e34c325e
Also I think the wrapper for wp_enqueue_script()
can reduce a lot of code by simply calling wp_enqueue_script()
after calling the wrapper for wp_register_script()
.
@adamsilverstein Here's a hybrid of the two approaches: westonruter/9694840a1cb940e66bdfb650e34c325e
Great!
Also I think the wrapper for wp_enqueue_script() can reduce a lot of code by simply calling wp_enqueue_script() after calling the wrapper for wp_register_script().
Nice!
I'll update here based on that (except the WP_HTML_Tag_Processor
bit so the code can work with older WP versions)
@adamsilverstein I would just suggest making the replacement logic a bit more robust, for example:
$tag = str_replace( '<script ', '<script ' . $args['strategy'] . ' ', $tag );
This would prevent situations where the occurrence of ' src'
somewhere in the string is mistakenly replaced, for example in this string:
<script src="/foo.js" class="foo-js src-local"></script>
@adamsilverstein I would just suggest making the replacement logic a bit more robust, for example:
Good suggestion. Done!
Thanks for the feedback!
Right, that makes sense. I like how your approach only adds a single filter, that is much cleaner. I don't think I'll use
WP_HTML_Tag_Processor
though since it might not be available and thestr_replace
approach already feels reliable and performant.I also like the wrapper functions because they maintain the same function signature, making implementation simple.