Why WP-Emoji is bad

tl;dr wp-emoji.js creates expensive event listeners that run every time you touch the DOM and it gives you no way to disable them.

At 1Pass we like to replace HTML with other HTML. That’s how we put the full text of articles onto publisher’s pages so our users can read them without buying subscriptions.

This afternoon my colleague @benilovj and I found a problem. Recently on The Browser, content was taking up to 9 seconds to replace. 9 seconds. And during those 9 seconds, the computer would become essentially unresponsive.

A few `console.log`s later, we found the problem here:

render: (article) ->
  el = document.querySelector(article.selector)
  el.innerHTML = article.content

This function returned more or less instantly. But the page lay around on the screen— burning hot, like the rest of the laptop—for a good few seconds before the content would show up. The machine started responding slowly, and the spinning beach ball of death would look in sometimes.

WP Emoji was to blame

wp-emoji.js, included in WordPress 4.2, is a JavaScript library that parses the DOM looking for smiley faces and whatnot, and turns them into pretty emoji graphics. To do this it uses a recherché piece of DOM-watching technology called a MutationObserver to react to any change on any element in the entire document body. You can see it get hooked up on GitHub here.

This is not a terrible idea. Every time something adds content to the DOM, like infinite scroll, for example, it’ll have its emoji sprayed on automatically. In our case, as we injected our blob of HTML which was the article, wp emoji went bananas parsing every last little bit of it for smiles (that weren’t there).

Really, all this would be fine—convenience over configuration and all—if it weren’t for one fatal oversight. The only way to disable these observers is to get a reference to them and disconnect() it. But in wp-emoji, when the observer is created it’s just new’d up into oblivion. No reference is retained. And that means there is no way to unhook it except by some uncomfortable hacking. And without that, it murders performance for us.

Our library is designed to run on sites we don’t control, so we don’t have the liberty of installing the emoji disabling plugin.

All this… for emoji?

Two WordPress functions I can’t do without

These two remove the awful tedium of writing filter functions that just return things or append things to arrays. Inspired by ‘__return_true’ and ‘__return_false’ from WP core.

function __provide( $var ) {
	return function() use ( $var ) {
		return $var;
	};
}

function __append( $var ) {
	return function( $arr ) use ( $var ) {
		$arr[] = $var;
		return $arr;
	};
}

Usage example

Before:

add_filter( 'excerpt_more', 'my_remove_excerpt_more' );
function my_remove_excerpt_more( $excerpt_more_string ) {
	return '';
}

After:

add_filter( 'excerpt_more', __provide( '' ) ); 

A better way to handle POSTs in WordPress using closures

Ever find yourself doing this? Repeating the entire logic of a basic POST, only to change some minute detail of the function. The only difference between the two functions below is the meta_key.

add_action( 'save_post', 'update_my_meta_value' );

function update_my_meta_value( $post_id ) {
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    if ( !wp_verify_nonce( $_POST['my_nonce'], 'nonce' ) ) {
        return;
    }

    if ( wp_is_post_revision( $post_id ) ) {
        $post_id = wp_is_post_revision($post_id);
    }

    $meta_value = intval( $_POST['my_meta_value'] ); // sanitize

    // ok, update the single meta value I just posted
    update_post_meta( $post_id, 'my_meta_key', $meta_value );
}

add_action( 'save_post', 'update_another_meta_value' ); 

function update_another_meta_value( $post_id ) {
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    if ( !wp_verify_nonce( $_POST['my_nonce'], 'nonce' ) ) {
        return;
    }

    if ( wp_is_post_revision( $post_id ) ) {
        $post_id = wp_is_post_revision($post_id);
    }

    $meta_value = intval( $_POST['another_meta_value'] ); // sanitize

    // ok, update the single meta value I just posted
    update_post_meta( $post_id, 'another_meta_value', $meta_value );
}

This is hideous. Globals, repetition, etcetera. We can dramatically clean it up with a simple closure.

function db_post_handler( $callback ) {
    return function( $post_id ) use ( $callback ) {
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return;
        }

        if ( !wp_verify_nonce( $_POST['my_nonce'], 'nonce' ) ) {
            return;
        }

        if ( wp_is_post_revision( $post_id ) ) {
            $post_id = wp_is_post_revision($post_id);
        }

        call_user_func( $callback, $post_id );
    };
}

This way we can bang out functions that have *some* logic in common, and we can hand the result straight to add_action:

function update_my_meta_value( $post_id ) {
    // all the nonce-checking etc is something else's responsibility
    // so i can focus on what i wanted to do
    $meta_value = intval( $_POST['another_meta_value'] ); 
    update_post_meta( $post_id, 'another_meta_value', $meta_value );
}

add_action( 'save_post', db_post_handler( 'update_my_meta_value' ) );

Benefits:

  • Remove duplication
  • Clearer intention in the functions that do the actual updating
  • More secure (in that you can’t forget to do nonce-checking etc)
  • Testable functions that don’t depend on globals

Retrieving a password you accidentally deleted from your OS X Stickies

If you are careless, you regularly delete stickies containing important information. Thankfully, your Tax ID code is a 12-digit number.

# OSX may have left an old copy of the stickies lying around. this is where we will look.
# get the stickies and back them up
$ cd /Users/[you]/Library/Preferences/
$ mkdir ~/stickybackup
$ cp *.stickies.plist* ~/stickybackup
$ cd ~/stickybackup
# we want to grep the .plist files, but they're technically binary blobs
# so we must convert them. repeat for each file - i can't write bash loops
$ plutil -convert xml1 -o - [your sticky filename] > plain_text.txt
$ grep '[0-9]\{12\}' plain_text.txt 

cf: http://apple.stackexchange.com/questions/101719/trouble-opening-plist-files-in-text-editor and https://discussions.apple.com/thread/1231501

Drive-by recursion in PHP

PHP does not make it easy to program with recursion offhand, but it’s possible. This is from a WordPress function which needed to get IDs from terms and all their parents:

// get the term ids from this term and all its parents
$all_ids = call_user_func($func = function( $term, $ids ) use ( &$func ) {
    if( !$term ) { return $ids; }

    $ids[] = $term->term_id;
    $next_term = get_term_by( 'id', $term->parent, $term->taxonomy );

    return call_user_func( $func, $next_term, $ids );
}, $term, array() );