Chapter 14. Building a Multidevice CMS with the Observer Pattern

If there were observers on Mars, they would probably be amazed that we have survived this long.

In dream consciousness... we make things happen by wishing them, because we are not only the observer of what we experience but also the creator.

Knowledge is what we get when an observer, preferably a scientifically trained observer, provides us with a copy of reality that we can all recognize.

One of the many treats found within PHP 5.1.0 and up is a set of interfaces that can be used with the Observer design pattern. This chapter will look at an Observer pattern beginning from scratch without any built-in features, but to get started we’ll have a short overview of the SplObserver interface along with the SplSubject and the SplObjectStorage interfaces that make building an Observer pattern with PHP a snap. “SPL” is shorthand for Standard PHP Library and consists of a collection of interfaces and classes used to solve standard problems.

However, before we get ahead of ourselves, you need some idea of what the Observer pattern is and what it does. Fortunately, its class diagram is quite detailed, and many of the features prized in the Model-View-Controller (MVC) pattern can be found in the Observer. (You might even think of the pattern as an alternative to the MVC.) At the root of the Observer are Subject and Observer interfaces. The Subject holds a given state and the observers “subscribe” to the subject to be informed of the current state. You can think of it as a blog with many subscribers—one set of information is routinely updated for a variety of users who subscribe or regularly read the blog. Each time the blog is changed (its state changes), all of the subscribers “are informed.” Figure 14-1 shows the Observer class diagram.

One of the more interesting and possibly perplexing features of this pattern is the Subject’s methods. While the italicized title “Subject” title indicates an interface (an abstract class in this case), abstract methods are italicized as well. However, as you can see in Figure 14-1, none of the methods are italicized. It is clear which methods Subject generates, and the Notify() method even has pseudocode to help out. You will find several different implementations of the Observer pattern, and even the one built into PHP has its own spin.

The Observer pattern was designed so that a single object could keep track of a state and when the state changed, all of the subscribing objects would be informed. In situations where you need consistency in one state but may have several different views of a given state, the Observer pattern is both appropriate and helpful. You can maintain consistency while keeping down the number of objects that must create a given state on their own.

The Observer pattern makes intuitive sense. Why should more than one object do the work to create or keep track of a given state? If one object can do the job and then inform others that may use the state, it makes a lot more sense.

One ongoing issue for PHP developers and designers is taking one set of data in the form of text, numbers, and graphics and formatting them for different devices for web presentation. Mobile devices ranging from smartphones to tablets of varying sizes, and nonmobile devices like desktop computers and laptops all need different design configurations. However, they need the same data. Figure 14-2 illustrates this relationship.

Connected to a content management system (CMS), the Observer design pattern is a way to make sure that all configurations are presenting the same materials. Each observer can use the identical data, and all the developer and designer have to concern themselves with is setting up the content for the different devices and getting the data information to the Subject participant.

The three SPL interfaces/classes that can be employed usefully with the Observer design pattern are the following:

A quick review of each shows its structure in relationship to the Observer design pattern. Figure 14-3 gives a quick conceptual layout.

Compare the SPL Observer class diagram in Figure 14-3 with the GoF version in Figure 14-1. All of the same parts are there, but the SPL diagram has more abstractions.

The subject needs to attach and detach observers and to notify subscribers (attached observers) of the change. A private variable $observers encapsulates the property. In this implementation, the $observers property is instantiated as a SplObjectStorage object. Think of it as something like an array. Then, individual $observer instances are attached as SplObserver objects (The storage unit is $observers that stores individual $observer instances.) Figure 14-4 shows this arrangement.

What makes the SplObjectStorage valuable is its built-in attach() and detach() methods, which make it easy to program which observer instances will “subscribe” and “unsubscribe” to update notifications:

<?
class ConcreteSubject implements SplSubject
{
    private $observers;
    private $data;

    public function setObservers()
    {
        $this->observers=new SplObjectStorage();
    }

    public function attach(SplObserver $observer)
    {
        $this->observers->attach($observer);
    }

    public function detach(SplObserver $observer)
    {
        $this->observers->detach($observer);
    }

