Like the blog? Get the book »

WordPress uninstall.php file – The Complete Guide

WordPress uninstall.php file – The Complete Guide

WordPress plugins that clean up after themselves are pure awesome sauce. If you are developing a plugin that adds any sort of data to the WordPress database, it is important that the plugin removes any unwanted or unused data if and when the plugin ever is uninstalled. This complete guide explains useful techniques for doing this using the powerful and handy uninstall.php file.

A simple yet complete guide to uninstall.php, with a TON of useful uninstall techniques!

Contents

How uninstall.php works

Basically you add a file named uninstall.php to the plugin’s main (root) directory. For example, say we have a plugin named “Add Data to WP Database”, and the main directory is /add-data-wp-database/. Inside of that directory, we can add uninstall.php like so:

/add-data-wp-database/
	/add-data-wp-database.php
	/index.php
	/uninstall.php <--- here it is.
	/readme.txt

Now whenever the plugin is uninstalled (removed) via the “Plugins” screen in the Admin Area (see thumbnail at beginning of this post), WordPress will find and execute uninstall.php. This makes it possible for plugin developers to “clean up” any unwanted or unused data from the database.

That’s how it works in a nutshell. For more details, check out the WP Codex.

What’s inside uninstall.php

The uninstall.php file may include any code that is necessary to handle proper clean up and uninstall routines for the plugin. At the very least, some code should be included that protects the file from unwanted access. Here is a simple, recommended way to do it:

<?php // exit if uninstall constant is not defined
if (!defined('WP_UNINSTALL_PLUGIN')) exit;

Those lines (or something similar) should be located at the beginning of uninstall.php, before any other code. This code works by checking for the WP_UNINSTALL_PLUGIN constant, which is defined by WordPress just prior to loading uninstall.php. So if anything other than WordPress tries to access the file, the script will exit immediately.

To give you a better idea, here is an example uninstall.php file that removes several types of data. Note that most of the code is excluded for clarity.

<?php // exit if uninstall constant is not defined
if (!defined('WP_UNINSTALL_PLUGIN')) exit;

// remove plugin options

// remove plugin transients

// remove plugin cron events

// ..etc., based on what needs to be removed

So the actual code techniques that are added to the uninstall file depend on the data that needs removed or modified. They will vary from plugin to plugin, so you will need to know exactly which data need removed before adding the appropriate technique. In the next sections of this tutorial, you can find techniques for removing all sorts of data from the WordPress database.

Important: Only add the techniques that are necessary. You have to be very precise, as explained in this word to the wise.

Delete plugin options

Probably the most common uninstall technique is to delete plugin options using the WordPress function delete_option(). For example, to remove an option named myplugin_options, add this code to uninstall.php:

// delete plugin options
delete_option('myplugin_options');

Or if you have multiple options to delete, you can loop through an array:

// delete multiple options
$options = array(
	'myplugin_option_1',
	'myplugin_option_2',
	'myplugin_option_3',
);
foreach ($options as $option) {
	if (get_option($option)) delete_option($option);
}

To use this code, rename the array items (e.g., myplugin_option_1 et al) with the names of the plugin options that you want to remove. Easy peasy.

Pro Tip: If using plugin options for any other uninstall technique, do NOT delete the options until after you are finished using them.

Delete plugin transients

Another common technique is to remove any plugin transients from the database. To do so, we can use the delete_transient() function:

// delete plugin transient
delete_transient('myplugin_transient');

Or if you have multiple transients to delete, you can loop through an array:

// delete multiple transients
$transients = array(
	'myplugin_transient_1',
	'myplugin_transient_2',
	'myplugin_transient_3',
);
foreach ($transients as $transient) {
	delete_transient($transient);
}

To use this code, rename the array items (e.g., myplugin_transient_1 et al) with the names of the plugin transients that you want to remove.

Delete cron events

The next technique shows how to delete cron events using the WordPress function, wp_unschedule_event(). Here is an example:

// delete cron event
$timestamp = wp_next_scheduled('myplugin_cron_event');
wp_unschedule_event($timestamp, 'myplugin_cron_event');

The trick here is to use wp_next_scheduled() to get the correct $timestamp value for the transient that you want to delete.

Delete database tables

Here is one of my favorites. You have to be very careful with this technique. Make absolute certain that the table name is correct. With that in mind, here is an example that shows how to delete a table named myplugin_table:

// delete database table
global $wpdb;
$table_name = $wpdb->prefix .'myplugin_table';
$wpdb->query("DROP TABLE IF EXISTS {$table_name}");

This technique uses the wpdb class to define the $table_name and then drop (delete) the table if it exists. Again, it is very important that you replace myplugin_table with the correct name of the table that you want to remove. No other changes to the code are required. Remember to test thoroughly!

Delete posts and pages

To delete WP Posts and Pages, we can use wp_trash_post(). For example, to delete the post with ID equal to 44:

// delete post id 44
wp_trash_post(44);

So the trick with this technique is determining the correct post or page ID(s). There are several ways to do this; probably easiest way is to record the ID of any posts or pages that are added by your plugin. So for example, if your plugin adds three pages when it is first activated, you could add those page IDs to the database as an option. That way you can check the options to get the correct posts to be deleted. In code, that might look something like this:

