PHP Conference Japan 2024

物件與參考

PHP OOP 中經常被提到的一個重點是「物件預設以參考傳遞」。這並不完全正確。本節將使用一些範例來糾正這種普遍的想法。

PHP 的參考是一個別名,它允許兩個不同的變數寫入同一個值。在 PHP 中,物件變數不包含物件本身作為值。它只包含一個物件識別符,允許物件存取器找到實際的物件。當物件透過參數傳遞、回傳或賦值給另一個變數時,不同的變數不是別名:它們持有識別符的副本,指向同一個物件。

範例 #1 參考與物件

<?php
class A {
public
$foo = 1;
}

$a = new A;
$b = $a; // $a 和 $b 是相同識別符的副本
// ($a) = ($b) = <id>
$b->foo = 2;
echo
$a->foo."\n";


$c = new A;
$d = &$c; // $c 和 $d 是參考
// ($c,$d) = <id>

$d->foo = 2;
echo
$c->foo."\n";


$e = new A;

function
foo($obj) {
// ($obj) = ($e) = <id>
$obj->foo = 2;
}

foo($e);
echo
$e->foo."\n";

?>

以上範例會輸出

2
2
2
新增筆記

使用者貢獻筆記 15 則筆記

351
miklcct at gmail dot com
14 年前
關於參考的筆記
參考不是指標。然而,物件句柄是個指標。範例
<?php
class Foo {
private static
$used;
private
$id;
public function
__construct() {
$id = $used++;
}
public function
__clone() {
$id = $used++;
}
}

$a = new Foo; // $a 是一個指向 Foo 物件 0 的指標
$b = $a; // $b 是一個指向 Foo 物件 0 的指標,但是,$b 是 $a 的副本
$c = &$a; // $c 和 $a 現在是個指向 Foo 物件 0 的指標的參考
$a = new Foo; // $a 和 $c 現在是指向 Foo 物件 1 的指標的參考,$b 仍然是指向 Foo 物件 0 的指標
unset($a); // 參考計數為 1 的參考會自動轉換回值。現在 $c 是指向 Foo 物件 1 的指標
$a = &$b; // $a 和 $b 現在是指向 Foo 物件 0 的指標的參考
$a = NULL; // $a 和 $b 現在變成 NULL 的參考。Foo 物件 0 現在可以被垃圾回收
unset($b); // $b 不再存在,$a 現在為 NULL
$a = clone $c; // $a 現在是指向 Foo 物件 2 的指標,$c 仍然是指向 Foo 物件 1 的指標
unset($c); // Foo 物件 1 現在可以被垃圾回收。
$c = $a; // $c 和 $a 是指向 Foo 物件 2 的指標
unset($a); // Foo 物件 2 仍然被 $c 指向
$a = &$c; // Foo 物件 2 只有 1 個指標指向它,該指標有 2 個參考:$a 和 $c;
const ABC = TRUE;
if(
ABC) {
$a = NULL; // Foo 物件 2 現在可以被垃圾回收,因為 $a 和 $c 現在是同一個 NULL 值的參考
} else {
unset(
$a); // Foo 物件 2 仍然被 $c 指向
}
260
匿名
13 年前
這裡似乎有些混淆。指標 (pointer) 和參照 (reference) 之間的區別並不是特別有幫助。
一些已經發布的「綜合」範例中的行為,可以用更簡單且統一的術語來解釋。例如,Hayley 的程式碼完全按照您應該預期的那樣運作。(使用 >= 5.3)

第一原則
指標儲存一個記憶體位址,用於存取一個物件。每當賦值一個物件時,就會產生一個指標。(我還沒有深入研究 Zend 引擎,但就我所見,這適用)

第二原則,也是造成最大困惑的根源
將變數傳遞給函數,預設是以傳值 (value pass) 的方式進行,也就是說,您正在使用副本。「但是物件是以傳參考 (reference) 的方式傳遞!」這是這裡和 Java 世界中常見的誤解。我從未說過是什麼的副本。預設的傳遞方式是傳值。永遠都是。然而,被複製和傳遞的是指標。當使用「->」時,您當然會存取與呼叫函數中原始變數相同的內部結構。僅使用「=」只會操作副本。

