Reputation: 16045
Basically how I understand references work is
$a = 5;
$b = &$a;
$a = 10;
echo $b; // 10;
However in this bit of code I'm getting unexpected (for me, which probably has an explanation) result
class Room {
private $users = array();
public function addUser(&$user){
$this->users[] = $user;
}
}
$users = array(
1 => 'Tom',
2 => 'Hank',
3 => 'Sam',
4 => 'John'
);
$room = new Room();
$room->addUser($users[1]);
$room->addUser($users[3]);
unset($users[3]);
echo "<pre>" . print_r($room, true) . "</pre>";
echo "<pre>" . print_r($users, true) . "</pre>";
I expect, after unsetting $users[3]
, the only user inside of $room
to be Tom
, but that is not the case, both Tom
and Sam
are present in the object. Why is unset
not affecting the object's property?
Even if I take things a step further with the example and create a class User
the effect is still the same
class Room {
private $users = array();
public function addUser(&$user){
$this->users[] = $user;
}
}
class User {
public $name;
function __construct($name){
$this->name = $name;
}
}
$users = array(
1 => new User('Tom'),
2 => new User('Hank'),
3 => new User('Sam'),
4 => new User('John')
);
$room = new Room();
$room->addUser($users[1]);
$room->addUser($users[3]);
unset($users[3]);
echo "<pre>" . print_r($room, true) . "</pre>";
echo "<pre>" . print_r($users, true) . "</pre>";
Upvotes: 2
Views: 268
Reputation: 479
You can change a value of arrays, like this:
CODE:
private $users = array();
public function addUser(&$user){
$this->users[] = &$user;
}
}
$users = array(
1 => 'Tom',
2 => 'Hank',
3 => 'Sam',
4 => 'John'
);
$room = new Room();
$room->addUser($users[1]);
$room->addUser($users[3]);
$users[3] = "AAA123";
echo "<pre>" . print_r($room, true) . "</pre>";
echo "<pre>" . print_r($users, true) . "</pre>";
OUTPUT:
Room Object
(
[users:Room:private] => Array
(
[0] => Tom
[1] => AAA123
)
)
Array
(
[1] => Tom
[2] => Hank
[3] => AAA123
[4] => John
)
But delete it's not possible this way... I don't know how to explain, so just give example:
$a = 10;
$b = &$a;
unset($a);
echo $b; // 10
Then you deleting variable name, you not delete zval(container), until refcount
reach 0... then "Garbage Collection" do all work and delete zval...
So method unset() remove variable name only in this case...
Upvotes: 0
Reputation: 1163
Unset operates on symbols, not reference targets.
That is why using unset on an undefined variable doesn't raise any kind of error.
$a = 10;
$b = &$a;
unset($b); // forget the name "$b" exists.
echo $a; // 10
If you want to unset it in both places, you have to assign null
to one of the variables. This is a "hard unset", as opposed to a "soft unset" which is what you are currently doing.
Also you are not assigning a reference, you're assigning a copy.
$this->users[] = &$user;
Upvotes: 4
Reputation: 8885
I think there's a slight logical problem between your desired effect and the way you try to do it.
If I understand correctly, you want to assign users to a container, then unsetting one of those user in a way that it will also be unsetted in your container. This
unset($users[3]);
unsets the value of the fourth element of your users array.
if we did $user[3] = 'foo';
the value contained in the corresponding container's entry will be set to 'foo'
as well, but the container's index key itself will not get unset, or affected by the reference, because it is not part of the referenced value
If you want to unset the user, either you keep track of which index key is assigned to which user in your container and then delete users with this index key, or you set the value of $users[3]
to null
(or whatever suits your needs) and skip the null
values when dealing with your container
Upvotes: 0
Reputation: 2115
A PHP variable is stored in a container called a "zval". A zval container contains, besides the variable's type and value, two additional bits of information. The first is called "is_ref" and is a boolean value indicating whether or not the variable is part of a "reference set". (...) Since PHP allows user-land references, as created by the & operator, a zval container also has an internal reference counting mechanism to optimize memory usage. This second piece of additional information, called "refcount", contains how many variable names (also called symbols) point to this one zval container.
(...)
Variable containers get destroyed when the "refcount" reaches zero. The "refcount" gets decreased by one when any symbol linked to the variable container leaves the scope (e.g. when the function ends) or when unset() is called on a symbol.
Example with arrays:
<?php
$a = array(
0 => 'aaa',
1 => 'bbb',
2 => 'ccc',
);
debug_zval_dump($a);
// ... string(3) "bbb" refcount(1) ...
$b = array();
$b[0] = &$a[0];
$b[1] = &$a[1];
$a[1] = 'ddd';
debug_zval_dump($a);
// ... &string(3) "bbb" refcount(2) ...
debug_zval_dump($b);
// ... &string(3) "bbb" refcount(2) ...
unset($a[1]);
debug_zval_dump($a);
/*
array(2) refcount(2){
[0]=>
&string(3) "aaa" refcount(2)
[1]=>
&string(3) "ddd" refcount(2)
}
*/
debug_zval_dump($b);
// ... string(3) "ddd" refcount(1) ...
var_dump($a);
/*
array (size=2)
0 => &string 'aaa' (length=3)
2 => string 'ccc' (length=3)
*/
var_dump($b);
/*
array (size=2)
0 => &string 'aaa' (length=3)
1 => string 'ddd' (length=3)
*/
Upvotes: 1
Reputation: 597
Be careful.
You are passing to addUser()
a reference to the string 'Tom'
allocated while building the array $users
.
First, addUser()
should read $this->users[] =& $user;
, otherwise you will be copying the value into $this->users[]
instead of sharing the reference.
Now, both $users
and Room::$users
share the same objects, however unset($users[3])
removes the element mapped by the index 3
from the array, it does not destroy the mapped object.
Upvotes: -1