SOLID principles knowledge base for PHP 8.4 projects. Provides quick reference for SRP, OCP, LSP, ISP, DIP with detection patterns, PHP examples, and antipattern identification. Use for architecture audits and code quality reviews.
From accnpx claudepluginhub dykyi-roman/awesome-claude-code --plugin accThis skill uses the workspace's default tool permissions.
assets/report-template.mdreferences/antipatterns.mdreferences/dip-patterns.mdreferences/isp-patterns.mdreferences/lsp-patterns.mdreferences/ocp-patterns.mdreferences/srp-patterns.mdSOLID is a set of five design principles for writing maintainable, extensible software.
| Principle | Name | Core Idea |
|---|---|---|
| S | Single Responsibility | One class = one reason to change |
| O | Open/Closed | Open for extension, closed for modification |
| L | Liskov Substitution | Subtypes must be substitutable for base types |
| I | Interface Segregation | Many specific interfaces > one general |
| D | Dependency Inversion | Depend on abstractions, not concretions |
# God classes (>500 lines)
find . -name "*.php" -exec wc -l {} \; | awk '$1 > 500 {print}'
# Classes with "And" in name
grep -rn "class.*And[A-Z]" --include="*.php"
# Classes with >7 dependencies
grep -rn "public function __construct" --include="*.php" -A 20 | grep -E "private|readonly" | wc -l
# Multiple responsibility indicators
grep -rn "class.*Manager\|class.*Handler\|class.*Processor" --include="*.php"
Signs of SRP Violation:
# Switch on type
grep -rn "switch.*instanceof\|switch.*::class" --include="*.php"
# Type checking conditionals
grep -rn "if.*instanceof\|elseif.*instanceof" --include="*.php"
# Hardcoded type maps
grep -rn "\[.*::class.*=>" --include="*.php"
Signs of OCP Violation:
# Exception in overridden methods
grep -rn "throw.*NotImplemented\|throw.*NotSupported" --include="*.php"
# Empty overrides
grep -rn "public function.*\{[\s]*\}" --include="*.php"
# Type checks in child classes
grep -rn "if.*parent::" --include="*.php"
Signs of LSP Violation:
# Large interfaces (>5 methods)
grep -rn "interface\s" --include="*.php" -A 30 | grep -c "public function"
# Empty interface implementations
grep -rn "// TODO\|// not implemented\|// unused" --include="*.php"
Signs of ISP Violation:
# Direct instantiation in constructors
grep -rn "new\s\+[A-Z]" --include="*.php" | grep -v "Exception\|DateTime\|stdClass"
# Static method calls
grep -rn "::[a-z].*(" --include="*.php" | grep -v "self::\|static::\|parent::"
# Concrete class type hints (not interfaces)
grep -rn "function.*([A-Z][a-z]*[A-Z]" --include="*.php"
Signs of DIP Violation:
new ConcreteClass() inside methods<?php
declare(strict_types=1);
// BAD: Multiple responsibilities
final class UserService
{
public function register(UserData $data): User { /* ... */ }
public function sendEmail(User $user): void { /* ... */ }
public function generateReport(User $user): Report { /* ... */ }
}
// GOOD: Single responsibility each
final readonly class RegisterUserHandler
{
public function __construct(
private UserRepository $users,
private EventDispatcher $events,
) {}
public function __invoke(RegisterUserCommand $command): UserId
{
$user = User::register($command->email, $command->password);
$this->users->save($user);
$this->events->dispatch($user->releaseEvents());
return $user->id;
}
}
<?php
declare(strict_types=1);
// BAD: Modification required for new types
final class PaymentProcessor
{
public function process(Payment $payment): void
{
match ($payment->type) {
'card' => $this->processCard($payment),
'paypal' => $this->processPaypal($payment),
// Must modify for new payment types
};
}
}
// GOOD: Extension without modification
interface PaymentGateway
{
public function supports(Payment $payment): bool;
public function process(Payment $payment): PaymentResult;
}
final readonly class PaymentProcessor
{
/** @param iterable<PaymentGateway> $gateways */
public function __construct(
private iterable $gateways,
) {}
public function process(Payment $payment): PaymentResult
{
foreach ($this->gateways as $gateway) {
if ($gateway->supports($payment)) {
return $gateway->process($payment);
}
}
throw new UnsupportedPaymentException($payment->type);
}
}
<?php
declare(strict_types=1);
// BAD: Violates substitutability
abstract class Bird
{
abstract public function fly(): void;
}
final class Penguin extends Bird
{
public function fly(): void
{
throw new CannotFlyException(); // LSP violation!
}
}
// GOOD: Proper abstraction hierarchy
interface Bird
{
public function move(): void;
}
interface FlyingBird extends Bird
{
public function fly(): void;
}
final readonly class Penguin implements Bird
{
public function move(): void
{
$this->swim();
}
private function swim(): void { /* ... */ }
}
final readonly class Eagle implements FlyingBird
{
public function move(): void
{
$this->fly();
}
public function fly(): void { /* ... */ }
}
<?php
declare(strict_types=1);
// BAD: Fat interface
interface UserRepository
{
public function find(UserId $id): ?User;
public function findByEmail(Email $email): ?User;
public function save(User $user): void;
public function delete(User $user): void;
public function findAll(): array;
public function count(): int;
public function export(): string;
public function import(string $data): void;
}
// GOOD: Segregated interfaces
interface UserReader
{
public function find(UserId $id): ?User;
public function findByEmail(Email $email): ?User;
}
interface UserWriter
{
public function save(User $user): void;
public function delete(User $user): void;
}
interface UserExporter
{
public function export(): string;
public function import(string $data): void;
}
// Compose as needed
interface UserRepository extends UserReader, UserWriter {}
<?php
declare(strict_types=1);
// BAD: Depends on concretions
final class OrderService
{
public function process(Order $order): void
{
$mailer = new SmtpMailer(); // Concrete dependency
$logger = Logger::getInstance(); // Static dependency
$validator = new OrderValidator(); // Hidden dependency
// ...
}
}
// GOOD: Depends on abstractions
final readonly class OrderService
{
public function __construct(
private OrderRepository $orders,
private Mailer $mailer,
private LoggerInterface $logger,
private OrderValidator $validator,
) {}
public function process(Order $order): void
{
$this->validator->validate($order);
$this->orders->save($order);
$this->mailer->send(new OrderConfirmation($order));
$this->logger->info('Order processed', ['id' => $order->id->value]);
}
}
| SOLID | DDD Application |
|---|---|
| SRP | Aggregates have single consistency boundary |
| OCP | Domain Events enable extension without modification |
| LSP | Value Objects are substitutable (same type = same behavior) |
| ISP | Repository interfaces segregated (Reader/Writer) |
| DIP | Domain depends on Repository interfaces, not implementations |
| Layer | SOLID Focus |
|---|---|
| Domain | SRP (Entities), LSP (Value Objects), ISP (Repository interfaces) |
| Application | SRP (Use Cases), DIP (Port interfaces) |
| Infrastructure | OCP (Adapters), DIP (Implements ports) |
| Presentation | SRP (Controllers), ISP (API contracts) |
| Level | Description | Example |
|---|---|---|
| CRITICAL | Fundamental violation affecting entire system | God class, no DI |
| WARNING | Localized violation, should be fixed | instanceof chains |
| INFO | Minor issue, consider refactoring | Interface with 6 methods |
See detailed documentation in references/:
srp-patterns.md - Single Responsibility patternsocp-patterns.md - Open/Closed patternslsp-patterns.md - Liskov Substitution patternsisp-patterns.md - Interface Segregation patternsdip-patterns.md - Dependency Inversion patternsantipatterns.md - Common SOLID violationsSee assets/report-template.md for audit report format.
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.