第三原則
「&」會自動且永久地將另一個變數名稱/指標設定為與其他東西相同的記憶體位址,直到您將它們解耦。「別名 (alias)」這個詞在這裡使用是正確的。把它想成是將兩個指標緊密連結在一起,直到使用「unset()」強制分離為止。此功能存在於相同的作用域中,以及當參數傳遞給函數時。通常,傳遞的參數被稱為「參考」,因為「傳值」和「傳參考」之間存在某些區別,這些區別在 C 和 C++ 中更為明確。

請記住:傳遞給函數的是指向物件的指標,而不是物件本身。這些指標是原始指標的副本,除非您在參數列表中使用「&」,才會實際傳遞原始指標。只有當您深入研究物件的內部結構時,原始物件才會發生變化。

範例

<?php

//這兩個應該是相同的
$a = "克拉克肯特"; //a==克拉克肯特
$b = &$a; //這兩個現在會同甘共苦。

$b="超人"; // $a=="超人" 也會。
echo $a;
echo
$a="克拉克肯特"; // $b=="克拉克肯特" 也會。
unset($b); // $b 與 $a 分離
$b="比薩羅";
echo
$a; // $a=="克拉克肯特" 仍然,因為 $b 現在是一個自由指標。

//這兩個不應該是相同的。
$c="國王";
$d="王位覬覦者";
echo
$c."\n"; // $c=="國王"
echo $d."\n"; // $d=="王位覬覦者"
swapByValue($c, $d);
echo
$c."\n"; // $c=="國王"
echo $d."\n"; // $d=="王位覬覦者"
swapByRef($c, $d);
echo
$c."\n"; // $c=="王位覬覦者"
echo $d."\n"; // $d=="國王"

function swapByValue($x, $y){
$temp=$x;
$x=$y;
$y=$temp;
//所有這些美好的工作都會消失
//因為它是在指標的副本上完成的。
//原始指標仍然指向它們原來的位置。
}

function
swapByRef(&$x, &$y){
$temp=$x;
$x=$y;
$y=$temp;
//請注意參數列表:現在我們真的交換了它們。
}

?>
54
Aaron Bond
15 年前
我遇到一種行為,它幫助我釐清了物件和識別符號之間的差異。

當我們傳遞一個物件變數時,我們得到該物件值的識別符號。這意味著,如果我從傳遞的變數中修改物件,則來自該物件實例的所有變數都會更改。

然而,如果我將該物件變數設定為新的實例,它會用新的識別符號取代識別符號本身,並保持舊的實例完好無損。

以下面的範例為例

<?php
class A {
public
$foo = 1;
}

class
B {
public function
foo(A $bar)
{
$bar->foo = 42;
}

public function
bar(A $bar)
{
$bar = new A;
}
}

$f = new A;
$g = new B;
echo
$f->foo . "\n";

$g->foo($f);
echo
$f->foo . "\n";

$g->bar($f);
echo
$f->foo . "\n";

?>

如果物件變數始終是參考,我們會預期得到以下輸出
1
42
1

然而,我們得到
1
42
42

原因很簡單。在 B 類的 bar 函數中,我們將您傳入的識別符號取代掉,該識別符號與您的 $f 變數識別相同的 A 類實例,並用一個全新的 A 類識別符號取代。建立一個 A 的新實例不會改變 $f,因為 $f 沒有作為參考傳遞。

要獲得參考行為,必須為 B 類輸入以下內容

<?php
class B {
public function
foo(A $bar)
{
$bar->foo = 42;
}

public function
bar(A &$bar)
{
$bar = new A;
}
}
?>

foo 函數不需要參考,因為它正在修改 $bar 識別的物件實例。但是 bar 將會取代物件實例。如果只傳遞一個識別符號,則變數識別符號將會被覆蓋,但物件實例將會保留在原地。
27
kristof at viewranger dot com
12 年前
我希望這能更清楚地說明參考

