DZone
Web Dev Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Web Dev Zone > Practical PHP Patterns: Memento

Practical PHP Patterns: Memento

Giorgio Sironi user avatar by
Giorgio Sironi
·
Mar. 04, 10 · Web Dev Zone · News
Like (0)
Save
Tweet
7.38K Views

Join the DZone community and get the full member experience.

Join For Free
The pattern of today is the Memento one, whose intent is storing the state of an Originator object without breaking its encapsulation, which typically consists in a set of private fields.



For the sake of preserving encapsulation, the ConcreteMemento should be created only by its correspondent Originator. Only Originator has access to its private members, and only Originator can restore its state given that a ConcreteMemento has been handed back to it.
Another caveat is that only Originator sees the full contract of ConcreteMemento, while the Caretaker objects, which pass the Memento around, work with a very narrow interface, mostly present only for type safety and possibly without any methods on it. Any access to the ConcreteMemento data out of the Originator will, again, break its encapsulation and the best way to avoid tampering is defining Caretaker's method signatures to accept a Memento instead of ConcreteMemento: for instance, you can unit test them with a Memento mock.
ConcreteMemento itself is a dumb object, usually implemented as an immutable ValueObject, with little or no behavior. Thus, there is no need for a factory that takes care of its creation and it can be instantiated directly both in Originator and in test suites (no behavior to stub out).
Php's dynamic typing is very useful since avoids the casting of Memento to ConcreteMemento when is passed back to the Originator. What in a statically typed language should be seriously dealt with, in php is a minor inconvenience.
Anyway, why not storing the whole object whose state we want to preserve instead of a dumb, little Memento? I see two diffused situations where this pattern is applicable:
  • the object whose state has to be maintained is heavy, and serializing it would bring together many other collaborators.
  • moreover, in php scripts it may be linked to external services or resources that do not survive the http request.
The code sample approaches the latter case, tackling the problem by recreating a Service and re-establishing its state with a serializable Memento. The Service may contain anything from database connections to stream wrappers - only the Memento should be stored in $_SESSION.
<?php
/**
* This interface is primarily here for type safety,
* specifically to avoid that someone passes to Caretaker's
* methods strings, instead of Memento instances.
*/
interface Memento
{
}

/**
* The ConcreteMemento should be used only in the Originator
* (and in testing).
*/
class ConcreteMemento implements Memento
{
protected $_url;
protected $_currentLine;

public function __construct($url, $currentLine)
{
$this->_url = $url;
$this->_currentLine = $currentLine;
}

public function getUrl()
{
return $this->_url;
}

public function getCurrentLine()
{
return $this->_currentLine;
}
}

/**
* The Originator. Creates a ConcreteMemento and returns it
* as a Memento, taking back the Memento and checking it
* to rebuild its internal state.
* This class responsibility is to take an url and
* returning one line from it at the time. Its work is
* often broken into multiple [Ajax] requests, depending
* on the parts the user wants to see.
*/
class Service
{
protected $_url;
protected $_currentLine = 0;

public function __construct($url = null)
{
$this->_url = $url;
}

public function init()
{
$this->_content = file($this->_url);
}

/**
* @return ConcreteMemento
*/
public function getState()
{
return new ConcreteMemento($this->_url, $this->_currentLine);
}

public function setState(ConcreteMemento $state)
{
if (!($state instanceof Memento)) {
throw new Exception('Memento object not recognized.');
}
$this->_url= $state->getUrl();
$this->_currentLine = $state->getCurrentLine();
$this->init();
}

public function getLine()
{
$line = $this->_content[$this->_currentLine];
$this->_currentLine++;
return $line;
}
}

//Caretaker code
$service = new Service('http://giorgiosironi.blogspot.com');
$service->init();
echo $service->getLine();
echo $service->getLine();
echo $service->getLine();
$memento = $service->getState();
// now we can store Memento in the session
// let's simulate its handling
$mementoString = serialize($memento);
$newService = new Service();
$newService->setState(unserialize($mementoString));
echo $newService->getLine();
echo $newService->getLine();
echo $newService->getLine();
PHP

Published at DZone with permission of Giorgio Sironi, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Open Source Monitoring and Metrics Landscape
  • 10 Programming Habits a Web Developer Should Embrace
  • Progressive Web Apps vs Native Apps: Differences and Similarities
  • Screen Sharing in Java

Comments

Web Dev Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo