Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to dereference a reference in PHP?

Tags:

php

I have an array I'm using as a stack to store a path through a tree. Each element points to a node in the tree and I want to pop the last element off and then set the object referred to by that element to null.

Basically:

$node = array_pop($path);
*$node = null;

assuming that PHP had a '*' operator as C-ish languages do. Right now I have the ugly solution of starting at the parent node and remembering which child I took and then setting that to null as in:

if($goLeft) {
    $parent->left = null;
} else {
    $parent->right = null;
}

I say this is ugly because the array containing the path is created by a public function in my tree class. I'd like to expose the ability to work directly on the nodes in a path through the tree without exposing an implementation detail that addresses an idiosyncrasy (feature?) in PHP. ATM I need to include a boolean in the return value ($goLeft in this case) just so I can workaround an inability to dereference a reference.

This is the second time I've encountered this problem, so if anyone knows a way I can do something similar to the first block of code please share!

(EDIT)

After experimenting with many permutations of &'s and arrays, it turns out that the basic problem was that I had misinterpreted the reason for an error I was getting.

I tried

$a = ($x > $y) ? &$foo[$bar] : $blah;

and got " syntax error, unexpected '&' ". I interpreted this to mean that the problem was using the &-operator on $foo[$bar]. It actually turns out that the culprit is the ?-operator, as

if($x > $y) {
    $a = &$foo[$bar];
} else {
    $a = null;
}

works perfectly fine. I thus went on a wild goose chase looking for a workaround for a problem that didn't exist. As long as I don't break the chain of &'s, PHP does what I want, which is to operate on the object referred to by a variable (not the variable itself). Example

$a1 = new SomeClass;
$a2 = &$a1;
$a3 = &$a2;
$a4 = &$a3;

$a4 = 42;    // This actually sets $a1 to 42
var_dump($a1); // Emits 42

What messed me up is that I thought objects are passed around by reference anyway (this is wrong), so I didn't think the & was necessary if the expression resolved to an object. I mean:

class A {
    public $b;
}

class B {}

$a = new A;
$a->b = new B; 

$c1 = $a->b; 
$c2 = &$a->b;

$c1 = 42; // Merely assigns 42 to $c1
$c2 = 42; // Assigns 42 to $a->b

It turns out that this exact issue is addressed at http://www.php.net/manual/en/language.oop5.references.php. Wish that had sunk in the first time I read it!

like image 308
kahulio Avatar asked May 15 '12 15:05

kahulio


2 Answers

Very interesting question! I may have found a workaround: if you populate the array with object references, with the & operator, you can destroy the original object by setting that array value to NULL. You have to operate on the array directly, instead of using a variable returned by array_pop. After that you can pop the array to free that position (that would then contain a NULL value).

This is what I mean (based on Rocket's code):

$a=(object)'a';
$b=array(&$a);
$b[0] = NULL;
// array still contains an element
array_pop($b);
// now array is empty
var_dump($a); // NULL

http://codepad.org/3D7Lphde

like image 195
bfavaretto Avatar answered Sep 20 '22 13:09

bfavaretto


I wish I could remember where I read this, but PHP works by maintaining a counter of references to a given object. You have some object (e.g. a Tree) that has a reference to some nodes. When you use array_pop, a reference to the node object is returned (i.e. an additional reference is created), but the original reference still exists. When you unset the popped reference, that is destroyed but the original object is not destroyed because Tree still has that reference. The only way to free the memory of that object is to have Tree destroy it personally (which seems to be what you're doing in the second code block).

PHP does not seem to have any method for forcing memory deallocation or garbage collection, so unless you carefully handle your references, you're stuck.

This is not possible

P.S. I'm still really confused about what you're trying to do. Rocket's explanation helps, but what is $path, and how does it relate to the second block?

like image 40
Explosion Pills Avatar answered Sep 21 '22 13:09

Explosion Pills