Inheritance and Polymorphism in PHP: Building a Form Generator – Part III

In the last part of this three-part series of articles, we will find out how to improve the form generator we created in part II by learning about Polymorphism and adding it to the mix.

Introduction

Welcome to Part III of the series “Inheritance and Polymorphism in PHP: Building a Form Generator.” I hope that you had read the previous articles, Part I and Part II, so you have a good understanding of the overall concepts explained during the development of these sections.

If you haven’t read the previous articles, let’s take a quick look at what we’ve done for now. In order to try out some of the most powerful foundations of OOP (Object Oriented Programming), we’ve faced the task of applying Inheritance and Polymorphism in PHP, by taking a rather practical approach and using these pillars to create a specific project: an extensible Form Generator.

To begin with, we defined a base class named “formObject”, which is the super class from which we derived all of the necessary subclasses to generate each form element, using the power of Inheritance in PHP. Once we created the proper subclasses, all we needed to do to create our working Form Generator is instantiate some objects from the subclasses — and that’s it, we have an extensible form generator!

Well, not so fast. If we remind ourselves of the approach taken in the previous article, we’ll have to admit that using a “switch” statement to determine what type of form object we’re dealing with, is extremely inefficient. Our goal is to try to fix up that method and implement a much better solution. So, what’s our next route to presenting a decent approximation? The answer is Polymorphism. Sounds like another buzz word, but it’s really good to learn how it can be implemented in our Web projects.

{mospagebreak title=Turning back time: that old inefficient script}

Let’s take a quick look at the previous approach presented to implement our form generator, as it was initially conceived, so that we can reorder the concepts and try to fix it up. Here’s the original code:

// include required class file

require_once(‘formclasses.php’);

// instantiate derived inputText objects from super class formObject

$textField1=&new inputTextObject(‘First Name’,'firstname’,'textinput’,”,30);

$textField2=&new inputTextObject(‘Last Name’,'lastname’,'textinput’,”,30);

// instantiate derived checkbox object from super class formObject