<?php
class A {
public
$foo = 1;
}

$a = new A;
$b = $a;
$a->foo = 2;
$a = NULL;
echo
$b->foo."\n"; // 2

$c = new A;
$d = &$c;
$c->foo = 2;
$c = NULL;
echo
$d->foo."\n"; // 注意:嘗試獲取非物件的屬性...
?>
4
rnealxp at yahoo dot com
4 年前
簡潔地提醒物件賦值的行為,無論是否使用「&」運算子...
<?php
class clsA{
public
$propA = 2;
}
class
clsB{
public
$propB = 3;
}
//------------
$a = new clsA();
$c = $a; //這兩個變數現在都指向同一個物件實體(以參照方式賦值)。
$c->propA = 22; //使用其中一個變數來變更實體的屬性。
echo $c->propA . "\n"; //輸出:22
echo $a->propA . "\n"; //輸出:22
//------------
$b = new clsB();
$a = $b; //在這次賦值之前,$c 和 $a 都指向同一個物件實體;在 $a 被切換為指向 clsB 的實體之後,情況就不同了。
echo $c->propA . "\n"; //輸出:22(之所以有效,是因為 $c 仍然是指向 clsA 類型物件實體的參照)
echo $c->propB . "\n"; //輸出:"Undefined property: clsA::$propB"(無效,因為 $c 不是指向 clsB 類型物件實體的參照)
//------------
//重新開始並使用 "&" 運算符...
$a = new clsA();
$b = new clsB();
$c = &$a; //<--$c 將指向 $a 目前和「未來」所指向的任何內容(也是一種以參照方式賦值);在 C 語言中,您會認為這是複製一個指標。
echo $c->propA . "\n"; //輸出:2
$a = $b; //這個賦值會導致 $c 指向新的/不同的物件。
echo $c->propA . "\n"; //輸出:"Undefined property: clsB::$propA"(無效,因為 $c 不再指向 clsA 類型的物件實體)
echo $c->propB . "\n"; //輸出:3(有效,因為 $c 現在指向 clsB 類型的物件實體)
//------------
?>
8
wassimamal121 at hotmail dot com
9 年前
PHP 手冊提供的範例相當聰明且簡單。
這個範例首先解釋了當兩個指向相同物件的別名被變更時會發生什麼,只需重新思考範例的第一部分
<?php

class A { public $foo = 1;}
function
go($obj) { $obj->foo = 2;}
function
bo($obj) {$obj=new A;}

function
chan($p){$p=44;}
function
chanref(&$p){$p=44;}

/**************操作簡單變數******************/
$h=2;$k=$h;$h=4; echo '$k='.$k."<br/>";
// $k 指向一個包含值 2 的記憶體儲存格
// $k 被建立並指向 RAM 中的另一個儲存格
// $k=$h 表示取得 $h 指向的儲存格的內容
// 並將其放入 $k 指向的儲存格
// $h=4 表示變更 $h 指向的儲存格的內容
// 並不表示變更 $k 指向的儲存格的內容

$h=2;$k=&$h;$h=4; echo '$k='.$k."<br/>";
// 這裡 $k 指向與 $h 相同的記憶體儲存格

$v=2;chan($v); echo '$v='.$v."<br/>";
// $v 的值沒有變更,因為函數會將
// 作為引數的別名指向值 2,在函數中我們
// 只變更這個別名指向的值

$u=2;chanref($u); echo '$u='.$u."<br/>";
// 這裡的值會變更,因為我們傳遞了 $u 指向的
//記憶體儲存格的位址,函數正在操作
//這個記憶體儲存格的內容

/***************操作物件************************/
$a = new A;
// 藉由在記憶體中配置一些儲存格來建立物件,$a 指向
// 這些儲存格

$b = $a;
// $b 指向相同的儲存格,這與簡單的變數不同
// 簡單的變數會先建立,然後我們複製內容

$b->foo = 2;echo $a->foo."<br/>";
// 您可以使用 $a 或 $b 來存取相同的物件