    public function notify()
    {
        foreach ($this->observers as $observer) {

            $observer->update($this);
        }
    }

    public function setData($dataNow)
    {
       $this->data=$dataNow;
    }

    public function getData()
    {
        return $this->data;
    }
}
?>

Getter and setter methods are not part of the SplSubject interface, but they are part of the design pattern and so need to be added. The setter method, setData(), includes a parameter for any kind of data to be added. The getter method, getData(), stores the current subject state and is used by the concrete observer to update observer data.

The setObservers() method is added as well. Rather than setting the SplObjectStorage() instance in a constructor function that would require a new instance of the ConcreteSubject class or placing it inside the setData() method, the separate setObservers() method provides a looser coupling and allows for the possibility of more than one set of observers.

The “SPL” Client class is just a standard client. The client makes several requests to concrete subjects and observers based on SPL interfaces but implements no SPL classes or interfaces itself.

In this example, the Client creates a single subject instance and three concrete observer instances. Then it sets a new state using the setData() method and attaches the three observers to the subject. Finally, it calls the concrete subject instance notify() method to send the current state to the subscribing observers:

<?php
//Client
function __autoload($class_name)
{
    include $class_name . '.php';
}
//
class Client
{
    public function __construct()
    {
        echo "<p>Create three new concrete observers, a new concrete subject:
              </p>";
        $ob1 = new ConcreteObserver();
        $ob2 = new ConcreteObserver();
        $ob3 = new ConcreteObserver();

        $subject = new ConcreteSubject();
        $subject->setObservers();
        $subject->setData("Here's your data!");
        $subject->attach($ob1);
        $subject->attach($ob2);
        $subject->attach($ob3);

        $subject->notify();

        echo "<p>Detach observer ob3. Now only ob1 and ob2 are notified:</p>";
        $subject->detach($ob3);
        $subject->notify();

        echo "<p>Reset data and reattach ob3 and detach ob2--only ob 1 and 3 are
              notified:</p>";
        $subject->setData("More data that only ob1 and ob3 need.");
        $subject->attach($ob3);
        $subject->detach($ob2);
        $subject->notify();
    }
}

$worker=new Client();
?>

The output from the Client is as follows:

Create three new concrete observers, a new concrete subject:
Here's your data!
Here's your data!
Here's your data!

Detach observer ob3. Now only ob1 and ob2 are notified:

Here's your data!
Here's your data!

Reset data and reattach ob3 and detach ob2--only ob 1 and 3 are notified:

More data that only ob1 and ob3 need.
More data that only ob1 and ob3 need.

One possible source of confusion may exist in differentiating the ConcreteSubject->attach() and ConcreteSubject->detach() methods from the SplObjectStorage->attach() and SplObjectStorage->detach() methods. The ConcreteSubject class wraps the SplObjectStorage attach() and detach() methods in its own attach() and detach() methods. The following pseudocode shows a class version of how the ConcreteSubject created its attach/detach methods:

public function attach(SplObserver $observer)
    {
        SplObjectStorage->attach($observer);
    }

    public function detach(SplObserver $observer)
    {
        SplObjectStorage->detach($observer);
    }

Changing the concrete subject class attach/detach methods to some other name would probably be equally confusing, so just understand that built-in SPL attach/detach methods are used to create ConcreteSubject attach/detach methods.

Moving now from some of the few SPL interfaces in PHP designed specifically for a design pattern to using non-SPL participants, you can get an alternative view of the Observer closer to the original structure as shown in Figure 14-1. The main differences are the following:

  • The Subject participant is an abstract class instead of an interface.

  • The notify() method is implemented in the Subject instead of in the concrete subject.

  • The concrete subject uses an array object instead of an SplObjectStorage object to store observer instances.

The observer participants are virtually identical to the SPL ones except that the Subject is an abstract class (instead of an interface) that includes a concrete notify() method. Only slight implementation differences, such as including a $currentState property in the concrete observer, are found.

Unlike the SPL implementation where the Subject participant is an interface and all methods are abstract, this implementation is close to the original by GoF and employs implemented attach/detach methods. The attach/detach methods include an Observer interface as a parameter that is structurally the same as using the SplObserver as a parameter in the SPL example:

