# Building a pre-loader for performance gains in Wordpress

As the title states, I am building a pre-loader for Wordpress running on PHP 7.3.5+ that will deliver performance boosts in loading, as well as metrics such as Google Page Speed.

This is something that we do at work, and I could quite easily copy what has been used there but feel it is 'stolen knowledge'. I could be writing anything and have very little understanding of what I am typing as it was developed by seniors.

So, given that I know what the basics of the pre-loader are (load critical styles, defer certain scripts, remove unwanted WP scripts) I had a bash at creating one myself that appears to work:

// If there are problems with caching,
// change this version number
define('CACHE_VERSION', '1.0.0');

class WpboilerInliner {

function __construct() {
add_action( 'init', array(&$this, 'init') ); add_action( 'wp_head', array(&$this, 'addCriticalCss') );
add_action( 'wp_footer', array(&$this, 'addGeneralCss') ); } // This will add the critical CSS to the header function addCriticalCss() { // Set to not load in the admin as this will 'break' it if(!is_admin()) {$criticalFonts = '<link rel="preconnect" href="https://fonts.googleapis.com">

$criticalCSSContent = file_get_contents( get_template_directory_uri() . '/css/atf.css' );$criticalCSS = "<style type='text/css'>
<!-- BEGIN CRITICAL STYLES -->
{$criticalCSSContent} <!-- END CRITICAL STYLES --> </style>"; echo$criticalFonts . $criticalCSS; } } // General styles, these will be added in the footer function addGeneralCss() { // Add the filename to be added to the footer(below the fold) here // Add files in their correct cascade order // e.g filename.css // filename.min.css // subdirectory/filename.css$generalCssFileName = array(
'general.css',
'type.css',
);

foreach($generalCssFileName as$cssFileName) {
$linkFormat = '<link rel="stylesheet" href="' . get_template_directory_uri() . '/css/%s?ver=%s" />';$cssLink = sprintf($linkFormat,$cssFileName, CACHE_VERSION);

echo $cssLink; } } function init() { // Remove everything to do with emojis remove_action( 'wp_head', 'print_emoji_detection_script', 7 ); remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); remove_action( 'wp_print_styles', 'print_emoji_styles' ); remove_action( 'admin_print_styles', 'print_emoji_styles' ); remove_filter( 'the_content_feed', 'wp_staticize_emoji' ); remove_filter( 'comment_text_rss', 'wp_staticize_emoji' ); remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' ); add_filter( 'tiny_mce_plugins', 'disable_emojis_tinymce' ); add_filter( 'wp_resource_hints', 'disable_emojis_remove_dns_prefetch', 10, 2 ); // Remove version number from header remove_action( 'wp_head', 'wp_generator' ); remove_action( 'wp_head', 'wlwmanifest_link'); remove_action( 'wp_head', 'rsd_link' ); // Removes shortlink remove_action( 'wp_head', 'wp_shortlink_wp_head' ); // Removes feed links remove_action( 'wp_head', 'feed_links', 2 ); // // Removes comments feed remove_action( 'wp_head', 'feed_links_extra', 3 ); /** * Filter function used to remove the TinyMCE emoji plugin * * @param array$plugins
* @return array Difference between the two arrays
*/
function disable_emojis_tinymce( $plugins ) { if( is_array($plugins ) ) {
return array_diff( $plugins, array( 'wpemoji' ) ); } else { return array(); } } /** * Remove emoji CDN hostname from DNS prefetching hints * * @param array$urls URLs to print for resource hints
* @param string $relation_type The relation type the URLs are printed for * @return array Difference between the two arrays */ function disable_emojis_remove_dns_prefetch($urls, $relation_type ) { if( 'dns-prefetch' ==$relation_type ) {
/** This filter is documented in wp-includes/formatting.php */
$emoji_svg_url = apply_filters( 'emoji_svg_url', 'https://s.w.org/images/core/emoji/2/svg/' );$urls = array_diff( $urls, array($emoji_svg_url ) );
}

return $urls; } // Load JS files wp_enqueue_script('wpboiler-critical-js', get_template_directory_uri() . '/js/atf.min.js', array(), CACHE_VERSION, false); wp_enqueue_script('wpboiler-general-js', get_template_directory_uri() . '/js/general.min.js', array(), CACHE_VERSION, true); } }$wpboilerInliner = new WpboilerInliner();


Are there ways of improving this? It has not yet been tested with plugins, but on a basic install it seems to work as expected (from what I can tell). I came into an issue with the admin area when trying to load in the fonts and the critical styles which is why they are wrapped in !is_admin(). Functions disable_emojis_tinymce() and disable_emojis_remove_dns_prefetch() were taken from a post by Kinsta.

Are there methods I can improve here? Are there some major errors that I am likely to encounter with what I have currently written? My PHP knowledge is limited so I understand some of the more basic concepts such as sprintf and the basic use of arrays and loops.

I honestly haven’t used Wordpress much - just assisted with a company project that used it about 6 years ago. The biggest thing I notice is that the syntax used to declare arrays is the legacy syntax, which is fine but could be converted to the shorter syntax - i.e. []. I know it only saves five characters but it is more in-line with JavaScript's shorthand array declaration style.

For instance, these three lines in the constructor:

add_action( 'init', array(&$this, 'init') ); add_action( 'wp_head', array(&$this, 'addCriticalCss') );
add_action( 'wp_footer', array(&$this, 'addGeneralCss') );  Could be updated to: add_action( 'init', [&$this, 'init'] );
add_action( 'wp_head', [&$this, 'addCriticalCss'] ); add_action( 'wp_footer', [$this, 'addGeneralCss'] );


And I could be wrong but the & before $this can be removed. The documentation for passing callables states: A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1. Accessing protected and private methods from within a class is allowed. Notice that it doesn't state the object needs to be passed by reference. In the function addGeneralCss() there is a loop over file names. $generalCssFileName = array(
'general.css',
'type.css',
);

foreach($generalCssFileName as$cssFileName) {
$linkFormat = '<link rel="stylesheet" href="' . get_template_directory_uri() . '/css/%s?ver=%s" />';$cssLink = sprintf($linkFormat,$cssFileName, CACHE_VERSION);

echo $cssLink; }  That is 9+ lines to add two <link> tags. While it is great to avoid repetition, making a loop for two items may not be worth the cost of setting up the loop. echo '<link rel="stylesheet" href="' . get_template_directory_uri() . '/css/general.css?ver=' . CACHE_VERSION . '" />'; echo '<link rel="stylesheet" href="' . get_template_directory_uri() . '/css/type.css?ver=' . CACHE_VERSION . '" />';  The method init is a bit long. There are multiple calls to remove_action() with wp_head as the first parameter, although some calls have a third parameter. If it wasn't for that third parameter in some cases it may be simple to loop over an array containing values for the second parameter. The function disable_emojis_tinymce() contains an if with an else block. if( is_array($plugins ) ) {
return array_diff( $plugins, array( 'wpemoji' ) ); } else { return array(); }  The if block contains a return statement so the else can be removed. if( is_array($plugins ) ) {
return array_diff( $plugins, array( 'wpemoji' ) ); } return array();  If the line in the if block was longer, then it may be worth reversing the logic, to decrease the indentation level: if( !is_array($plugins ) ) {
return array();
}
return array_diff( \$plugins, array( 'wpemoji' ) );


This concept of avoiding the else is part of the Object Calisthenics - you can read more about it here.

• Thanks for that, I've only just had an opportunity to look through the answers you gave and they were all very useful. With the add_action()s, I'll have a read through what is possible and what can be omitted to try and clean that up a bit. But all very clear in your responses. May 28 at 10:56