当前位置: 首页 > 图文教程 > 网络编程 > PHP > PHP强制对象类型之instanceof操作符

PHP
让我们来编写一些PHP实用的脚本
七种缓存使用武器 为网站应用和访问加速
动态网页PHP中引用&的使用注意事项
在PHP中全面阻止SQL注入式攻击
PHP自带可以代替echo调试的unit函数
小结:PHP动态网页程序优化及高效提速问题
php对特殊语句查询结果进行数组排序
实例:用PHP技术解决网站URL格式过长的问题
小结:PHP动态网页程序两个有用的小技巧
动态网页中直接不让访问PHP程序文件
网页实例:详细介绍用PHP来编写网页记数器
菜鸟学习:动态网页PHP基础学习笔记
利用Apache实现禁止图片盗链
PHP编程中常用的三则技巧
PHP制作的网站意见在线反馈表
大型Web需求解决方案 PHP定位突出
PHP实例:精确到每一秒钟的在线人数显示代码
实用:动态网页制作技术PHP的十个应用技巧
常见php页面漏洞分析及相关问题解决
PHP和MYSQL制作动态网站开发经验之谈

PHP强制对象类型之instanceof操作符


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-09-23   浏览: 71 ::
收藏到网摘: n/a

一、简介

在PHP中实现强制对象类型有时可能非常重要。如果缺少了它,或是因为缺乏这方面的知识——基于不正确的编程假设,或者仅仅是由于懒惰,那么你会在特定的Web应用程序中看到你所不希望的结果。特别是当用PHP4进行编程时,使用"is_a()"函数(尽管还有其它方法)来验证你所使用的对象的类型是非常容易的事情。毫无疑问,强制对象类型还可以被用于过滤输入对象(需要被作为参数传递到同一个应用程序中的其它PHP类)。

不过,PHP4并没有暴露一些有关于它的对象模型的弱点-为了实现某些在成熟的面向对象的语言中出现的特征,它偶而可能要求编写另外的代码。长时间以来,这一事实已经为PHP社区众所周知。然而,随着PHP5的发行,许多这些极有价值的特征作为改进的对象模型的一部分被添加到其中。它们将有助于更为紧密地实现基于对象的代码的开发-允许你使用特定的对象特征。

在上面的情况下,当涉及到对象类型强制时应该特别注意。实际上,在一个Web应用程序的执行期间,PHP5提供给开发者至少两种方法来检查对象类型——它们分别是“instanceof”操作符和“类型提示”特征。现在转到本文的主题,我将介绍PHP5中"instanceof"操作符的使用;你很快就会发现,它可以非常方便地用来确定是否你正在使用的对象属于一个特定的类型。

本文将通过一些面向对象的示例来帮助你理解如何在PHP5中实现强制对象类型。

二、你不该做什么

为了展示在PHP5中如何实现对象类型强制,我将使用(X)HTMLwidget类,还有一个简单的页面生成器类,并作了简单的修改以适合PHP5开发环境。

我的第一个示例列举了一些派生自一个抽象的基类"HTMLElement"的(X)HTMLwidget类,它跳过了到它们的输入对象类型的检查。请先看下面的类:

  //定义抽象类'HTMLElement'