<?php
//Subject.php
abstract class Subject
{
    protected $stateNow;
    protected $observers=array();

    public function attachObser(Observer $obser)
    {
        array_push($this->observers,$obser);
    }

    public function detachObser(Observer $obser)
    {
        $position=0;
        foreach($this->observers as $viewer)
        {
            ++$position;
            if($viewer==$obser)
            {
                array_splice($this->observers,($position),1);
            }
        }
    }

    protected function notify()
    {
        foreach($this->observers as $viewer)
        {
            $viewer->update($this);
        }
    }
}
?>

The notify() method is concrete as well, and is inherited and used by the Subject child classes. As a result, the concrete subject does not have to implement attachObser(), detachObser(), or notify(), but it uses the methods without any additional code in the ConcreteSubject:

<?php
//ConcreteSubject.php
class ConcreteSubject extends Subject
{
    public function setState($stateSet)
    {
        $this->stateNow=$stateSet;
        $this->notify();
    }

    public function getState()
    {
        return $this->stateNow;
    }
}
?>

As you can see in the detachObser() method, multiple observers must be notified by iterating through the $observers array. Of course, you can use the SplObjectStorage class even though you’re not using the SPL subject and observer. In that way, you could use a SplObjectStorage object for holding the attached observers instead of an array. In addition, you could also use the attach/detach methods built into the SplObjectStorage class.

Up to this point, a single concrete observer has been used with an observer interface. In this implementation, the Observer pattern uses more than a single observer type. In the most rudimentary way, the multiple concrete observers represent displays that will be shown on a desktop computer, an Internet-connected tablet, and a smartphone. The subject generates a single graphic URL, and the different observers use that data to generate the graphic provided in the same subject state.

The Observer is an interface with a single method, update(). It is virtually identical to the SplObserver:

<?php
//Observer.php
interface Observer
{
    function update(Subject $subject);
}
?>

The abstract method update() awaits the child classes to provide it with a specific implementation. In the following implementations of the concrete observer, the differences are minor but important for development, now and later.

The Client in this context is most likely to come from a source that recognizes a device as one of three types: desktop, tablet, or phone. For the purposes of illustration, the following client calls up all three to illustrate how they would appear in different sizes:

<?php
//Client.php
function __autoload($class_name)
{
    include $class_name . '.php';
}
class Client
{
    public function __construct()
    {
        $sub=new ConcreteSubject();

        $ob1=new ConcreteObserverPhone();
        $ob2=new ConcreteObserverTablet();
        $ob3=new ConcreteObserverDT();

        $sub->attachObser($ob1);
        $sub->attachObser($ob2);
        $sub->attachObser($ob3);
        $sub->setState("decoCar.png");
    }
}
$worker=new Client();
?>

A single concrete subject and three instances of three different concrete observers make up the set of instances. All three observer instances use the same subject source for data. Using the concrete subject’s setState() method, the Client sets the current state that will be used by all observers. Figure 14-5 shows the results in a tablet device.

As the gradations in mobile and desktop devices become finer, a good site may need more sets of bitmap graphics to optimize user experience. Likewise, more CSS files may be required to optimize text viewing on an array of devices. However, by having a single data source and multiple subscribers, keeping track of the current desired state is much easier using a single concrete subject with multiple subscribers.

The “CMS” being built will use a MySQL database to store data used for a simple database. A summary of the design pattern is stored in a MySQL text field, and finally, the URL to a graphic is to be stored in a third field. The table will also include an automatically generated field for a unique numbered record stored as an integer.

The Observer pattern will be used to get the information from the table and send it to selected observers. The single data set stored in an array goes out to observers viewed by a desktop computer, a tablet, or a smartphone. An HTML selection form provides the user with selections of available patterns generated as web pages through PHP. The phone viewing uses jQuery Mobile to optimize viewer experience. The tablet view and desktop view are in two columns, while the phone view is a single column.

To get started, create a MySQL table and a module to enter and update the data for the web pages. First, create the table using the connection utility interface and class that has been employed throughout the book. They are listed first.

