WordPress Plugin Architecture - Best Practices and Patterns
Mahesh Waghmare Building maintainable WordPress plugins requires solid architecture. This guide covers professional plugin architecture patterns, file organization, and best practices for scalable plugin development.
Introduction to Plugin Architecture
Good plugin architecture ensures:
- Maintainability and scalability
- Code reusability
- Easy testing
- Clear separation of concerns
- Professional development standards
Architecture Principles:
- Single Responsibility Principle
- Separation of concerns
- Dependency management
- Proper abstraction layers
Optimal File Structure
Recommended Structure
my-plugin/
├── my-plugin.php # Main plugin file
├── uninstall.php # Uninstall handler
├── composer.json # Dependencies
├── package.json # Build tools
├── src/
│ ├── Admin/
│ │ ├── Admin.php
│ │ └── Settings.php
│ ├── Frontend/
│ │ └── Frontend.php
│ ├── API/
│ │ └── RestAPI.php
│ ├── Database/
│ │ └── Database.php
│ └── Core/
│ └── Plugin.php
├── includes/
│ └── functions.php
├── assets/
│ ├── css/
│ ├── js/
│ └── images/
└── languages/
Key Directories:
src/- Main plugin code (PSR-4 autoloaded)includes/- Helper functionsassets/- CSS, JS, imageslanguages/- Translation files
Namespacing and Autoloading
PSR-4 Autoloading
composer.json:
{
"autoload": {
"psr-4": {
"MyPlugin\\": "src/"
}
}
}
Main Plugin File:
<?php
namespace MyPlugin;
if (!defined('ABSPATH')) {
exit;
}
require_once plugin_dir_path(__FILE__) . 'vendor/autoload.php';
class Plugin {
private static $instance = null;
public static function getInstance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
$this->init();
}
private function init() {
new Admin\Admin();
new Frontend\Frontend();
new API\RestAPI();
}
}
Plugin::getInstance();
Object-Oriented Patterns
Singleton Pattern
For main plugin class:
class Plugin {
private static $instance = null;
public static function getInstance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
// Prevent direct instantiation
}
}
Factory Pattern
For creating related objects:
class ServiceFactory {
public static function create($service_type) {
switch ($service_type) {
case 'api':
return new API\RestAPI();
case 'database':
return new Database\Database();
default:
throw new \InvalidArgumentException('Unknown service type');
}
}
}
WordPress Hooks System
Organized Hook Registration
class Admin {
public function __construct() {
add_action('admin_menu', [$this, 'addMenu']);
add_action('admin_init', [$this, 'registerSettings']);
add_filter('plugin_action_links', [$this, 'addActionLinks'], 10, 2);
}
public function addMenu() {
add_menu_page(
'My Plugin',
'My Plugin',
'manage_options',
'my-plugin',
[$this, 'renderPage']
);
}
public function registerSettings() {
register_setting('my_plugin_settings', 'my_plugin_option');
}
public function addActionLinks($links, $file) {
if ($file === plugin_basename(__FILE__)) {
$links[] = '<a href="' . admin_url('admin.php?page=my-plugin') . '">Settings</a>';
}
return $links;
}
}
Dependency Injection
Constructor Injection
class Service {
private $database;
private $logger;
public function __construct(Database $database, Logger $logger) {
$this->database = $database;
$this->logger = $logger;
}
public function doSomething() {
$this->logger->log('Starting operation');
$result = $this->database->query('SELECT * FROM table');
$this->logger->log('Operation complete');
return $result;
}
}
Service Container
class Container {
private $services = [];
public function register($name, $callback) {
$this->services[$name] = $callback;
}
public function get($name) {
if (isset($this->services[$name])) {
return call_user_func($this->services[$name], $this);
}
throw new \Exception("Service {$name} not found");
}
}
Testing and Quality
Unit Testing Setup
use PHPUnit\Framework\TestCase;
class PluginTest extends TestCase {
public function testPluginInitializes() {
$plugin = Plugin::getInstance();
$this->assertInstanceOf(Plugin::class, $plugin);
}
}
Code Quality Tools
- PHP_CodeSniffer for coding standards
- PHPStan for static analysis
- PHPUnit for unit testing
- WordPress Coding Standards
Conclusion
Professional plugin architecture requires:
- Proper file organization
- PSR-4 autoloading
- Object-oriented design patterns
- Organized hook registration
- Dependency management
- Testing infrastructure
Following these patterns creates maintainable, scalable WordPress plugins that stand the test of time.
Written by Mahesh Waghmare
I bridge the gap between WordPress architecture and modern React frontends. Currently building tools for the AI era.
Follow on Twitter →