2024年PHP日本大会

对象和引用

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条注释

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只有一个指针指向它,该指针有两个引用:$a和$c;
const ABC = TRUE;
if(
ABC) {
$a = NULL; // Foo对象2现在可以被垃圾回收,因为$a和$c现在是对相同NULL值的引用
} else {
unset(
$a); // Foo对象2仍然由$c指向
}
匿名
13年前
这里似乎有一些混淆。指针和引用之间的区别并不是特别有用。
之前发布的一些“综合”示例中的行为可以用更简单的统一术语来解释。例如,Hayley 的代码正是你所期望的那样。(使用 >= 5.3)

第一原则
指针存储访问对象的内存地址。每次分配对象时,都会生成一个指针。(我还没有深入研究 Zend 引擎,但据我所知,这适用)

第二原则,也是最容易混淆的地方
默认情况下,将变量传递给函数是按值传递,即你正在使用副本。“但是对象是按引用传递的!”这是一个常见的误解,在 Java 世界中也是如此。我从未说过什么东西的副本。默认传递是按值进行的。总是如此。但是,复制和传递的是什么?是指针。当使用“->”时,你当然可以访问调用函数中原始变量的相同内部内容。只使用“=”只会操作副本。

第三原则
“&”自动且永久地将另一个变量名/指针设置为与其他内容相同的内存地址,直到你解除它们的关联。在这里使用“别名”一词是正确的。可以将其视为将两个指针连接在一起,直到用“unset()”强制分离。此功能同时存在于相同的范围内,以及当参数传递给函数时。由于“按值传递”和“按引用传递”之间的某些区别在 C 和 C++ 中更清晰,因此传递的参数通常被称为“引用”。

记住:传递给函数的是对象的指针,而不是对象本身。除非你在参数列表中使用“&”来实际传递原件,否则这些指针是原件的副本。只有当你深入研究对象的内部结构时,原件才会发生变化。

示例

<?php

//两者本应相同
$a = "Clark Kent"; //a==Clark Kent
$b = &$a; //两者现在将共享相同的命运。

$b="Superman"; // $a=="Superman" 也是。
echo $a;
echo
$a="Clark Kent"; // $b=="Clark Kent" 也是。
unset($b); // $b 与 $a 分离
$b="Bizarro";
echo
$a; // $a=="Clark Kent" 仍然是,因为 $b 现在是一个自由的指针。

//两者本不相同。
$c="King";
$d="Pretender to the Throne";
echo
$c."\n"; // $c=="King"
echo $d."\n"; // $d=="Pretender to the Throne"
swapByValue($c, $d);
echo
$c."\n"; // $c=="King"
echo $d."\n"; // $d=="Pretender to the Throne"
swapByRef($c, $d);
echo
$c."\n"; // $c=="Pretender to the Throne"
echo $d."\n"; // $d=="King"

function swapByValue($x, $y){
$temp=$x;
$x=$y;
$y=$temp;
//所有这些漂亮的工作都将消失
//因为它是对指针副本进行的。
//原始指针仍然指向它们所指向的位置。
}

function
swapByRef(&$x, &$y){
$temp=$x;
$x=$y;
$y=$temp;
//注意参数列表:现在我们真正交换了它们。
}

?>
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 函数中,我们用一个全新的 A 类标识符替换了你传入的标识符,该标识符识别与你的 $f 变量相同的 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 将替换对象实例。如果只传递标识符,变量标识符将被覆盖,但对象实例将保留在原位。
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"; // Notice: Trying to get property of non-object...
?>
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 类型的对象实例)
//------------
?>
[email protected]
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 时
//指针被解耦
?>
[email protected]
15年前
对象引用的终极解释
注意:“指向”一词可以很容易地替换为“引用”,此处使用较为宽松。
<?php
$a1
= new A(1); // $a1 == handle1-1 指向 A(1)
$a2 = $a1; // $a2 == handle1-2 指向 A(1) - 按值赋值(复制)
$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 指向 handle4-1 指向 A(4)
$a5 = $a4; // $a5 == handle4-2 指向 A(4) (复制)
$a6 = &$a4; //$a6 指向 (handle4-1),而不是 $a4 (引用指向引用,指向被引用的对象 handle4-1,而不是引用本身)