<?php
//Filename: IConnectInfo.php
interface IConnectInfo
{
    const HOST ="localhost";
    const UNAME ="uname";
    const PW ="password";
    const DBNAME = "dataBase";

    public function doConnect();
}

?>

The output to alert the user to a successful connection is commented out, since this same connection class is used with other MySQL connections that make up the program:

<?php
include_once('IConnectInfo.php');

class UniversalConnect implements IConnectInfo
{
    private static $server=IConnectInfo::HOST;
    private static $currentDB= IConnectInfo::DBNAME;
    private static $user= IConnectInfo::UNAME;
    private static $pass= IConnectInfo::PW;
    private static $hookup;

    public function doConnect()
    {
        self::$hookup=mysqli_connect(self::$server, self::$user, self::$pass,
                      self::$currentDB);
        if(self::$hookup)
        {
            //comments for debugging
        }
        elseif (mysqli_connect_error(self::$hookup))
        {
            echo('Here is why it failed: '  . mysqli_connect_error());
        }
        return self::$hookup;
    }
}
?

The following code creates the table:

<?php
include_once('UniversalConnect.php');
class CreateTable
{
    private $tableMaster;
    private $hookup;

    public function __construct()
    {
        $this->tableMaster="cms";
        $this->hookup=UniversalConnect::doConnect();

        $drop = "DROP TABLE IF EXISTS $this->tableMaster";

        if($this->hookup->query($drop) === true)
        {
            printf("Old table %s has been dropped.<br/>",$this->tableMaster);
        }

        $sql = "CREATE TABLE $this->tableMaster (
            id           SERIAL,
            dpHeader     NVARCHAR(50),
            textBlock    TEXT,
            imageURL     NVARCHAR(60),
                         PRIMARY KEY (id))";

        if($this->hookup->query($sql) === true)
        {
            printf("Table $this->tableMaster has been created successfully.
                    <br/>");
        }
        $this->hookup->close();
    }
}
$worker=new CreateTable();
?>

Once the table is in place, you will need to add data to it that will be used in the little design pattern web pages.

The CMS table stores the required data used for the web pages. The UI, Admin.html, is a simple HTML document calling up PHP classes to enter and update the text write-ups:

<!doctype html>
<!-- Admin.html -->
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="desktop.css">
<title>CMS Admin Module</title>
</head>
<body>
<h1>CMS Administrative Module</h1>
<h2>Data Entry</h2>
<form action="DataEntry.php" method="post">
  <input type="text" name="dpHeader">
  &nbsp;Design pattern name<br />
  Write up for design pattern:<br />
  <textarea name="textBlock" cols="48" rows="16"></textarea>
  <p />
  <input type="text" name="imageURL">
  &nbsp;Graphic URL
  <p />
  <input type="submit" name="entry" value="Enter Page Data">
</form>
<h2>Data Update</h2>
<form action="DataUpdate.php" method="post">
  <input type="text" name="dpUpdate">
  &nbsp;Design pattern name to update<br />
  New write-up for design pattern:<br />
  <textarea name="newData" cols="48" rows="16"></textarea>
  <p />
  <input type="submit" name="update" value="Update Page Data">
</form>
</body>
</html>

The data input simply enters the appropriate data into the three fields where the header, body text, and image go in each page.

<?php
//DataEntry.php
include_once('UniversalConnect.php');
class DataEntry
{
    private $tableMaster;
    private $hookup;
    private $sql;

    public function __construct()
    {
        $this->tableMaster="cms";
        $this->hookup=UniversalConnect::doConnect();

        if ( $_POST['dpHeader'] )
        $dpHeader=$this->hookup->real_escape_string($_POST['dpHeader']);
        if ( $_POST['textBlock'] )
        $textBlock=$this->hookup->real_escape_string($_POST['textBlock']);
        if ( $_POST['imageURL'] )
        $imageURL=$this->hookup->real_escape_string($_POST['imageURL']);

        $this->sql = "INSERT INTO $this->tableMaster
            (dpHeader,textBlock,imageURL) VALUES
            ('$dpHeader','$textBlock','$imageURL')";

        if($this->hookup->query($this->sql))
        {
            printf("Successful data entry for table: $this->tableMaster <br/>");
        }
        elseif ( ($result = $this->hookup->query($this->sql))===false )
        {
              printf("Invalid query: %s <br/> Whole query: %s <br/>",
                      $this->hookup->error, $this->sql);
              exit();
        }
        $this->hookup->close();
    }
}
$worker=new DataEntry();
?>

