tools/wordpress-starter-plugin-generator
Starter Plugin Generator
Build a whole plugin folder rather than a snippet: the main file with its header and constants, an optional main class, activation and deactivation hooks that actually fire, and an uninstall routine — each in the one file WordPress reads it from.
my-plugin/my-plugin.php
<?php
/**
* Plugin Name: My Plugin
* Description: Lorem ipsum dolor sit amet.
* Version: 1.0.0
* Requires at least: 6.5
* Requires PHP: 7.4
* License: GPL v2 or later
* Text Domain: my-plugin
* Domain Path: /languages
*
* Generated by wp-skills.com
*
* @package My Plugin
*/
// A direct request for this file must not execute it.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/*
* The version lives here and nowhere else. Anything that needs it — an asset
* cache-buster, a stored option, an upgrade routine — reads the constant, so
* there is one number to change on release rather than four.
*/
const MY_PLUGIN_VERSION = '1.0.0';
/*
* __FILE__ captured here, at the top of the main file, because that is the only
* place it means what everything else assumes it means: plugin_basename(),
* plugin_dir_url() and the activation hooks all resolve against the plugin's
* main file, and __FILE__ inside includes/ is a different path.
*
* MY_PLUGIN_PATH already ends in a slash — plugin_dir_path() adds a trailing
* slash, verified — so concatenating a relative path needs no separator.
*/
const MY_PLUGIN_FILE = __FILE__;
define( 'MY_PLUGIN_PATH', plugin_dir_path( MY_PLUGIN_FILE ) );
define( 'MY_PLUGIN_URL', plugin_dir_url( MY_PLUGIN_FILE ) );
/*
* These three stay in this file, and passing __FILE__ is why.
*
* register_activation_hook() turns its first argument into the plugin's
* folder/file.php path and hangs the callback on "activate_" plus that path.
* Called from includes/anything.php, __FILE__ is that file — so the hook is
* registered under a path WordPress never activates, and the callback simply
* never runs. Measured on WordPress 7.1: no error, no warning, nothing.
*/
/**
* Runs once, when the plugin is activated.
*/
function my_plugin_activate(): void {
// add_option() rather than update_option(): reactivating a plugin must
// not overwrite settings the site already has.
add_option(
'my_plugin_settings',
array(
'version' => MY_PLUGIN_VERSION,
)
);
}
register_activation_hook( MY_PLUGIN_FILE, 'my_plugin_activate' );
/**
* Runs once, when the plugin is deactivated.
*/
function my_plugin_deactivate(): void {
// Runs on deactivation. Scheduled events belong here; stored data does
// not — a deactivated plugin is expected to keep its settings.
}
register_deactivation_hook( MY_PLUGIN_FILE, 'my_plugin_deactivate' );
require_once MY_PLUGIN_PATH . 'includes/class-my-plugin-plugin.php';
/**
* The one instance of the plugin.
*
* Returned rather than only constructed, so other code can reach it without a
* global: my_plugin()->something().
*/
function my_plugin(): My_Plugin_Plugin {
static $instance = null;
if ( null === $instance ) {
$instance = new My_Plugin_Plugin();
}
return $instance;
}
add_action( 'plugins_loaded', 'my_plugin' );