$c = new A;$d = &$c;$d->foo = 2;echo $c->foo."<br/>";
// $d 和 $c 不只是指向相同的記憶體空間,
// 而是它們是相同的

$e = new A;
go($e);
// 我們傳遞指標的副本
echo $e->foo."<br/>";
bo($e);
echo
$e->foo."<br/>";
// 如果您認為是 1,很抱歉,我沒解釋清楚
// 請記住,您只是傳遞一個指標,當呼叫 new 時
// 指標會分離
?>
15
mjung at poczta dot onet dot pl
15 年前
物件參照的終極解釋
注意:用詞「指向」可以很容易地替換為「參照」,並且使用時較為寬鬆。
<?php
$a1
= new A(1); // $a1 == 指向 A(1) 的 handle1-1
$a2 = $a1; // $a2 == 指向 A(1) 的 handle1-2 - 以值賦予 (複製)
$a3 = &$a1; // $a3 指向 $a1 (handle1-1)
$a3 = null; // 使 $a1==null, $a3 (仍然) 指向 $a1, $a2 == handle1-2 (相同的物件實例 A(1))
$a2 = null; // 使 $a2 == null
$a1 = new A(2); //使 $a1 == 指向新物件的 handle2-1 且 $a3 (仍然) 指向 $a1 => handle2-1 (新物件),所以 $a1 和 $a3 的值是新物件,$a2 == null
// 參照傳遞:
$a4 = &new A(4); //$a4 指向 A(4) 的 handle4-1
$a5 = $a4; // $a5 == 指向 A(4) 的 handle4-2 (複製)
$a6 = &$a4; //$a6 指向 (handle4-1),而不是 $a4 (參照的參照是參照到被參照的物件 handle4-1,而不是參照本身)

$a4 = &new A(40); // $a4 指向 handle40-1, $a5 == handle4-2 且 $a6 仍然指向 A(4) 的 handle4-1
$a6 = null; // 設定 handle4-1 為 null;$a5 == handle4-2 = A(4);$a4 指向 handle40-1;$a6 指向 null
$a6 =&$a4; // $a6 指向 handle40-1
$a7 = &$a6; //$a7 指向 handle40-1
$a8 = &$a7; //$a8 指向 handle40-1
$a5 = $a7; //$a5 == handle40-2 (複製)
$a6 = null; //使 handle40-1 為 null,所有指向 (hanlde40-1 ==null) 的變數都是 null,除了 ($a5 == handle40-2 = A(40))
?>
希望這有幫助。
7
gevorgmelkoumyan at gmail dot com
5 年前
我認為這個範例應該可以釐清 PHP 參照 (別名) 和指標之間的差異

<?php

class A {
public
$var = 42;
}

$a = new A; // $a 指向 id=1 的物件
echo 'A: ' . $a->var . PHP_EOL; // A: 42

$b = $a; // $b 指向 id=1 的物件
echo 'B: ' . $b->var . PHP_EOL; // B: 42

$b->var = 5;
echo
'B: ' . $b->var . PHP_EOL; // B: 5
echo 'A: ' . $a->var . PHP_EOL; // A: 5

$b = new A; // 現在 $b 指向 id=2 的物件,但是 $a 仍然指向第 1 個物件
echo 'B: ' . $b->var . PHP_EOL; // B: 42
echo 'A: ' . $a->var . PHP_EOL; // A: 5

?>
5
Jon Whitener
12 年前
當將物件傳遞給函式時,使用 clone 可能會得到您預期的行為,如下面使用 DateTime 物件作為範例所示。

<?php
date_default_timezone_set
( "America/Detroit" );

$a = new DateTime;
echo
"a = " . $a->format('Y-m-j') . "\n";

// 這可能不會給您預期的結果...
$b = upDate( $a ); // a 和 b 都被更新了
echo "a = " . $a->format('Y-m-j') . ", b = " . $b->format('Y-m-j') . "\n";
$a->modify( "+ 1 day" ); // a 和 b 都被修改了
echo "a = " . $a->format('Y-m-j') . ", b = " . $b->format('Y-m-j') . "\n";

// 這可能是您想要的...
$c = upDateClone( $a ); // 只有 c 被更新,a 沒有變動
echo "a = " . $a->format('Y-m-j') . ", c = " . $c->format('Y-m-j') . "\n";

function
upDate( $datetime ) {
$datetime->modify( "+ 1 day" );
return
$datetime;
}

function
upDateClone( $datetime ) {
$dt = clone $datetime;
$dt->modify( "+ 1 day" );
return
$dt;
}
?>

上面的程式碼會輸出類似以下的內容

a = 2012-08-15
a = 2012-08-16, b = 2012-08-16
a = 2012-08-17, b = 2012-08-17
a = 2012-08-17, c = 2012-08-18
3
wbcarts at juno dot com
16 年前
有點模糊... 但還算可以!

在上面的 PHP 範例中,函式 foo($obj) 實際上會為傳遞給它的「任何物件」建立一個 $foo 屬性 - 這對我來說有些困惑
$obj = new stdClass();
foo($obj); // 在物件上標記一個 $foo 屬性
// 為什麼這裡有這個方法?
此外,在 OOP 中,「全域函式」對物件的屬性進行操作並不是一個好主意... 並且讓您的類別物件允許它們也不是一個好主意。為了說明這一點,範例應該是

<?php

class A {
protected
$foo = 1;

public function
getFoo() {
return
$this->foo;
}

public function
setFoo($val) {
if(
$val > 0 && $val < 10) {
$this->foo = $val;
}
}

public function
__toString() {
return
"A [foo=$this->foo]";
}
}

$a = new A();
$b = $a; // $a 和 $b 是同一個識別符號的副本
// ($a) = ($b) = <id>
$b->setFoo(2);
echo
$a->getFoo() . '<br>';

$c = new A();
$d = &$c; // $c 和 $d 是參考
// ($c,$d) = <id>
$d->setFoo(2);
echo
$c . '<br>';

$e = new A();
$e->setFoo(16); // 將被忽略
echo $e;

?>
- - -
2
A [foo=2]
A [foo=2]
- - -
因為全域函數 foo() 已被刪除,類別 A 被定義得更完整、更穩固,並且將處理所有 foo 操作...而且只針對 A 類型的物件。我現在可以理所當然地清楚看到你在談論「A」物件及其參考。但它仍然讓我想起太多關於複製和物件比較的概念,對我來說,這幾乎接近機器式的程式設計,而不是物件導向程式設計,這是一種完全不同的思考方式。
3
匿名
13 年前
這個範例可能會有所幫助

<?php
class A {
public
$testA = 1;
}

class
B {
public
$testB = "class B";
}

$a = new A;
$b = $a;
$b->testA = 2;

$c = new B;
$a = $c;

$a->testB = "Changed Class B";

echo
"<br/> object a: "; var_dump($a);
echo
"<br/> object b: "; var_dump($b);
echo
"<br/> object c: "; var_dump($c);

// by reference

$aa = new A;
$bb = &$aa;
$bb->testA = 2;

$cc = new B;
$aa = $cc;

$aa->testB = "Changed Class B";

echo
"<br/> object aa: "; var_dump($aa);
echo
"<br/> object bb: "; var_dump($bb);
echo
"<br/> object cc: "; var_dump($cc);

?>
4
Ivan Bertona
16 年前
在我看來,手冊頁面中沒有充分強調的一點是,在 PHP5 中,將物件作為函數呼叫的參數傳遞,而不使用 & 運算符,表示傳遞該物件的唯一識別符號(意指類別的實例)的「值」,該識別符號將儲存在具有函數作用域的另一個變數中。

這種行為與 Java 中使用的行為相同,在 Java 中,實際上沒有按參考傳遞參數的概念。另一方面,在 PHP 中,您可以按參考傳遞值(在 PHP 中,我們將參考稱為「別名」),如果您不了解自己實際在做什麼,這會構成威脅。請考慮以下兩個類別

<?php
class A
{
function
__toString() {
return
"Class A";
}
}