The second administrative tool for the CMS is an update for the content write-up about a design pattern. One of the problems users encounter with long, detailed write-ups is space. Even with a single column, the write-ups seem to always need tweaking, and so this tool allows for all the tweaking a web administrator could want.

<?php
//DataUpdate.php
include_once('UniversalConnect.php');
class DataUpdate
{
    private $tableMaster;
    private $hookup;
    private $sql;

    public function __construct()
    {
        $this->tableMaster="cms";
        $this->hookup=UniversalConnect::doConnect();

        if ( isset($_POST['dpUpdate'] ))
        $dpHeader=$this->hookup->real_escape_string($_POST['dpUpdate']);
        if ( $_POST['newData'] )
        $newData=$this->hookup->real_escape_string($_POST['newData']);

        $changeField="textBlock";
        $this->sql = "UPDATE $this->tableMaster SET $changeField='$newData'
                      WHERE dpHeader='$dpHeader'";

        if ($result = $this->hookup->query($this->sql))
        {
            echo "$changeField changed to:<br /> $newData";
        }
        else
        {
            echo "Change failed: " . $hookup->error;
        }
    }
}
$worker=new DataUpdate();
?>

Finally, the CSS for viewers using desktop computers is set for two columns—one for the graphic and one for the write-up—but it works well for the single-column administrative UI as well:

//CSS
@charset "UTF-8";
/* desktop.css */
/* CSS Document */
/* 595241,B8AE9C,FFFFFF,ACCFCC,8A0917 */
body
{
    font-family:Verdana, Geneva, sans-serif;
    background-color:#ffffff;
    color:#595241;
    padding-right:10px;
}

h1
{
    font-family:"Arial Black", Gadget, sans-serif;
    text-align:center;
    color:#8A0917;
    background-color:#ACCFCC;
}

img
{
    padding-right:10px;
    float:left;
}

The CSS file, desktop.css, is also used with requests recognized as nonmobile, so future references should use the same file.

From the previous examples in this chapter, you should be familiar with the basic structure of the Observer pattern. Given that the CMS portion of the plan stores and updates data for web pages, this part of the project retrieves the data and places them on those pages configured for different viewing devices: smartphone, tablet, or desktop. In order to get an overview of the entire project and the role of the Observer design pattern, Figure 14-6 shows a file diagram with all of the different objects and related parts.

The project is divided into three groups: the HTML UI, the PHP Observer pattern, and Helper objects. The rest of this section explains how the parts work together. The following shows the sequence of the object communications:

  1. User requests web page through HTML UI.

  2. Request is forwarded to SniffClient, which determines the viewer device.

  3. Concrete observer instance is created based on viewer device.

  4. Concrete subject instance attaches observer (determined by device).

  5. Current state from MySQL data is set by page selected by user by the ConcreteSubject class.

  6. Subscribing (attached) observers receive data from concrete subject.

  7. The concrete observer takes the state information from the concrete subject and displays page on screen.

Of course, all of the requests are transparent to the user.

In order to avoid typing errors in requesting a page (a design pattern), the design patterns are selected using radio buttons:

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="desktop.css">
<title>Design Pattern Summary Viewer</title>
</head>
<body>
<h1>Select Pattern</h1>
<h2>Available Patterns</h2>
<form action="SniffClient.php" method="post">
  <input type="radio" name="dp" id="tm" value="Template Method"  />
  <label for="tm">Template Method</label>
  <br/>
  <input type="radio" name="dp" id="bld" value="Builder"  />
  <label for="bld">Builder</label>
  <br/>
  <input type="radio" name="dp" id="fm" value="Factory Method"  />
  <label for="fm">Factory Method</label>
  <p/>
  <input type="submit" value="View Pattern">
