The Singleton and Factory Patterns in PHP: Working With Singletons

In this fourth part of the series covering the Singleton and Factory Design Patterns in PHP, we will discuss issues stemming from the fact that PHP 4 does not have an abstract class. Since we found it useful in the previous article to define the form element factory class as an abstract class, in this article we will discuss the process for making the form element factory class a Singleton, and how this serves our purposes.

Introduction

Welcome to the fourth part of the series “The Singleton and Factory Design Patterns in PHP.” Throughout the previous article, I applied the Factory pattern for implementing an object-oriented method, in order to simplify the creation of regular web forms.

As you remember, in the previous part, I developed a simple form element factory class that handles all of the tasks related to object instantiation. It allows you to define a centralized mechanism within an application for rendering form elements. Certainly, implementing this method makes web form generation a flexible and easy-to-maintain process.

Now that I’ve explained the inherent advantages of applying the Factory pattern to tackle the form developing process, probably you’ll have even more ideas for starting to use design patterns across different applications.

So, returning to this part of the series, the form element factory class was defined as an abstract class. The immediate advantage of having an abstract class rests on using its methods without the concerns related to object instantiation. This means that the functionality of the class is available within the program, but no objects exist to worry about.

However, when we’re working with PHP 4, things are a bit more complex. Unfortunately, PHP 4 doesn’t offer support for abstract classes, although there is a workaround. This presents some difficulties, particularly when a single instance of an object is needed. In the specific case of having a class that instantiates form objects, it’s highly desirable to work with a single instance of it, instead of dealing with multiple and resource-consuming instances.

Over the next few lines, I’ll explain the process for making the form element factory class a Singleton. This will help you to easily avoid multiple object instantiation issues, specifically when working on a PHP 4 development platform. Now that we have the theory well under the way, let’s get started.

{mospagebreak title=Working with a single object instance: making the “formElementFactory” class a Singleton}

A good place to start turning the form element factory class into a Singleton is by taking a look at its definition for use in PHP5. As you hopefully remember, the class looks like this:

abstract class formElementFactory{
    private function __construct(){}
    public static function createElement($type,$attributes=array()){
        if(!class_exists($type)||!is_array($attributes)){
            throw new Exception(‘Invalid method parameters’);
        }
        // instantiate a new form element object
        $formElement=new $type($attributes);
        // return form object’s HTML
        return $formElement->getHTML();
    }
}

As I mentioned before, the class is defined as abstract, and presents a single functional method, “createElement()”, which carries out the instantiation of form objects, as well as the implementation of Polymorphism. It does this through the “getHTML()” method, which is useful for returning the proper (X)HTML code for each form element.

A possible use of the class would be the following, assuming that form element classes have already been defined:

// make array with parameters to be passed to the class

$formElements=array(‘textinput’=>array(‘name’=>’username’,’value’=>”,
‘maxlength’=>’20’),
‘passwordinput’=>array(‘name’=>’password’,’value’=>”,
‘maxlength’=>’20’),
‘submitbutton’=>array(‘name’=>’send’,’value’=>’Send’));

foreach($formElements as $element=>$attributes){

    // display form elements

    echo formElementFactory::createElement($element,$attributes).'<br />';

}

Through the above lines of code, we first created a few form objects, and then displayed their HTML code, by using a “foreach” loop. Although basic, the example reveals the strengths of the factory class, since rendering form elements becomes an extremely easy process.

As you can see, working with an abstract class in PHP5 is straightforward. However, when programming in PHP 4, things are a bit more messy. This is particularly true if we’re working with a form factory class, while a single instance of it is needed. Using references may certainly help, due to the fact that we’re not spawning multiple copies of an object. When building large applications, however, tracking the existence of an object can sometimes be a cumbersome task.

There’s a better approach to solving this issue. By applying the Singleton pattern, we can make sure that only a single instance of the factory class is available across the program, and other objects are using this unique instance. Based on this concept, making the class a Singleton is as simple as this:

class formElementFactory{
    function formElementFactory(){}
    function getInstance(){
            static $instance;
            if(!isset($instance)){
                $object= __CLASS__;
                $instance=new $object;
            }
            return $instance;
    }
    function createElement($type,$attributes=array()){
        if(!class_exists($type)||!is_array
($attributes)){
            trigger_error(‘Invalid method
parameters’);
                exit();
        }
        // return a new form element object
        return new $type($attributes);
    }
}

Let’s analyze the Singleton version of the class. Notice the existence of the same “createElement()” method that returns a new form object, according to the type of input passed to it, which is identical to the PHP5 incarnation. Until now, we’ve seen nothing unexpected. However, there’s another relevant method, “getInstance()” that makes sure that only a single instance of the object is always returned.

By declaring a static $instance variable, we’re ensuring that once an instance of the object has been created, no additional instances will exist. Just in case you’re not aware of this: when a static variable is defined as static within a function scope, it won’t lose its value, even if the program has left this scope.

Therefore, because of the static type of the $instance variable, the following expression:

 if(!isset($instance)){
      $object= __CLASS__;
      $instance=new $object;
  }
  return $instance;

always returns a single instance of the object. As a result, a Singleton version of the factory class has been defined. Considering this, whenever a PHP program creates an object belonging to the class, a unique instance will be available within its execution.

Perhaps the implementation of the Singleton pattern is a little harder to understand. Definitely, the concept will be  understood best by taking a look at some examples. Join me in the next explanation to see how a Singleton class can work along with some form element classes.

{mospagebreak title=The previous step: defining form element classes in PHP4}

Before working with the Singleton model of the form element factory, it’s best to define all of the form element classes, suitable for use in PHP4. Since they’re closely similar to their counterparts in PHP5, this shouldn’t present any difficulties. Here they are:

