作者:Leon Atkinson 翻譯:Haohappy 本章的最后一節(jié),Zeev討論了Zend引擎帶來的對象模型,特別提到它與PHP的前幾個版本中的模型有什么不同. 當(dāng)1997年夏天,我們開發(fā)出PHP3, 我們沒有計劃要使PHP具備面向?qū)ο蟮哪芰? 當(dāng)時沒有任何與類和對象有關(guān)的想法. PHP3是一個純粹面向過程的語言. 但是,在1997.8.27的晚上PHP3 alpha版中增加了對類的支持. 增加一個新特性給PHP,當(dāng)時僅需要極少的討論,因為當(dāng)時探索PHP的人太少. 于是從1997年八月起, PHP邁出了走向面向?qū)ο缶幊陶Z言的第一步.
確實,這只是第一步. 因為在這個設(shè)計中只有極少的相關(guān)的想法,對于對象的支持不夠強(qiáng)大. 這個版本中使用對象僅是訪問數(shù)組的一個很酷的方法而已. 取代使用$foo[“bar”],你可以使用看起來更漂亮的$foo->bar. 面向?qū)ο蠓椒ǖ闹饕膬?yōu)勢是通過成員函數(shù)或方法來儲存功能. 例子6.18中顯示了一個典型的代碼塊. 但是它和例6.19中的做法其實并沒有太大不同.
Listing 6.18 PHP 3 object-oriented programming PHP3中的面向?qū)ο缶幊?BR>
<?php class Example { var $value = "some value"; function PrintValue() { print $this->value; } } $obj = new Example(); $obj->PrintValue(); ?> Listing 6.19 PHP 3 structural programming PHP3 PHP3中的結(jié)構(gòu)化編程
<?php function PrintValue($arr) { print $arr["value"]; }
function CreateExample() { $arr["value"] = "some value"; $arr["PrintValue"] = "PrintValue";
return $arr; }
$arr = CreateExample();
//Use PHP's indirect reference $arr["PrintValue"]($arr); ?> 以上我們在類中寫上兩行代碼,或者顯示地傳遞數(shù)組給函數(shù). 但考慮到PHP3中這兩種選擇并沒有任何不同,我們?nèi)匀豢梢詢H把對象模型當(dāng)成一種”語法上的粉飾”來訪問數(shù)組.
想要用PHP來進(jìn)行面向?qū)ο箝_發(fā)的人們,特別是想使用設(shè)計模式的人,很快就發(fā)現(xiàn)他們碰壁了. 幸運地,當(dāng)時(PHP3時代)沒有太多人想用PHP來進(jìn)行面向?qū)ο箝_發(fā).
PHP4改變了這種情況. 新的版本帶來了引用(reference)的概念, 它允許PHP的不同標(biāo)識符指向內(nèi)存中的同一個地址. 這意味著你可以使用兩個或更多的名稱來給同一個變量命名,就像例6.20那樣.
Listing 6.20 PHP 4 references PHP4中的引用
<?php $a = 5;
//$b points to the same place in memory as $a $b與$a指向內(nèi)存中同個地址 $b = &$a;
//we're changing $b, since $a is pointing to 改變$b,指向的地址改變 //the same place - it changes too $a指向的地址也改變 $b = 7;
//prints 7 輸出7 print $a; ?> 由于構(gòu)建一個指向彼此的對象網(wǎng)絡(luò)是所有面向?qū)ο笤O(shè)計模式的基礎(chǔ),這個改進(jìn)具有非常重大的意義.當(dāng)引用允許建立更多強(qiáng)大的面向?qū)ο髴?yīng)用程序, PHP對待對象和其它類型數(shù)據(jù)相同的做法帶給開發(fā)者極大的痛苦.就像任何PHP4的程序員將會告訴你的, 應(yīng)用程序?qū)庥鯳TMA(Way Too Many Ampersands過多&)綜合癥. 如果你想構(gòu)建一個實際應(yīng)用,你會感到極為痛苦,看看例6.21你就明白.
Listing 6.21 Problems with objects in PHP 4 PHP4中使用對象的問題
1 class MyFoo { 2 function MyFoo() 3 { 4 $this->me = &$this; 5 $this->value = 5; 6 } 7 8 function setValue($val) 9 { 10 $this->value = $val; 11 } 12 13 function getValue() 14 { 15 return $this->value; 16 } 17 18 function getValueFromMe() 19 { 20 return $this->me->value; 21 } 22 } 23 24 function CreateObject($class_type) 25 { 26 switch ($class_type) { 27 case "foo": 28 $obj = new MyFoo(); 29 break; 30 case "bar": 31 $obj = new MyBar(); 32 break; 33 } 34 return $obj; 35 } 36 37 $global_obj = CreateObject ("foo"); 38 $global_obj->setValue(7); 39 40 print "Value is " . $global_obj->getValue() . "n"; 41 print "Value is " . $global_obj->getValueFromMe() . "n";
讓我們一步步來討論. 首先,有一個MyFoo類.在構(gòu)造函數(shù)里,我們給$this->me一個引用,并設(shè)定 我們有其它三個成員函數(shù): 一個設(shè)定this->value的值;一個返回this->value的值;另一個返回this->value->me的值. 但是--$this不是相同的東西嗎? MyFoo::getValue()和MyFoo::getValueFromMe()返回的值不是一樣的嗎?
首先,我們調(diào)用CreateObject("foo"),這會返回一個MyFoo類型的對象. 然后我們調(diào)用MyFoo::setValue(7). 最后,我們調(diào)用MyFoo::getValue() 和MyFoo::getValueFromMe(), 期望得到返回值7. 當(dāng)然,如果我們在任何情況下都得到7, 以上這個例子將不是本書中最沒有意義的例子. 所以我相信你已經(jīng)猜到—我們得不到兩個7這樣的結(jié)果.
但是我們將得到什么結(jié)果,并且更重要地,為什么呢?
我們將得到的結(jié)果分別是7和5. 至于為什么—--有三個很好的理由.
首先,看構(gòu)造函數(shù). 當(dāng)在構(gòu)造函數(shù)內(nèi)部,我們在this和this->me間建立引用. 換句話說,this和this->me是同個東西. 但是我們是在構(gòu)造函數(shù)內(nèi). 當(dāng)構(gòu)造函數(shù)結(jié)束,PHP要重新建立對象(new MyFoo的結(jié)果,第28行)分配給$obj. 因為對象沒有特殊化對待,就像其它任何數(shù)據(jù)類型一樣,賦值X給Y意味著Y是X的一個副本. 也就是說,obj將是new MyFoo的一個副本,而new MyFoo是一個存在于構(gòu)造函數(shù)的對象. Obj->me怎么樣呢? 因為它是一個引用,它原封不動仍然指向原來的對象—this. Voila-obj和obj->me不再是同個東西了—改變其中一個另一個不變.
以上是第一條理由. 還有其它類似于第一條的理由. 奇跡般地我們打算克服實例化對象這個問題(第28行). 一旦我們把CreateObject返回的值賦給global_object,我們?nèi)匀灰采舷嗤膯栴}—global_object將變成返回值的一個副本,并且再次地,global_object和global_object->me將不再相同. 這就是第二條理由.
但是,事實上我們還走不了那么遠(yuǎn)— 一旦CreateObject返回$obj,我們將破壞引用(第34行) . 這就是第三條理由.
那么,我們?nèi)绾胃恼@些? 有兩個選擇. 一是在所有地方增加&符號,就像例6.22那樣(第24, 28, 31, 37行). 二.如果你幸運地使用上了PHP5,你可以忘了以上這一切,PHP5會自動為你考慮這些. 如果你想知道PHP5是如何考慮這些問題的,繼續(xù)閱讀下去.
Listing 6.22 WTMA syndrome in PHP 4 PHP4中的WTMA綜合癥
1 class MyFoo { 2 function MyFoo() 3 { 4 $this->me = &$this; 5 $this->value = 2; 6 } 7 8 function setValue($val) 9 { 10 $this->value = $val; 11 } 12 13 function getValue() 14 { 15 return $this->value; 16 } 17 18 function getValueFromMe() 19 { 20 return $this->me->value; 21 } 22 }; 23 24 function &CreateObject($class_type) 25 { 26 switch ($class_type) { 27 case "foo": 28 $obj =& new MyFoo(); 29 break; 30 case "bar": 31 $obj =& new MyBar(); 32 break; 33 } 34 return $obj; 35 } 36 37 $global_obj =& CreateObject ("foo"); 38 $global_obj->setValue(7); 39 40 print "Value is " . $global_obj->getValue() . "n"; 41 print "Value is " . $global_obj->getValueFromMe() . "n"; PHP5是第一個把對象看成與其它類型數(shù)據(jù)不同的PHP版本. 從用戶的角度看,這證明它非常明白的方式—在PHP5中,對象總是通過引用來傳遞,而其它類型數(shù)據(jù)(如integer,string,array)都是通過值來傳遞. 最顯著地,沒有必要再用&符號來表示通過引用來傳遞對象了.
面向?qū)ο缶幊虖V泛利用了對象網(wǎng)絡(luò)和對象間的復(fù)雜關(guān)系,這些都需要用到引用. 在PHP的前些版本中,需要顯示地指明引用. 因此, 現(xiàn)在默認(rèn)用引用來移動對象,并且只有在明確要求復(fù)制時才復(fù)制對象,這樣比以前更好.
它是如何實現(xiàn)的呢?
在PHP5之前,所有值都存在一個名為zval(Zend Value)的特殊結(jié)構(gòu)里. 這些值可以存入簡單的值,如數(shù)字和字符串,或復(fù)雜的值如數(shù)組和對象. 當(dāng)值傳給函數(shù)或從函數(shù)返回時,這些值會被復(fù)制,在內(nèi)存的另一個地址建立一個帶有相同內(nèi)容的結(jié)構(gòu).
在PHP5中,值仍存為zval結(jié)構(gòu)中,但對象除外. 對象存在一個叫做Object Store的結(jié)構(gòu)里,并且每個對象有一個不同的ID. Zval中,不儲存對象本身,而是存著對象的指針. 當(dāng)復(fù)制一個持有對象的zval結(jié)構(gòu),例如我們把一個對象當(dāng)成參數(shù)傳給某個函數(shù),我們不再復(fù)制任何數(shù)據(jù). 我們僅僅保持相同的對象指針并由另一個zval通知現(xiàn)在這個特定的對象指向的Object Store. 因為對象本身位于Object Store,我們對它所作的任何改變將影響到所有持有該對象指針的zval結(jié)構(gòu).這種附加的間接作用使PHP對象看起來就像總是通過引用來傳遞,用透明和有效率的方式.
使用PHP5,我們現(xiàn)在可以回到示例6.21,除去所有的&符號, 一切代碼都仍然可以正常工作.當(dāng)我們在構(gòu)造函數(shù)(第4行)中持有一個引用時一個&符號都不用. <全篇完>
超越PHP(www.phpe.org) 及Haohappy特別授權(quán)藍(lán)色理想轉(zhuǎn)載本系列文章,謝絕其它媒體任何形式轉(zhuǎn)載。
出處:超越PHP
責(zé)任編輯:cjj
上一頁 第十四節(jié) 命名空間 [14] 下一頁
◎進(jìn)入論壇網(wǎng)絡(luò)編程版塊參加討論
|