HomePHP Page 2 - Enforcing Object Types in PHP: Using the PHP5 instanceof Operator
Checking object types in PHP 5: what you shouldn’t do - PHP
This is the second installment of the series “Enforcing object types in PHP.” Welcome back. As you’ll surely know, this three-part series goes through the basic concepts of object type enforcement in PHP 4/PHP 5. It explores different approaches for checking types of objects to help you avoid possible code contamination when objects of incorrect type are inputted within PHP classes.
In order to demonstrate how you can force the type of your objects in PHP 5, I’ll use the set of (X)HTML widget classes that I showed you in the first article, in conjunction with a simple page generator class, in this case modified to work in a PHP 5 development environment.
My first example lists some (X)HTML widget classes derived from an abstract “HTMLElement” base class, which skip checking the type of input objects passed to them. Please have a look at the classes below:
// define abstract class 'HTMLElement' abstract class HTMLElement{ protected $attributes; protected function __construct($attributes){ if(!is_array($attributes)){ throw new Exception('Invalid attribute type'); } $this->attributes=$attributes; } // abstract 'getHTML()' method abstract protected function getHTML(); } // define concrete class 'Div' - extends HTMLElement class Div extends HTMLElement{ private $output='<div '; private $data; public function __construct($attributes=array(),$data){ parent::__construct($attributes); $this->data=$data; } // concrete implementation for 'getHTML()' method public function getHTML(){ foreach($this->attributes as $attribute=>$value){ $this->output.=$attribute.'="'.$value.'" '; } $this->output=substr_replace($this->output,'>',-1); $this->output.=$this->data.'</div>'; return $this->output; } } // define concrete class 'Header1' - extends HTMLElement class Header1 extends HTMLElement{ private $output='<h1 '; private $data; public function __construct($attributes=array(),$data){ parent::__construct($attributes); $this->data=$data; } // concrete implementation for 'getHTML()' method public function getHTML(){ foreach($this->attributes as $attribute=>$value){ $this->output.=$attribute.'="'.$value.'" '; } $this->output=substr_replace($this->output,'>',-1); $this->output.=$this->data.'</h1>'; return $this->output; } } // define concrete class 'Paragraph' - extends HTMLElement class Paragraph extends HTMLElement{ private $output='<p '; private $data; public function __construct($attributes=array(),$data){ parent::__construct($attributes); $this->data=$data; } // concrete implementation for 'getHTML()' method public function getHTML(){ foreach($this->attributes as $attribute=>$value){ $this->output.=$attribute.'="'.$value.'" '; } $this->output=substr_replace($this->output,'>',-1); $this->output.=$this->data.'</p>'; return $this->output; } } // define concrete class 'UnorderedList' - extends HTMLElement class UnorderedList extends HTMLElement{ private $output='<ul '; private $items=array(); public function __construct($attributes=array(),$items=array()){ parent::__construct($attributes); if(!is_array($items)){ throw new Exception('Invalid parameter for list items'); } $this->items=$items; } // concrete implementation for 'getHTML()' method public function getHTML(){ foreach($this->attributes as $attribute=>$value){ $this->output.=$attribute.'="'.$value.'" '; } $this->output=substr_replace($this->output,'>',-1); foreach($this->items as $item){ $this->output.='<li>'.$item.'</li>'; } $this->output.='</ul>'; return $this->output; } }
As you can see, the above (X)HTML widget classes are quite useful for generating specific elements of a web page, but I purposely wrote the code of each class in such a way that they aren’t capable of verifying the validity of input parameters. As you probably realize, incoming arguments are directly passed to class constructors and assigned as properties. The question that comes up is: is there anything wrong with doing so? Yes, there is. Now, I’ll define my simple page generator class, and feed it with some of these widgets, so you can see how this class’ input is messed up with incorrect objects. Here’s the signature for the page generator class:
class PageGenerator{ private $output=''; private $title; public function __construct($title='Default Page'){ $this->title=$title; } public function doHeader(){ $this->output='<html><head><title>'.$this- >title.'</title></head><body>'; } public function addHTMLElement($htmlElement){ $this->output.=$htmlElement->getHTML(); } public function doFooter(){ $this->output.='</body></html>'; } public function fetchHTML(){ return $this->output; } }
Now, it’s time to instantiate a few (X)HTML widget objects, and pass them to the corresponding generator class, as shown in the example below:
try{ // spawn some HTML elements $h1=new Header1(array ('name'=>'header1','class'=>'headerclass'),'Content for H1 element goes here'); $div=new Div(array ('name'=>'div1','class'=>'divclass'),'Content for Div element goes here'); $par=new Paragraph(array ('name'=>'par1','class'=>'parclass'),'Content for Paragraph element goes here'); $ul=new UnorderedList(array ('name'=>'list1','class'=>'listclass'),array ('item1'=>'value1','item2'=>'value2','item3'=>'value3')); // instantiate page generator class $pageGen=new PageGenerator(); $pageGen->doHeader(); // add 'HTMLElement' objects $pageGen->addHTMLElement($h1); $pageGen->addHTMLElement($div); $pageGen->addHTMLElement($par); $pageGen->addHTMLElement($ul); $pageGen->doFooter(); // display web page echo $pageGen->fetchHTML(); } catch(Exception $e){ echo $e->getMessage(); exit(); }
After running the above PHP code, what you obtain as a result is a simple web page that includes some of the (X)HTML objects created before. In this situation, it’s easy to understand what would happen if, for some reason, the web page generator class received an incorrect object when its “addHTML()” method is called. Here, I recreate this conflictive condition by using a non-existing (X)HTML widget object. Again, take a look at the sample code below:
try{ // spawn some HTML elements $h1=new Header1(array ('name'=>'header1','class'=>'headerclass'),'Content for H1 element goes here'); $div=new Div(array ('name'=>'div1','class'=>'divclass'),'Content for Div element goes here'); $par=new Paragraph(array ('name'=>'par1','class'=>'parclass'),'Content for Paragraph element goes here'); $ul=new UnorderedList(array ('name'=>'list1','class'=>'listclass'),array ('item1'=>'value1','item2'=>'value2','item3'=>'value3')); // instantiate page generator class $pageGen=new PageGenerator(); $pageGen->doHeader(); // add 'HTMLElement' objects $pageGen->addHTMLElement($fakeobj) //pass non-existing object to this method $pageGen->addHTMLElement($div); $pageGen->addHTMLElement($par); $pageGen->addHTMLElement($ul); $pageGen->doFooter(); // display web page echo $pageGen->fetchHTML(); } catch(Exception $e){ echo $e->getMessage(); exit(); }
In this case, as the following line shows:
$pageGen->addHTMLElement($fakeobj) //pass non-existing object to this method
a non-existing (X)HTML widget object is passed to the page generator class, which raises a fatal error:
Fatal error: Call to a member function on a non-object in path/to/file
Right, that’s a direct punishment for not checking the type of objects being passed to the generator class, so be aware of this problem when coding your scripts. Luckily, there’s a simple solution for solving these issues, and here’s where the “instanceof” operator comes in very handy. If you want to see how this operator will be used, go ahead and read the next section.