Need a WordPress website this weekend? Start here...

Technical Tuesday: Another Look at WordPress register_activation_hook

(Reading time: 7 – 11 minutes)

Wait! Stop!

There’s much more here than source code. You can skip right over the register_activation_hook example code. Unless you’re desperately seeking solutions, then you want to read every word.

Still with me? Cool. Let’s get started.

Technical documentation is hard to read and harder to write. As a programmer, you aren’t rewarded for writing documentation, no matter what management says, so documentation tends to be lean, spare.

As a reader, you know you’re putting on the turswiry hat before you even crack open the manual.

When you finally break out the manual, you’re invariably faced with learning about how complicated it all is. Then,

Once you figure it out on your own, you can’t say the man pages don’t say that. – Jason Cox.

Wisdom from East Tennessee indeed.

It’s that “figure it out on your own” part that’s so hard. It takes a lot of practice, both to do the figuring, and knowing when you need to take the time to do the figuring.

Because it is time consuming.

Here’s my practice “figuring it out” for today.

(Non-programmers, skip past the code).

Example code from WordPress.org forum

WordPress is a large and rich application. It’s fairly well written; I’ve seen far worse code. And it’s fairly well-documented.

But there are still a lot of dusty corners. One of these is the register_activation_hook function. Most plugins need to use, but there’s not a lot of accurate discussion about exactly how to use it.

I was poking around for some example code, and ran across this snippet posted by Gleb Esman. Gleb seems pretty cool, he’s the author of the MemberWing plugin and service. But he didn’t get this one right, and I’m going to show you why. Let’s take a look:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
/*
Plugin Name: A Test
Description: A Test
*/
 
require_once (dirname(__FILE__) . '/my_other_file.php');
 
// This code *will not* work. Activation hook function must be defined in the main plugin file.
//register_activation_hook (dirname(__FILE__) . '/my_other_file.php', 'my_other_function');
 
//(Dave) But this code works just fine! 
register_activation_hook (dirname(__FILE__) . '/activation1.php', 'my_other_function');
register_deactivation_hook (dirname(__FILE__) . '/activation1.php', 'clean_em_up');
 
// This code will work.
//register_activation_hook (__FILE__, 'test_activated');
 
// This is correct way to declare/access globals.
global $some_var;    // globals must be declared explicitly. Without this you will not be able to access '$some_var' from within 'test_activated()' function.
$some_var = 'hey';
 
//===========================================================================
function test_activated ()
{
   global $some_var; // Now it will be 'hey'.
 
   // This function is defined in 'my_other_file.php'
   my_other_function ();
 
   // This will not work, so don't try. If you need logging write something in temporary log file in here via fopen/fwrite.
	// If you want to quickly test if your activation hook works - put exit() into it. At least you'll see error during activation.
   echo 'test_activated called!';
}
//===========================================================================
?>

First, please accept my apologies for the misaligned line numbers. This is a Thesis CSS issue, the table layout design between Thesis and the plugin doesn’t work very well. But it’s still workable. Let’s walk through it line-by-line:

  • Line 3: the plugin name. Critical. We’ll return to this later.
  • Line 7: I’ve listed this file below as well, we’ll look at it line by line next.
  • Line 10: Doesn’t work, true, but the explanation is wrong.

    Fact: WordPress will register any callback in scope.

    The problem here is that activation hook operates on the plugin name. The name of the file (line 10) containing the callback is irrelevant.

  • Line 13: Registering the callback with the correct filename works.
  • Line 13: (Bonus) register_deactivation_hook works too.

I’ve left the rest of the code in place so that you can compare my additions linewise with the original register_activation_hook example.

Callbacks in different file

Now we need to take a look at the code in my_other_file.php. Having these functions in a different file is irrelevant for a plugin this small, but it does serve a useful purpose for testing. For larger applications, separating important parts of the code is an important principle of software design.

1
2
3
4
5
6
7
8
9
10
11
<?php
/* Callback for activation hook */
function my_other_function() {
	add_option('activation1','Activated');
}
 
/* Callback for deactivation hook */
function clean_em_up() {
	delete_option('activation1');
}
?>

This is easy stuff, the callbacks just add or remove an option in the WordPress options table.

When you run the code presented in these two listings, take a look at your options database, as shown in this screenshot:

register_activation_hook test using options database

register_activation_hook test using options database


There’s other ways to check, but this is pretty fast, and if you aren’t familiar with the options table you need to be.

Why is register_activation_hook so hard?

I can’t speak for everyone, but here’s what I think:

  • PHP variable scoping rules feel kooky coming from a C/C++/Java background. PHP looks like C, smells like C, but doesn’t really walk like C. Trouble comes when programmers absent-mindedly crank out PHP code, unaware of the differences in scoping rules.
  • Callbacks give people trouble, even these activation/deactivation functions which are so simple they don’t even take arguments.
  • Man pages are inaccessibly written, and often out of date. But manual should be the first place to start. If the manual has test code, run it to check it’s accuracy.
  • Even fewer people read the code. The comments for register_activation_hook are crystal clear about what’s expected. If only people would read the source!

You should probably jump over to Reverse Engineering Activation Hook In WordPress Plugin API for more information. This is not really a series of articles… yet. Although I do have at least a couple more articles to write on register_activation_hook. Maybe three or four more. At the end of the last article (that is, when I get tired of writing them), I’ll give you a two-step procedure ensuring you will never, ever have trouble with this function again.


Ok, enough preaching… here’s something for you scanners…

What’s in it for me?

More work, if you’re so inclined. But this is good work. Your work.

Here’s the deal. My friend Deacon and I have a long running discussion on deliberate practice. We both crave personal and professional excellence, and we believe that the path to mastery is in the practice of mastery. Deacon cuts wood blocks. I cut code.

masteryPart of our inspiration comes from this little book, George Leonard’s Mastery. In about 100 small pages, Leonard lays out how mastery is achieved through practice. Practice in the sense of “doing what you do” on a regular basis, with deep concentration.

Practice puts the journey first.

The means become the ends.

Here, taking on some silly misconceptions about some random WordPress function is not what’s important.

Instead, the process of examining the problem, understanding why the problem exists, and constructing an elegant solution is my path to mastery.

I don’t care about register_activation_hook. I care about:

  1. Understanding WordPress coding conventions. Understanding this code helps me “get inside the developer’s heads.” I want to think how they think. That’s when the “programming” ends… and the creativity begins.
  2. Learning PHP behavior, specifically, variable scoping rules. Same as above: practice the programming so I don’t have to think about it.
  3. Ensuring my plugins work correctly when I need to invoke the activation hook. I have a legitimate need to understand how this function works.
  4. And… (long time readers know what’s coming) smacking down an under-served keyword. Actually, two. An hour of free blog post engineering to the first person who guesses my second keyword.

Learning the intricacies of register_activation_hook is just the convenient vehicle carrying me further along my path.

What’s your path to mastery?

Do you have a regular practice? If so, tell us about it!

Reverse Engineering Activation Hook In WordPress Plugin API

(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:

  1. Is the application well-engineered?
  2. Is the well-engineered application well-documented?
  3. 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.

Function listing.

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).