class
B
{
function
__toString() {
return
"Class B";
}
}
?>

在第一個測試案例中,我們從類別 A 和 B 建立兩個物件,然後使用暫存變數和一般賦值運算符 (=) 交換變數。

<?php
$a
= new A();
$b = new B();

$temp = $a;
$a = $b;
$b = $temp;

print(
'$a: ' . $a . "\n");
print(
'$b: ' . $b . "\n");
?>

如預期,腳本將輸出

$a: Class B
$b: Class A

現在考慮以下程式碼片段。它與前者相似,但賦值 $a = &$b 使 $a 成為 $b 的「別名」。

<?php
$a
= new A();
$b = new B();

$temp = $a;
$a = &$b;
$b = $temp;

print(
'$a: ' . $a . "\n");
print(
'$b: ' . $b . "\n");
?>

這個腳本將輸出

$a: Class A
$b: Class A

也就是說,修改 $b 會反映 $a 的相同賦值...這兩個變數最終指向同一個物件,而另一個則遺失了。總而言之,處理 PHP5 物件時最好不要使用別名,除非您真的非常確定自己在做什麼。
0
Hayley Watson
14 年前
使用 &$this 可能會導致一些奇怪且違反直覺的行為 - 它開始對您說謊。

<?php

class Bar
{
public
$prop = 42;
}

class
Foo
{
public
$prop = 17;
function
boom()
{
$bar = &$this;
echo
"\$bar 是 \$this 的別名,一個 Foo 物件。\n";
echo
'$this 是一個 ', get_class($this), '; $bar 是一個 ', get_class($bar), "\n";

echo
"它們是同一個物件嗎? ", ($bar === $this ? "是\n" : "否\n");
echo
"它們相等嗎? ", ($bar === $this ? "是\n" : "否\n");
echo
'$this 說它的 prop 值是 ';
echo
$this->prop;
echo
' 而 $bar 說它是 ';
echo
$bar->prop;
echo
"\n";

echo
"\n";

$bar = new Bar;
echo
"\$bar 已經被建立成一個新的 Bar 物件。\n";
echo
'$this 是一個 ', get_class($this), '; $bar 是一個 ', get_class($bar), "\n";

echo
"它們是同一個物件嗎? ", ($bar === $this ? "是\n" : "否\n");
echo
"它們相等嗎? ", ($bar === $this ? "是\n" : "否\n");
echo
'$this 說它的 prop 值是 ';
echo
$this->prop;
echo
' 而 $bar 說它是 ';
echo
$bar->prop;
echo
"\n";

}
}

$t = new Foo;
$t->boom();
?>
在上面的程式碼中,$this 宣稱自己是一個 Bar 物件 (事實上它宣稱自己是與 $bar 完全相同的物件),但仍然擁有 Foo 物件的所有屬性和方法。

幸運的是,它不會在您犯錯的方法之外持續存在。
<?php
echo get_class($t), "\t", $t->prop;
?>
-4
lazybones_senior
16 年前
哇... 保持簡單!

關於 secure_admin 的筆記:您已經使用 OOP 來簡化 PHP 建立和使用物件參考的能力。現在使用 PHP 的 static 關鍵字來簡化您的 OOP。

<?php

class DataModelControl {
protected static
$data = 256; // 預設值;
protected $name;

public function
__construct($dmcName) {
$this->name = $dmcName;
}

public static function
setData($dmcData) {
if(
is_numeric($dmcData)) {
self::$data = $dmcData;
}
}

public function
__toString() {
return
"DataModelControl [name=$this->name, data=" . self::$data . "]";
}
}

# 建立 DataModelControl 的數個實例...
$dmc1 = new DataModelControl('dmc1');
$dmc2 = new DataModelControl('dmc2');
$dmc3 = new DataModelControl('dmc3');
echo
$dmc1 . '<br>';
echo
$dmc2 . '<br>';
echo
$dmc3 . '<br><br>';

