(Reading time: 10 – 16 minutes)
Ahhh… June Gloom. I’ve started this article early morning on June 1. The weather is summer-normal for my little corner of the San Francisco Bay Area: overcast, fog halfway up the East Bay Hills. Let’s spin some tunes and get to work. I’m in the mood for something chilled out. How ’bout some London Lounge – London by Day… yeah, that’ll do it. 
Now, I’m not normally a fan of compilation albums, but over the last year or so I’ve started to come around. As my friend Travis says, “Dude, these collection CDs are awesome! I get ‘em at Amoeba for $2, and I hear tons of different artists I’ve never heard of.” Travis has a point. In fact, he pointed me at London Lounge in the first place. When he gets off his butt and creates his own website, I’ll give you a link to it.
But chill music may not be what you’re here for… you wanna know about WordPress, and plugins, and how to figure out whether to use add_action(activation_my-sweet-plugin/my-sweet-plugin.php, ...) or register_activation_hook(basename(__FILE__) . 'my-sweet-plugin.php, ...), and how to make ‘em work in either case. We’ll talk about that “...” too, and what that means depending how your plugin is structured.
Ok, then
Onward, through the fog.
Use the source for true understanding
We have to back up just a little bit first. This is stuff you need to know before the rest of the article makes sense. If you know all this already, skip to next the section. If not… here’s the deal: If you really want to understand what’s going on, you must read the source code. (Right here is where 90% of you check out…)
But wait! Don’t go! It’s not that hard!
Reading and understanding source depends mainly on the answers to these three questions:
- Is the application well-engineered?
- Is the well-engineered application well-documented?
- Is the source code hosted such that it’s browser-readable by revisions, cross-referenced and indexed?
If the answer to all 3 questions is “Yes!” then reading source code will NOT be a tedious exercise in futility. It will merely be difficult, but only in the beginning. Reading source code becomes much easier once you understand: 1. the syntax of the programming language, 2. the design and design idiom of the application, and 3. how to use source code browsing tools. Simple, no?
As it turns out, the WordPress source code is very well engineered and documented, and is widely hosted across the internet. We’re going to use two of these source listings. The first is the official repository for the WordPress source, which is hosted on a server with Trac installed. The second is a source listing indexed by PHPXRef, which will give us very fast lookup for variables, functions and classes. I’ve already written about how fantastic Trac is for helping solve various technical tasks, now you will see a different side to the Trac tools.
Digging into WordPress plugin implementation
You just started creating WordPress plugins. You are looking at the source code for many of the plugins you have already downloaded from wordpress.org. After you install the plugin, you want to make sure it get’s activated, but there’s several ways to accomplish plugin activation. Peruse any dozen random plugins, you will find activation triggered by a variety of different functions. Some plugins use add_action, some use add_filter, yet others use register_activation_hook.
Which one should you use?
This is a very good question… and searching for an answer on Goolge is going to get you an overwhelming amount of frankly irrelevant information… none of which is going to tell you outright how you need to accomplish activation.
In part, what you should use depends on how your plugin and it’s directory is named. For example, consider the case when the plugin file is different than the directory name:
// actions add_action('activate_my-cool-plugin/coolplugin.php', array(&$this, 'activate')); add_action('deactivate_my-cool-plugin/coolplugin.php', array(&$this, 'deactivate'));
Compare this to
// actions register_activation_hook(__FILE__, array(&$this, 'activate')); register_deactivation_hook(__FILE__, array(&$this, 'deactivate'));
Keep reading, and I’ll tell you not only what to do, but why you should do my way.
The next two things you need to do are open the WordPress source and it’s cross-referenced listing in new browser windows. Windows are better than tabs because you will find it very useful to compare source listings.
Source listing of /wordpress/wp-includes/plugin.php in phpxref, and in the Edgewall Trac browser:
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | /** * Set the activation hook for a plugin. * * When a plugin is activated, the action 'activate_PLUGINNAME' hook is * activated. In the name of this hook, PLUGINNAME is replaced with the name of * the plugin, including the optional subdirectory. For example, when the plugin * is located in wp-content/plugin/sampleplugin/sample.php, then the name of * this hook will become 'activate_sampleplugin/sample.php'. When the plugin * consists of only one file and is (as by default) located at * wp-content/plugin/sample.php the name of this hook will be * 'activate_sample.php'. * * @package WordPress * @subpackage Plugin * @since 2.0 * * @param string $file The filename of the plugin including the path. * @param callback $function the function hooked to the 'activate_PLUGIN' action. */ function register_activation_hook($file, $function) { $file = plugin_basename($file); add_action('activate_' . $file, $function); } |
Go ahead and dig this out of your own WordPress installation and take a look at the code. See how much nicer everything looks with syntax highlighting? And how handy it is to have everything cross-referenced? Programmers are smart!
Let’s proceed. In PHPXRef, click on the function add_action link, then click through again on the next page to find the source implementation for add_action:
/**
* Hooks a function on to a specific action.
*
* Actions are the hooks that the WordPress core launches at specific points
* during execution, or when specific events occur. Plugins can specify that
* one or more of its PHP functions are executed at these points, using the
* Action API.
*
* @uses add_filter() Adds an action. Parameter list and functionality are the same.
*
* @package WordPress
* @subpackage Plugin
* @since 1.2
*
* @param string $tag The name of the action to which the $function_to_add is hooked.
* @param callback $function_to_add The name of the function you wish to be called.
* @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
* @param int $accepted_args optional. The number of arguments the function accept (default 1).
*/
function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
return add_filter($tag, $function_to_add, $priority, $accepted_args);
}
Now that’s really interesting: we can activate a WordPress plugin directly with add_filter, because add_action is simply a wrapper for add_filter. I have a couple of plugins floating around using add_filter, whence my note above. My hunch (from long experience) is that using add_filter is the old way. If you have a plugin using add_filter, it’s probably pretty long in the tooth.
I think we have an answer: use register_activation_hook.
Just for fun, let’s take a look back through the revision history and find out exactly when register_activation_hook was added to the WordPress API, and who added it.
Clicking all the way back through the revision history, we get to where plugin.php was first added to the repository, namely, Revision 3893 checked in by “ryan” on 06/20/06 02:03:24 (3 years ago). And we see that register_activation_hook has been in there from the beginning!
So, what’s the deal?
Hard to say.
Since I have been telling you this story as I go, I’m as surprised as you are to find out how old register_activation_hook is, and surprised that I would still see add_action in use in a plugin distributed 3 years later. add_action is perfectly adequate, although not that bit literate (more on bit literacy in a moment). Perhaps the particular plugin I was using as an example was first written for WordPress before the plugin API. Perhaps the original plugin author preferred the “purity” of using “lower level” API calls. Hard to say.
A professor of mine once informed a class full of engineers: “If you don’t know what to do, look around and see what everyone else is doing, then do that.” Ok, let’s take a look through my locally hosted WordPress installation, and see what my installed plugins are using. This is easy to do using the cygwin unix tools find, xargs and grep:
$ find . *.php | xargs grep "activation_"
Here’s the result, 10 register_activation_hook calls:
./action-optin/actionoptin-plugin.php:register_activation_hook(__FILE__, array($aowp, 'install')); ./amazon-reloaded-for-wordpress/Amazon-Reloaded.php:register_activation_hook( __FILE__, array( &$this, 'on_activate' ) ); ./best-post-page/bestpost.php:register_activation_hook(__FILE__,'bestpostpage_activation'); ./booklinker/booklinker.php:register_activation_hook(__FILE__,'set_booklinker_options'); ./demotemplate/demotemplate.php:register_activation_hook(__FILE__,array($plugin_class, 'Activate')); ./link-harvest/link-harvest.php://register_activation_hook(__FILE__, 'aklh_install'); ./seo-automatic-wp-core-tweaks/page-link.php:register_activation_hook( __FILE__, 'gdmPageLinkActivate'); ./simple-tags/2.7/simple-tags.client.php:register_activation_hook(__FILE__, array(&$simple_tags_admin, 'installSimpleTags') ); ./sociable/sociable.php:register_activation_hook(__FILE__, 'sociable_activation_hook'); ./stats/stats.php:register_activation_hook(__FILE__, 'stats_activate');
Looking for the add_action technique:
$ find . *.php | xargs grep action\(\'activate_
We find:
./aweber-registration/wpaweber.class.php:add_action('activate_aweber-registration/wpaweber.php',array(&$this, 'install')); ./aweber-super-simple/aweber.class.php:add_action('activate_aweber-registration/wpaweber.php', array(&$this, 'install')); ./broken-link-checker/broken-link-checker.php:add_action('activate_' . plugin_basename(__FILE__), array(&$this,'activation')); ./demo-plugin-database/demo-plugin-database.php:add_action('activate_demo-plugin-database/demo-plugin-database.php', array ( & $this, 'install_db')); ./quote-rotator/quote-rotator.php:add_action('activate_quote-rotator/quote-rotator.php', array(&$quoteRotator, 'createDatabaseTable')); ./wp-dbmanager/wp-dbmanager.php:add_action('activate_wp-dbmanager/wp-dbmanager.php', 'dbmanager_init'); ./wp-shortstat2/wp-shortstat.php:add_action('activate_'.plugin_basename(__FILE__), array(&$wpss, 'setup'));
The two aweber plugins count as 1, so there’s 6 with add_action and 10 with register_activation_hook.
Is that all there is to the story? Maybe not. Some plugins don’t use an activation hook at all. That’s a topic for another article.
Dig deeper: Literate programming
Literate programming is the notion promoted by Donald Knuth that your source code should be “readable” in the same way a book is readable. In general, literate programming is a bit of pipe dream, for a large number of reasons none of which are worth discussing here.
However…
Knuth’s book Literate Programming belongs on every developer’s bookshelf. Literate programming is composed of large component of “bit literate” techniques, all of which are extremely valuable, and which are starting to emerge in the WordPress source. Using bit literate naming conventions for functions and variables goes a long way towards increasing the readability of any source code. “register_activation_hook” is a perfect example.
The upshot: use register_activation_hook. It’s a cleaner abstraction, and very bit literate. It’s used for one and only one very important task, “doing stuff when activating plugin.”
If you’ve made it this far, and want 30 minutes of free website or editorial consulting, tell me the source of the quote: “Onward, through the fog.” Tell me where you last saw it on a bumper sticker, I’ll give you an hour (first 3 people only, expires July 31 2009).
Would you like more? Send me a letter...

Comments on this entry are closed.
{ 2 trackbacks }