對象
一、簡介
在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"操作符的威力所在。如果你想要看一下這個操作符是如何使用的,請繼續往下讀吧。
[1] [2] 下一頁