Composite Pattern
debt(d5/e5/b5/t5)
Closest to 'specialist tool catches' (d5). PHPStan can detect type inconsistencies and missing interface implementations, but detecting inappropriate composite pattern usage (isinstance checks handling leaves vs composites differently, lack of uniform interface) requires static analysis rules or careful code review. The detection_hints indicate automated detection is 'no', suggesting beyond simple linting.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix suggests implementing a Component interface with Leaf and Composite classes — this requires creating or modifying multiple classes, updating client code to use the uniform interface, and potentially restructuring existing hierarchies. Not a one-line fix, but contained within a single architectural concern.
Closest to 'persistent productivity tax' (b5). The pattern applies across web/cli/queue-worker contexts and once adopted for a hierarchy (file systems, UI components, permissions), it shapes how all code interacts with that structure. However, it's typically localized to specific subsystems rather than defining the entire system's architecture, placing it at moderate reach/burden.
Closest to 'notable trap' (t5). The misconception explicitly states developers wrongly believe 'composite pattern is only useful for tree data structures' when it applies broadly to uniform treatment of objects and collections. Additionally, common_mistakes cite parent reference memory leaks and failing to implement uniform interfaces — documented gotchas that most developers eventually learn but aren't immediately obvious.
Also Known As
TL;DR
Explanation
The Composite pattern composes objects into tree structures to represent part-whole hierarchies. Both leaf objects and composites implement the same Component interface, so client code treats them identically — calling render() on a single widget or a container of widgets uses the same API. In PHP, this appears in menu systems, file system trees, expression parsers, and UI component hierarchies. The key benefit is that client code doesn't need to distinguish between individual items and collections. The trade-off is that it can make it too easy to build overly general designs.
Common Misconception
Why It Matters
Common Mistakes
- Adding parent references to nodes without managing them — circular references cause memory leaks in PHP.
- Not making leaf nodes and composite nodes implement the same interface — forces type-checking in the client.
- Using Composite when the hierarchy is fixed and shallow — a simple array or collection is sufficient.
- Exposing the children collection directly — encapsulate add/remove to maintain invariants.
Code Examples
// Type-checking instead of uniform interface:
function renderMenu(mixed $item): string {
if ($item instanceof MenuItem) return $item->render();
if ($item instanceof MenuGroup) { // Separate handling — use Composite
return implode('', array_map(fn($i) => renderMenu($i), $item->items));
}
}
// Treat individual objects and compositions uniformly
interface HtmlElement {
public function render(): string;
}
class TextNode implements HtmlElement {
public function __construct(private string \$text) {}
public function render(): string { return htmlspecialchars(\$this->text); }
}
class HtmlTag implements HtmlElement {
private array \$children = [];
public function __construct(private string \$tag) {}
public function add(HtmlElement \$e): void { \$this->children[] = \$e; }
public function render(): string {
\$inner = implode('', array_map(fn(\$c) => \$c->render(), \$this->children));
return "<{\$this->tag}>{\$inner}</{\$this->tag}>";
}
}