If you are building a complex plugin, or one which needs it’s own database tables, you’ll likely be installing all kinds of things during activation or first run. Uninstalling your data however may be an after thought.
In this post I’ll explain techniques you can use to install and remove your data to keep things tidy, should the user decide they no longer want your plugin.
During plugin activation you may need to setup your plugin’s initial data, and do things such as:
- Install some tables using dbdelta
- Setup some roles and capabilities using add_role and add_cap respectively.
- Create a cron job using wp_schedule_event
- Create some initial data i.e. pages, terms
Registering your activation function
To hook in your activation function, you’ll need to register it with the register_activation_hook function.
It is important to note that:
- activation hooks only run when the plugin is activated – not during plugin updates.
- activation hooks need to be registered outside of other WP hooks such as
plugins_loadedbecause your activation function is called prior to the plugin being loaded/activated.
- If you are creating an install class, you can register your hook in its constructor, if its constructed right away.
- If you are not registering your function in the main plugin file,
__FILE__won’t work. You may want to store
__FILE__in a constant for reference from other files.
- File doesn’t work with symlinks (if you develop with these), but you can use (a little hacky):
basename( dirname( __FILE__ ) ) . '/' . basename( __FILE__ )instead.
Notes on custom post types
If you are installing some default terms or custom post types, you’ll need to ensure the post types are registered prior to running your code. A quick and easy way to do this would be to register multiple activation hooks in order, for example:
You can use the same technique to flush permalinks after installing a custom post type too which is also in that snippet, which calls
flush_rewrite_rules after we’ve installed.
This occurs when the user deactivates your plugin from admin. At this point you shouldn’t delete data, because the plugin is still technically installed (its just inactive), but you can:
- Remove cron jobs
- Remove roles
Personally I tend to leave out a deactivation hook and run all removal logic during uninstall instead as this shouldn’t cause any major performance issues, but if you need to use it you can hook in your deactivation function just like the activation one (for examples see here).
when the user presses ‘delete’ on the admin plugins screen (if the plugin is already deactivated), WordPress runs a special file in your plugin called uninstall.php if it exists.
At this point you’ll most commonly want to delete any stored data your plugin may have made so the DB is left clean. You may:
- Remove settings data using a wpdb query or delete_option.
- Remove post type data with a query
- Delete DB tables with a query
How to format your uninstall.php
Uninstall.php needs to be protected – you don’t want someone viewing that file directly and triggering an uninstall maliciously.
Checking for WP_UNINSTALL_PLUGIN is enough – WordPress defines this just prior to loading your file.
Uninstall.php is still ran when using multisite. In this instance however you may have more data to remove (from each site on the network); in this case you can use switch_to_blog to loop through all blogs and delete what you need to.
Some users have been known to ‘delete’ a plugin to re-install it or update it – obviously this isn’t the ideal situation as it means they lose their data when uninstall is triggered.
One day I’d like to see a way for WP to ‘flag’ whether or not data should be removed so its user-optional, until then you need to handle this yourself. In WooCommerce we now do this from the status page:
This prevents accidental loss of data/angry folk with pitchforks.
- When using any of these functions/hooks avoid echo’ing any data it may cause “headers already sent” errors as they hooks are ran early.
- To debug your install/uninstall functions, you could use WP_DEBUG_LOG