Activation Hooks Generator
The three lifecycle routines, in the order they really run — with the version gate that makes upgrades possible and the uninstall file that keeps the database clean.
The plugin
Version 1.2.0 is stored in acme_version. install() re-runs on plugins_loaded whenever the code is ahead of the stored version.
On activation
PHP and WordPress guard
Deactivates itself with a readable message instead of a fatal error.
Deferred rewrite flush
Flags on activation, flushes on wp_loaded once post types exist.
Schedule cron events
Guarded with wp_next_scheduled so activation cannot double-book.
Version-gated upgrades
Compares the stored version with the constant and re-runs install().
Multisite aware
Loops every site with switch_to_blog on a network activation.
Options and tables to manage
1 option · 1 table · 1 cron
On uninstall
Options, site options and scheduled events go. Custom tables and content are left alone — reversible, and usually the right default.
The main plugin file — register_activation_hook needs __FILE__ to be this file.
<?php // Generated with WP CodeKit — powered by GrowQuest (https://growquest.io). /** * Plugin Name: Acme plugin * Description: Lifecycle routines: activation, deactivation and upgrades. * Version: 1.2.0 * Requires PHP: 7.4 * Requires at least: 6.0 */ defined( 'ABSPATH' ) || exit; define( 'ACME_VERSION', '1.2.0' ); /** * Fires once when the plugin is activated. */ function acme_activate() { if ( version_compare( PHP_VERSION, '7.4', '<' ) || version_compare( get_bloginfo( 'version' ), '6.0', '<' ) ) { deactivate_plugins( plugin_basename( __FILE__ ) ); wp_die( esc_html__( 'This plugin needs PHP 7.4 and WordPress 6.0 or newer.', 'acme' ), '', array( 'back_link' => true ) ); } acme_install(); } /** * Seed everything this plugin needs. Safe to run more than once. */ function acme_install() { $defaults = array( 'acme_settings' => array(), ); foreach ( $defaults as $option => $value ) { add_option( $option, $value ); } global $wpdb; $table = $wpdb->prefix . 'acme_events'; $charset = $wpdb->get_charset_collate(); require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta( "CREATE TABLE {$table} ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, created datetime NOT NULL DEFAULT '0000-00-00 00:00:00', post_id bigint(20) unsigned NOT NULL DEFAULT 0, payload longtext NOT NULL, PRIMARY KEY (id), KEY post_id (post_id) ) {$charset}" ); // Rewrite rules cannot be built here — the post type is not registered yet. // Flag it and flush on the next load, once init has run. update_option( 'acme_flush_rules', 1 ); if ( ! wp_next_scheduled( 'acme_sync_products' ) ) { wp_schedule_event( strtotime( 'tomorrow 03:00' ), 'daily', 'acme_sync_products' ); } update_option( 'acme_version', ACME_VERSION ); } /** * Fires when the plugin is deactivated — including on every update. */ function acme_deactivate() { wp_clear_scheduled_hook( 'acme_sync_products' ); flush_rewrite_rules(); // Nothing is deleted here. Deactivation happens on every update. } /** * Run install again when the stored version is behind the code. */ function acme_maybe_upgrade() { $stored = get_option( 'acme_version', '0' ); if ( version_compare( $stored, ACME_VERSION, '>=' ) ) { return; } acme_install(); } add_action( 'plugins_loaded', 'acme_maybe_upgrade' ); /** * Flush rewrite rules once, after everything is registered. */ function acme_maybe_flush_rules() { if ( ! get_option( 'acme_flush_rules' ) ) { return; } delete_option( 'acme_flush_rules' ); flush_rewrite_rules(); } add_action( 'wp_loaded', 'acme_maybe_flush_rules' ); register_activation_hook( __FILE__, 'acme_activate' ); register_deactivation_hook( __FILE__, 'acme_deactivate' ); // Deletion is handled by uninstall.php — see the other output mode.