//定义抽象类'HTMLElement' abstract class HTMLElement{ protected $attributes; protected function __construct($attributes){ if(!is_array($attributes)){ throw new Exception('Invalid attribute type'); } $this->attributes=$attributes; } // 抽象的'getHTML()'方法 abstract protected function getHTML(); } //定义具体的类'Div'-扩展HTMLElement class Div extends HTMLElement{ private $output='<div '; private $data; public function __construct($attributes=array(),$data){ parent::__construct($attributes); $this->data=$data; } //'getHTML()'方法的具体实现 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; } } //定义具体类'Header1'-扩展HTMLElement class Header1 extends HTMLElement{ private $output='<h1 '; private $data; public function __construct($attributes=array(),$data){ parent::__construct($attributes); $this->data=$data; } //'getHTML()'方法的具体的实现 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; } } //定义具体类'Paragraph'-扩展HTMLElement class Paragraph extends HTMLElement{ private $output='<p '; private $data; public function __construct($attributes=array(),$data){ parent::__construct($attributes); $this->data=$data; } //'getHTML()'方法的具体实现 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; } } //定义具体类'UnorderedList'-扩展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; } //'getHTML()'方法的具体实现 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; } } 如你所见,上面的(X)HTML widget类在生成一个网面中特定的元素时是非常有用的,但是我有意地把每一个类的代码写成这样,这样它们就不能够验证输入参数的有效性。你可能已经想到,输入参数将直接被传递到类构造器中并且作为属性赋值。问题出现了:这样做有什么错误吗?是的,有。现在,我将定义我的最简单的页面生成器类,并且用这样一些widget来填充(feed)它,这样你就可以看到这个类的输入是如何与不正确的对象相混杂。下面是该页面生成器类的签名:
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; } } 现在,我们开始实例化一些(X)HTML widget对象,并且把它们传递到相应的生成器类,如下面的示例所示:
try{ //生成一些HTML元素 $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')); //实例化页面生成器类 $pageGen=new Page生成器(); $pageGen->doHeader(); // 添加'HTMLElement'对象 $pageGen->addHTMLElement($h1); $pageGen->addHTMLElement($div); $pageGen->addHTMLElement($par); $pageGen->addHTMLElement($ul); $pageGen->doFooter(); //显示网面 echo $pageGen->fetchHTML(); } catch(Exception $e){ echo $e->getMessage(); exit(); } 在运行上面的PHP代码后,你所得到的结果是一个简单的网页-它包含一些前面创建的(X)HTML对象。这种情况下,如果因某些原因该网页生成器类收到一个不正确的对象并调用它的"addHTML()"方法,那么你很容易理解将会发生的事情。在此,我重新修改了这里的冲突条件-通过使用一个不存在的(X)HTML widget对象。请再次看一下下面的代码:
try{ //生成一些HTML元素 $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')); //实例化页面生成器类 $pageGen=new Page生成器(); $pageGen->doHeader(); //添加'HTMLElement'对象 $pageGen->addHTMLElement($fakeobj) //把并不存在的对象传递 到这个方法 $pageGen->addHTMLElement($div); $pageGen->addHTMLElement($par); $pageGen->addHTMLElement($ul); $pageGen->doFooter(); // 显示网面 echo $pageGen->fetchHTML(); } catch(Exception $e){ echo $e->getMessage(); exit(); } 在这种情况中,如下面一行所显示的:
Fatal error: Call to a member function on a non-object in path/to/file 怎么样?这就是对传递到生成器类的对象的类型不进行检查的直接惩罚!因此在编写你的脚本时一定要记住这个问题。幸好,还有一个简单的方案来解决这些问题,而且这也正是"instanceof"操作符的威力所在。如果你想要看一下这个操作符是如何使用的,请继续往下读吧。