I created a simple class to manage a tree data structure which seemed to behave strangely (code to follow). When it became apparent that this was not a mistake by me I created a test case which produced equally puzzling behavior. This is the same in 5.3 and 5.4.
This is my test case:
<?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);
Now the output that I expected was to see the value for $one increment 1,2,3,1,2,3 and produce a tree shape.
This is the output I actually got:
$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
)
)
)
)
)
)
)
)
This confused me at first but I wondered if maybe the chained usage was setting values in a way I was not expecting.
So I tried this which I added after the previous code:
...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);
This produced a different but still unpredictable set of results.
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=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:
Get:EU:[3]
[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.