Design Patterns: Memento
A Behavioural design pattern that lets you capture and restore an object’s state without exposing its internal details, allowing you to implement undo or history functionality safely.
Overview
The memento pattern is all about saving and restoring an object’s state without exposing its inner workings. Think of it like hitting Ctrl+Z in your editor, you don’t care how it works behind the scenes, you just want things back the way they were.
I’ve used variations of this in projects where undo/redo or draft recovery were needed. It’s one of those patterns that doesn’t sound glamorous but becomes a lifesaver when you need it.
The problem
Imagine we’re building a text editor:
class TextEditor
{
private string $text = '';
public function type(string $words): void
{
$this->text .= $words;
}
public function getText(): string
{
return $this->text;
}
}Simple enough right? There’s a type function which updates a string and a getText function… which gets the text, but what if we ever need to undo changes?
You could add an undo function, a history class property and update it as text is entered?
class TextEditor
{
private string $text = '';
private array $history = [];
public function type(string $words): void
{
// Save current state before typing
$this->history[] = $this->text;
$this->text .= $words;
}
public function getText(): string
{
return $this->text;
}
public function undo(): void
{
if (empty($this->history)) {
$this->text = '';
return;
}
$this->text = array_pop($this->history);
}
}Why this is bad / dirty:
The
TextEditornow has two responsibilities: editing text and managing undo history.History is tightly coupled to the class internals—if you wanted to save it externally, tough luck.
If your editor grows (like adding bold/italic formatting, cursor positions, or multiple documents), this class will explode with complexity.
You can’t easily implement redo, multiple undo stacks, or persist versions elsewhere.
In short, it “works,” but it’s spaghetti waiting to happen. We can definitely clean this up.
The solution
Enter the Memento pattern. Instead of cluttering the editor with history logic, we separate responsibilities:
The Originator (TextEditor) knows how to save and restore its state.
class TextEditor
{
private string $text = '';
public function type(string $words): void
{
$this->text .= $words;
}
public function getText(): string
{
return $this->text;
}
public function save(): TextMemento
{
return new TextMemento($this->text);
}
public function restore(?TextMemento $memento): void
{
if (!$memento instanceof TextMemento) {
$this->text = '';
return;
}
$this->text = $memento->getText();
}
}The Memento (TextMemento) stores that state privately.
class TextMemento
{
public function __construct(private readonly string $text)
{
//
}
public function getText(): string
{
return $this->text;
}
}The Caretaker (History) manages a collection of mementos, without knowing their internals.
class History
{
/** @var TextMemento[] */
private array $mementos = [];
public function push(TextMemento $memento): void
{
$this->mementos[] = $memento;
}
public function pop(): ?TextMemento
{
if ($this->mementos === []) {
return null;
}
array_pop($this->mementos);
$previous = end($this->mementos);
if ($previous === false) {
return null;
}
return $previous;
}
}This way, the editor stays focused on typing, the history stays focused on managing versions, and the memento keeps the state private.
Here’s a quick usage example:
$editor = new TextEditor();
$history = new History();
// Type some text and save drafts
$editor->type("Hello ");
$history->push($editor->save());
$editor->type("World!");
$history->push($editor->save());
echo $editor->getText(); // Hello World!
// Undo last change
$editor->restore($history->pop());
echo $editor->getText(); // Hello
// Undo again
$editor->restore($history->pop());
echo $editor->getText(); // (empty string)Advantages
Separation of Concerns – The editor doesn’t manage history; the caretaker does.
Undo/Redo becomes trivial – Just push and pop mementos.
Encapsulation preserved – Mementos hide the internal state.
Flexible version management – Keep as many snapshots as you like, discard old ones, or persist them externally.
Disadvantages
Memory overhead – Saving large states frequently can be heavy.
Extra classes – You need the memento and caretaker, even for small use cases.
Edge case handling – Restoring from a null or invalid memento must be handled gracefully.
Conclusion
The Memento pattern is a lifesaver whenever you need undo/redo functionality or draft/version management. It keeps classes clean, encapsulated, and maintainable, while providing a robust way to rewind state safely.
It shines in scenarios like:
Text editors with undo/redo.
CMS draft/version management.
Games with save/load points.
So the next time someone says “we need undo,” you know exactly which pattern to reach for. And yes, it’s a lot cleaner than stuffing a history array into your editor like spaghetti code.
Want to see the Memento Pattern in action? Check out this simple example: Github


