GeneratorsCoreActivation Hooks

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.

acme.php
Saved just now
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.