Akismet case study: Maintaining support for legacy versions

Automattic‘s Akismet, the awesome spam-fighting plugin, needs to maintain support for the oldest of WordPress installations. That poses an issue when new APIs are introduced and old APIs are deprecated. I recently patched up the plugin to make it work great with 3.0 while still supporting far older versions. It also fixed a few minor bugs. I’ve sent the patch over to the Akismet team, but there were a few parts to the patch I figured I would share.

if ( !function_exists('esc_url') ) {
	function esc_url( $url ) { return clean_url( $url ); }
}
if ( !function_exists('esc_html') ) {
	function esc_html( $html ) { return wp_specialchars( $html ); }
}
if ( !function_exists('esc_attr') ) {
	function esc_attr( $attr ) { return attribute_escape( $attr ); }
}

There, now go ahead and use esc_url(), esc_html(), and esc_attr() throughout your plugin. Make sure you always check each function individually, even though these were all introduced in the same version — what if another plugin used the same technique? (What I mean by that is, if you just check esc_url() and define esc_html() and esc_url() with it, but another plugin checks esc_html() and that plugin is loaded first, your plugin will cause a fatal error for trying to redeclare esc_html(), because you didn’t check it first.) And, use the 2.8 widget API if possible, because it’s the Right Thing to do:

if ( function_exists( 'wp_register_sidebar_widget' ) && function_exists('wp_register_widget_control') ) {
	wp_register_sidebar_widget( 'akismet', 'Akismet', 'widget_akismet' );
	wp_register_widget_control( 'akismet', 'Akismet', 'widget_akismet_control', array( 'height' => 75 ) );
} else {
	register_sidebar_widget('Akismet', 'widget_akismet', null, 'akismet');
	register_widget_control('Akismet', 'widget_akismet_control', null, 75, 'akismet');
}

It should be noted, of course, that Akismet already employs this technique for other functions. The goal here is to just catch it up to functions introduced in 2.8.

Complete diff available for download here. It also fixed a few notices caused by unset variables and unchecked indexes, and discovered some unreachable code.

Akismet also assumed the $pagenow global would always be set. This is true — except on activation. (See the notes on the Codex page.) WordPress 3.0 also has stricter activation checks now (which I imagine I will cover in a post soon), which thus prevents Akismet from activating when WP_DEBUG is enabled. The $pagenow issue was handled a few days ago in Akismet trunk, so the patch just cleans up deprecated function use and a few minor notices at this point.