PHP Memento Design Pattern Part II: Store & Retrieve

mementoWith a Little Help from Our Friends

As you saw in Part I of the Memento Design Pattern post, the design itself is fairly simple; at least judging from the class diagram. Also, in the first example, you saw how a state could be preserved in a Caretaker array and recalled upon request. However, I wanted to use the Memento with a more practical implementation of remembering a previous state to which users could return. For instance, the capacity to remember a choice on a Web page after looking at a series of choices would be an ideal use of the Memento pattern. The idea is to build an encapsulated set of objects that encompasses the UI, and the previous post shows how that can be accomplished. The latest problem encountered using the Memento pattern with PHP is that PHP doesn’t have the necessary UI event handlers, and the UI event handlers in JavaScript, while able to pass data, must do so through a PHP file; not an object.

The result is that each time JavaScript passes a bit of data, it also re-instantiates a class in a file. When that happens, all of the stored states are reset to null. So while the Memento is great at storing data in an array (or scalar for that matter), getting that state back with another call via JavaScript (even if it’s encapsulated in a PHP object) nulls all of the stored states.

So, while JavaScript is OK for handling UI events and passing states to PHP, every state passed resets any saved states because the participants have to be re-instantiated. After trying out different methods, I eventually reached the conclusion that I was going to have to stash the Memento’s current state in a JSON (JavaScript Object Notation) file. PHP includes JSON methods, and while this does not exactly solve the problem of passing data without resetting the state, it can preserve the state stored in a Memento object. Take a look at the example and download the files to see all of the code employed:
PlayDownload

JSON and PHP

For those of you unfamiliar with JSON and PHP, you can find it here in the PHP Manual. As noted, to work with PHP and a Memento design pattern the way I wanted to implement it required that I have some way to provide a persistent record of a “saved state.” In a nutshell, JSON is a faster-loading version of XML with hooks into PHP where data can be exchanged. That is an oversimplification to be sure, but as far as I was concerned if a state could be saved and retrieved quickly when wanted, my requirements would be met. I did not want to involve MySQL, XML or simple text files (even though JSON files [ext .json] are text files). JSON was the fastest in saving and retrieving, and PHP has methods for parsing JSON data.

The Caretaker

If you have not done so already, read Part I of the Memento Design Pattern. That will bring you up to speed on the structure and participants of the Memento design pattern.

The Caretaker participant in the Memento pattern is the warehouse for the saved Memento objects passed through the Originator. Essentially, the Memento is passed to the Caretaker where it is stored until called for retrieval. However, a PHP object cannot be stored as a straight JSON file, and so once the Memento object is passed to the Caretaker (in this example), I passed the Memento value to a JSON file. The second Caretaker method returns the stored JSON value directly to the client rather than re-loading a Memento object and returning it. You can see how this all works in the Caretaker class code:

<?php
class Caretaker
{
    private $storage;
    private $caretakerStore;
    private $caretakerStorage;
 
    public function addMemento (Memento $m)
    {
	$jsonData["hold"][0] = $m->getState();
	$this->caretakerStore="store.json";
	file_put_contents($this->caretakerStore,json_encode($jsonData,JSON_UNESCAPED_UNICODE));
    }
 
    public function getMemento()
    {
	$this->caretakerStorage = file_get_contents("store.json");
	$jsonData = json_decode($this->caretakerStorage,true);
	$this->storage= $jsonData["hold"][0];
	return $this->storage;
    }
}
?>

The two methods in the Caretaker are pretty straightforward getter/setter ones. The addMemento($m) method expects to receive a Memento object. (The type hint enforces it to.) However, instead of storing the entire Memento as was done in the example in Part I, the Caretaker uses the Memento’s getState() method to access the saved state. Then the state is stored in an associative array element named “hold.” ($jsonData[“hold”][0]). Written to a .json file the saved state might look like the following:

{"hold":["7"]}

The value “7” is the saved string that is used to recall the correct file (dp7.jpg) in the patterns folder. While this might appear to be breaking encapsulation, the value is returned in a Caretaker private property, $this->storage.

To get a better sense of the Caretaker in the context of this particular implementation take a look at Figure 2:

Figure 2: File diagram of Memento used for recalling Web image

Figure 2: File diagram of Memento used for recalling Web image

