Using WordPress Hooks Clean Up Code, Activate & Uninstall

Using WordPress Hooks Clean Up Code, Activate & Uninstall

Plugin authors devote so much time and energy into the main functionality of their products, that they allow less important stuff to fall by the wayside.

Take activation and deactivation, for example. While activation hooks are widespread – many plugins need to add some options, flush rewrite rules, maybe create a database table, or check for version differences on installation – deactivation and uninstallation hooks are far less common.

The point here? Many plugin authors don’t take the time to clean up after themselves. Does the WordPress installation really need the custom table you created after removing the plugin? Why not clear out a few options that are exclusive to the plugin before trashing it?

In this article, I’ll show you how to use activation, deactivation and uninstallation hooks to initialize your plugin and clean things up more easily after users are done with your product.

Note: If you plan to skim through this article, I strongly suggest taking a peek at the “Additional Security” section at the end, which complements the code with some valuable security checks. Also, if you need help with WordPress plugin hooks, here is a quick refresher on using WordPress hooks and how to activate a function in WordPress.

The Activation Hook

Although the activation hook is quite straightforward, installing it is a bit of a special case, so we’ll need to pay attention to the sequence of events. Before we go into all this, here’s a simple example:

The key to it all is the register_activation_hook() function. The first parameter is the path to the main plugin file; the second parameter defines the function to run. Internally, the register_activation_hook() function is a wrapper for the “activate_[plugin_name]” action, but since it’s a bit easier to use, it’s unusual to see the hook in plugins.

The Installation Sequence

Understanding the installation sequence is important because it prevents using methods you may be used to. register_activation_hook() is called in-between the user clicking on the activation link and consequently seeing the activation notice. It runs on an intermediary page, which redirects immediately before any hooks can have a chance to run.

Let’s look at an example to see why this is a huge bummer:

Flushing Rewrite Rules

A number of plugins create custom post types. Flushing the rewrite rules on activation to make sure that users don’t get a 404 error when visiting a post from the new custom post type is a smart move.

The code below seems logical but will fail.

It seems perfectly fine. The custom post type is created and on activation, we flush the rewrite rules. The problem is that the custom post types have not yet been created when we flush the rewrite rules.

Here’s how the process flow looks:

  1. The user installs the plugin.
  2. The user clicks the activation link.
  3. An intermediary page runs the activation hook only, nothing else. This flushes the rewrite rules.
  4. The plugin is active and code runs as usual.  The custom post type is registered.

A solution posted on Stack Overflow, which is officially endorsed by the WordPress Codex, solves our little problem. The solution involves adding an option to indicate that the plugin has just been installed.

If this option exists, we do our activation stuff and then delete it.

Something like this:

Personally, I don’t like this solution too much. The problem is that the check on line eight runs on every single page load. It’s nothing to be concerned about, as it won’t put a big load on your servers and it won’t slow down the website for your users. It’s a very quick check with a negligible impact on performance. It is, however, unnecessary 99.9% of the time.

There is a better solution mentioned in the Codex in the documentation for the flush_rewrite_rules() function. In this solution, we use the modularity of our functions to register the custom post type on activation separately:

Instead of relying on a check that needs to run all the time, we use the activation function to register our post types. Note that once our plugin is activated, the post types will always be registered from the init hook.

This is a sad example of the Codex being all over the place. Generally, WordPress does have good documentation, but if something seems wasteful or illogical, don’t be afraid to do some research of your own.

Creating Database Tables

Another task that some plugins perform is to create database tables. More often than not, this is unnecessary, but there are some legit use cases.

This example from the Codex article on Creating Tables shows how multiple activation calls can be used:

The first function, jal_install() creates a new database table. The second function, jal_install_data adds initial data to the table. Instead of using register_activation_hook() to add one function containing all of this code, we can use register_activation_hook multiple times.

