Memento Pattern
debt(d7/e5/b3/t5)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints indicate automated detection is 'no' and the tool listed (phpstan) would not catch pattern-level misuse such as a caretaker inspecting memento internals or missing deep copies — these require human code review. Common mistakes like shallow copies or memory leaks from unbounded history are silent until runtime.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix describes capturing state as an immutable snapshot in a stack, but correcting misuse (e.g. fixing shallow copies across a state-heavy object graph, separating caretaker from originator concerns, or switching to diff-based storage) touches multiple classes and likely ripples through the undo infrastructure. It is not a single-line swap nor a full architectural rework.
Closest to 'localised tax' (b3). Memento is a localised design choice that applies to specific undo/state-capture subsystems. The applies_to scope covers web and CLI contexts broadly, but the pattern itself only burdens the component implementing undo — the rest of the codebase is largely unaffected unless undo is pervasive.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The canonical misconception — that mementos require serialisation — is a documented but non-obvious surprise. Additionally, shallow-copy vs deep-copy confusion and memory exhaustion from unbounded history are well-known gotchas that developers typically discover through experience rather than intuition, making this a notable but not catastrophic trap.
Also Known As
TL;DR
Explanation
Memento has three roles: Originator (the object whose state is saved), Memento (the snapshot — an opaque object holding saved state), and Caretaker (manages the collection of mementos, initiates save/restore). The key insight: the caretaker never inspects the memento's contents — it just holds them. This preserves encapsulation. PHP applications: undo stacks in editors, game save states, form wizard step history, and domain aggregate snapshots in event sourcing.
Common Misconception
Why It Matters
Common Mistakes
- Caretaker that inspects or modifies memento contents — defeats the encapsulation purpose.
- Deep copies not made — if state contains objects, shallow copy means memento shares references.
- Too many mementos for large objects — consider storing diffs rather than full snapshots.
- Not clearing old mementos — unlimited undo history can exhaust memory.
Code Examples
// No memento — state lost when form progresses:
class WizardForm {
public array $step1Data = [];
public array $step2Data = [];
// User goes back — step2Data overwritten, step1Data lost
// No way to restore previous state cleanly
}
// Memento pattern:
class WizardForm {
private array $state = [];
public function save(): WizardMemento {
return new WizardMemento($this->state); // Snapshot
}
public function restore(WizardMemento $memento): void {
$this->state = $memento->getState();
}
}
class WizardMemento {
public function __construct(private readonly array $state) {}
public function getState(): array { return $this->state; }
}
// Caretaker:
$history = [];
$history[] = $wizard->save(); // Before step 2
// User goes back:
$wizard->restore(array_pop($history));