$a4 = &new A(40); // $a4 指向 handle40-1,$a5 == handle4-2,$a6 仍然指向 handle4-1 指向 A(4)
$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))
?>
希望这有帮助。
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 仍然指向第一个对象
echo 'B: ' . $b->var . PHP_EOL; // B: 42
echo 'A: ' . $a->var . PHP_EOL; // A: 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
wbcarts at juno dot com
16年前
有点淡化了……但还可以!

在上面的PHP示例中,函数foo($obj)实际上会为传递给它的“任何对象”创建一个$foo属性——这让我有些困惑
$obj = new stdClass();
foo($obj); // 为对象添加$foo属性
// 为什么这里会有这个方法?
此外,在OOP中,“全局函数”操作对象的属性不是一个好主意……你的类对象也不应该允许它们这样做。为了说明这一点,示例应该是

<?php

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=1]
- - -
由于全局函数 foo()已被删除,类 A 的定义更加清晰、健壮,并且将处理所有 foo 操作……并且仅针对 A 类型对象。我现在可以理所当然地认为您谈论的是“A”对象及其引用。但是它仍然让我想起了克隆和对象比较,对我来说这接近于机器式编程,而不是面向对象编程,这是一种完全不同的思维方式。
匿名用户
13年前
这个例子可能会有所帮助

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

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

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

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

$a->testB = "已更改的类 B";

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

// 通过引用

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

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

$aa->testB = "已更改的类 B";

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

?>
Ivan Bertona
16年前
在我看来,手册页中没有充分强调的一点是,在 PHP5 中,在不使用 & 运算符的情况下将对象作为函数调用参数传递,意味着按值传递该对象的唯一标识符(作为类的实例),该标识符将存储在具有函数作用域的另一个变量中。

这种行为与 Java 中使用的行为相同,在 Java 中确实没有按引用传递参数的概念。另一方面,在 PHP 中,您可以按引用传递值(在 PHP 中,我们将引用称为“别名”),如果您不知道自己在做什么,这就会带来威胁。请考虑这两个类

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

B
{
function
__toString() {
return
"类 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: 类 B
$b: 类 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: 类 A
$b: 类 A

也就是说,修改 $b 会反映 $a 上的相同赋值……这两个变量最终指向同一个对象,而另一个对象丢失了。总而言之,在处理 PHP5 对象时,最好不要使用别名,除非您非常确定自己在做什么。
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 的 prop 属性值为 ';
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 的 prop 属性值为 ';
echo
$bar->prop;
echo
"\n";

}
}

$t = new Foo;
$t->boom();
?>
在上面的代码中,`$this` 声称自己是一个 Bar 对象(事实上,它声称自己与 `$bar` 是同一个对象),同时仍然拥有 Foo 对象的所有属性和方法。

幸运的是,这种错误不会延续到方法之外。
<?php
echo get_class($t), "\t", $t->prop;
?>
lazybones_senior
16年前
哇……保持简洁!

关于 secure_admin 的说明:你使用了面向对象编程来简化 PHP 创建和使用对象引用的能力。现在,使用 PHP 的 `static` 关键字来简化你的面向对象编程。

<?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 对象共享此副本。
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
'已构造: '.$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
'调用存储的方法: '.$method.'{ <br/>';
call_user_func_array(array($this->stored_object, $method), $parameters);
echo
'} <br/>';
}

public function
stored_method2($method, array $parameters) {
echo
'调用存储的方法 2: '.$method.'{ <br/>';
call_user_func_array(array($this->stored_object2, $method), $parameters);
echo
'} <br/>';
}

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

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

$foo->store_object($new_foo);
$foo->store_object2($third_foo);
$foo->stored_method('store_object', array($third_foo)); //调用存储的方法: store_object{ }

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

//下面,我更新表示为 A(c) 的对象,但我通过 A 对象更新它
$unserialized_foo->stored_method2('store_value', array('Super Last Foo!')); // 调用存储的方法 2: store_value{}
$unserialized_foo->echo_value(); // 值: Hello!
$unserialized_foo->stored_method('echo_value', array());
//调用存储的方法: echo_value{
//值: New Foo 2!
//}

// 最后,我检查 A(c) 的值,因为它存储在 A(b) 中,以查看通过 A 更新 A(c) 是否也更新 A(b) 的副本/引用:
$unserialized_foo->stored_method('stored_method', array('echo_value', array()));
//调用存储的方法: stored_method{
//调用存储的方法: echo_value{
//值: Super Last Foo!
//}
//}
?>

根据最后一行,即使在反序列化之后,A(b) 的 A(c) 的“副本”仍然是存储在 A 中的原始 A(b) 的引用。
To Top