# easy-invoice/trunk/uninstall.php

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version trunk. 63 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/trunk/code/uninstall.php
- Raw: https://pluginprobe.com/plugins/easy-invoice/trunk/raw/uninstall.php
- Modified: 2026-09-15T12:31:20+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/trunk/code/uninstall.php#L10-L20`.

```php
<?php
/**
 * Runs when the plugin is deleted from the Plugins screen.
 *
 * Invoices are tax records, so nothing is removed unless the site owner
 * turned on Settings → Advanced → "Remove all data when the plugin is
 * deleted". With it off, deleting and reinstalling the plugin picks up
 * exactly where it left off.
 *
 * @package EasyInvoice
 */

if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
	exit;
}

// Scheduled work stops either way — the code that would run it is gone.
foreach ( [ 'easy_invoice_payment_reminder', 'easy_invoice_quote_expiration_check', 'easy_invoice_send_payment_reminders' ] as $hook ) {
	wp_clear_scheduled_hook( $hook );
}

if ( 'yes' !== get_option( 'easy_invoice_remove_data_on_uninstall', 'no' ) ) {
	return;
}

global $wpdb;

// Documents and their meta: every post type the plugin and its addons
// register starts with easy_invoice (invoices, quotes, payments, credit
// notes, payment links…). Straight SQL, because the plugin is not loaded
// during uninstall and its own guard against deleting issued invoices
// must not apply to a deliberate wipe.
$ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} WHERE post_type LIKE 'easy\_invoice%'" );
if ( ! empty( $ids ) ) {
	$id_list = implode( ',', array_map( 'intval', $ids ) );
	$wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE post_id IN ({$id_list})" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- integers.
	$wpdb->query( "DELETE FROM {$wpdb->term_relationships} WHERE object_id IN ({$id_list})" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- integers.
	$wpdb->query( "DELETE FROM {$wpdb->posts} WHERE ID IN ({$id_list})" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- integers.
}
// Meta the plugin attaches to other posts (WooCommerce orders, pages).
$wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE '\_easy\_invoice\_%' OR meta_key LIKE 'easy\_invoice\_%'" );

// Client records live on users as meta; the users themselves stay.
$wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE '\\_easy\\_invoice\\_%' OR meta_key LIKE 'easy\\_invoice\\_%'" );

// Options and transients, ours and the addons'.
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE 'easy\\_invoice\\_%' OR option_name LIKE '\\_transient\\_easy\\_invoice\\_%' OR option_name LIKE '\\_transient\\_timeout\\_easy\\_invoice\\_%' OR option_name LIKE '\\_site\\_transient\\_easy\\_invoice\\_%'" );

// Tables the addons created.
$tables = $wpdb->get_col( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $wpdb->prefix . 'easy_invoice_' ) . '%' ) );
foreach ( $tables as $table ) {
	$wpdb->query( "DROP TABLE IF EXISTS `{$table}`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from SHOW TABLES.
}

// Roles the plugin added.
foreach ( [ 'ei_manager', 'ei_accountant', 'ei_sales', 'ei_viewer', 'easy_invoice_client' ] as $role ) {
	if ( get_role( $role ) ) {
		remove_role( $role );
	}
}

wp_cache_flush();

```
