Design Pattern: Memento Pattern in PHP

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 BigBoxCode\DesignPattern\Memento\General;


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 BigBoxCode\DesignPattern\Memento\General;

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 BigBoxCode\DesignPattern\Memento\General;


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 BigBoxCode\DesignPattern\Memento\General\Caretaker;
use BigBoxCode\DesignPattern\Memento\General\Originator;

$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 state\n";

$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:

Other Code Implementations

Use the following links to check the Memento pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.