# 若要變更資料,請使用任何 DataModelControl 物件...
$dmc2->setData(512);
# 或者,直接從類別呼叫 setData() ...
DataModelControl::setData(1024);
echo
$dmc1 . '<br>';
echo
$dmc2 . '<br>';
echo
$dmc3 . '<br><br>';
?>

DataModelControl [name=dmc1, data=256]
DataModelControl [name=dmc2, data=256]
DataModelControl [name=dmc3, data=256]

DataModelControl [name=dmc1, data=1024]
DataModelControl [name=dmc2, data=1024]
DataModelControl [name=dmc3, data=1024]

... 更好!現在,PHP 建立一個 $data 的副本,該副本在所有 DataModelControl 物件之間共享。
-4
Joe F
5 年前
我計畫將物件序列化和反序列化作為儲存方式,並且我的應用程式可以方便地將大量物件分組到單個要序列化的物件中。但是,這提出了一些我需要回答的問題

假設我計畫序列化的父物件是 "A",而我儲存在其中的物件將會是 A(a-z)。如果我將 A(b) 傳遞給 A(c),這會通過參考傳遞。因此,如果 A(c) 執行會影響 A(b) 值的動作,這也會更新儲存在 A 中的原始 A(b)。太棒了!

但是,當我序列化 A 時會發生什麼?其中 A(c) 引用了 A(b),然後我進行反序列化?A(c) 會擁有 A(b) 的新獨立副本,還是仍然會參考儲存在 A 中的 A(b)?

答案是,PHP 5.5 和 PHP 7 都會追蹤在反序列化過程中,某個東西是否是它已經「重新建立」的物件的參考,請參閱以下範例

<?php

class foo {
protected
$stored_object;
protected
$stored_object2;
protected
$stored_value;

public function
__construct($name, $stored_value) {
$this->store_value($stored_value);
echo
'Constructed: '.$name.' => '.$stored_value.'<br/>';
}

public function
store_object(foo $object) {
$this->stored_object = $object;
}

public function
store_object2(foo $object) {
$this->stored_object2 = $object;
}

public function
store_value($value) {
$this->stored_value = $value;
}

public function
stored_method($method, array $parameters) {
echo
'Call stored method: '.$method.'{ <br/>';
call_user_func_array(array($this->stored_object, $method), $parameters);
echo
'} <br/>';
}

public function
stored_method2($method, array $parameters) {
echo
'Call stored method 2: '.$method.'{ <br/>';
call_user_func_array(array($this->stored_object2, $method), $parameters);
echo
'} <br/>';
}

public function
echo_value() {
echo
'Value: '.$this->stored_value.'<br/>';
}
}

$foo = new foo('foo', 'Hello!'); // Constructed: foo => Hello!
$new_foo = new foo('new_foo', 'New Foo 2!'); // Constructed: new_foo => New Foo 2!
$third_foo = new foo('third_foo', 'Final Foo!'); // Constructed: third_foo => Final Foo!

$foo->store_object($new_foo);
$foo->store_object2($third_foo);
$foo->stored_method('store_object', array($third_foo)); //Call stored method: store_object{ }

$serialized = serialize($foo);
unset(
$foo);
unset(
$new_foo);
unset(
$third_foo);
$unserialized_foo = unserialize($serialized);

//Below, I update the object represented as A(c) but I update it via the A object
$unserialized_foo->stored_method2('store_value', array('Super Last Foo!')); // Call stored method 2: store_value{}
$unserialized_foo->echo_value(); // Value: Hello!
$unserialized_foo->stored_method('echo_value', array());
//Call stored method: echo_value{
//Value: New Foo 2!
//}

// Last, I check the value of A(c) as it was stored in A(b) to see if updating A(c) via A also updates A(b)'s copy/reference:
$unserialized_foo->stored_method('stored_method', array('echo_value', array()));
//Call stored method: stored_method{
//Call stored method: echo_value{
//Value: Super Last Foo!
//}
//}
?>

根據最後一行,A(b) 中 A(c) 的「副本」仍然是對儲存在 A 中的原始 A(b) 的參照,即使在還原序列化之後也是如此。
To Top