</form>
</body>
</html>

Figure 14-7 shows the UI.

Because reading the choices and making a selection on a mobile device was difficult using the general UI shown in Figure 14-7, a second UI was developed for mobile devices. Fortunately, a whole set of mobile device formats is available from jQuery Mobile, and they were incorporated into a second UI for phone-size devices:

<!DOCTYPE html>
<html>
<head>
<title>Mobile Viewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.2.0
                             /jquery.mobile-1.2.0.min.css" />
<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
<script src="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.js">
             </script>
</head>
<body>
<div data-role="page">
  <div data-role="header">
    <h1>Design Patterns</h1>
  </div>
  <form action="SniffClient.php" method="post">
    <fieldset data-role="controlgroup">
      <legend>&nbsp;Select Pattern:</legend>
      <input type="radio" name="dp" id="tm" value="Template Method" />
      <label for="tm">Template Method</label>
      <input type="radio" name="dp" id="bld" value="Builder"  />
      <label for="bld">Builder</label>
      <input type="radio" name="dp" id="fm" value="Factory Method"  />
      <label for="fm">Factory Method</label>
    </fieldset>
    <input type="submit" data-theme="e" value="View Pattern">
  </form>
  <div data-role="footer">&nbsp;PHP Patterns</div>
</div>
</body>
</html>

Figure 14-8 shows how the mobile UI appears on an iPhone.

Both UIs work the same. The user selects a pattern to view and then taps a button that generates the page. Unfortunately, the UI is not automatically selected as the viewing pages are.

Because the request is optimized on the basis of the user’s device, the first job of the client is to determine which device is in use. The SniffClient class selects from a small subset of mobile devices with a default value of a desktop device:

<?php
//User agent as property of object
function __autoload($class_name)
{
    include $class_name . '.php';
}
class SniffClient
{
    private $userAgent;
    private $mobile=false;
    private $deviceObserver;
    private $dpNow;
    private $sub;

    public function __construct()
    {
        if (isset($_POST['dp'] ))
            $this->dpNow=$_POST['dp'];
        $this->sub=new ConcreteSubject();
        $this->userAgent=$_SERVER['HTTP_USER_AGENT'];
        if(stripos($this->userAgent,'iphone'))
        {
            $this->mobile=true;
            $this->deviceObserver=new ConcreteObserverPhone();
        }
        if(stripos($this->userAgent,'android'))
        {
            $this->mobile=true;
            $this->deviceObserver=new ConcreteObserverPhone();
        }
        if(stripos($this->userAgent,'blackberry'))
        {
            $this->mobile=true;
            $this->deviceObserver=new ConcreteObserverPhone();
        }
        if(stripos($this->userAgent,'ipad'))
        {
            $this->mobile=true;
            $this->deviceObserver=new ConcreteObserverTablet();
        }
        if(stripos($this->userAgent,'trident'))
        {
            $this->mobile=true;
            $this->deviceObserver=new ConcreteObserverTablet();
        }
        if(stripos($this->userAgent,'kindle fire'))
        {
            $this->mobile=true;
            $this->deviceObserver=new ConcreteObserverTablet();
        }
        if(stripos($this->userAgent,'silk'))
        {
            $this->mobile=true;
            $this->deviceObserver=new ConcreteObserverTablet();
        }

        if(!$this->mobile)
        {
            $this->deviceObserver=new ConcreteObserverDT();
        }
        $this->sub->attachObser($this->deviceObserver);
        $this->sub->setState($this->dpNow);
    }
}

$worker=new SniffClient();

?>

The client is simple and could be cast as a Chain of Responsibility pattern with new handlers added as needed. However, to show how an Observer pattern can be used with a simple CMS and device-sensitive functionality, this client demonstrates the important capabilities.

The interface for this Observer implementation uses an abstract class but with concrete methods for attaching and detaching observers. Likewise, the notify() method is fully implemented and will be used by the ConcreteSubject class:

<?php
//Subject.php
abstract class Subject
{
    protected $observers=array();

