PHP Memento Pattern
A short screencast: the Memento pattern — capture the state of an object so you can roll back to it later. We implement undo for text edits.
Overview
Memento lets you save and restore the state of an object without exposing its internals. The editor Editor is the source of the state: its save() method creates a Memento holding the current text, and restore() brings the text back from a memento. The snapshots are kept by a separate caretaker object, History: it stacks them (push) and takes the latest one back (pop). The client asks the editor to save a memento and hands it to the history — the editor itself does not know where or how many snapshots are stored, and the memento is read-only from the outside.
UML diagram
Code
Memento.php
class Memento
{
public function __construct(private readonly string $text) {}
public function getText(): string
{
return $this->text;
}
}
Editor.php
class Editor
{
private string $text = '';
public function setText(string $text): void
{
$this->text = $text;
}
public function getText(): string
{
return $this->text;
}
public function save(): Memento
{
return new Memento($this->text);
}
public function restore(Memento $memento): void
{
$this->text = $memento->getText();
}
}
History.php
class History
{
private array $mementos = [];
public function push(Memento $memento): void
{
$this->mementos[] = $memento;
}
public function pop(): ?Memento
{
return array_pop($this->mementos);
}
}
demo.php
require_once 'Memento.php';
require_once 'Editor.php';
require_once 'History.php';
$editor = new Editor;
$history = new History;
$editor->setText('Version 1');
$history->push($editor->save());
$editor->setText('Version 2');
$history->push($editor->save());
$editor->setText('Version 3');
echo $editor->getText().PHP_EOL;
$editor->restore($history->pop());
echo $editor->getText().PHP_EOL;
Example output
After php demo.php we see version 3, then a rollback to version 2 from a snapshot:
Version 3
Version 2
Advantages
- State can be saved and rolled back without breaking the object's encapsulation.
- The memento is immutable (
readonly), so storing it is safe. - The history storage logic lives in a separate caretaker object.
- It makes undo, history and transactions straightforward.
When to use
- When you need undo or rollback of an object's state.
- When you must keep a history of changes and return to a past snapshot.
- When copying an object's fields directly would break encapsulation.
- When a snapshot must stay immutable once created.
Usage in Laravel
In Laravel the Memento shows up wherever state must be preserved for a rollback. Eloquent keeps the original attribute values of a model, and getOriginal(), getChanges() and isDirty() let you return to the state before saving. Migrations use schema snapshots for rollback, and Illuminate\Support\Facades\Cache stores ready values that are later restored. Closest to the classic memento is a database transaction with a rollback: DB::beginTransaction() and DB::rollBack() return the data to a savepoint.