PHP5:ReturnByRef方法产生意外结果
我创建了一个简单的类来管理一个树数据结构,它的行为似乎很奇怪(下面的代码)。当这显然不是我的错误时,我创建了一个测试用例,它产生了同样令人费解的行为。这在5.3和5.4中是相同的 这是我的测试用例:PHP5:ReturnByRef方法产生意外结果,php,testing,pointers,byref,Php,Testing,Pointers,Byref,我创建了一个简单的类来管理一个树数据结构,它的行为似乎很奇怪(下面的代码)。当这显然不是我的错误时,我创建了一个测试用例,它产生了同样令人费解的行为。这在5.3和5.4中是相同的 这是我的测试用例: <?php class testcaseA { public function __construct($one=0, $two=1, $three=2) { $this->obj = new testcaseB($one++, $three, $two);
<?php
class testcaseA {
public function __construct($one=0, $two=1, $three=2) {
$this->obj = new testcaseB($one++, $three, $two);
}
public function &get($what){
return $this->obj->get($what);
}
}
class testcaseB {
public function __construct($one, &$two, &$three) {
$this->one=$one;
$this->two=$two;
$this->three=$three;
echo "\$one={$one}";
}
public function &get($what){
echo "<p>You asked for $what. $what ain't no country I ever heard of.<br />";
echo "Check: [{$this->one}], {$this->two}, {$this->three}. Is this thing on?</p>";
$this->obj[$what] = new testcaseB($this->one++,$this->two,$this->three);
return $this->obj[$what];
}
}
ini_set('display_errors',1);
error_reporting(E_ALL);
$bob = new testcaseA();
$bob->get("What")->get("Spam")->get("America");
$bob->get("What")->get("EU")->get("France");
echo "<pre>";
print_r($bob);
这一点一开始让我感到困惑,但我想知道,这种链式用法是否以我不期望的方式设置了值
因此,我尝试了在前面的代码之后添加的:
You asked for What. What ain't no country I ever heard of.
Check: [2], 2, 1. Is this thing on?
$one=2
You asked for Spam. Spam ain't no country I ever heard of.
Check: [2], 2, 1. Is this thing on?
$one=2
You asked for America. America ain't no country I ever heard of.
Check: [2], 2, 1. Is this thing on?
$one=2
You asked for EU. EU ain't no country I ever heard of.
Check: [3], 2, 1. Is this thing on?
$one=3
You asked for France. France ain't no country I ever heard of.
Check: [3], 2, 1. Is this thing on?
$one=3testcaseA Object
(
[obj] => testcaseB Object
(
[one] => 3
[two] => 2
[three] => 1
[obj] => Array
(
[What] => testcaseB Object
(
[one] => 4
[two] => 2
[three] => 1
[obj] => Array
(
[Spam] => testcaseB Object
(
[one] => 3
[two] => 2
[three] => 1
[obj] => Array
(
[America] => testcaseB Object
(
[one] => 2
[two] => 2
[three] => 1
)
)
)
[EU] => testcaseB Object
(
[one] => 4
[two] => 2
[three] => 1
[obj] => Array
(
[France] => testcaseB Object
(
[one] => 3
[two] => 2
[three] => 1
)
)
)
)
)
)
)
)
这产生了一系列不同但仍不可预测的结果
class testcaseA {
public function __construct($one=0, $two=1, $three=2) {
$this->obj = new testcaseB(++$one, $three, $two);
}
public function &get($what){
return $this->obj->get($what);
}
}
class testcaseB {
public function __construct($one, &$two, &$three) {
$this->one=$one;
$this->two=$two;
$this->three=$three;
echo "[New:\$one={$one}]:";
}
//public function &get($what){
public function &get($what){
//echo "<p>You asked for $what. $what ain't no country I ever heard of.<br />";
echo "Get:{$what}:[{$this->one}]<br />";
if(!isset($this->obj[$what])){
$this->obj[$what] = new testcaseB(++$this->one,$this->two,$this->three);
}
return $this->obj[$what];
}
}
echo "STARTING:<br />";
ini_set('display_errors',1);
error_reporting(E_ALL);
echo "REALLY STARTING:<br />";
echo "<pre>";
echo "<p>One at a time:</p>";
$bob = new testcaseA();
$a=$bob->get("What");
$b=$a->get("Spam");
$c=$b->get("America");
$d=$a->get("EU");
$e=$d->get("France");
echo "<br />";
print_r($bob);
echo "<p>Chained:</p>";
$bobby = new testcaseA();
$bobby->get("What")->get("Spam")->get("America");
$bobby->get("What")->get("EU")->get("France");
echo "<br />";
print_r($bob);
这仍然不是我所追求的行为,但它更接近。我需要的是使用一个对象链来遍历树(与第一种情况一样),一个指向$2和$3的指针,在实际情况下,它们是数组,不交换。我不想做的是不必要地复制对象
另一方面,我确实需要让所有对象共享一对它们都使用的变量
我的猜测是,方法get()
可以是byval
而不是byref
,尽管本能上这似乎是错误的
有人能解释一下$one
值在做什么吗
还有谁能帮助我理解第一个测试用例的行为,特别是第一次对数组中的值的行为
更新
利用令人敬畏的建议,我们的测试用例现在看起来像:
STARTING:
REALLY STARTING:
One at a time:
[New:$one=1]:Get:What:[1]
[New:$one=2]:Get:Spam:[2]
[New:$one=3]:Get:America:[3]
[New:$one=4]:Get:EU:[3]
[New:$one=4]:Get:France:[4]
[New:$one=5]:
testcaseA Object
(
[obj] => testcaseB Object
(
[one] => 2
[two] => 2
[three] => 1
[obj] => Array
(
[What] => testcaseB Object
(
[one] => 4
[two] => 2
[three] => 1
[obj] => Array
(
[Spam] => testcaseB Object
(
[one] => 4
[two] => 2
[three] => 1
[obj] => Array
(
[America] => testcaseB Object
(
[one] => 4
[two] => 2
[three] => 1
)
)
)
[EU] => testcaseB Object
(
[one] => 5
[two] => 2
[three] => 1
[obj] => Array
(
[France] => testcaseB Object
(
[one] => 5
[two] => 2
[three] => 1
)
)
)
)
)
)
)
)
Chained:
[New:$one=1]:Get:What:[1]
[New:$one=2]:Get:Spam:[2]
[New:$one=3]:Get:America:[3]
[New:$one=4]:Get:What:[2]
Get:EU:[3]
[New:$one=4]:Get:France:[4]
[New:$one=5]:
testcaseA Object
(
[obj] => testcaseB Object
(
[one] => 2
[two] => 2
[three] => 1
[obj] => Array
(
[What] => testcaseB Object
(
[one] => 4
[two] => 2
[three] => 1
[obj] => Array
(
[Spam] => testcaseB Object
(
[one] => 4
[two] => 2
[three] => 1
[obj] => Array
(
[America] => testcaseB Object
(
[one] => 4
[two] => 2
[three] => 1
)
)
)
[EU] => testcaseB Object
(
[one] => 5
[two] => 2
[three] => 1
[obj] => Array
(
[France] => testcaseB Object
(
[one] => 5
[two] => 2
[three] => 1
)
)
)
)
)
)
)
)
输出数字似乎正确,但堆栈中的
$one
已关闭 如果我理解你的困惑(我想我也理解),你就会发现两者之间的细微差别。这可以很容易地在代码中演示:
$this->obj[$what] = new testcaseB($this->one++,$this->two,$this->three);
另一方面:
$this->obj[$what] = new testcaseB(++$this->one,$this->two,$this->three);
基本上,通过将++
放在您增加的值之前,您将获得新值。通过将其放在后面(如您所做的),您将获得旧值
我认为您代码中的关键行是:
…应该是这样的:
使用post增量,$this->one
的初始值将通过所有后续迭代进行
作为旁注,我认为您不必担心在这里通过引用返回,因为PHP5中的所有对象都是通过引用传递的。如果我理解您的困惑(我想我也理解),那么您已经遇到了它们之间的微妙区别。这可以很容易地在代码中演示:
$this->obj[$what] = new testcaseB($this->one++,$this->two,$this->three);
另一方面:
$this->obj[$what] = new testcaseB(++$this->one,$this->two,$this->three);
基本上,通过将++
放在您增加的值之前,您将获得新值。通过将其放在后面(如您所做的),您将获得旧值
我认为您代码中的关键行是:
…应该是这样的:
使用post增量,$this->one
的初始值将通过所有后续迭代进行
顺便说一句,我认为您不必担心在这里通过引用返回,因为PHP5中的所有对象都是通过引用传递的。有趣的是,当get成为一个普通的byval函数时,我没有得到预期的行为。运行这两个版本(一个接一个和chain)在另一个顺序中,仍然有链对对象堆栈做了不好的事情。是的,$I++和+++$I不一样,这是我现在接受的教育。有趣的是,当get变成一个普通的byval函数时,我没有得到预期的行为。以另一个顺序运行两个版本(一个接一个和chain)仍然会让chain对对象堆栈做坏事。是的,$I++和+++$I不一样,这就是为什么这些值有自己的作用。我以后一定会小心的。让我真正感到困惑的是$bob不是一棵树-“垃圾邮件”和“美国”首先不见了。@MatthewBrown好的,让我再看一眼。太棒了,谢谢。在我最初的(非平凡的示例)中,
$this->value
似乎不存在于名为&get($child)
的方法中,但在所有其他位置都可用。$one++只是用来表示这一点(令人尴尬的是,我忽略了一个细微的差别)。@MatthewBrown好的,我明白了。问题行实际上与我上面提到的相同。因为在创建新对象之前没有检查$this->obj[$what]
是否已经存在,所以您正在覆盖上一次迭代中的实例。要解决此问题,只需在行前面加上if(!isset($this->obj[$what])
。PS我喜欢名为$bob
的变量,这也有助于解释测试用例的行为。然而:$bobby->get(“什么”)->get(“垃圾邮件”)->get(“美国”)代码>$bobby->get(“什么”)->get(“欧盟”)->get(“法国”)当调用get(“What”)
时,code>生成1和3。这并不是说你的答案不正确,只是因为我的测试用例是垃圾。这就解释了价值观在做自己的事情。我以后一定会小心的。让我真正感到困惑的是$bob不是一棵树-“垃圾邮件”和“美国”首先不见了。@MatthewBrown好的,让我再看一眼。太棒了,谢谢。在我最初的(非平凡的示例)中,$this->value
似乎不存在于名为&get($child)
的方法中,但在所有其他位置都可用。$one++只是用来表示这一点(令人尴尬的是,我忽略了一个细微的差别)。@MatthewBrown好的,我明白了。问题行实际上与我上面提到的相同。因为在创建新对象之前没有检查$this->obj[$what]
是否已经存在,所以您正在覆盖上一次迭代中的实例。要解决此问题,只需在行前面加上if(!isset($this->obj[$what])
。PS我喜欢名字像$bob
的变量,这也有助于解释
$this->obj[$what] = new testcaseB($this->one++,$this->two,$this->three);
$this->obj[$what] = new testcaseB(++$this->one,$this->two,$this->three);