The PhpCatcher class is an attempt to encapsulate the HTML UI into an object and have methods available to set and retrieve Memento object values using the Caretaker as a warehouse. To set the Memento and send it to the Caretaker, the exact same object communications are used as in Part I. However, the Caretake extracts the value from the Memento and stores it in the .json file instead of in a PHP array that stays extant through UI interactions. So, to recall a Memento value, the PhpCatcher goes directly to the Caretaker’s getMemento() method. (Perhaps a more accurate name for the getter would be getMementoValue().) In any event, at the time of this posting, I was unable to find a way to store the Memento in a JSON object and retrieve it; so the PHPCatcher communicates directly with the Caretaker as a client.

You need to use PHP 5.4+ and you need to set your .json file permissions

  • The PHP built-in JSON methods and constants used require PHP 5.4. If you find it impossible to install 5.4, instead of using JSON, you can use a text file, an XML file or even a MySQL file for storing the Memento value.
  • File permissions are grouped into three categories:

    1. Owner
    2. Group
    3. Everyone

    Further, each permission has three levels:

    1. Read
    2. Write
    3. Execute

    You need to set your .json file so that everyone can read, write and execute the .json file. I set mine for ‘777’ so that all groups and the owner had total access. On your computer or LAN, there’s not a lot to worry about; however, if you are nervous about opening up your .json file to the world on your hosting service, you need to read up on permissions security to see if doing so will cause unwanted problems.

If you’re using a Raspberry Pi, you can find out how to change your permissions for this implementation here.

The PhpCatcher

While communication between PHP and HTML/JavaScript is possible by encapsulation into a single class, I found that it doesn’t do a lot to ameliorate the lack of UI event handlers in PHP. Whenever a JavaScript event handler is used to kick something to be handled by PHP, it still requires a file name to initiate a change. As things worked out, the PhpCatcher is sort of a “super-client” since both the requests via HTML/JavaScript and class methods both are launched from the class.

Taking a quick look at the class, you can see a lot of the same structures discussed in the post on encapsulating JavaScript and HTML in a PHP class.

<?php
class PhpCatcher
{
	//Encapsulated properties
	private $imgNum;
	private $pageNow;
	private $state=0;
	private $orig;
	private $ct;
	private $ctStorage;
 
	public function getPage()
	{
		//HEREDOC string 
		$this->pageNow=<<<CATCHER
		<!doctype html>
		<html>
		<head>
		<meta charset="UTF-8">
		<!--JavaScript link-->
		<script src="patterns.js"></script>
 
		<!--CSS link-->
		<link rel="stylesheet" type="text/css" href="patterns.css" >
		<title>Pattern Saver</title>
		</head>
 
		<!--JavaScript method 'starterHere()' loads value from 'starter' element-->
		<body onload="startHere()">
 
		<!--Store PHP value in 'starter' element-->
		<input type="hidden" name="starter" value={$this->numGetter()}>
 
		<h2>Design Pattern Selector</h2>
		<img id="patternDepot" width="520" height="520" src={$this->getImage()}></img>
		<p>
			<input type="button" id="back" value="Back" onclick="pictureBack()">
			<input type="button" id="next" value="Next" onclick="pictureNext()"
		</p>
		<p>Design Pattern</p>
		<input type="button" id="send" value="Make Memento" onclick="sender()" target="feedback">
		<input type="button" id="storeNow" value="Get Memento" onclick="storeGetter()" target="feedback">
		<p name="feedback">Currently Stored <em>via</em> Caretaker: $this->state</p>
		</body>
		</html>
CATCHER;
	//Send the page back to the client
	return $this->pageNow;
	}
 
	//Client sets value
	public function imageSetter($n)
	{
		$this->imgNum=$n;
	}
 
	public function store()
	{
		if (isset($_GET['passed']))
		{
			$this->state=$_GET['passed'];
			$this->imageSetter($this->state);
			$this->ct=new Caretaker();
			$this->orig = new Originator();
			$this->orig->setState($this->numGetter());
			$this->ct->addMemento($this->orig->createMemento());
			unset($_GET['passed']);
		}
		elseif (isset($_GET['stored']))
		{
			$this->state=$this->getStored();
			unset($_GET['stored']);
		}
		else
		{
			$this->state=5;
		}
		return $this->state;
	}
 
