Note to WordPress plugin developers: use the pre_comment_approved filter with caution if there is a chance that Akismet will be active on sites using your plugin.

There is a long standing bug in WordPress, which is that a self-referential call to remove_filter breaks the filter — any functions that had been scheduled to run after the one being removed will not be called.

Unfortunately, this is exactly what Akismet does in the akismet_result_spam function — it tries to remove its own hook. The result is that whenever Akismet identifies a comment as spam, any functions hooked to the pre_comment_approved hook with priority greater than the default (10), will silently fail to execute.

As an aside, there are other, simpler ways to ensure that a function runs only once — static or class variables, for example.

Bug report and Trac ticket.

While working on the latest release of Conditional CAPTCHA for WordPress, I realised that Akismet stores a whole bunch of metadata for every comment, which can seriously bloat your WordPress database.

For each comment, Akismet records whether or not the comment was flagged as spam. Then, it stores a comment history which logs every change that is made to the status of the comment (spam, pending, approved etc), along with details of user who made it. This metadata is never deleted! At the very least, it means one extra row of metadata per comment (more for spam comments that are later approved).

I don’t find this history particularly useful — certainly not so much that it needs to be kept forever. Here’s what I use to prevent Akismet from storing this metadata:

function prevent_akismet_history( $check, $object_id, $meta_key ) {
    $to_filter = array( 'akismet_result', 'akismet_history', 'akismet_user', 'akismet_user_result' );
    if( in_array( $meta_key, $to_filter ) )
        return false;
    return $check;
}

add_filter( 'add_comment_metadata', 'prevent_akismet_history', 10, 3 );

I’ve also added an option to Conditional CAPTCHA to do this automatically.

To delete existing metadata, the following SQL query would work:

DELETE FROM `$wpdb->commentmeta` WHERE `meta_key` 
    IN ( 'akismet_result', 'akismet_history', 'akismet_user', 'akismet_user_result' );

There are two ways that plugin and theme authors can check whether comments are allowed on a post in WordPress:

Old-school way (wrong unless you have a very good reason):

if( $post->comment_status == 'open' ) {
	// do something here
}

Correct way:

if( comments_open() ) {
	// do something here
}

The second way is better because the comments_open() function is filterable — other plugins and themes can alter its output depending on context.

What makes WordPress so powerful is the underlying API. Plugin and theme authors can modify almost anything, and this is why there are so many thousands of plugins and themes out there. But we plugin and theme authors need to make sure we return the favour, so that our themes/plugins can play nicely with the rest. So, if your plugin or theme is using the first method above, please change it to use the second.

One other note for developers: WordPress has for some time now supported arbitrary post types (not just the posts and pages that we’re all used to), some of which don’t necessarily support comments at all. So it’s a good idea to check whether a post type even supports comments before running any comment-specific code:

if( post_type_supports( get_post_type(), 'comments' ) ) {
	// put comment related code here, including...
	if( comments_open() ) {
		// ...a comment form, maybe
	}
}
else {
	// sit back and relax
}

The code beneath the default WordPress theme uses both of the functions mentioned above.