Minify Redux

As you may or may not know, I wrote a plugin a while back called Minify. Its purpose was to automatically concatenate your JS and CSS files for you in your WordPress theme. Concatenation and minification are good front-end performance optimization techniques.  The thinking is simple: why request 10 files when we can request 1 optimized file? So that plugin existed for a little while, I said nothing about it, 20 people downloaded it, and then it completely disappeared from the WordPress plugin repo – random.

As of today, it’s back, and it’s way different. When I originally wrote it, I wasn’t working in a load-balanced environment with WordPress. I was working on one or 2 servers at a time on sites that were inconsequential as far as traffic goes. So initially, I was generating flat files.

When you get into the cluster business, you need to give up the notion of working with dynamically-generated flat files. Unless a user is “pinned” to a particular server in the environment, there is no way to determine if you are on the same server from page to page without some logic you shouldn’t feel like messing with – ah, the statelessness of HTTP.

When you are on many servers, Memcached is a great conduit for maintaining state and sharing resources. If that is true, why not use it for JS and CSS? As with all static assets, in the end, you are producing a URL that points at data. Where that data comes from shouldn’t matter. It should be produced fast, and if it can be distributed, great. Especially if you are behind a CDN like Akamai, once the data is requested, it will be cached there, where your local flat files aren’t being utilized.

How Does It Work

WordPress provides tools for enqueue’ing scripts / styles and resolving dependencies among them. When all is said and done, WP will output these scripts / styles when wp_head() and wp_footer() are called. Minify works by using add_action() and output buffering. Let’s take a look:

<?php
/**
 * Essentially, what happens
 *
 */
function minify_start_buffer() {
    ob_start();
}
add_action( 'wp_head', 'minify_start_buffer', 0 );
add_action( 'wp_footer', 'minify_start_buffer', 0 );

function minify_combine_scripts() {
...... a bunch of stuff happens / buffer is released .......
}
add_action( 'wp_head', 'minify_combine_scripts', 10000 );
add_action( 'wp_footer', 'minify_combine_scripts', 2000 );

From there, JS and CSS files are slurped out. Using JS as an example:

  • All srcs are retrieved
  • They are placed side by side in a string, which is then md5‘d
  • At this point – we either already Minify’d them, or they need to be Minify’d
  • Provide locking to serve old scripts temporarily to prevent cache miss stampedes on the new script generation

Cache Busting

Minify will essentially serve your files from the cache into infinity unless:

  • the cache expires
  • you edit the file to indicate expiration
  • you change the name of any file that is enqueued in the set

A Minify tab is specified in the admin to let you update your Minify increment. This is crucial for cache-busting a CDN like Akamai that won’t always request a new version of the file when the query string changes. The increment is included in the list of files that is stored and the generated source, so updating it will create a new list and new source.

Loading WordPress Without Loading ALL of WordPress

Using Andrew Nacin’s favorite constant, SHORTINIT, you can require wp-load.php and not load a billion plugins along with it. This is useful when you are pointing at a PHP file with a RewriteRule and need some WordPress functionality, but you have no desire to generate HTML source, or use plugins and themes. Since plugins are loaded by calling require( $plugin_file ) anyways, after you load wp-load.php, you can selectively load any plugin you need just by requiring it. That’s what I do in Minify:

<?php
define( 'SHORTINIT', 1 );

/**
 * load bare-bones WP so we get Cache / Options / Transients
 *  
 */
$load_path = $_SERVER['DOCUMENT_ROOT'] . '/wordpress/wp-load.php';
if ( !is_file( $load_path ) )
	$load_path = $_SERVER['DOCUMENT_ROOT'] . '/wp-load.php'

if ( !is_file( $load_path ) )
	die( 'WHERE IS WORDPRESS? Please edit: ' . __FILE__ );

require_once( $load_path );
/**
 * SHORTINIT does NOT load plugins, so load our base plugin file
 *  
 */
require_once( 'minify.php' );

Anyways, we use Minify at eMusic. It has dramatically sped up our site / Javascript execution, perhaps more than any other single optimization. Take a look and drop a note if anything offends you.

6 thoughts on “Minify Redux

  1. Dang… we just built the same thing last week. I know you’re working on a CDN plugin… let me prempt by saying we just did that as well… we should talk. ;)

    • the CDN plugin has existed for a year, I just haven’t released it because I don’t want to get emails from people if it doesn’t work for them – I get flooded with emails for other plugins I have written from people who can’t figure them out or shouldn’t be using them. I’ll post here if I unleash the CDN plugin, might be soon unless I get busy

  2. One thing that we built in our version last week was a filter of the css/js to ensure that all relative links are accounted for and made relative to the site root since there are css files in plugins etc. Did you include that in this plugin?

  3. This works great, thanks Scott.

    Two things I note when installing from scratch:
    1. make.php seems to be missing a semi-colon on line 25?
    2. I needed to add a cache buster value to get it working (no default value).

    Anyway, it’s awesome, thanks again!

  4. Pingback: Audio Redux + Updated Plugins | Scott Taylor

Holler

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s