	//Encapsulated method for getting stored value from Caretaker
        private function getStored()
	{
		$this->ctStorage=new Caretaker();
		$storedValue=$this->ctStorage->getMemento();
		return $storedValue;	
	}
 
	//Encapsulated method for getting image number
	private function numGetter()
	{
		return $this->imgNum;
	}
 
	//Encapsulated method for initial image display
	private function getImage()
	{
		$head="patterns/dp";
		$tail=".jpg";
		//Get the content for the page
		$pix=$head . $this->numGetter() . $tail;
		return $pix;	
	}
}
?>

The PhpCatcher is called by the Client, but PhpCatcher is itself a client. It is made up of the following methods:

  • getPage() : Sets up the HTML page with PHP hooks and embedded JavaScript functionality
  • imageSetter($n) : Sets the number used to identify the image to display
  • store(): Responsible for getting a value passed by a JavaScript function and storing it in the Caretaker, retrieving stored data and setting the default image.
  • getStored(): Actually retrieves the value stored by Caretaker
  • numGetter(): Returns the number set in the imageSetter()
  • getImage(): Concatenates the image URL with the selected number and returns it to be displayed

All of the UI event handling has to be done by JavaScript and by looking at the PhpCatcher class and JavaScript, it will be easier to understand.

?View Code JAVASCRIPT
//Strings for image url
var head="patterns/dp";
var tail =".jpg";
//JavaScript value 'n' initialized
var n;
 
//Moves to higher value 'n' in img element
function pictureNext()
{
    if(n<=9)
    {
	n++;
    }
    document.getElementById("patternDepot").src=head + n + tail;
}
 
//Moves to lower value 'n'
function pictureBack()
{
    if(n>=2)
    {
	n--;
    }
    document.getElementById("patternDepot").src=head + n + tail;
}
 
//Initial 'n' value from PHP via HTML5 element
function startHere()
{
    n=document.getElementsByName("starter")[0].value;
}
 
//Pass to Global Variable Land (aka In-The-Wind)
function sender()
{
    window.location.href="Client.php?passed="+n;
}
 
//Triggers the PHP function that returns the stored value
function storeGetter()
{
    window.location.href="Client.php?stored";
}

The two key JavaScript function are sender() and storeGetter(). The sender function actually passes a value to PHP using the value of the ‘n’ variable. That same variable is used to rotate through the images–in this case images of design pattern class diagrams. When either of these functions are fired, they set the super global variables $_Get[‘passed’] and $_Get[‘stored’]. You can see in the PhpCatcher class where the value of $_Get[‘passed’] is used to set the $state value, but the $_Get[‘stored’] super global passes no data. It functions to trigger the PHP to get the stored value from the Caretaker.

The Originator and Memento

There was absolutely no reason to change the Memento and Originator classes. Each was reused in the form of the first implementation of the Memento from Part I of the Memento Design Pattern post.

<?php
class Originator
{
    //state property must be public for
    //access by Memento
    public $state;
 
    //Request coming from Caretaker object
    public function createMemento()
    { 
       //Passes self ($this) to Memento
       return (new Memento($this));
    }
 
    public function setMemento(Memento $cm)
    {
       $this->state=$cm->getState();
    }    
 
    public function setState($mstate)
    {
        $this->state=$mstate;
    }
}
?>

The Memento is used in a slightly different manner because after passing a stored value to the Caretaker, it is no longer used.

<?php
class Memento
{
    //Memento state
    private $mstate;
 
    public function __construct(Originator $orig)
    {
         $this->setState($orig->state);
    }
 
    //Only accessable from within Memento
    private function setState($stateSetter)
    {
        $this->mstate=$stateSetter;
    }
 
    //No mutations in the getter
    public function getState()
    {
        return $this->mstate;
    }
}
?>

So while the Memento is the name of the pattern, and it is used to capture the value passed from the Originator, in this particular implementation, most of the work was done by other classes dealing with communication between the Client, PhpCatcher and Caretaker with the JavaScript handing the UI events and passing values it generates or were passed to it via PHP.

Share

Copyright © 2014 William Sanders. All Rights Reserved.

0 Responses to “PHP Memento Design Pattern Part II: Store & Retrieve”


  • No Comments

Leave a Reply