Enforcing Object Types in PHP: Using the PHP5 instanceof Operator - Checking object types in PHP 5: what you shouldn’t do (
Page 2 of 4 )
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.