abstractclassHTMLElement{
 protected$attributes;
 protectedfunction__construct($attributes){
if(!is_array($attributes)){
 thrownewException('Invalidattributetype');
}
$this->attributes=$attributes;
 }
 //抽象的'getHTML()'方法
 abstractprotectedfunctiongetHTML();
}
//定义具体的类'Div'-扩展HTMLElement
classDivextendsHTMLElement{
 private$output='<div';
 private$data;
 publicfunction__construct($attributes=array(),$data){
parent::__construct($attributes);
$this->data=$data;
 }
 //'getHTML()'方法的具体实现
 publicfunctiongetHTML(){
foreach($this->attributesas$attribute=>$value){
 $this->output.=$attribute.'="'.$value.'"';
}
$this->output=substr_replace($this->output,'>',-1);
$this->output.=$this->data.'</div>';
return$this->output;
 }
}
//定义具体类'Header1'-扩展HTMLElement
classHeader1extendsHTMLElement{
 private$output='<h1';
 private$data;
 publicfunction__construct($attributes=array(),$data){
parent::__construct($attributes);
$this->data=$data;
 }
 //'getHTML()'方法的具体的实现
 publicfunctiongetHTML(){
foreach($this->attributesas$attribute=>$value){
 $this->output.=$attribute.'="'.$value.'"';
}
$this->output=substr_replace($this->output,'>',-1);
$this->output.=$this->data.'</h1>';
return$this->output;
 }
}
//定义具体类'Paragraph'-扩展HTMLElement
classParagraphextendsHTMLElement{
 private$output='<p';
 private$data;
 publicfunction__construct($attributes=array(),$data){
parent::__construct($attributes);
$this->data=$data;
 }
 //'getHTML()'方法的具体实现
 publicfunctiongetHTML(){
foreach($this->attributesas$attribute=>$value){
$this->output.=$attribute.'="'.$value.'"';
 }
 $this->output=substr_replace($this->output,'>',-1);
 $this->output.=$this->data.'</p>';
 return$this->output;
}
}
//定义具体类'UnorderedList'-扩展HTMLElement
classUnorderedListextendsHTMLElement{
 private$output='<ul';
 private$items=array();
 publicfunction__construct($attributes=array(),$items=array()){
parent::__construct($attributes);
if(!is_array($items)){
 thrownewException('Invalidparameterforlistitems');
}
$this->items=$items;
 }
 //'getHTML()'方法的具体实现
 publicfunctiongetHTML(){
foreach($this->attributesas$attribute=>$value){
 $this->output.=$attribute.'="'.$value.'"';
}
$this->output=substr_replace($this->output,'>',-1);
foreach($this->itemsas$item){
 $this->output.='<li>'.$item.'</li>';
} 
$this->output.='</ul>';
return$this->output;
 }
}
如你所见,上面的(X)HTMLwidget类在生成一个网面中特定的元素时是非常有用的,但是我有意地把每一个类的代码写成这样,这样它们就不能够验证输入参数的有效性。你可能已经想到,输入参数将直接被传递到类构造器中并且作为属性赋值。问题出现了:这样做有什么错误吗?是的,有。现在,我将定义我的最简单的页面生成器类,并且用这样一些widget来填充(feed)它,这样你就可以看到这个类的输入是如何与不正确的对象相混杂。下面是该页面生成器类的签名:

  classPageGenerator{
 private$output='';
 private$title;
 publicfunction__construct($title='DefaultPage'){
$this->title=$title;
 }
 publicfunctiondoHeader(){
$this->output='<html><head><title>'.$this-
>title.'</title></head><body>';
 }
 publicfunctionaddHTMLElement($htmlElement){
$this->output.=$htmlElement->getHTML();
 }
 publicfunctiondoFooter(){
$this->output.='</body></html>';
 }
 publicfunctionfetchHTML(){
return$this->output;
 }
}
现在,我们开始实例化一些(X)HTMLwidget对象,并且把它们传递到相应的生成器类,如下面的示例所示:

  try{
 //生成一些HTML元素
 $h1=newHeader1(array('name'=>'header1','class'=>'headerclass'),'ContentforH1
elementgoeshere');
 $div=newDiv(array('name'=>'div1','class'=>'divclass'),'ContentforDivelement
goeshere');
 $par=newParagraph(array('name'=>'par1','class'=>'parclass'),'ContentforParagraph
elementgoeshere');
 $ul=newUnorderedList(array('name'=>'list1','class'=>'listclass'),array
('item1'=>'value1','item2'=>'value2','item3'=>'value3'));
//实例化页面生成器类
 $pageGen=newPage生成器();
 $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)HTMLwidget对象。请再次看一下下面的代码:

  try{
 //生成一些HTML元素
 $h1=newHeader1(array('name'=>'header1','class'=>'headerclass'),'ContentforH1
elementgoeshere');
 $div=newDiv(array('name'=>'div1','class'=>'divclass'),'ContentforDivelement
goeshere');
 $par=newParagraph(array('name'=>'par1','class'=>'parclass'),'ContentforParagraph
elementgoeshere');
 $ul=newUnorderedList(array('name'=>'list1','class'=>'listclass'),array
('item1'=>'value1','item2'=>'value2','item3'=>'value3'));
 //实例化页面生成器类
 $pageGen=newPage生成器();
 $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();
}
在这种情况中,如下面一行所显示的:

  $pageGen->addHTMLElement($fakeobj)//把不存在的对象传递到这个方法
一个并不存在的(X)HTMLwidget对象被传递到该页面生成器类,这样会导致一个致命性错误:

  Fatalerror:Calltoamemberfunctionalign=centerbgColor=#e3e3e3border=1>   if(objectinstanceofclassname){
 //做一些有用的事情
}
现在,既然你已经了解了这个操作符在PHP5是如何使用的,那么,为了验证被传递到它的"addHTMLElement()"方法的对象的类型,让我们再定义相应的网页生成器类。下面是这个类的新的签名,我在前面已经提到,它使用了"instanceof"操作符:

  classPageGenerator{
 private$output='';
 private$title;
 publicfunction__construct($title='DefaultPage'){
$this->title=$title;
 }
 publicfunctiondoHeader(){
$this->output='<html><head><title>'.$this->title.'</title></head><body>';
 }
 publicfunctionaddHTMLElement($htmlElement){
if(!$htmlElementinstanceofHTMLElement){
 thrownewException('Invalid(X)HTMLelement');
}
$this->output.=$htmlElement->getHTML();
 }
 publicfunctiondoFooter(){
$this->output.='</body></html>';
 }
 publicfunctionfetchHTML(){
return$this->output;
 }
}
请注意,在上面的类中,为了确定所有传递的对象是早些时候定义的"HTMLElement"类的实例,"instanceof"操作符是如何包含在"addHTMLElement()"方法中的。现在,有可能重新构建你前面看到的网页,在这种情况下,请确保所有的传递到该网页生成器类的输入对象都是真正的(X)HTMLwidget对象。下面是相应示例:

  try{
 //生成一些HTML元素
 $h1=newHeader1(array('name'=>'header1','class'=>'headerclass'),'ContentforH1elementgoeshere');
 $div=newDiv(array('name'=>'div1','class'=>'divclass'),'ContentforDivelementgoeshere');
 $par=newParagraph(array('name'=>'par1','class'=>'parclass'),'ContentforParagraphelementgoeshere');
 $teststr='ThisisnotaHTMLelement';
 //实例化页面生成器类
 $pageGen=newPage生成器();
 $pageGen->doHeader();
 //添加'HTMLElement'对象
 $pageGen->addHTMLElement($teststr)//把简单的字符串传递到这个方法
 $pageGen->addHTMLElement($h1);
 $pageGen->addHTMLElement($div);
 $pageGen->addHTMLElement($par);
 $pageGen->doFooter();
 //显示网页
 echo$pageGen->fetchHTML();
}
catch(Exception$e){
 echo$e->getMessage();
 exit();
}
正如你在上面的示例已经看到的,我把一个简单的测试用字符串(并不是一个"HTMLElement"对象)传递到该页面生成器类中,这将通过addHTMLElement()"方法抛出一个异常-为特定的"catch"块所捕获,如下所示:

  Invalid(X)HTMLelement


此时,为了确定输入对象的有效性,我使用了"instanceof"操作符,这样以来,可以把上面的网页生成器类转换成一部分更为有效的代码片断。我希望你能真正体会到,通过使用这个操作符,对你的类的方法的输入进行过滤的极端重要性,这样就可以免除外来的不正确的数据输入。

在展示了"instanceof"操作符在网页生成器类内的正确实现后,还有更多的事情要做。类似于我在前面一篇文章中为PHP4所编写的(X)HTMLwidget类,我想包含这个操作符作为它们的"getHTML()"方法的一部分,这样就可以允许创建生成嵌套的(X)HTML元素的网页。下面,让我们讨论这是如何实现的。

 

 

四、扩展"instanceof"操作符的使用:嵌套(X)HTMLwidget

好。你已经看到了"instanceof"操作符在被直接注入到页面生成器类的输入对象进行类型检查方面所表现出的良好功能。现在,我将再进一步来把一个检查例程添加到(X)HTMLwidget类的构造器和"getHTML()"方法中,这样它们可以接受其它的widget作为输入参数。请检查下面改进的类:

  classDivextendsHTMLElement{
 private$output='<div';
 private$data;
 publicfunction__construct($attributes=array(),$data){
if(!$datainstanceofHTMLElement&&!is_string($data)){
 thrownewException('Invalidparametertype');
}
parent::__construct($attributes);
$this->data=$data;
 }
 //'getHTML()'方法的具体实现
 publicfunctiongetHTML(){
foreach($this->attributesas$attribute=>$value){
 $this->output.=$attribute.'="'.$value.'"';
}
$this->output=substr_replace($this->output,'>',-1);
$this->output.=($this->datainstanceofHTMLElement)?
$this->data->getHTML():$this->data;
$this->output.='</div>';
return$this->output;
 }
}
classHeader1extendsHTMLElement{
 private$output='<h1';
 private$data;
 publicfunction__construct($attributes=array(),$data){
if(!$datainstanceofHTMLElement&&!is_string($data)){
 thrownewException('Invalidparametertype');
}
parent::__construct($attributes);
$this->data=$data;
 }
 //'getHTML()'方法的具体实现
 publicfunctiongetHTML(){
foreach($this->attributesas$attribute=>$value){
 $this->output.=$attribute.'="'.$value.'"';
}
$this->output=substr_replace($this->output,'>',-1);
$this->output.=($this->datainstanceofHTMLElement)?
$this->data->getHTML():$this->data;
$this->output.='</h1>';
return$this->output;
 }
}
classParagraphextendsHTMLElement{
 private$output='<p';
 private$data;
 publicfunction__construct($attributes=array(),$data){
if(!$datainstanceofHTMLElement&&!is_string($data)){
 thrownewException('Invalidparametertype');
}
parent::__construct($attributes);
$this->data=$data;
 }
 //'getHTML()'方法的具体实现
 publicfunctiongetHTML(){
foreach($this->attributesas$attribute=>$value){
 $this->output.=$attribute.'="'.$value.'"';
}
$this->output=substr_replace($this->output,'>',-1);
$this->output.=($this->datainstanceofHTMLElement)?
$this->data->getHTML():$this->data;
$this->output.='</p>';
return$this->output;
 }
}
classUnorderedListextendsHTMLElement{
 private$output='<ul';
 private$items=array();
 publicfunction__construct($attributes=array(),$items=array()){
parent::__construct($attributes);
if(!is_array($items)){
 thrownewException('Invalidparameterforlistitems');
 }
 $this->items=$items;
}
//'getHTML()'方法的具体实现
publicfunctiongetHTML(){
 foreach($this->attributesas$attribute=>$value){
$this->output.=$attribute.'="'.$value.'"';
 }
 $this->output=substr_replace($this->output,'>',-1);
 foreach($this->itemsas$item){
$this->output.=($iteminstanceof
HTMLElement)?'<li>'.$item->getHTML().'</li>':'<li>'.$item.'</li>';
 }
 $this->output.='</ul>';
 return$this->output;
}
}
如上面的类所展示的,为了允许在生成相应的网页时实现嵌套的(X)HTML元素,我分别重构了它们的构造器和"getHTML()"方法。请注意,我在每一个类的构造器中包含了下面的条件块:

  if(!$datainstanceofHTMLElement&&!is_string($data)){
thrownewException('Invalidparametertype');
}
至此,我实际做的是确保仅有字符串数据和"HTMLElement"类型对象允许作为每一个类的输入参数。否则,将分别由各自方法抛出一个异常,并且有可能导致应用程序的停止执行。所以,这就是对输入数据的检查过程。现在,让我们看一下"getHTML()"方法的新的签名,其中也使用了"instanceof"操作符:

  $this->output.=($this->datainstanceofHTMLElement)?$this->data-
>getHTML():$this->data;
如你所见,在这种情况下,对于利用(X)HTMLwidget类的多态性特征方面this操作符是非常有用的。如果$data属性也是一个widget,那么它的"getHTML()"方法将被正确调用,这会导致显示嵌套的网页元素。另一方面,如果它仅是一个字符串,那么它就被直接添加到当前类的所有输出上。

至此,为了确保某些对象属于一个特定的类型,你可能已经理解了PHP5中"instanceof"操作符的用法。正如你在本文中所见,在PHP5中强制对象类型其实是一个相当直接的工作。现在,你最好开发一个使用这个方法来过滤你的PHP应用程序中的对象的例子来加深自己的理解。

五、小结

在本文中,你学习了如何使用PHP5中的"instanceof"操作符来检查你的输入对象的类型;然而,我所向你展示的方法不是唯一的。在后面的一篇中,我将向你解释怎样实现PHP5中的良好的"类型提示"特征,这是实现强制对象类型的另外一种方法。