This is a great practice for modularity. On one hand, you don’t have to add initial test data – it’s as simple as removing the activation hook – so you can keep the function intact. On the other hand, you are free to reuse these functions anywhere since they are separate.

Dependency Checks

Another common task for the activation function is to check for dependencies. Your plugin may rely on a specific version of WordPress, another plugin, or even a specific version of PHP.

The code below checks for a minimum WP and PHP version and redirects the user (without activating the plugin) if necessary:

The Deactivation Hook

Deactivation hooks run when a user has deactivated a plugin, but before it is uninstalled (deleted). Deactivation hooks are used in the same way as activation hooks:

Deactivation means that the user has only deactivated your plugin, so you won’t want to do as much as you would during an uninstall. You may want to flush rewrite rules perhaps, but at this stage, you won’t want to get rid of all your options and your database table (if you have one).

This is fairly straightforward but I will pay special attention to flushing rewrite rules as, once again, these are problematic.

The codex recommends doing them as shown below, but this does not work:

The reason this doesn’t work is the same as before. Running a deactivation performs the init hook, which means that as we are deactivating our plugin, we are also registering our custom post type. The rewrite rules are flushed, but they take into account the custom post type.

A Trac ticket is in place to tackle this, but until then, I can’t give you a very good way of doing this. The only way I’ve found that works is to delete the rewrite rules altogether:

While this has worked for me in the past, I would not recommend it. It introduces a greater uncertainty than the problem of having a few extra rewrite rules. I would rather display a note to users asking them to visit the permalink settings after deactivation, which would flush the rewrite rules. Until a better solution is implemented, we’re stuck with this… sorry!

The Uninstallation Hook

There are two ways to run code when uninstalling a plugin. You can use the uninstallation hook via register_uninstall_hook() or you can use a dedicated uninstall.php file within your plugin. I will show you both, but the preferred method is to use the uninstall file.

The main issue with the uninstallation hook is that “it prevents the main plugin file from being run during uninstall, which can be problematic if the plugin runs code in the global space. It’s also better in that the uninstall code is centralized.” – Scott Riley

The code below shows the uninstallation process using a basic hook:

As discussed, this is not the best solution. A far better way to handle uninstallations is to use the uninstall.php file. All you need to do is create it and it will be used if it is available.

As you can see, this is actually a simpler solution. Best of all, it is self-contained.

Additional Security

I didn’t want to over-complicate the examples shown so far with security-related issues, but you really should take some steps to ensure that only those who are allowed to do so can run these actions.

Use the following snippet on activation and deactivation:

This code block makes sure that the user has the permissions to perform this action and that the action originated on the proper page. This should protect against most malicious attempts.

The uninstallation process is special, so we’ll need to use slightly different code:

It’s Time To Clean Up

If your plugin adds stuff to WordPress, then it’s your duty as a developer to remove it when a user decides to delete your plugin.

Using the activation, deactivation and uninstallation methods outlined above will allow you to build a system that does this safely and securely. I also highly recommend reading this Stackexchange thread, which outlines these processes in OOP environments.

If you’re not a WPMU DEV member yet, sign up today for a 30-day risk-free trial. As a member, you’ll get access to all of our great plugins and blazing fast hosting service, plus professional WordPress plugin development training courses.

Tags:
Martin Aranovitch
Martin Aranovitch Martin believes that we can all live sustainably in a WordPress-centric digital universe and spends most of his time taking copious notes and creating epic tutorials to prove his theory. Before joining WPMU DEV, Martin authored many WordPress guides and courses for beginners.
Daniel Pataki
Daniel Pataki Hello, I'm Daniel and I make things for the web. I'm the CTO at Kinsta and I write for a number of amazing publications like WPMU DEV and Smashing Magazine. In my spare time you'll find me playing board games or planning the next amazing office game like the not-at-all-destructive Megaball.
Do you find that plugins leave your database in a mess when you remove them? Let us know what you think in the comments below.