// delete pages
$myplugin_pages = get_option('myplugin_pages');
if (is_array($myplugin_pages) && !empty($myplugin_pages)) {
	foreach ($myplugin_pages as $myplugin_page) {
		wp_trash_post($myplugin_page);
	}
}

Here we use get_option() to grab our array of page IDs. Then we loop through the array and use wp_trash_post() to delete each of them. Note that this same technique can be used to delete any post or post type, not just limited to pages.

Important: Only delete posts and pages if you are absolutely sure that the user does not want them. One way to handle this is to add an option in the plugin settings, where you ask the user if the whatever posts and/or pages should be deleted when the plugin is uninstalled. More about this here.

Delete Custom Post Type posts

Here we are talking about deleting posts that are defined as a certain Custom Post Type (CPT). So we’re not deleting the post type per se, rather all posts that belong to the post type. To do this, we can use the wp_delete_post() function. Here is an example that shows how to delete all posts that belong to the custom post type named myplugin_cpt.

// delete custom post type posts
$myplugin_cpt_args = array('post_type' => 'myplugin_cpt', 'posts_per_page' => -1);
$myplugin_cpt_posts = get_posts($myplugin_cpt_args);
foreach ($myplugin_cpt_posts as $post) {
	wp_delete_post($post->ID, false);
}

Here we use get_posts() to get all posts that belong to the myplugin_cpt custom post type. We then loop through the results and use wp_delete_post() to delete each CPT post. The only thing that you need to change in this code is the name (slug) of the custom post type, myplugin_cpt. And reminder, make sure that the user actually wants to have all of their CPT posts deleted, maybe by adding a plugin option that asks them. Don’t just assume, especially when post content is concerned.

Note: The above technique uses wp_delete_post() to delete CPT posts. Understand that this function also deletes everything that is tied to the posts, including comments, post meta fields, terms, and more.
Note: The second parameter of the wp_delete_post() function currently is set to false. That tells WordPress to send the post to the Trash, where the user can delete it (or restore it) later. If you would rather force delete the post completely, change the parameter to true.

Real-world example

Here is an example of the previous technique for deleting custom post types. The following code is used in the uninstall file for my free (and awesome) contact form plugin.

$cfx_email_posts = get_posts(array('post_type' => 'cfx_email', 'post_status' => 'any', 'posts_per_page' => -1));

if ($cfx_email_posts) {
	
	foreach ($cfx_email_posts as $cfx_email_post) wp_delete_post($cfx_email_post->ID, false); 
	
}

The lesson here is about including the any parameter when calling get_posts(). My contact form plugin saves sent emails in the database via private custom post type. And it saves them with “Draft” status. By default, get_posts() only returns posts with “Publish” status, so it was not returning my email draft posts. To get the draft posts, it was necessary to add the any parameter, which tells get_posts() to return posts of any status, Draft, Pending, Publish, whatever.

Delete user meta

To delete user metadata, we can use the delete_user_meta() function. For example, if we want to delete all user meta data named myplugin_user_meta, we can do this:

// delete user meta
$users = get_users();
foreach ($users as $user) {
	delete_user_meta($user->ID, 'myplugin_user_meta');
}

Here we use get_users() to get an array of all users. Then we loop through the array and use delete_user_meta to delete all user meta data named myplugin_user_meta. Thus, to use this code technique, replace the myplugin_user_meta with the name of the metadata that you want to delete.

Note: As written, the above technique removes meta from all users. If for some reason you would like to select only a subset of users, you can do so by passing an array of arguments to get_users(). For more information, check out WP_User_Query::prepare_query() in the WordPress documentation.

Delete post meta

If your plugin adds any post metadata, you can remove it using delete_post_meta(). Here is an example showing how to remove all post meta named myplugin_post_meta.

// delete post meta
$myplugin_post_args = array('posts_per_page' => -1);
$myplugin_posts = get_posts($myplugin_post_args);
foreach ($myplugin_posts as $post) {
	delete_post_meta($post->ID, 'myplugin_post_meta');
}

This technique uses get_posts() to get all posts (see note below). Then it loops through the posts and deletes all meta named myplugin_post_meta. So to use this technique, replace myplugin_post_meta with the name of the post meta data that you want to delete. Bada bing, bada boom.

Note: To get all posts using get_posts(), set the posts_per_page parameter to -1, as shown in the above example.

Limit remove meta to CPT

One more tip for removing post meta. If you want to remove meta from only a specific post type (CPT), replace the value of $myplugin_post_args in the above code to the following:

array('post_type' => 'myplugin_cpt', 'posts_per_page' => -1);

Here we add the post_type parameter for get_posts(). To use for your own post type, replace myplugin_cpt with the CPT that you want to select.

Alternate method using delete_metadata()

An alternate, equivalent way to remove all meta is to use the delete_metadata() function. The nice thing about this function is that it can be used to delete all meta for any comment, post, term, or user. Here is an example where we delete all post meta named myplugin_meta_key:

// delete post meta
delete_metadata('post', 0, 'myplugin_meta_key', '', true);

