In this post I’m going to describe how I got Alex King’s popularity contest Wordpress plugin to work with WP-Cache2. If you’re not interested in how it works, you can skip reading the article and just download my modified version of popularity-contest.php [pretty-printed version] that you can put in your plugins/popularity-contest directory replacing the original file. Make sure you delete your old cache files after installing it to get things working right away.

Update: Make sure you check that your feeds still work after the plugin is enabled. The PHP engine may try to interpret the XML declaration at the top of the file, causing a scripting error. If this happens you can tell WP-Cache not to cache your feed by adding /feed (or something else that matches your feed’s URL) to the list do not cache list under the WP-Cache options. If you really need your feeds cached, you could add some logic to popularity-contest.php to fix the problem.


Alex King’s popularity contest and WP-Cache2 are both popular Wordpress plugins. Unfortunately, the two are not compatible, and I’m not the only one who finds this a bit irritating. The popularity contest readme states:

Since Popularity Contest hooks into the WordPress code that actually displays post content, any caching plugin (like WP-Cache) will affect the ability of Popularity Contest to accurately record views.

This is a tricky problem. Popularity contest is executed as a Wordpress plugin, which means that it is not called when WP-Cache serves pages out of it’s cache. Editing WP-Cache so that it executes the normal WP build process would be a poor solution, as we’d be completely nullifying the performance gains that WP-Cache provides. Luckily, popularity contest relies only loosely on the various facilities provided by wordpress after it is initialized, and I believe I’ve come up with a way to get WP-Cache and popularity contest to work together (that is, it works for me). Even better, you don’t have to change anything in WP-Cache for this method to work.

After analyzing the code for the popularity contest plugin, I realized that the only method of the ak_popularity_contest class we need to worry about is record_view(). The other methods, such as record_feedback() and create_post_record() are only called during POST actions that WP-Cache doesn’t cache.

The record_view() function only relies on two Wordpress components to work: the globals $wpdb and $posts. Getting access to $wpdb is fairly easy, $posts was a bit tricker.

On line 57 of popularity-contest.php there is a line that tests whether $wpdb is set, and includes wp-db-header.php if it isn’t. Changing this line to use an absolute path, and adding one more include will setup $wpdb for us. Lines 56 through 60 should look like the following:

if (!isset($wpdb)) {
  require(ABSPATH . 'wp-blog-header.php');
  require(ABSPATH . 'wp-includes/wp-db.php');
  akpc_init();
}

Next we need to deal with $posts. This global object is a bit more problematic, because initializing it requires pulling all of the post data from the database to build the requested page. That’s an expensive query that we’d prefer to avoid. Looking at the code in record_view(), the only information required from the $posts object are the id(s) of the post(s) being displayed on the current page. If we can somehow store that information along with the cached page we wouldn’t need to worry about the $posts object.

It turns out that WP-Cache is based on a a plugin called Staticize Reloaded, and has inherited that program’s mechanism for executing dynamic content. Thus, we can inject some dynamic content into the cached file, including a callback into popularity contest whenever the cached file is sent back to a client. The callback is constructed as a string, and we can use php’s var_export function to export an array of post ids as an argument. We’ll export a second argument that tells our callback method the type of page that is being displayed (archive, category, admin, post, etc).

To implement these changes I’ve added one method, and one function to popularity-contest.php. The method is called ak_popularity_contest::record_view_ids() and works just like ak_popularity_contest::record_view() except that it takes an array of ids, and an argument specifying the request type rather than relying on internal Wordpress functions to collect this information.

The new function is called akpc_cache_hook(). It generates the callback code to include in the cache file. It’s basically a simplified version of the ak_popularity_contest::record_view() method that collects all of the information our new method needs and exports it to the cached page. The results of this function can be concatenated to post content as it’s generated in the akpc_view() function by adding the line $content .= akpc_cache_hook(); before the return statement.

That’s pretty much it. Popularity contest will now export some PHP code whenever there is a cache-miss, and tack it onto the cached file. The code is dynamically executed whenever a cached page is served. This adds a bit of overhead to each page request (a database hit and a few includes), but it’s nothing compared to the cost of a full dynamic page generation. Maybe Alex could include some of these modifications in the next version of his plugin.

Download modified popularity-contest.php [Pretty-printed version]