1
\$\begingroup\$

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">
                              <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
                              <link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,200;0,300;1,300&display=swap" rel="stylesheet"> ';
    
            $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.

\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ 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. \$\endgroup\$
    – Bagseye
    May 28, 2022 at 10:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.