This technique is all about parameters. There are five of them, in order:

  • $meta_type — Object type (comment, post, term, or user)
  • $object_id — Object ID or 0 for all
  • $meta_key — Name of the meta data
  • $meta_value — Value of the meta data
  • $delete_all — Delete all meta data

So looking at the above technique, we pass the following arguments for these five parameters:

  • post — We want to select post objects
  • 0 — We use zero to select all posts
  • myplugin_meta_key — The name of our metadata
  • (empty) — Leave blank to select all
  • true — Important to set as true to delete all matching results

As one might imagine, delete_metadata() is very flexible and powerful, enabling deletion of virtually any meta data that exists in the WordPress database.

Complete uninstall.php example

Well, almost complete. This next code snippet includes most of the examples provided in this tutorial. It doesn’t include all of them, only one snippet from each technique. Advanced and alternate techniques are not included.

<?php
/*
	Example uninstall.php file
	Contains examples provided in the tutorial:
	WordPress uninstall.php file - The Complete Guide
	@ https://digwp.com/2019/10/wordpress-uninstall-php/
*/

// exit if uninstall constant is not defined
if (!defined('WP_UNINSTALL_PLUGIN')) exit;

// delete plugin options
delete_option('myplugin_options');

// delete plugin transient
delete_transient('myplugin_transient');

// delete cron event
$timestamp = wp_next_scheduled('myplugin_cron_event');
wp_unschedule_event($timestamp, 'myplugin_cron_event');

// delete database table
global $wpdb;
$table_name = $wpdb->prefix .'myplugin_table';
$wpdb->query("DROP TABLE IF EXISTS {$table_name}");

// delete pages
$myplugin_pages = get_option('myplugin_pages');
if (is_array($myplugin_pages) && !empty($myplugin_pages)) {
	foreach ($myplugin_pages as $myplugin_page) {
		wp_trash_post($myplugin_page);
	}
}

// delete custom post type posts
$myplugin_cpt_args = array('post_type' => 'myplugin_cpt', 'posts_per_page' => -1);
$myplugin_cpt_posts = get_posts($myplugin_cpt_args);
foreach ($myplugin_cpt_posts as $post) {
	wp_delete_post($post->ID, false);
}

// delete user meta
$users = get_users();
foreach ($users as $user) {
	delete_user_meta($user->ID, 'myplugin_user_meta');
}

// delete post meta
$myplugin_post_args = array('posts_per_page' => -1);
$myplugin_posts = get_posts($myplugin_post_args);
foreach ($myplugin_posts as $post) {
	delete_post_meta($post->ID, 'myplugin_post_meta');
}

This code is meant to be added to a blank/empty uninstall.php file. Hopefully this helps anyone who wants to get started quickly. Remember more techniques and variations of included techniques can be found elsewhere in this tutorial. Have fun :)

Word to the wise

As useful as uninstall.php may be, it is important to be careful when removing any data from the database. For example in some cases, users may uninstall a plugin accidentally or temporarily. So if the user has configured options or other data for the plugin, they may be extremely upset to find that changes were lost.

To help prevent such accidents, it may be a good idea to provide an option that asks users if they want to remove all data when the plugin is uninstalled. Like a checkbox option that may be checked in the uninstall.php file, before any data is removed. That could be as simple as adding this line:

if (!get_option('plugin_do_uninstall', false)) exit;

That way you can check whether or not the user actually wants to remove data when the plugin is uninstalled. This technique can prevent unwanted data loss and upset, confused users. Always think of the user, is my best advice :)

if (tutorial_complete()) exit;

It’s great to help users with awesome plugins. Plugins that clean up after themselves are simply the BEST. If your plugin makes any changes to the WordPress database, best practice is to ensure that everything is restored to original conditions when the plugin is uninstalled. In some cases, that means removing all added data and restoring any modified data. In other cases, you may want to remove only certain types of data.

For example, my free WordPress plugin User Submitted Posts enables users to submit posts from the front-end of the site. So its uninstall.php file removes only the plugin options; it does NOT remove any of the user-submitted posts from the database. So you have to be smart about which data to remove.

Bottom line: It is your responsibility as a plugin developer to ensure that the appropriate data is removed and/or restored when your plugin is uninstalled. As explained in this tutorial, adding a well-equipped uninstall.php file is an easy, consistent, and reliable way to make it happen.

6 responses

  1. Many thanks for the very good article. Good work! But I have a question: shouldn’t the cron tasks be removed first? When the cron task accesses plugin options, you quickly create a race condition with the given order.

    • Thanks for the feedback, Jens. As far as I know WP Cron does not run during plugin removal/deletion, so order of scripts in uninstall.php should not matter.

  2. Nice tutorial, but I have faced a little problem to follow it. Could I share here? Or mail you?

  3. Hi, Jeff Very detailed tutorial here, thanks for sharing. If a plugin doesn’t have the uninstall.php is it OK to delete in in PHPMyAdmin manually?

Comments are closed for this post. Contact us with any critical information.
© 2009–2024 Digging Into WordPress Powered by WordPress Monzilla Media shapeSpace