PHP Design Patterns
Use When
Use for deliberate PHP pattern selection or review where types, ownership, and boundaries affect maintainability. Use php-engineering to implement the chosen design, php-testing-quality for test execution, and php-antipatterns for smell-focused review.
Selection Rules
Choose the smallest pattern that encodes an actual invariant, ownership boundary, or substitution seam. Prefer concrete collaborators until multiple implementations or an external boundary justify an interface. Do not introduce a pattern only to mirror framework vocabulary.
Patterns To Prefer
- Value objects and readonly DTOs for validated, immutable concepts; use enums plus
match for finite states.
- Named constructors or factories when creation has validation, normalization, or multiple meaningful entry paths.
- Small behavior-oriented interfaces and constructor dependency injection at external boundaries.
- Functional core/imperative shell for pure policy separated from I/O.
- Adapters for host, framework, filesystem, HTTP, or vendor APIs; domain-specific repositories only where collection-like persistence behavior belongs to the domain.
- Application services or commands for use-case coordination; presenters/view models for render-ready data; middleware for cross-cutting request flow.
- Deterministic resource and transaction ownership: one owner opens, commits/rolls back, and cleans up.
Property Hooks And Encapsulation
Property hooks suit local property invariants, normalization, or computed views. Keep hooks deterministic and local. Do not perform service lookup, database/network I/O, logging, or surprising mutation in a hook; use a method or application service when work has observable behavior.
Architecture And Methods
Keep domain behavior near the values it protects. Let controllers, handlers, and templates adapt inputs and outputs rather than carry policy. Prefer explicit method names for state transitions and return domain values or explicit result/error contracts.
Testing Seams
Test pure policy directly. Substitute external adapters at ports, not arbitrary internals. Verify transactions, retries, and resource cleanup at integration boundaries where their observable contracts exist.
Review Checklist
- Does the pattern encode a named invariant or external boundary?
- Are interfaces small and owned by their consumer?
- Is construction valid by default and state transition explicit?
- Is I/O outside value objects, property hooks, and pure policy?
- Is resource and transaction ownership singular and testable?
Common Mistakes
Avoid service bags, generic repository wrappers, inheritance trees used only for reuse, DTOs that leak framework types, hooks with side effects, and abstraction before multiple real callers require it.
1---2name: php-design-patterns3description: PHP-native design-pattern guidance. Use when choosing or reviewing value objects, readonly DTOs, enums, factories, interfaces, dependency injection, adapters, repositories, application services, presenters, middleware, property hooks, or resource ownership in PHP. Do not use for ordinary PHP implementation, test-lane execution, or smell-focused review; use php-engineering, php-testing-quality, or php-antipatterns instead.4---56# PHP Design Patterns78## Use When910Use for deliberate PHP pattern selection or review where types, ownership, and boundaries affect maintainability. Use [`php-engineering`](../php-engineering/SKILL.md) to implement the chosen design, [`php-testing-quality`](../php-testing-quality/SKILL.md) for test execution, and [`php-antipatterns`](../php-antipatterns/SKILL.md) for smell-focused review.1112## Selection Rules1314Choose the smallest pattern that encodes an actual invariant, ownership boundary, or substitution seam. Prefer concrete collaborators until multiple implementations or an external boundary justify an interface. Do not introduce a pattern only to mirror framework vocabulary.1516## Patterns To Prefer1718- Value objects and readonly DTOs for validated, immutable concepts; use enums plus `match` for finite states.19- Named constructors or factories when creation has validation, normalization, or multiple meaningful entry paths.20- Small behavior-oriented interfaces and constructor dependency injection at external boundaries.21- Functional core/imperative shell for pure policy separated from I/O.22- Adapters for host, framework, filesystem, HTTP, or vendor APIs; domain-specific repositories only where collection-like persistence behavior belongs to the domain.23- Application services or commands for use-case coordination; presenters/view models for render-ready data; middleware for cross-cutting request flow.24- Deterministic resource and transaction ownership: one owner opens, commits/rolls back, and cleans up.2526## Property Hooks And Encapsulation2728Property hooks suit local property invariants, normalization, or computed views. Keep hooks deterministic and local. Do not perform service lookup, database/network I/O, logging, or surprising mutation in a hook; use a method or application service when work has observable behavior.2930## Architecture And Methods3132Keep domain behavior near the values it protects. Let controllers, handlers, and templates adapt inputs and outputs rather than carry policy. Prefer explicit method names for state transitions and return domain values or explicit result/error contracts.3334## Testing Seams3536Test pure policy directly. Substitute external adapters at ports, not arbitrary internals. Verify transactions, retries, and resource cleanup at integration boundaries where their observable contracts exist.3738## Review Checklist3940- Does the pattern encode a named invariant or external boundary?41- Are interfaces small and owned by their consumer?42- Is construction valid by default and state transition explicit?43- Is I/O outside value objects, property hooks, and pure policy?44- Is resource and transaction ownership singular and testable?4546## Common Mistakes4748Avoid service bags, generic repository wrappers, inheritance trees used only for reuse, DTOs that leak framework types, hooks with side effects, and abstraction before multiple real callers require it.