Automatic Updates For Private And Commercial Plugins

September 2nd, 2010

Since time immemorial, only plugins hosted in the official WordPress.org plugin directory have supported automatic updates. Now, I’ve written a PHP library that you can use to add automatic update capabilities to any plugin. Public, private and commercial plugins alike – all can now enjoy the benefits of automatic update notifications and one-click upgrades.

The custom update checker integrates closely with the upgrade system already built into WordPress, producing a seamless user experience. Observe :

An upgrade notice for a privately hosted plugin.

The 'Plugin Information' window with placeholder data.

(Excuse my marketing-speak. It’s a thing. On with the practicalities of the matter.)

Download

Quick-start Guide

This section describes the quickest way to get automatic updates working for your plugin. Here’s what you’ll need to do: create a metadata file for your plugin, host it somewhere publicly accessible, and tell the update checker where to find it.

Lets start with the metadata. Copy the JSON code below into a new file and replace the placeholder values with your plugin’s info.

{
    "name" : "My Cool Plugin",
    "slug" : "my-cool-plugin",
    "download_url" : "http://example.com/plugins/my-cool-plugin.zip",
    "version" : "2.0",
    "author" : "John Smith",
    "sections" : {
        "description" : "Plugin description here. Basic HTML allowed."
    }
}

(This is the minimal amount data required to make automatic updates work. In most cases, you will probably want to add a couple more fields. See the metadata docs below for details.)

Most of the fields should be pretty self-explanatory, with one possible exception – the “slug”. WordPress expects all plugins that support automatic updates to have a unique textual identifier called the “slug”. Normally, slugs are assigned by the official plugin directory. For a private/commercial plugin that’s hosted elsewhere you’ll have to make something up. If unsure, just use the plugin’s file name without the “.php” extension (my-cool-plugin/my-cool-plugin.php becomes my-cool-plugin).

Upload the metadata file you just created to your web server. It doesn’t matter where exactly you put the file or how you name it. The important thing is for its URL to be accessible from wherever someone might install your plugin.

Next, copy the “update-checker” directory from the client library archive to your plugin’s directory. Then fire up your favourite code editor and add the following lines to the top of your plugin file:

require 'plugin-updates/plugin-update-checker.php';
$MyUpdateChecker = new PluginUpdateChecker(
    'http://example.com/path/to/metadata.json',
    __FILE__,
    'your-chosen-slug'
);

(If you followed my advice and used the plugin’s file name as the slug, you can omit the third parameter of the PluginUpdateChecker constructor.)

And that, believe it or not, is it.

The PluginUpdateChecker class will handle the rest. It’ll check the metadata file every 12 hours and, if it discovers that a new version has been released, twiddle the right bits in the undocumented WP API to make it show up as a standard upgrade notification in the “Plugins” tab. Assuming you’ve set up the download_url correctly, users will be able to install the update with a single click.

The rest of this post will be devoted to a more in-depth discussion of the update checker class and the metadata format.

The PluginUpdateChecker class

This class is the core of the update checker. It’s also the only part of the updater that you should need to deal with unless you decide to  extend the library yourself.

Class constructor

All configuration settings should be specified by passing them to the PluginUpdateChecker constructor. It takes the following parameters :

  • $metadataUrl – The full URL of the plugin’s metadata file.
  • $pluginFile – The fully qualified path to the plugin file. In most cases you can simply use the __FILE__ constant here.
  • $slug – The plugin’s ‘slug’. If not specified, the filename part of $pluginFile (sans “.php”) will be used as the slug.
  • $checkPeriod – How often to check for updates (in hours). Defaults to checking every 12 hours. Set to zero to disable automatic update checks.
  • $optionName – Where to store book-keeping info about updates. Defaults to “external_updates-$slug”.

checkForUpdates()

Manually trigger an update check. This is especially useful when you’ve disabled automatic checks by setting $checkPeriod (above) to zero. This method takes no parameters and returns nothing.

addQueryArgFilter($callback)

Register a callback for filtering query arguments. Whenever the update checker needs to retrieve the metadata file, it will first run each filter callback and attach the query arguments that they return to the metadata URL. This lets you pass arbitrary data to the server hosting the metadata. For example, commercial plugins could use it to implement some kind of authorization scheme where only users that have the right “key” get automatic updates.

The callback function will be passed an associative array of query arguments and should return a modified array. By default, the update checker will add these arguments to the metadata URL:

  • installed_version – set to the currently installed version of the plugin.
  • checking_for_updates – set to 1 if checking for updates, absent otherwise (i.e. when loading data for the “Plugin Information” box).

This method takes one parameter – the callback function.

addHttpRequestArgFilter($callback)

Register a callback for filtering the various options passed to the built-in helper function wp_remote_get that the update checker uses to periodically download plugin metadata. The callback function should take one argument – an associative array of arguments – and return a modified array or arguments. See the WP documentation on wp_remote_get for details about what arguments are available and how they work.

This method takes one parameter – the callback function.

addResultFilter($callback)

Register a callback for filtering plugin info retrieved from the metadata URL.

The callback function should take two arguments. If the metadata was retrieved successfully, the first argument passed will be an instance of PluginInfo (see the source for a description of this class). Otherwise, it will be NULL. The second argument will be the corresponding return value of wp_remote_get (see WP docs for details). The callback function should return a new or modified instance of PluginInfo or NULL.

