Structure WordPress plugins for maintainability. Use activation hooks for setup, always prevent direct access, and store plugin-specific data in custom tables or options.
Plugin Header
<?php
/**
* Plugin Name: My Custom Plugin
* Plugin URI: https://example.com/plugin
* Description: What this plugin does in one sentence.
* Version: 1.0.0
* Author: Your Name
* Author URI: https://example.com
* License: GPL v2 or later
* Text Domain: my-plugin
* Domain Path: /languages
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Plugin constants
define('MYPLUGIN_VERSION', '1.0.0');
define('MYPLUGIN_PATH', plugin_dir_path(__FILE__));
define('MYPLUGIN_URL', plugin_dir_url(__FILE__));
define('MYPLUGIN_BASENAME', plugin_basename(__FILE__));
Activation and Deactivation Hooks
register_activation_hook(__FILE__, 'myplugin_activate');
function myplugin_activate() {
myplugin_create_tables();
flush_rewrite_rules();
// Set default options
add_option('myplugin_settings', [
'enabled' => true,
'version' => MYPLUGIN_VERSION,
]);
}
register_deactivation_hook(__FILE__, 'myplugin_deactivate');
function myplugin_deactivate() {
flush_rewrite_rules();
}
Create Custom Database Table
function myplugin_create_tables() {
global $wpdb;
$table_name = $wpdb->prefix . 'myplugin_leads';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE {$table_name} (
id mediumint(9) NOT NULL AUTO_INCREMENT,
created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
name varchar(100) NOT NULL,
email varchar(100) NOT NULL,
message text,
status varchar(20) DEFAULT 'new',
PRIMARY KEY (id)
) {$charset_collate};";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
}
Admin Settings Page
function myplugin_admin_menu() {
add_options_page(
__('My Plugin Settings', 'my-plugin'),
__('My Plugin', 'my-plugin'),
'manage_options',
'myplugin-settings',
'myplugin_settings_page'
);
}
add_action('admin_menu', 'myplugin_admin_menu');
function myplugin_settings_page() {
if (!current_user_can('manage_options')) {
return;
}
// Render settings form
echo '<div class="wrap"><h1>' . esc_html(get_admin_page_title()) . '</h1></div>';
}
File Structure
my-plugin/
├── my-plugin.php # Main plugin file (header here)
├── includes/
│ ├── class-admin.php # Admin functionality
│ ├── class-frontend.php # Frontend functionality
│ └── class-database.php # DB operations
├── assets/
│ ├── css/
│ └── js/
└── languages/ # Translations (.pot file)