class Registry {
public $reg = null;
static function _access(&$obj, &$accessor = null) {
if ($obj === null || !is_a($obj, __CLASS__)) {
$obj = new Registry();
}
$accessor = $obj->reg;
$obj->reg = &$accessor; /* -- error here -- */
}
}
$reg = null;
$obj = &$reg;
$acc = null;
for ($i=0; $i<3;++$i) {
Registry::_access($obj, $acc);
$obj = &$acc;
}
var_dump($reg);
I tried to boil down my code to this example function, here is what it should do: It takes an object and an accessor. If the object is null or not an obj of class Registry, it should make it one. Then it should let the accessor have the value of the reg field and make it point to it.
In the for-loor loop, I do this 3 times, setting the next obj to the previous accessor. So the expected data-structure in the end is:
Registry $obj has a field $reg that is a Registry with a field $reg that is a Registry with a field $reg that is null. Probably its one more but you get the point.
However, I get this error: " Creating default object from empty value in … ", pointing to the marked line.
What is happening there and how can I do this…?
Supplement:
Before the line with the error, $obj is an object of class Registry, so it has a field $reg. It should not be possible that it is a "default object". So: how can this happen?
Expected results
0th loop: $obj = null
1st loop: $obj = Registry with $reg = null
2nd loop: $obj = Registry with $reg = Registry with $reg = null
etc.
The problem is, that regardless of how you declare a function, if you use an ampersand (function foo(&$obj)
) or dont use it function foo($obj)
on an object, you will always get an "unset-on-reference"-reference to the object. So, you can change the value of a reference inside a function, but not what the reference is referencing:
$a = 'test1';
$b = &$a; // b is linked to the value of a
foo($b);
function foo(&$obj) {
// $obj is linked to the value of a
$obj = &$whatever; // $obj is now a link to $whatever,
// but outside $b has NOT changed accordingly.
}
The only way I have found to circumvent this is by either return-by-reference (function &foo()
) – but then you can only return one value – or by using a wrapper:
class Wrapper {
public $pt = null;
}
class Test {
public $test1 = 'test1';
public $test2 = 'test2';
// wont work, you cant change the reference inside
// just its value.
public function set(&$obj, $num) {
if ($num == 1) {
$obj = $this->test1;
$this->test1 = &$obj;
$obj = &$this->test2; // nope
} else {
$obj = $this->test2;
$this->test1 = &$obj;
$obj = &$this->test2; // nope
}
}
// same, but with wrapper
public function setW($obj, $num) {
if ($num == 1) {
$obj->pt = &$this->test1;
} else {
$obj->pt = &$this->test2;
}
}
}
$T = new Test();
$k = 'test0';
$T->set($k,1);
echo $k."<br>"; // test1
$T->set($k,2);
echo $k."<br>"; // test2
$T->test2 = 'changed2';
echo $k."<br>"; // still test2 :-(
echo "<br>Test with wrapper<br>";
$T = new Test();
$k = new Wrapper();
$k->pt = 'test0';
$T->setW($k,1);
echo $k->pt."<br>"; // test1
$T->setW($k,2);
echo $k->pt."<br>"; // test2
$T->test2 = 'changed2';
echo $k->pt."<br>"; // changed2