Memento pattern keeps track of the internal state of an object, so that the program can track back and restore to any previous state of the object. This pattern enables us to implement the undo of any state change.
This article demonstrates Memento pattern implementations in PHP. Check the implementation process and examples.
Implementation
Follow the steps below to implement the Memento pattern in PHP-
- Create a memento class, this is the class of which we want to keep the history. Define some properties(states) in the class, as per requirement.
- Create originator class. Define same properties(state) as the memento class. Create the getter and setter methods for the properties(states).
- Create a caretaker class. Keep an array to hold the list of state. Define some methods for adding/removing new memento object.
Examples
Check the following examples of Memento pattern-
Example #1: General Memento
Here is a demonstration of general memento pattern implementation.
Memento
This is the original class that we want to track. We want to store the history of the state change of this object.
- Create file “Memento.php”.
- Define class “Memento”.
- Define private property “$state” of type string (can be of any type according to the requirement). Accept and set the “$state” in the constructor.
- Define a getter method “getState” for “$state”.
<?php
// Memento.php
namespace BigBoxCodeDesignPatternMementoGeneral;
class Memento {
public function __construct(private string $state) {
}
public function getState(): string {
return $this->state;
}
}
Originator
- Create file “Originator.php”.
- Define class “Originator”.
- Define the same property “$state”. The state should match the memento class.
- Define the getter and setter for the state.
- Define method for generating a new “Memento” object with the defined state, method named “setMemento”.
- Define method for getting state from the “Memento” object. Method named – “getMementoState”.
<?php
// Originator.php
namespace BigBoxCodeDesignPatternMementoGeneral;
class Originator {
private string $state = '';
public function setState(string $state): void {
$this->state = $state;
}
public function getState(): string {
return $this->state;
}
public function setMemento(): Memento {
echo "Memento Saved with timestamp => " . $this->state . "n";
return new Memento($this->state);
}
public function getMementoState(Memento $memento): void {
$this->state = $memento->getState();
}
}
Caretaker
- Create file “Caretaker.php”.
- Define class “Caretaker”.
- Define an array “$mementoList”. This is used for storing the full list of memento objects for each state change.
- Define method “add”(for adding memento object to the list), “getByIndex”(for getting the memento object at any specific index in the array), “getCurrent”(for getting the last item form the “$mementoList”), “undo” (for undoing the last change).
<?php
// Caretaker.php
namespace BigBoxCodeDesignPatternMementoGeneral;
class Caretaker {
private array $mementoList = [];
public function add(Memento $memento): void {
array_push($this->mementoList, $memento);
}
public function getByIndex(int $index): Memento {
return $this->mementoList[$index];
}
public function getCurrent(): Memento {
return $this->mementoList[count($this->mementoList) - 1];
}
public function undo(): void {
unset($this->mementoList[count($this->mementoList) - 1]);
}
}
Demo
In the client create a “Caretaker” and a “Originator” object. Then use the originator to set the state, and add that to the memento list.
Then we can use the undo or backtracking of the memento object for different states.
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternMementoGeneralCaretaker;
use BigBoxCodeDesignPatternMementoGeneralOriginator;
$caretaker = new Caretaker();
$originator = new Originator();
$originator->setState("Time - 1 : " . microtime(true));
$caretaker->add($originator->setMemento());
// Add some delay if required for testing
$originator->setState("Time - 2 : " . microtime(true));
$caretaker->add($originator->setMemento());
// Add delay if required for testing
$originator->setState("Time - 3 : " . microtime(true));
$caretaker->add($originator->setMemento());
echo "Check state at index 1 (index starts at 0):n";
$stateAtIndex1 = $caretaker->getByIndex(1);
echo $stateAtIndex1->getState() . "n";
echo "Check last state:n";
$lastState = $caretaker->getCurrent();
echo $lastState->getState() . "n";
echo "Undoing last staten";
$caretaker->undo();
echo "Check last state after undo:n";
$lastStateAfterUndo = $caretaker->getCurrent();
echo $lastStateAfterUndo->getState();
Output
Following output will be generated-
Memento Saved with timestamp => Time - 1 : 1695661648.9259
Memento Saved with timestamp => Time - 2 : 1695661648.9339
Memento Saved with timestamp => Time - 3 : 1695661648.934
Check state at index 1 (index starts at 0):
Time - 2 : 1695661648.9339
Check last state:
Time - 3 : 1695661648.934
Undoing last state
Check last state after undo:
Time - 2 : 1695661648.9339
Source Code
Use the following link to get the source code:
Example | Source Code Link |
---|---|
Example #1: General Memento | GitHub |
Other Code Implementations
Use the following links to check the Memento pattern implementation in other programming languages.