// class textinput
class textinput{
    var $html;
    function textinput($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
exit();
        }
        $this->html='<input type=”text” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    function getHTML(){
        return $this->html;
    }
}
// class password input
class passwordinput{
    var $html;
    function passwordinput($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
exit();
        }
        $this->html='<input type=”text” ‘;
        foreach($attributes as
$attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    function getHTML(){
        return $this->html;
    }
}
// class hidden input
class hiddeninput{
    var $html;
    function textinput($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
exit();
        }
        $this->html='<input type=”hidden” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    function getHTML(){
        return $this->html;
    }
}
// class file input
class fileinput{
    var $html;
    function fileinput($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
exit();
        }
        $this->html='<input type=”file” ‘;
        foreach($attributes as
$attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    function getHTML(){
        return $this->html;
    }
}
// class image input
class imageinput{
    var $html;
    function imageinput($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
exit();
        }
        $this->html='<input type=”image” ‘;
        foreach($attributes as
$attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    function getHTML(){
        return $this->html;
    }
}
// class radio button
class radiobutton{
    var $html;
    function radiobutton($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
            exit();
        }
        $this->html='<input type=”radio” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    function getHTML(){
        return $this->html;
    }
}
// class check box
class checkbox{
    var $html;
    function checkbox($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
exit();
        }
        $this->html='<input type=”checkbox” ‘;
        foreach($attributes as
$attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    function getHTML(){
        return $this->html;
    }
}

 

All right, let’s pause for a moment and relax. At this point, most of the form element classes has been listed, so the walk through the source code is almost finished. There are a few more classes to be defined yet, so keep reading.

{mospagebreak title=Ending up the coding round: defining the remaining form element classes}

Returning to the list of form element classes, there are still some that need to be defined. So, here are the proper definitions for form buttons, submit buttons and reset controls. Also, text areas and select boxes are included:

// class button
class button{
    var $html;
    function button($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
            exit();
        }
        $this->html='<input type=”button” ‘;
        foreach($attributes as
$attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    function getHTML(){
        return $this->html;
    }
}
// class submit button
class submitbutton{
    var $html;
    function submitbutton($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
            exit();
        }
        $this->html='<input type=”submit” ‘;
        foreach($attributes as $attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    function getHTML(){
        return $this->html;
    }
}
// class reset button
class resetbutton{
    var $html;
    function resetbutton($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
            exit();
        }
        $this->html='<input type=”reset     ” ‘;
        foreach($attributes as
$attribute=>$value){
            $this->html.=$attribute.’=”‘.$value.'” ‘;
        }
        $this->html.=’/>';
    }
    function getHTML(){
        return $this->html;
    }
}
// class textarea
class textarea{
    var $html;
    function textarea($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
            exit();
        }
        $this->html='<textarea ‘;
        $textvalue=”;
        foreach($attributes as
$attribute=>$value){
            ($attribute!=’value’)?$this-
>html.=$attribute.’=”‘.$value.'” ‘:$textvalue=$value;
        }
        $this->html=preg_replace(“/”? $/”,””>”,$this->html);
        $this->html.=$textvalue.'</textarea>';
    }
    function getHTML(){
        return $this->html;
    }
}
// class selectbox
class selectbox{
    var $html;
    function selectbox($attributes=array()){
        if(count($attributes)<1){
            trigger_error(‘Invalid number of
attributes’);
            exit();
        }
        $this->html='<select ‘;
        $options=”;
        foreach($attributes as
$attribute=>$value){
            if($attribute!=’options’){
                $this->html.=$attribute.’=”‘.$value.'” ‘;
            }
            else{
                foreach($value as
$values=>$label){
                    $options.='<option
value=”‘.$values.'”>’.$label.'</option>';
                }
            }
        }
        $this->html=preg_replace(“/”?
$/”,””>”,$this->html);
        $this->html.=$options.'</select>';
    }
    function getHTML(){
        return $this->html;
    }
}

With all of the form element classes listed, we can go ahead and set up an example for rendering form elements, by using the Singleton model of the form factory class. So, let’s begin instantiating an object from the Singleton factory class, and next spawn several form objects. The sample code is as follows:

// get a single instance of the form element
factory
$formFactory=formElementFactory::getInstance();
// create form objects
$textinput=$formFactory->createElement
(‘textinput’,array(‘name’=>’username’,’value’=>”,
‘maxlength’=>’20’));
$radiobutton=$formFactory->createElement
(‘radiobutton’,array
(‘name’=>’option1′,’value’=>’1′,’checked’=>
‘true’));
$checkbox=$formFactory->createElement
(‘checkbox’,array
(‘name’=>’option2′,’value’=>’1′,’checked’=>
‘true’));
$textarea=$formFactory->createElement
(‘textarea’,array
(‘name’=>’comments’,’rows’=>’20’,
‘cols’=>’30’));
// make array with form objects
$formElements=array($textinput,$radiobutton,$checkbox,$textarea);
// display form elements
foreach($formElements as $element){
            echo $element->getHTML();
}

As you can see, an object is instantiated from the factory class, by calling first the “getInstance()” method. Doing so, we’re ensuring that a single instance of the object is created across the snippet, thus the Singleton pattern is properly applied.

Then, some form objects are directly created through the “createElement()” method, as we’ve seen in previous examples. The only thing to note here is that the call to this method is carried out within the object context, that is using the (->) syntax, since now the method is not static. Therefore, the scope resolution operator (::) is not used any longer.

Finally, we make an array with the form objects, which are displayed through a common “foreach” loop.

At this point, we’ve applied both the Singleton and Factory patterns to building web-based forms within a PHP 4 controlled environment. Hopefully, a practical application of these patterns in a real scenario helps to demonstrate how a specific problem can be addressed by using a proven solution.

However, the mechanism for factoring form elements exposes some drawbacks, since the form rendering process is still immature. Definitely, greater control of layout and visual presentation is required.

To wrap up

In the next part of the series, I’ll tackle the issues related to visual presentation and layout of form elements. I will apply what we’ve learned until now as a starting point for building an extensible form generator class. Probably, you’ll have some time to test your skills by successfully implementing these popular design patterns in your PHP programs.

[gp-comments width="770" linklove="off" ]

chat