Strategy Pattern
debt(d7/e5/b5/t5)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints note automated=no, and while phpstan/psalm are listed as tools, they cannot automatically flag the absence of a strategy pattern or the presence of switch/if-else branching on algorithm type — that requires a human reviewer recognising the code smell. The pattern's misuse (e.g. strategies coupled to context, or forgetting injection) is invisible to static analysers without custom rules.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix describes extracting the varying algorithm into an interface and creating one class per strategy — this means creating new files per strategy class, an interface, and updating the context to accept injection. This spans multiple files but is contained within one component rather than a cross-cutting codebase refactor.
Closest to 'persistent productivity tax' (b5). The pattern applies across web, cli, and queue-worker contexts. When misapplied (e.g. too many trivial strategies, strategies coupled to context, or concrete-type coupling), it introduces ongoing friction in every work stream that touches that subsystem. However it doesn't define the system's entire shape, so b7/b9 would be too high.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field explicitly identifies the Strategy vs State confusion — both swap behaviour, and a competent developer unfamiliar with the distinction will conflate them. This is a well-documented gotcha that most developers eventually learn but frequently get wrong initially, matching the t5 anchor.
Also Known As
TL;DR
Explanation
The Strategy pattern encapsulates each algorithm in its own class implementing a common interface, making them interchangeable. The context class accepts a strategy via constructor injection and delegates behaviour to it. This eliminates switch/if-else chains that select algorithms (replacing switch smell with polymorphism), makes algorithms independently testable, and follows Open/Closed Principle — new strategies are added without modifying the context. Classic PHP examples: payment processors, sorting algorithms, and report formatters.
Common Misconception
Why It Matters
Common Mistakes
- Strategies that depend on the context object — they should be independent algorithms.
- Too many strategies for trivial variations — a simple parameter is cleaner than a strategy class.
- Not using interfaces for strategies — callers become coupled to concrete strategy types.
- Forgetting to inject the strategy — instantiating it inside the context defeats the purpose.
Code Examples
function sortUsers(array $users, string $by): array {
if ($by === 'name') {
usort($users, fn($a,$b) => strcmp($a->name, $b->name));
} elseif ($by === 'age') {
usort($users, fn($a,$b) => $a->age <=> $b->age);
} elseif ($by === 'score') {
usort($users, fn($a,$b) => $b->score <=> $a->score);
} // must edit this function for every new sort
return $users;
}
interface SortStrategy {
public function compare(User $a, User $b): int;
}
class SortByName implements SortStrategy { public function compare(User $a, User $b): int { return strcmp($a->name, $b->name); } }
class SortByAge implements SortStrategy { public function compare(User $a, User $b): int { return $a->age <=> $b->age; } }
class SortByScore implements SortStrategy { public function compare(User $a, User $b): int { return $b->score <=> $a->score; } }
function sortUsers(array $users, SortStrategy $strategy): array {
usort($users, $strategy->compare(...));
return $users;
}