1

我创建了一个简单的类来管理一个看起来很奇怪的树数据结构(后面的代码)。当很明显这不是我的错误时,我创建了一个测试用例,它产生了同样令人费解的行为。这在 5.3 和 5.4 中是相同的。

这是我的测试用例:

<?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);

现在我期望的输出是看到 $one 的值增加 1,2,3,1,2,3 并产生一个树形。

这是我实际得到的输出:

$one=0

You asked for What. What ain't no country I ever heard of.
Check: [0], 2, 1. Is this thing on?
$one=0

You asked for Spam. Spam ain't no country I ever heard of.
Check: [0], 2, 1. Is this thing on?
$one=0

You asked for America. America ain't no country I ever heard of.
Check: [0], 2, 1. Is this thing on?
$one=0

You asked for What. What ain't no country I ever heard of.
Check: [1], 2, 1. Is this thing on?
$one=1

You asked for EU. EU ain't no country I ever heard of.
Check: [1], 2, 1. Is this thing on?
$one=1

You asked for France. France ain't no country I ever heard of.
Check: [1], 2, 1. Is this thing on?
$one=1

testcaseA Object
(
    [obj] => testcaseB Object
        (
            [one] => 2
            [two] => 2
            [three] => 1
            [obj] => Array
                (
                    [What] => testcaseB Object
                        (
                            [one] => 2
                            [two] => 2
                            [three] => 1
                            [obj] => Array
                                (
                                    [EU] => testcaseB Object
                                        (
                                            [one] => 2
                                            [two] => 2
                                            [three] => 1
                                            [obj] => Array
                                                (
                                                    [France] => testcaseB Object
                                                        (
                                                            [one] => 1
                                                            [two] => 2
                                                            [three] => 1
                                                        )

                                                )

                                        )

                                )

                        )

                )

        )

)

起初这让我很困惑,但我想知道链接使用是否以我没有预料到的方式设置值。

所以我尝试了在前面的代码之后添加的这个:

...same classes and initial code as before...
$a=$bob->get("What");
$b=$a->get("Spam");
$c=$b->get("America");
//$bob->get("What")
$d=$a->get("EU");
$e=$d->get("France");  
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
                                                        )

                                                )

                                        )

                                )

                        )

                )

        )

)

This is still not the behavior that I am after but it is closer. What I need is to use a chain of objects to traverse the tree (as with the first case), a pointer to the values $two and $three which in the real case are arrays and not swapped about. What I don't want to do is copy objects needlessly.

On the other hand I do need to have all the objects share a single pair of variables which they all use.

My guess is that the method get() could afford to be byval rather than byref although instinctively this seems wrong.

Can anyone explain what the $one value is doing?

Also can anyone help me understand the behavior of the first test case especially to the values in the array the first time?

UPDATE

Making use of the awesome suggestions our test case now looks like:

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);

The out put of which is:

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
                                                        )

                                                )

                                        )

                                )

                        )

                )

        )

)

The output numbers seem right but the $one are off in the stack.

4

1 回答 1

1

If I understand what you are confused about (and I think I do) you have run into the subtle difference between pre- and post-increment. This can be most easily demonstrated in code:

$a = 1;
echo $a++; // 1
echo $a;   // 2

On the other hand:

$a = 1;
echo ++$a; // 2
echo $a;   // 2

Basically, by placing ++ before the value you increment, you will get the new value. By placing it after (as you have done) you will get the old value.

I think the key line in your code is this:

$this->obj[$what] = new testcaseB($this->one++,$this->two,$this->three);

...should be this:

$this->obj[$what] = new testcaseB(++$this->one,$this->two,$this->three);

With the post-increment, the initial value of $this->one will be carried through all subsequent iterations.

As a side note, I don't think you need to worry about returning by reference here, since all objects in PHP5 are passed by reference anyway.

于 2012-07-03T15:00:35.827 回答