All skills
Skillintermediate
Plugin Architecture
``` plugin-name/ ├── plugin-name.php # Main plugin file with header ├── uninstall.php # Cleanup on uninstall ├── includes/ │ ├── class-plugin-name.php │ ├── class-activator.php │ ├── class-deactivator.php │ └── class-loader.php ├── admin/ │ ├── class-admin.php │ ├── css/ │ └── js/ ├── public/ │ ├── class-public.php │ ├── css/ │ └── js/ ├── languages/ │ └── plugin-name.pot └── README.txt ```
Claude Code Knowledge Pack7/10/2026
Overview
Plugin Architecture
Plugin Structure
Minimal Plugin Structure
plugin-name/
├── plugin-name.php # Main plugin file with header
├── uninstall.php # Cleanup on uninstall
├── includes/
│ ├── class-plugin-name.php
│ ├── class-activator.php
│ ├── class-deactivator.php
│ └── class-loader.php
├── admin/
│ ├── class-admin.php
│ ├── css/
│ └── js/
├── public/
│ ├── class-public.php
│ ├── css/
│ └── js/
├── languages/
│ └── plugin-name.pot
└── README.txt
Full Plugin Structure (Enterprise)
plugin-name/
├── plugin-name.php
├── uninstall.php
├── composer.json
├── phpcs.xml.dist
├── phpunit.xml.dist
├── includes/
│ ├── class-plugin.php # Main plugin class
│ ├── class-activator.php # Activation logic
│ ├── class-deactivator.php # Deactivation logic
│ ├── class-loader.php # Hook loader
│ ├── class-i18n.php # Internationalization
│ ├── Traits/
│ │ └── Singleton.php
│ ├── Interfaces/
│ │ ├── Registrable.php
│ │ └── Hookable.php
│ ├── Services/
│ │ └── class-api-service.php
│ └── Repositories/
│ └── class-data-repository.php
├── admin/
│ ├── class-admin.php
│ ├── class-settings.php
│ ├── partials/
│ │ └── settings-page.php
│ ├── css/
│ └── js/
├── public/
│ ├── class-frontend.php
│ ├── partials/
│ ├── css/
│ └── js/
├── blocks/
│ └── custom-block/
├── templates/
│ └── single-custom-type.php
├── languages/
├── tests/
│ ├── bootstrap.php
│ └── unit/
└── vendor/
Main Plugin File
Plugin Header
<?php
/**
* Plugin Name: Plugin Name
* Plugin URI: https://example.com/plugin-name
* Description: A brief description of what this plugin does.
* Version: 1.0.0
* Requires at least: 6.4
* Requires PHP: 8.1
* Author: Author Name
* Author URI: https://example.com
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: plugin-name
* Domain Path: /languages
* Update URI: https://example.com/plugin-name
*
* @package PluginName
*/
declare(strict_types=1);
namespace PluginName;
// Prevent direct access
defined('ABSPATH') || exit;
// Plugin constants
define('PLUGIN_NAME_VERSION', '1.0.0');
define('PLUGIN_NAME_FILE', __FILE__);
define('PLUGIN_NAME_PATH', plugin_dir_path(__FILE__));
define('PLUGIN_NAME_URL', plugin_dir_url(__FILE__));
define('PLUGIN_NAME_BASENAME', plugin_basename(__FILE__));
// Autoloader
if (file_exists(PLUGIN_NAME_PATH . 'vendor/autoload.php')) {
require_once PLUGIN_NAME_PATH . 'vendor/autoload.php';
}
// Manual includes if no autoloader
require_once PLUGIN_NAME_PATH . 'includes/class-plugin.php';
require_once PLUGIN_NAME_PATH . 'includes/class-activator.php';
require_once PLUGIN_NAME_PATH . 'includes/class-deactivator.php';
/**
* Plugin activation hook
*/
function activate(): void {
Activator::activate();
}
register_activation_hook(__FILE__, __NAMESPACE__ . '\\\\activate');
/**
* Plugin deactivation hook
*/
function deactivate(): void {
Deactivator::deactivate();
}
register_deactivation_hook(__FILE__, __NAMESPACE__ . '\\\\deactivate');
/**
* Initialize the plugin
*/
function init(): void {
$plugin = new Plugin();
$plugin->run();
}
add_action('plugins_loaded', __NAMESPACE__ . '\\\\init');
Activation & Deactivation
Activator Class
<?php
declare(strict_types=1);
namespace PluginName;
/**
* Fired during plugin activation
*/
class Activator {
/**
* Activation tasks
*/
public static function activate(): void {
// Check requirements
self::check_requirements();
// Create database tables
self::create_tables();
// Set default options
self::set_default_options();
// Schedule cron events
self::schedule_events();
// Add capabilities
self::add_capabilities();
// Flush rewrite rules (if registering CPT/taxonomy)
flush_rewrite_rules();
// Set activation flag for welcome notice
set_transient('plugin_name_activated', true, 30);
}
/**
* Check system requirements
*/
private static function check_requirements(): void {
if (version_compare(PHP_VERSION, '8.1', '<')) {
deactivate_plugins(PLUGIN_NAME_BASENAME);
wp_die(
esc_html__('This plugin requires PHP 8.1 or higher.', 'plugin-name'),
'Plugin Activation Error',
['back_link' => true]
);
}
global $wp_version;
if (version_compare($wp_version, '6.4', '<')) {
deactivate_plugins(PLUGIN_NAME_BASENAME);
wp_die(
esc_html__('This plugin requires WordPress 6.4 or higher.', 'plugin-name'),
'Plugin Activation Error',
['back_link' => true]
);
}
}
/**
* Create custom database tables
*/
private static function create_tables(): void {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'plugin_name_data';
$sql = "CREATE TABLE IF NOT EXISTS {$table_name} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned NOT NULL DEFAULT 0,
data_key varchar(191) NOT NULL,
data_value longtext,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY user_id (user_id),
KEY data_key (data_key)
) {$charset_collate};";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
// Store DB version
update_option('plugin_name_db_version', PLUGIN_NAME_VERSION);
}
/**
* Set default options
*/
private static function set_default_options(): void {
$defaults = [
'enabled' => true,
'api_key' => '',
'cache_duration' => 3600,
'items_per_page' => 10,
'allowed_roles' => ['administrator', 'editor'],
];
if (get_option('plugin_name_settings') === false) {
add_option('plugin_name_settings', $defaults);
}
}
/**
* Schedule cron events
*/
private static function schedule_events(): void {
if (!wp_next_scheduled('plugin_name_daily_cleanup')) {
wp_schedule_event(time(), 'daily', 'plugin_name_daily_cleanup');
}
}
/**
* Add custom capabilities
*/
private static function add_capabilities(): void {
$admin = get_role('administrator');
if ($admin) {
$admin->add_cap('manage_plugin_name');
$admin->add_cap('edit_plugin_name_data');
}
}
}
Deactivator Class
<?php
declare(strict_types=1);
namespace PluginName;
/**
* Fired during plugin deactivation
*/
class Deactivator {
/**
* Deactivation tasks
*/
public static function deactivate(): void {
// Clear scheduled events
self::clear_scheduled_events();
// Clear transients
self::clear_transients();
// Flush rewrite rules
flush_rewrite_rules();
// Note: Do NOT delete options or tables here
// That should only happen in uninstall.php
}
/**
* Clear all scheduled cron events
*/
private static function clear_scheduled_events(): void {
$timestamp = wp_next_scheduled('plugin_name_daily_cleanup');
if ($timestamp) {
wp_unschedule_event($timestamp, 'plugin_name_daily_cleanup');
}
// Clear all instances of our events
wp_clear_scheduled_hook('plugin_name_daily_cleanup');
}
/**
* Clear transients
*/
private static function clear_transients(): void {
global $wpdb;
// Clear specific transients
delete_transient('plugin_name_cache');
delete_transient('plugin_name_activated');
// Clear all plugin transients (use with caution)
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
'_transient_plugin_name_%',
'_transient_timeout_plugin_name_%'
)
);
}
}
uninstall.php
<?php
/**
* Uninstall script - runs when plugin is deleted
*
* @package PluginName
*/
// If uninstall not called from WordPress, exit
if (!defined('WP_UNINSTALL_PLUGIN')) {
exit;
}
// Check if we should preserve data
$settings = get_option('plugin_name_settings', []);
$preserve_data = $settings['preserve_data_on_uninstall'] ?? false;
if (!$preserve_data) {
global $wpdb;
// Delete options
delete_option('plugin_name_settings');
delete_option('plugin_name_db_version');
// Delete user meta
$wpdb->query("DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE 'plugin_name_%'");
// Delete post meta
$wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE '_plugin_name_%'");
// Delete custom tables
$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}plugin_name_data");
// Delete transients
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
'_transient_plugin_name_%',
'_transient_timeout_plugin_name_%'
)
);
// Remove capabilities
$roles = ['administrator', 'editor'];
foreach ($roles as $role_name) {
$role = get_role($role_name);
if ($role) {
$role->remove_cap('manage_plugin_name');
$role->remove_cap('edit_plugin_name_data');
}
}
// Clear any cached data
wp_cache_flush();
}
Settings API
Settings Class
<?php
declare(strict_types=1);
namespace PluginName\\Admin;
/**
* Plugin settings management
*/
class Settings {
private const OPTION_GROUP = 'plugin_name_settings';
private const OPTION_NAME = 'plugin_name_settings';
private const PAGE_SLUG = 'plugin-name-settings';
/**
* Initialize settings
*/
public function init(): void {
add_action('admin_menu', [$this, 'add_menu_page']);
add_action('admin_init', [$this, 'register_settings']);
}
/**
* Add admin menu page
*/
public function add_menu_page(): void {
add_options_page(
__('Plugin Name Settings', 'plugin-name'),
__('Plugin Name', 'plugin-name'),
'manage_options',
self::PAGE_SLUG,
[$this, 'render_settings_page']
);
}
/**
* Register all settings
*/
public function register_settings(): void {
register_setting(
self::OPTION_GROUP,
self::OPTION_NAME,
[
'type' => 'array',
'sanitize_callback' => [$this, 'sanitize_settings'],
'default' => $this->get_defaults(),
]
);
// General section
add_settings_section(
'plugin_name_general',
__('General Settings', 'plugin-name'),
[$this, 'render_general_section'],
self::PAGE_SLUG
);
// API section
add_settings_section(
'plugin_name_api',
__('API Settings', 'plugin-name'),
[$this, 'render_api_section'],
self::PAGE_SLUG
);
// Fields
$this->add_fields();
}
/**
* Add settings fields
*/
private function add_fields(): void {
// Enable field
add_settings_field(
'enabled',
__('Enable Plugin', 'plugin-name'),
[$this, 'render_checkbox_field'],
self::PAGE_SLUG,
'plugin_name_general',
[
'label_for' => 'enabled',
'description' => __('Enable or disable the plugin functionality.', 'plugin-name'),
]
);
// Items per page
add_settings_field(
'items_per_page',
__('Items Per Page', 'plugin-name'),
[$this, 'render_number_field'],
self::PAGE_SLUG,
'plugin_name_general',
[
'label_for' => 'items_per_page',
'min' => 1,
'max' => 100,
'description' => __('Number of items to display per page.', 'plugin-name'),
]
);
// API Key
add_settings_field(
'api_key',
__('API Key', 'plugin-name'),
[$this, 'render_text_field'],
self::PAGE_SLUG,
'plugin_name_api',
[
'label_for' => 'api_key',
'type' => 'password',
'description' => __('Enter your API key for external service.', 'plugin-name'),
]
);
// Cache duration
add_settings_field(
'cache_duration',
__('Cache Duration', 'plugin-name'),
[$this, 'render_select_field'],
self::PAGE_SLUG,
'plugin_name_api',
[
'label_for' => 'cache_duration',
'options' => [
'900' => __('15 minutes', 'plugin-name'),
'1800' => __('30 minutes', 'plugin-name'),
'3600' => __('1 hour', 'plugin-name'),
'86400' => __('1 day', 'plugin-name'),
],
'description' => __('How long to cache API responses.', 'plugin-name'),
]
);
}
/**
* Get default settings
*/
private function get_defaults(): array {
return [
'enabled' => true,
'api_key' => '',
'cache_duration' => 3600,
'items_per_page' => 10,
];
}
/**
* Sanitize settings
*/
public function sanitize_settings(array $input): array {
$sanitized = [];
$sanitized['enabled'] = !empty($input['enabled']);
$sanitized['api_key'] = sanitize_text_field($input['api_key'] ?? '');
$sanitized['cache_duration'] = absint($input['cache_duration'] ?? 3600);
if (!in_array($sanitized['cache_duration'], [900, 1800, 3600, 86400], true)) {
$sanitized['cache_duration'] = 3600;
}
$sanitized['items_per_page'] = absint($input['items_per_page'] ?? 10);
$sanitized['items_per_page'] = max(1, min(100, $sanitized['items_per_page']));
return $sanitized;
}
/**
* Render settings page
*/