TWPUG問答 - PHP5 個體指派動作的陷阱
前幾天在 TWPUG 上,有位網友提了一個問題。大意是如何以一個個體為正本,透過指派動作複製多次到陣列中,每個陣列元素的內容應該不相同。我看出他碰到了一個語言陷阱,我也回答了。可惜,我當時的答案是錯的... 我重新思索了一下,本文才是正解。
在 PHP5 之後,個體(object)的指派動作皆是使用參照。換言之,當指派來源的資料型態是object時, PHP5 就會用參照;故 $a = $o
的動作實際上等於 $a =&$o
。
As of PHP 5, objects are assigned by reference unless explicitly told otherwise with the new clone keyword. PHP Manual: Assignment Operators
如果我們希望透過指派動作得到複製體,則必須使用關鍵字 clone 明確地告知 PHP5 產生一個複製體(右值的複製體)指派給對象(左值)(PHP5 預設的複製動作是淺拷貝(shallow copy)。可覆寫__clone()
自定複製內容。See also: PHP Manual: Object cloning)。如下列所示。
上列範例中,先使用一般的指派方式,結果陣列 $ar 中的每個元素都相同,其實都參照變數 $o。接著改用加上關鍵字 clone 的指派方式,這才使得陣列 $ar 中的每個元素都是一個獨立的個體,內容各不相同。
其他型態的指派動作仍然是「複製」
請留意, PHP5 仍非完全個體導向化的程式語言,並未將每個變數都視為一種個體。在 PHP5 中,「個體」僅指資料型態為 object (基礎類別為 StdClass) 的變數。布林、數值、字串及陣列等,各有其獨立的資料型態,如boolean, integer, string, array。它們與 object 是同層級的資料型態,不被視為「個體」(PHP5有8種資料原生型態,它們是平行非附屬的關係,故一個array的實例不是一種(is not a ) object的實例)。詳細內容請參考 PHP Manual: Types。
PHP5 對 object 型態的變數之指派動作預設是「參照」,但對其他型態的變數之指派動作仍然是「複製」。亦即,當右值之資料型態不是 object 時, PHP5 就會複製一個獨立的內容指派給左值。
下列示範當 $s 之資料型態分別是 string 與 object 時,PHP5 指派動作產生的結果差異。
先令 $s 之資料型態為字串 string ,再指派給陣列 $ar。結果顯示 $ar 各元素內容皆不相同,並非參照 $s。接著令 $s 之資料型態為 object ,再指派一次。結果顯示 $ar 各元素內容相同,其實都參照 $s。
基本上,這是一個語言陷阱(trick)。再者,若將上例第6行的指派動作加上 clone 關鍵字,將會產生非預期的結果。同時, PHP 會警告程序員:Warning: __clone method called on non-object
. 此意味著,程序員編寫之敘述必須區別個體與非個體資料型態。關鍵字 clone 僅作用於右值為 object 型態之變數,故而降低了 PHP 的泛型能力。
樂多舊回應