This method takes one parameter – the callback function.

Metadata format

The automatic update system uses a JSON-based file format to describe plugins.  Essentially, the entire file is one big JSON-encoded object (AKA hash-table or associative array). Each field – or array key – represents a piece of information about the latest version of the plugin. The full description of all available fields is here.

For the sake of simplicity, both general metadata and update-related information are stored in the same file. If this is undesirable, you can replace the plain JSON file with a script that checks for the presence of the the “checking_for_updates” query parameter and emits just the update-related fields if its set to “1″.


Fixing “Memory Exhausted” Errors In WP-DBManager

August 25th, 2010

WP-DBManager is a handy plugin that can, among other things, make periodic database backups and send them to a specified email address. I installed it on this blog months ago and up until a week ago everything was working perfectly. Then one day the backup emails simply stopped coming.

What Went Wrong?

A quick check of the server’s error_log revealed the problem: the plugin was running out of memory.

[19-Aug-2010 15:47:44] PHP Fatal error:  Allowed memory size of 33554432 bytes exhausted (tried to allocate 11145167 bytes) in /home/foo/public_html/wp-content/plugins/wp-dbmanager/wp-dbmanager.php on line 101
[20-Aug-2010 15:47:49] PHP Fatal error:  Allowed memory size of 33554432 bytes exhausted (tried to allocate 10929395 bytes) in /home/foo/public_html/wp-content/plugins/wp-dbmanager/wp-dbmanager.php on line 101
[21-Aug-2010 15:47:38] PHP Fatal error:  Allowed memory size of 33554432 bytes exhausted (tried to allocate 10693507 bytes) in /home/foo/public_html/wp-content/plugins/wp-dbmanager/wp-dbmanager.php on line 101

Alas, this was not all that surprising. Due to the way the mail() function works in PHP, WP-DBManager must read the entire backup file into memory before emailing it. Since the database gets bigger and bigger with each new post and comment, it will one day inevitably exceed the maximum amount of memory that PHP scripts are allowed to use and thus crash the plugin.

There are many ways to fix this problem, but most of them will only buy you some time. Increasing PHP memory limit will work in the short-term, but eventually the WP database will grow large enough to exceed the new limit. Similarly, cleaning up the DB – e.g. by removing old post revisions – can make it small enough that the backup file fits in the available memory. But that, too, is only a temporary solution.

The Fix

Ideally, the plugin shouldn’t even need to read the entire file into memory. It’s just the “convenient” design of the built-in mail() function that demands this. To solve the “allowed memory size of XXX bytes exhausted” problem once and for all, we need to replace mail() with something more flexible.

So, after an hour or two of hacking, I managed to rig WP-DBManager to use the free Swift Mailer library instead. Among the library’s many features is the ability to send file attachments of virtually any size without worrying about memory limits. Thus enhanced, the plugin should be able to handle large backups without a hitch.

You can download the fixed version below. I’ve successfully installed it on my site and the periodic backup emails are rolling in again (yay!). Note, however, that due to the inclusion of the Swift Mailer library the plugin now requires PHP 5.2 or later. Make sure your server has that before trying to install the modified version.

wp-dbmanager-modified.zip (190 KB)


AdSense Experiment: The Final Summary

August 24th, 2010

As you may remember, I’ve been running a little AdSense experiment on this site. Here’s a brief summary for new readers:

The core motivation for the experiment was to test the prevalent assumption that people coming from search engines are the ones most likely to click on ads. To this end, I used a little piece of JS that would present different ads to people based on how they arrived on this site – either directly, from a search engine, from another kind of external site, or from a different page on the same site. All the ads were visually identical but were tracked separately.

(See the original announcement and the first summary for more details.)

The visitor segmentation script has been running uninterrupted for more than five months, allowing me to collect CTR and CPM data on more than 10 000 AdSense clicks. Overall, the trends look stable enough that running it for a while longer probably wouldn’t change the results. So now is a good time to post one final summary and declare the experiment concluded.

Internal Traffic Wins

At least on paper, it does. Out of all possible traffic sources, internal traffic – i.e. people who browse more than one page into the site – has the highest click-through rate and the highest eCPM. However, it also accounts for the least number of ad impressions. In a nutshell, internal traffic is valuable but rare.

Conversely, search engine traffic comes third in terms of CTR and eCPM, but brings the most money due to a high number of impressions.

The full results are below. To comply with AdSense ToS which prevent me from displaying the actual numbers, the results have been normalized to display the relative CTR and eCPM of various traffic sources.

Source Data

The above results are based on:

  • > 18 000 clicks.
  • > 400 000 impressions (73% search engine traffic).

All data was collected during an uninterrupted 168 day period (2010.03.09 – 2010.08.24) on this very same site. Results may be different for other sites.


micro-tweet – The Twitter Client That Fits In a Tweet

August 10th, 2010

micro-tweet is an ultra-minimalist Twitter client that fits in a tweet. It can only do two things – display your friends tweets (one at a time) and post new tweets. It’s written in Python and works entirely from the command line.

Source Code

The source code of micro-tweet is exactly 137 [...] Continue Reading…


Towards a Better dbDelta

July 29th, 2010

When it comes to creating and updating database tables, WordPress has what appears to be a very handy utility function – dbDelta. In theory, this function can take one or more CREATE TABLE queries, compare them to the tables already in the database and automatically figure out how to [...] Continue Reading…