    public function attachObser(Observer $obser)
    {
        array_push($this->observers,$obser);
    }

    public function detachObser(Observer $obser)
    {
        $position=0;
        foreach($this->observers as $viewer)
        {
            ++$position;
            if($viewer==$obser)
            {
                array_splice($this->observers,($position),1);
            }
        }
    }

    protected function notify()
    {
        foreach($this->observers as $viewer)
        {
            $viewer->update($this);
        }
    }
}
?>

The ConcreteSubject class in this application connects to the cms table to get the data required to meet the request from the user. The attach/detach methods are the same as used in prior examples, but the setState() method is quite different than in previous examples:

<?php
//ConcreteSubject.php
class ConcreteSubject extends Subject
{
    private $hookup;
    private $tableMaster;
    private $designPattern;
    private $stateSet=array();

    public function setState($dpNow)
    {
        $this->designPattern=strtolower($dpNow);
        $this->tableMaster="cms";
        $this->hookup=UniversalConnect::doConnect();

        //Create Query Statement
        $sql = "SELECT * FROM $this->tableMaster WHERE dpheader=
                '$this->designPattern'";
        //Add appropriate data from MySQL table to $stateSet array
        if ($result = $this->hookup->query($sql))
        {
            while($row=$result->fetch_assoc())
            {
                    $this->stateSet[0]=$row["dpHeader"];
                    $this->stateSet[1]=$row["textBlock"];
                    $this->stateSet[2]=$row["imageURL"];
            }
            $result->close();
        }
    $this->hookup->close();
    //The update() method is part of notify()
    //implemented in Subject as concrete method.
    $this->notify();
    }

    public function getState()
    {
        return $this->stateSet;
    }
}
?>

All of the required data is placed into an array ($stateSet) and then made available through the getState() method to attached observers.

The Observer interface is unchanged from previous examples:

<?php
//Observer.php
interface Observer
{
    function update(Subject $subject);
}
?>

The update() method expects a Subject instance as an argument, but otherwise no other methods are declared.

The implementation of the Observer interface is another story. Each of the concrete observers uses the same data from the subject, but they create very different pages.

The greatest problem with generating pages for mobile phones is the size of the text and graphics. Using CSS and JavaScript, it is possible to create dynamically sized text and buttons that have a viable phone web page. However, by using jQuery Mobile, a lot of the work has been done. So the ConcreteObserverPhone class uses the prebuilt jQuery Mobile for phone formatting, as shown in the following:

<?php
//ConcreteObserverPhone.php
class ConcreteObserverPhone implements Observer
{
    private $currentState=array();
    private $dpHeader;
    private $bodytext;
    private $imageURL;

    public function update(Subject $subject)
    {
        $this->currentState=$subject->getState();
        $this->dpHeader=$this->currentState[0];
        $this->bodytext=$this->currentState[1];
        $this->imageURL=$this->currentState[2];
        $this->doMobile();
    }

    private function doMobile()
    {
$showPage = <<<MOBILE
    <html>
        <head>
            <title>Mobile Page</title>
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <link rel="stylesheet" href="http://code.jquery.com/mobile/1.2.0/
                                         jquery.mobile-1.2.0.min.css" />
            <script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
            <script src="http://code.jquery.com/mobile/1.2.0/
                         jquery.mobile-1.2.0.min.js"></script>
        </head>
    <body>

        <div data-role="page">
        <div data-role="header">
            <h1>$this->dpHeader</h1>
        </div>

        <div data-role="content">
        <p>$this->bodytext</p>
        <img src="mobile/$this->imageURL" alt="image urls" width="100%">
        </div>
        </div>

    </body>
    </html>
MOBILE;
    echo $showPage;
    }
}
?>

Figure 14-9 shows a page formatted for a mobile phone in an iPhone. As you can see, the text is sufficiently large to easily read without having to adjust the size.

A useful tool for formatting any HTML page within a PHP file uses the heredoc (here document) formatting. Basically, the code within a heredoc container accepts all formatting, including double and single quotation marks and PHP variables. It has the following format:

