I
SOLID Principles for WordPress Development
Interface Segregation Principle in WordPress
Many specific interfaces are better than one general-purpose interface
The Interface Segregation Principle (ISP) states that no client should be forced to depend on methods it doesn’t use. Split large, “fat” interfaces into smaller, focused ones — each serving a specific need.
Focused Interfaces
No Forced Dependencies
Better Composition
What ISP Requires
Clients should not be forced to depend on interfaces they don’t use. Split fat interfaces into smaller, role-specific ones:
Each interface has a single, clear responsibility
Classes implement only what they actually need
No empty or throw-only method implementations
Composition over one-size-fits-all interfaces
ISP Violation Signs
Watch out for these red flags indicating a “fat” interface problem:
Methods implemented as empty or throwing exceptions
Classes that “accidentally” depend on unrelated methods
Interface changes break unrelated implementing classes
Interface has a name like Manager or Handler
The Classic Worker / Robot Problem
The most famous ISP example — a fat interface forces irrelevant methods on implementing classes
Violating ISP
Fat interface forces robot to “eat” and “sleep”
// ❌ Violating ISP - one fat interface forces all methods
interface Worker {
public function work(): void;
public function eat(): void;
public function sleep(): void;
public function takeBreak(): void;
}
// Robot is forced to implement methods it doesn't need
class Robot implements Worker {
public function work(): void {
echo 'Robot working...';
}
public function eat(): void {
// Robot doesn't eat! Forced empty implementation
throw new \Exception("Robots don't eat!");
}
public function sleep(): void {
// Robot doesn't sleep either
throw new \Exception("Robots don't sleep!");
}
public function takeBreak(): void {
// Meaningless for a robot
}
}
PROBLEMS:
Robot is forced to implement meaningless methods
Adding a new human method breaks Robot class too
Violates SRP — interface has multiple unrelated roles
Following ISP
Segregated interfaces — each class picks what it needs
// ✅ ISP compliant - segregated interfaces
interface Workable {
public function work(): void;
}
interface Feedable {
public function eat(): void;
}
interface Restable {
public function sleep(): void;
public function takeBreak(): void;
}
// Robot only implements what it needs
class Robot implements Workable {
public function work(): void {
echo 'Robot working at full speed!';
}
}
// Human implements everything relevant
class Human implements Workable, Feedable, Restable {
public function work(): void { echo 'Human working...'; }
public function eat(): void { echo 'Having lunch...'; }
public function sleep(): void { echo 'Sleeping...'; }
public function takeBreak(): void { echo 'Coffee break!'; }
}
BENEFITS:
Robot only implements Workable
Changes to Feedable don’t affect Robot
Easy to add new capabilities without touching old classes
WordPress Examples: Violation vs Compliance
Real WordPress scenarios where ISP improves plugin architecture
One Massive Block Interface
All blocks forced to handle render, save, transform, variations, styles, deprecated…
// ❌ One massive block interface
interface Block_Interface {
public function render(): string;
public function save(): string;
public function get_attributes(): array;
public function validate(): bool;
public function transform_to(): array; // not all blocks transform
public function get_variations(): array; // not all blocks have variations
public function get_styles(): array; // not all blocks have styles
public function get_deprecated(): array; // new blocks have no deprecated
}
PROBLEMS:
New simple block must implement 8 methods
Most return empty arrays — noise in code
Breaks every block when interface changes
Role-Based Block Interfaces
Renderable_Block, Transformable_Block, Styleable_Block, Deprecated_Block — blocks pick what they need
// ✅ Segregated block interfaces
interface Renderable_Block {
public function render(array $attributes, string $content): string;
public function get_attributes(): array;
}
interface Transformable_Block {
public function transform_to(string $block_name): array;
}
interface Styleable_Block {
public function get_styles(): array;
}
interface Deprecated_Block {
public function get_deprecated(): array;
}
// Simple block - only what it needs
class Quote_Block implements Renderable_Block {
public function render(array $attributes, string $content): string {
return '<blockquote>' . $content . '</blockquote>';
}
public function get_attributes(): array {
return ['citation' => ['type' => 'string']];
}
}
// Complex block - implements more interfaces
class Cover_Block implements Renderable_Block, Styleable_Block, Deprecated_Block {
public function render(array $attributes, string $content): string { /* ... */ }
public function get_attributes(): array { /* ... */ }
public function get_styles(): array { return ['default', 'light-text', 'dark-text']; }
public function get_deprecated(): array { /* backward compat */ }
}
BENEFITS:
Simple Quote block has only 2 methods
Cover opts-in to styles and deprecated
Each interface can be tested in isolation
Fat Plugin Interface
All plugins forced to implement everything
// ❌ Fat WordPress plugin interface forces too much
interface WP_Plugin_Interface {
public function register_post_type(): void;
public function register_taxonomy(): void;
public function register_rest_route(): void;
public function register_widget(): void;
public function register_block(): void;
public function enqueue_scripts(): void;
public function add_admin_menu(): void;
public function handle_cron(): void;
}
// Simple plugin forced to implement everything
class Simple_SEO_Plugin implements WP_Plugin_Interface {
public function register_post_type(): void {} // not needed!
public function register_taxonomy(): void {} // not needed!
public function register_rest_route(): void {}
public function register_widget(): void {} // not needed!
public function register_block(): void {} // not needed!
public function enqueue_scripts(): void { /* actual logic */ }
public function add_admin_menu(): void { /* actual logic */ }
public function handle_cron(): void {} // not needed!
}// ❌ Fat WordPress plugin interface forces too much
interface WP_Plugin_Interface {
public function register_post_type(): void;
public function register_taxonomy(): void;
public function register_rest_route(): void;
public function register_widget(): void;
public function register_block(): void;
public function enqueue_scripts(): void;
public function add_admin_menu(): void;
public function handle_cron(): void;
}
// Simple plugin forced to implement everything
class Simple_SEO_Plugin implements WP_Plugin_Interface {
public function register_post_type(): void {} // not needed!
public function register_taxonomy(): void {} // not needed!
public function register_rest_route(): void {}
public function register_widget(): void {} // not needed!
public function register_block(): void {} // not needed!
public function enqueue_scripts(): void { /* actual logic */ }
public function add_admin_menu(): void { /* actual logic */ }
public function handle_cron(): void {} // not needed!
}
PROBLEMS:
Simple SEO plugin has 6 empty methods
Adding a new method breaks every plugin
Hard to test — too many responsibilities
Segregated Interfaces
Each plugin picks only what it needs (Has_Scripts, Has_Admin_Menu, Has_Rest_Routes…)
// ✅ ISP compliant - small focused interfaces
interface Has_Scripts {
public function enqueue_scripts(): void;
}
interface Has_Admin_Menu {
public function add_admin_menu(): void;
}
interface Has_Rest_Routes {
public function register_rest_route(): void;
}
interface Has_Cron {
public function handle_cron(): void;
}
interface Has_Post_Types {
public function register_post_type(): void;
}
// Each plugin implements only what it actually needs
class Simple_SEO_Plugin implements Has_Scripts, Has_Admin_Menu {
public function enqueue_scripts(): void {
wp_enqueue_style('seo-plugin', plugin_dir_url(__FILE__) . 'css/seo.css');
}
public function add_admin_menu(): void {
add_options_page('SEO Settings', 'SEO', 'manage_options', 'seo-settings', [$this, 'render_settings']);
}
}
class Advanced_Plugin implements Has_Scripts, Has_Admin_Menu, Has_Rest_Routes, Has_Cron {
public function enqueue_scripts(): void { /* ... */ }
public function add_admin_menu(): void { /* ... */ }
public function register_rest_route(): void { /* ... */ }
public function handle_cron(): void { /* ... */ }
}
BENEFITS:
Simple SEO plugin has zero unused methods
Adding Has_Cron doesn’t break SEO plugin
Easy to test each interface independently
Common ISP Violations in WordPress
Patterns that lead to fat interfaces and forced dependencies
The “God Interface” Anti-Pattern
One interface for everything — all plugins/classes implement one massive contract.
Fix: Split by role: Has_Scripts, Has_Admin, Has_REST. Use composition.
Empty Method Implementations
Classes implement methods with empty bodies return []; just to satisfy the contract.
Fix: If a class consistently returns empty, that method doesn’t belong in this class’s interface.
WP_Widget Misuse
Extending WP_Widget just for helpers — then leaving widget(), form(), update() empty.
Fix: Only extend WP_Widget when you need actual widget functionality. Otherwise extract helpers into a standalone class.
REST Controller Overload
One REST controller handling reads, writes, admin, bulk — all in one class.
Fix: Separate into Read_Controller, Write_Controller, Admin_Controller each with a focused interface.
Frequently Asked Questions
How small should an interface be?
A: An interface should represent a single role or capability. If you can name it with a single verb-based noun (Renderable, Cacheable, Hookable), it’s probably the right size. If you need “and” in the name — split it.
Doesn’t ISP lead to too many interfaces?
A: More interfaces is not a problem — it’s a feature. Many small interfaces are easier to understand, compose, and test than a few large ones. Name them clearly so the codebase stays readable.
How does ISP relate to WordPress hooks?
A: WordPress hooks are a natural ISP implementation. Focused hooks: wp_enqueue_scripts, init, admin_menu — each plugin only hooks into what it actually needs.
Is ISP the same as Single Responsibility?
A: They’re related but different. SRP is about classes having one reason to change. ISP is about interfaces not forcing clients to depend on methods they don’t use. Violating ISP often leads to SRP violations.