Making popularity contest play nice with WP-Cache
Blogging, Programming May 3rd, 2007 - 4,465 viewsIn 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]
May 5th, 2007 at 1:39 pm
Interesting implementation. I’m going to be taking a different approach that should support other caching systems (like memcached, etc.) as well, but it’s great that folks can use this now if they are using WP Cache.
May 5th, 2007 at 10:04 pm
Cool, I’ll be interested in seeing your implementation. Let me know if you need a hand with coding or whatever. I use both plugins so I’m definitely interested in seeing the compatibility issues officially resolved.
My implementation should work (with minor modifications) as long as the code is included() (vs. reading the file and echoing it). That would obviously be a problem for memcache, since you’d have to exec() the code after pulling it out of the cache.
On a side note - are there any WP cache plugins that use memcache, or was that just a hypothetical? If so I’d be interested in checking them out.
May 8th, 2007 at 8:44 am
I tried modified popularity-contest.php on my site and gives a error with Akismet plugin :|
I’m using wp 2.1.3
May 8th, 2007 at 10:26 am
Usgirl:
Can you be a bit more specific? What error are you getting, and on which pages?
I’ll try enableing Akismet and see if I can reproduce the problem.
May 10th, 2007 at 8:40 am
Today I’ve tried again and all seems ok. :|
May 10th, 2007 at 1:44 pm
Hrm. Well if anyone else has a problem let me know. If there is a bug I’d like to fix it! Hopefully it was just a fluke.
May 18th, 2007 at 4:22 pm
[…] 5/18/07* For the time being, I’ve installed WP-Cache2 and Mike’s modification of Alex’s plugin. I haven’t ventured into APC land, but I may, soon. :) Popularity: […]
May 24th, 2007 at 8:04 am
I can get the plugin to load into WP 2.1.3 but can’t save any changes to the parameters. I get ‘Wordpress can’t load plugin’. I’ve checked the permissions and ‘777′ doesn’t work. I’ve also ensured the plugin’s pure ASCII with no joy. Any ideas?
June 21st, 2007 at 4:59 pm
Hi There - I tried your fixed code and when wp-cache is enabled, I get the following error:
———————
Parse error: syntax error, unexpected T_LNUMBER, expecting T_VARIABLE or ‘$’ in /home/html-directory/wp-content/cache/wp-cache-9dce110aa5d1d36cd6e845a2bcad0ad6.html on line 126
———————
Any thoughts?
June 21st, 2007 at 6:51 pm
Jeff, I need a bit more info to figure out what’s going on… could you open up the file specified in the error and paste whatever is on that line, plus a few lines before and after that line?
Also, what version of PHP are you running and what version of Wordpress?
June 22nd, 2007 at 9:27 am
Hi Mike, Thanks for the quick reply.
WP - 2.2
WP-Cache - 2.1.1
PHP - 4.4.7
The file in the error I posted above is from my cached index.php. This is a custom page I created for the blog. Line 126 is just after the top story of the page, so it seems like Popularity Contest is trying to insert the popularity code after the post, even though I have this set to off [@define(’AKPC_SHOWPOP’, 0); and @define(’AKPC_SHOWHELP’, 0);].
Here is a bit from the cached page:
————————————————-
Title of post
Jun 22:
The text of the post
record_view_ids(array (
0 => ‘4910′,
1 => ‘4909′,
2 => ‘4907′,
3 => ‘4903′,
4 => ‘4900′,
5 => ‘4898′,
6 => ‘4897′,
7 => ‘4894′,
8 => ‘4893′,
9 => ‘4891′,
), array (
‘admin_page’ => false,
‘feed’ => false,
‘archive’ => true,
‘category’ => true,
’single’ => false,
)) –>
————————————————–
And here is a bit from the uncached php page:
————————————————–
“>
:
Posted at
by
|
—————————————————
June 22nd, 2007 at 9:32 am
Crud, code got mangled. I could email the source instead, perhaps.
June 22nd, 2007 at 9:36 am
Jeff, yea, please email it. You can find my email address under the “Contact” tab above, along with my AIM screen name. I’d like to work this out… hopefully it’s not a PHP4 problem. I don’t have a PHP4 box anymore, so I rarely test things on PHP4 these days and sometimes incompatibilities come back and bite me.
June 25th, 2007 at 1:35 pm
[…] Making popularity contest play nice with WP-Cache (tags: wordpress blog blogging plugins) Posted by Jason DeFillippo on Monday, June 25, 2007, at 12:23 am, and filed under links. Follow any responses to this post with its comments RSS feed. You can post a comment or trackback from your blog. […]
July 3rd, 2007 at 5:13 pm
[…] Not only did I just discover the appropriate tools to make the Popularity Contest plugin to play nice with the WP-Cache plugin, but I’ve found […]
July 12th, 2007 at 7:14 pm
[…] using the Popularity Contest plugin though as they didn’t play well together. Fortunately, I’m Mike figured that out and patched Popularity Contest back in […]
August 8th, 2007 at 1:29 pm
Many thanks for this update
Dumb question, but what tag do you use to display popular posts this month in your sidebar?
I tried following the example’s in Alex’s original plugin, but had no luck. I’m assuming it’s using akpc_most_popular_in_month(), but what to put inside the brackets?
(Sorry I know this question isn’t directly related to your enhancements, but I’d appreciate the help)
thanks
August 8th, 2007 at 1:45 pm
@snowcrash:
Not dumb at all. I’m actually not using popularity contest to do the “most popular in month” list. I used to use it, but I didn’t really like how it worked — it literally pulled the most popular posts for _this month_ (e.g., August) instead of pulling the most popular for the last 30 days. So, at the beginning of each month it would reset and show nothing… which was lame.
Right now I’m using some code that Paul Stamatiou wrote for his blog. He wrote a post about it, so you can head over there and check it out. There is one thing to watch out for, though. WP-Cache relies on the global $posts variable to do its magic, and Paul’s code overwrites $posts with his query results, which breaks WP-Cache. The easiest solution is to change the $posts variable in Paul’s code to something else, like $pop_posts.
If you really want to use akpc_most_popular_in_month(), this is the code that I used to use:
akpc_most_popular_in_month(5, '<li><strong>', '</strong></li>', date('Ym'));August 8th, 2007 at 1:53 pm
Hi
Thanks for the quick reply - will check your suggestions
(Hope your water’s back on!)
cheers
September 26th, 2007 at 7:13 am
[…] or at least that the original author will take this opportunity to implement the changes Richard made so that it can work with WP-Cache from now […]
September 30th, 2007 at 5:17 am
Hi, great job and I am using now this implementation but I get an error when I am trying to update feedback by cron:
“Cannot redeclare class wpdb in /wp-includes/wp-db.php on line 16″
The cron job is:
curl –silent http://www.bcnhoy.com/wp-content/plugins/popularity-contest.php?ak_action=recount_feedback -d update=quiet -d log=user -d pwd=password
where user and password obviously are the correct data to login in admin panel.
Please, let me know if you have a solution. Thenk you
October 10th, 2007 at 12:02 pm
[…] seems to cooperate nicely with wp-cache as well, something that in the previous version required a modified version. Unfortunately for some reason my most popular posts per category still do not seem to work so […]
October 30th, 2007 at 3:53 pm
I’ve copied and pasted your version of popularity-contest over mine and it’s still not updating the numbers in the backend anymore.
I’ve been trying to get these two to play nicely and I just can’t find a solution.
October 30th, 2007 at 3:53 pm
Oh, also, the popularity ranking doesn’t show up on the individual blog pages anymore…
November 1st, 2007 at 7:28 am
thanks god! i was sick to see my server overloaded all the time! thank you!
March 10th, 2008 at 1:18 am
Hey this looks really good! I was wondering if you were considering updating this guide for Alex King’s newest version (1.3b3) for those of us using WP2.3.
I noticed that he’s implemented some of the record_view_ids() function into the record_view() function now, so there are definitely some new things there.
Thanks!
March 10th, 2008 at 3:06 am
whoop, not sure if my last comment went through:
I was wondering if anyone has this working with the newest version of Popularity (1.3b3). And if so, is there a guide on how you did it or the php file itself?
Thanks! This tutorial was very useful! Just couldn’t get it working with 1.3b3.
April 14th, 2008 at 2:11 pm
Hey, Mike. I was also wondering if you were going to update Alex’s most recent popularity contest to make it wp-cache compatible. I was just forced to upgrade to WP2.3.3 after my WP2.1.3 was hacked. grrr…