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

日期:2006-09-28  作者:喜腾小二  来源:PHPChina


一、简介

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

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

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


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

  二、
你不该做什么


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

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

//定义抽象类'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();
}

  在这种情况中,如下面一行所显示的:

$pageGen->addHTMLElement($fakeobj)//把不存在的对象传递到这个方法

  一个并不存在的(X)HTML
widget对象被传递到该页面生成器类,这样会导致一个致命性错误:

Fatal error: Call to a member function on a non-object
in
path/to/file

  怎么样?这就是对传递到生成器类的对象的类型不进行检查的直接惩罚!因此在编写你的脚本时一定要记住这个问题。幸好,还有一个简单的方案来解决这些问题,而且这也正是"instanceof"操作符的威力所在。如果你想要看一下这个操作符是如何使用的,请继续往下读吧。


<<<返回技术中心

技术文章

站内新闻

我要啦免费统计