$checkboxField=&new checkBoxObject(‘I want to receive the weekly newsletter.’,'newsletter’,'checkbox’,'newsletter’);

// instantiate derived submit object from super class formObject

$submitButton=&new submitObject(”,’send’,'submitbutton’,'Send Data’);

// make objects array

$objects=array($textField1,$textField2,$checkboxField,$submitButton);

// generate form determining what type of object is treated

foreach($objects as $object){

switch (get_class($object)){

case “inputtextobject”:

echo $object->label.’<input name=”‘.$object->name.’” type=”text” maxlength=”‘.$object->maxlength.’” /><br />’;

break;

case “checkboxobject”:

echo ‘<input name=”‘.$object->name.’” type=”checkbox” value=”‘.$object->value.’” />’.$object->label.’<br />’;

break;

case “radiobuttonobject”:

echo ‘<input name=”‘.$object->name.’” type=”radio” value=”‘.$object->value.’” />’.$object->label.’<br />’;

break;

case “submitobject”:

echo $object->label.’<input name=”‘.$object->name.’” type=”submit” value=”‘.$object->value.’” /><br />’;

break;

default:

break;

}

}

As you can see, the above method generates the HTML form in a very inefficient way, since is employing a “switch” statement to evaluate what type of form element is being processed, and displaying the corresponding HTML markup. At the risk of being repetitive, there are some key issues that need to be explained here. First, we’re accessing properties of the form elements directly. That’s definitely a bad coding practice. Always use the interface of the object to access its properties. Doing so, we’re applying correctly the other pillar of OOP: Encapsulation.

Even more, since PHP 4 doesn’t offer a method to declare class properties to be either public or private, the PHP parser won’t complain at all, since each property is by default publicly accessible. However, it’s not recommended to access properties directly. In PHP 5, where you can declare properties and methods as being public or private (a definitively desirable improvement), any properties or methods defined as private but accessed directly will trigger an error.

The second issue to be addressed is maintenance. If we need to add one or more form elements to the script, the updating process is really painful and completely impractical. Just imagine adding a “case” statement for each form element added. It’s even worse if we need to implement many forms on our website, as usually happens in several projects. That will be a real updating nightmare. Definitely, this approach doesn’t require any further analysis.

By this point, we need to find another solution for implementing the form generator in an efficient way. However, all is not lost. This article’s title references Polymorphism in PHP, right? Let’s have a look at its core definition and practical implementation. Trust me, it’s really fun!

{mospagebreak title=Polymorphism: the core definition}

Without a doubt, Polymorphism is extremely useful in strongly typed languages such as Java or C++. But in a weakly typed language such as PHP, where the interpreter doesn’t care about the types of the objects manipulated, this technique is not going to have a huge impact when used.

However, taking a practical approach, the concept may have more sense. But, first, what is Polymorphism? Well, basically it means that different classes can have different behaviors for the same operation. In other words, a single class, method or variable may present itself in many different forms.

Certainly this is very interesting, but if we think about it, because of PHP’s nature, this is happening automatically most of the time, since there is not a native concept of strong typing. Does this sound a bit confusing? To demonstrate a practical use, let’s work with the example listed in the first part of this series, which defines a simple class to display a message:

class basicMessage {

var $message;

function basicMessage($message){

$this->message=$message;

}

function displayMessage(){

echo $this->message.’ from base class<br />’.”n”;

}

}

The base class has a single $message property, which is directly displayed in the browser using the “displayMessage()” method. So far, this is fairly understandable. Now, let’s define a couple of subclasses derived from the base class, in the following manner:

class derivedMessageOne extends basicMessage {

function derivedMessageOne($message){

parent::basicMessage($message);

}

//override base class displayMessage() method

function displayMessage(){

echo $this->message.’ from derivedMessageOne subclass<br />’.”n”;

}

}

class derivedMessageTwo extends basicMessage {

function derivedMessageTwo($message){

parent::basicMessage($message);

}

//override base class displayMessage() method

function displayMessage(){

echo $this->message.’ from derivedMessageTwo subclass<br />’.”n”;

}

}

Once we’ve created the two subclasses, let’s instantiate some objects to see Polymorphism in action:

$messages=array($basicMessage=&new basicMessage(‘Hello’),$derivedMessageOne=&new derivedMessageOne(‘Hello’),$derivedMessageTwo=&new derivedMessageTwo(‘Hello’));

foreach($messages as $message){

$message->displayMessage();

}

The output for the above snippet would look like this:

Hello from base class
Hello from derivedMessageOne subclass
Hello from derivedMessageTwo subclass

We simply instantiated an object from the base class $basicMessage, and two objects from the derived subclasses, $derivedMessageOne and $derivedMessageTwo, respectively. Then, we created an array with the objects, iterating over them and calling in turn its “displayMessage()” method. As you can see, it’s nothing special. However, there is something really interesting happening here. Did you notice that each time the “displayMessage()” is invoked inside the loop, the output is different. Isn’t this funny?

Here’s where Polymorphism comes in. We can say that the subclasses “derivedMessageOne” and “derivedMessageTwo” are derived from the base class, establishing a “is a” relationship with “basicMessage”, but they behave differently when their “displayMessage()” method is called. Thus, we have a class, which is behaving differently for the same method call. So, you’re probably wondering: where’s the big deal in that?

Maybe this is not going to change your big programming plans, but the concept has an inherent application. What if we define a base class to encapsulate generic code, and then, according to our needs, derive any number of subclasses, which expose specific properties and methods? It’s definitely an efficient and elegant approach to implementing some applications.

At this point, with a little bit of theory behind us, we have a good approach for implementing our form generator, turning it in a true polimorphic object. So, let’s present the new version of the form generator.

{mospagebreak title=Implementing the Form Generator: take two}

With the pillars of Inheritance and Polymorphism playing in our favor, it’s time to define the final version of our so-called form generator. Since each form element subclass derived from the base class exposes a specific “generateHTML()” method, the process for implementing Polymorphism is fairly straightforward. All that we need to do is get rid of any “switch” statements and take advantage of the capabilities present in the subclasses. Doing so, our revamped form generator should be coded in the following way:

// include required class file

require_once(‘formclasses.php’);

// instantiate derived inputText objects from super class formObject

$textField1=&new inputTextObject(‘First Name’,'firstname’,'textinput’,”,30);

$textField2=&new inputTextObject(‘Last Name’,'lastname’,'textinput’,”,30);

$textField3=&new inputTextObject(‘Email’,'email’,'textinput’,'Enter your@email’,30);

// instantiate derived checkbox object from super clas formObject

$checkBox1=&new checkBoxObject(‘I want to receive the weekly newsletter.’,'newsletter’,'checkbox’,'newsletter’);

// instantiate derived selectbox object from super class formObject

$selectBox1=&new selectObject(‘Occupation’,'occupation’,'selectbox’,1,array(‘Web Programmer’=>’programmer’,'Web Designer’=>’webdesigner’,'Graphic Designer’=>’graphicdesigner’));

// instantiate derived text area object

$textArea=&new textAreaObject(‘Comments’,'comment’,”,’Enter comments here’);

$submitButton=&new submitObject(”,’send’,'submitbutton’,'Send Data’);

// instantiate derived submit object from super class formObject

// make objects array

$objects=array($textField1,$textField2,$textField3,$checkBox1,
$selectBox1,$textArea,$submitButton);

// generate form using polymorphism

foreach($objects as $object){

echo $object->generateHTML().’<br /><br />’;

}

Whew! The code definitely looks a lot better. We simply instantiated a set of three of text input objects, a checkbox, and a select menu. Then we added a text area and, finally, a regular submit button.

With all of the form objects properly defined, we’ve created an array structure and looped through the objects, just invoking the “generateHTML()” method in each iteration. In this way we generated the complete HTML form. Simple enough, eh?

As you can see, this method is by far more efficient and maintainable. With a few changes on the base class, all of the subclasses would automatically inherit the new properties or methods. In a similar way, if we need to add another form element to the form generator, just adding the corresponding subclass, we get the job done nicely.

And that’s all for now…oh, wait a minute! A few final disclaimers are needed. Taking a deeper look at the code for each form object subclass, we clearly see that they offer limited possibilities for defining the overall layout for different form elements. However, this could be changed, easily adding an ID property to tie a style, or redefining the “generateHTML()” method, for major layout flexibility. I’ve shown a basic form layout. Now you can take the code, reuse it, expand it, or create your own version of it. The possibilities are vast.

My final observation points to application performance. Since we’re working possibly with numerous objects here, in slow systems this might cause an extra overload for the Web server. Having said that, we’re finally done. For those who want to include this solution in Web applications, we’ll list the complete source code, including the base class and its subclasses.

{mospagebreak title=Listing full source code}

As stated previously, here’s the full code for the form generator:

// base class formObject

class formObject {

var $label;

var $name;

var $style;

function formObject($label,$name,$style){

(!is_numeric($label))?$this->label=$label:die(‘Invalid parameter ‘.$label);

(!is_numeric($name))?$this->name=$name:die(‘Invalid
parameter ‘.$name);

(!is_numeric($style))?$this->style=$style:die(‘Invalid
parameter ‘.$style);

}

function generateHTML(){

echo ‘You are not overriding the generateHTML() method
of the formObject super class!’;

}

}

// derived class inputTextObject

class inputTextObject extends formObject {

var $value;

var $maxlength;

function inputTextObject
($label,$name,$style,$value,$maxlength){

parent::formObject($label,$name,$style);

(!is_numeric($value))?$this->value=$value:die(‘Invalid
parameter ‘.$value);

(is_int($maxlength)&&$maxlength>2&&$maxlenght<24)?
$this->maxlength=$maxlength:die(‘Invalid parameter
‘.$maxlength);

}

function generateHTML(){

$html=$this->label.’<input type=”text” name=”‘.$this-
>name.’” value=”‘.$this->value.’” class=”‘.$this-
>style.’” maxlength=”‘.$this->maxlength.’” />’;

return $html;

}

}

// derived class hiddenFieldObject

class hiddenFieldObject extends formObject {

var $value;

function hiddenFieldObject($label,$name,$style,$value){

parent::formObject($label,$name,$style);

(!is_numeric($value))?$this->value=$value:die(‘Invalid
parameter ‘.$value);

}

function generateHTML(){

$html=’<input type=”hidden” name=”‘.$this->name.’”
value=”‘.$this->value.’” class=”‘.$this->style.’” />’;

return $html;

}

}

// derived class radioButtonObject

class radioButtonObject extends formObject {

var $value;

var $checked;

function radioButtonObject
($label,$name,$style,$value,$checked=”){

parent::formObject($label,$name,$style);

(!is_numeric($value))?$this->value=$value:die(‘Invalid
parameter ‘.$value);

($checked==’checked’||$checked==”)?$this-
>checked=$checked:die(‘Invalid parameter ‘.$checked);

}

function generateHTML(){

$html=’<input type=”radio” name=”‘.$this->name.’”
value=”‘.$this->value.’” class=”‘.$this->style.’”‘;

($this->checked==’checked’)?$html.=’ checked=”‘.$this-
>checked.’” />’.$this->label:$html.=’ />’.$this->label;

return $html;

}

}

// derived class checkBoxObject

class checkBoxObject extends formObject {

var $value;

var $checked;

function checkBoxObject
($label,$name,$style,$value,$checked=”){

parent::formObject($label,$name,$style);

(!is_numeric($value))?$this->value=$value:die(‘Invalid
parameter ‘.$value);

($checked==’checked’||$checked==”)?$this-
>checked=$checked:die(‘Invalid parameter ‘.$checked);

}

function generateHTML(){

$html=’<input type=”checkbox” name=”‘.$this->name.’”
value=”‘.$this->value.’” class=”‘.$this->style.’”‘;

($this->checked==’checked’)?$html.=’ checked=”‘.$this-
>checked.’” />’.$this->label:$html.=’ />’.$this->label;

return $html;

}

}

// derived class selectObject

class selectObject extends formObject {

var $size;

var $options;

function selectObject
($label,$name,$style,$size,$options=array()){

parent::formObject($label,$name,$style);

(is_integer($size)&&$size>0)?$this->size=$size:die
(‘Invalid parameter ‘.$size);

(count($options)>0)?$this->options=$options:die
(‘Invalid parameter ‘.$options);

}

function generateHTML(){

$html=$this->label.’<select name=”‘.$this->name.’”
class=”‘.$this->style.’” size=”‘.$this->size.’”>’;

foreach($this->options as $option=>$value){

$html.=’<option
value=”‘.$value.’”>’.$option.’</option>’;

}

$html.=’</select>’;

return $html;

}

}

// derived class textAreaObject

class textAreaObject extends formObject {

var $value;

var $wrap;

function textAreaObject
($label,$name,$style,$value,$wrap=’virtual’){

parent::formObject($label,$name,$style);

(!is_numeric($value))?$this->value=$value:die(‘Invalid
parameter ‘.$value);

($wrap==’off’||$wrap==’physical’||$wrap==’virtual’)?
$this->wrap=$wrap:die(‘Invalid parameter ‘.$wrap);

}

function generateHTML(){

$html=$this->label.’<textarea name=”‘.$this->name.’”
class=”‘.$this->style.’” wrap=”‘.$this-
>wrap.’”>’.$this->value.’</textarea>’;

return $html;

}

}

// derived class buttonObject

class buttonObject extends formObject {

var $value;

function buttonObject($label,$name,$style,$value){

parent::formObject($label,$name,$style);

(!is_numeric($value))?$this->value=$value:die(‘Invalid
parameter ‘.$value);

}

function generateHTML(){

$html=$this->label.’<input type=”button” name=”‘.$this-
>name.’” value=”‘.$this->value.’” class=”‘.$this-
>style.’” />’;

return $html;

}

}

// derived class passwordObject

class passwordObject extends formObject {

var $maxlength;

function passwordObject($label,$name,$style,$maxlength){

parent::formObject($label,$name,$style);

(is_int($maxlength)&&$maxlength>8&&$maxlenght<24)?
$this->maxlength=$maxlength:die(‘Invalid parameter
‘.$maxlength);

}

function generateHTML(){

$html=$this->label.’<input type=”password”
name=”‘.$this->name.’” value=”‘.$this->value.’”
class=”‘.$this->style.’” />’;

return $html;

}

}

// derived class fileObject

class fileObject extends formObject {

function fileObject($label,$name,$style){

parent::formObject($label,$name,$style);

}

function generateHTML(){

$html=$this->label.’<input type=”file” name=”‘.$this-
>name.’” class=”‘.$this->style.’” />’;

return $html;

}

}

// derived class imageObject

class imageObject extends formObject {

var $value;

var $src;

var $alt;

function imageObject
($label,$name,$style,$value,$src,$alt){

parent::formObject($label,$name,$style);

(!is_numeric($value))?$this->value=$value:die(‘Invalid
parameter ‘.$value);

(!is_numeric($alt))?$this->alt=$alt:die(‘Invalid
parameter ‘.$alt);

(file_exists($src))?$this->src=$src:die(‘Invalid
parameter ‘.$src);

}

function generateHTML(){

$html=$this->label.’<input type=”image” name=”‘.$this-
>name.’” value=”‘.$this->value.’” class=”‘.$this-
>style.’” src=”‘.$this->src.’” alt=”‘.$this-
>alt.’” />’;

return $html;

}

}

// derived class resetObject

class resetObject extends formObject {

var $value;

function resetObject($label,$name,$style,$value){

parent::formObject($label,$name,$style);

(!is_numeric($value))?$this->value=$value:die(‘Invalid
parameter ‘.$value);

}

function generateHTML(){

$html=$this->label.’<input type=”reset” name=”‘.$this-
>name.’” value=”‘.$this->value.’” class=”‘.$this-
>style.’” />’;

return $html;

}

}

// derived class submitObject

class submitObject extends formObject {

var $value;

function submitObject($label,$name,$style,$value){

parent::formObject($label,$name,$style);

(!is_numeric($value))?$this->value=$value:die(‘Invalid
parameter ‘.$value);

}

function generateHTML(){

$html=$this->label.’<input type=”submit” name=”‘.$this-
>name.’” value=”‘.$this->value.’” class=”‘.$this-
>style.’” />’;

return $html;

}

}

Now you have some code to play around with. I hope you have a good time finding possible additions. Just be creative and put your background to work.

Conclusion

Over this three-part series, we’ve learned a bit more about the capabilities of Inheritance and Polymorphism in PHP 4, building a practical example to generate HTML forms using an Object-Oriented approach. Also, the process has introduced some theory concepts that hopefully will help you gain a better understanding of OOP. Without a doubt, the OOP approach is the best way to manage large projects in PHP, due to the pluggable nature of objects. When you have a couple of classes working for you, it’s just a matter of making them interact to rapidly develop Web applications. The learning curve is rather long, but the effort is truly worthwhile.

Google+ Comments

Google+ Comments