$stringVar = <<<CATCHER
<html>
  <body>
   <h1>Header</h1>
   Text that has dangerous "quotes" and outrageous ideas, even $variables.
 </body>
</html>
CATCHER;
echo $stringVar;

The heredoc <<< operator is used only at the beginning of the container. The $stringVar is assigned the literals within the CATCHER container, making it very easy to dynamically generate code that contains a lot of text or code.

At one time, the only view of a web page was from a desktop or laptop computer. However, it is now one view of many. The following code for the ConcreteObserverDT is similar to the previous first two:

<?php
//ConcreteObserverDT.php
class ConcreteObserverDT implements Observer
{
    private $currentState=array();
    private $dpHeader;
    private $bodytext;
    private $imageURL;

    public function update(Subject $subject)
    {
        $this->currentState=$subject->getState();
        $this->dpHeader=$this->currentState[0];
        $this->bodytext=$this->currentState[1];
        $this->imageURL=$this->currentState[2];
        $this->doDesktop();
    }

    private function doDesktop()
    {
$showPage = <<<DESKTOP
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <link rel="stylesheet" type="text/css" href="desktop.css">
            <title>Desk Top Page</title>
        </head>
    <body>
    <article>
        <header>
            <h1>$this->dpHeader</h1>
        </header>

        <section>
            <img src="desktop/$this->imageURL" alt="image urls" >
            <p>$this->bodytext</p>

        </section>
        </article>
    </body>
    </html>
DESKTOP;
    echo $showPage;
    }
}
?>

The desktop view is very similar to the tablet view, as can be seen in Figure 14-11.

The graphics for the three different device ranges are different as well. The desktop graphics are all configured in a 500 × 500 pixel frame; the table graphics all have a dark tan background; and the phone graphics are set for single-column viewing (reducing the need for added space, or gutters, between columns). However, all can be displayed from the single set of data provided by the Subject. Observers call different graphics with the same names stored in folders (directories) with uniquely named folders—mobile, tablet, and desktop.

One of the special features of the Observer design pattern in PHP lies in the built-in Standard PHP Library (SPL) interfaces designed specifically for the Observer pattern—SplSubject and SplObserver. In no small way, these library interfaces show unequivocal support for design patterns and OOP in the PHP community. At the same time, though, they do not insist that one must incorporate library supplied interfaces when developing an Observer pattern; you can use an abstract class instead of an interface for a Subject participant, as was done in the CMS example.

However, like a lot of the built-in features in PHP 5, many of them go unused or are used outside of an OOP framework or mindset. A lot of the interplay between HTML and PHP in the same file is often done outside of an OOP design, but as was seen using the heredoc operator and formatting, HTML can easily be incorporated into a PHP object.

An important lesson about the Observer pattern used in this last example is that it can be changed and improved. As noted, a Chain of Responsibility pattern could be used for the SniffClient to determine the type of device in use. OOP and design patterns were developed specifically for improving the development process. By thinking in terms of loosely coupled objects carrying out specific tasks and communicating with one another, the development process is much faster and easier to reuse. In the OOP learning process, design patterns are guides to how objects can be arranged to communicate with one another to solve different types of problems.

A further improvement to the CMS example involves adding buttons to the selection menus as new design patterns are added through the CMS. In the current form, it’s easy to add new design patterns—a header, a graphic, and a write-up. Each new pattern does not generate a new radio button in the menu to make the selection. The menu has to be rewritten with each new addition. How would one go about adding modules to the current CMS to update the menus as well as the content web pages that the menu document links to? Think in terms of what objects to create and how to communicate with or alter existing objects. Loose coupling should make that task less onerous.

Intermediate and advanced programmers would not hesitate to use a loop structure in PHP to complete a programming task that requires recurrent operations. However, like a design pattern, a loop structure is nothing more than a general solution to a common programming task. Design patterns are the same thing: general solutions to programming tasks. Yes, they are more advanced, but so are more familiar structures such as a loop or a function in comparison to a strict sequence structure. Once you get used to OOP and design patterns, you may wonder how you programmed for so long without them, just as you see the inevitable necessity of loops to ease the task of programming.