PHP引用不像我期望的那样工作

时间:2014-10-15 14:25:57

标签: php oop reference

基本上我理解参考工作是

$a = 5;
$b = &$a;
$a = 10;
echo $b; // 10;

然而,在这段代码中,我出乎意料(对我来说,可能有一个解释)结果

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

我希望在取消设置$users[3]之后,$room内的唯一用户为Tom,但情况并非如此,Tom和{{1}存在于对象中。为什么Sam不会影响对象的属性?

编辑:

即使我在示例中更进一步并创建了一个类unset,效果仍然相同

User

5 个答案:

答案 0 :(得分:4)

Unset对符号而非参考目标进行操作。

这就是为什么在未定义的变量上使用unset不会引发任何类型的错误。

$a = 10;
$b = &$a;
unset($b); // forget the name "$b" exists.
echo $a; // 10

如果要在两个地方取消设置,则必须将null分配给其中一个变量。这是一个“难以设置”,而不是你现在正在做的“软设置”。

您也没有指定参考,而是指定副本。

$this->users[] = &$user;

答案 1 :(得分:1)

Reference Counting Basics

  

PHP变量存储在名为&#34; zval&#34;的容器中。一个zval   容器除了包含变量的类型和值之外,还包含两个   额外的信息。第一个被称为&#34; is_ref&#34;而且是一个   boolean值,指示变量是否是a的一部分   &#34;参考集&#34;。 (...)因为PHP允许用户土地引用,如   由&amp;创建操作员,zval容器也有内部   引用计数机制来优化内存使用。这第二个   一条名为&#34; refcount&#34;的附加信息包含多少信息   变量名(也称为符号)指向这个zval容器

     

(...)

     

&#34; refcount&#34;达到零。   &#34;引用&#34;当任何符号链接到时,会减少一个   变量容器离开范围(例如,当函数结束时)或   在符号上调用unset()时

数组示例:

<?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)
 */

答案 2 :(得分:0)

我认为你想要的效果与你尝试的方式之间存在轻微的逻辑问题。

如果我理解正确,您希望将用户分配到容器,然后以一种在容器中也未设置的方式取消设置其中一个用户。此

unset($users[3]);

取消设置用户数组的第四个元素的值。

如果我们$user[3] = 'foo';,相应容器条目中包含的值也将设置为'foo',但容器的索引键本身不会被取消设置,或受参考影响,因为它不是参考值的一部分

如果要取消设置用户,要么跟踪分配给容器中哪个用户的索引键,然后删除具有此索引键的用户,要么将$users[3]的值设置为{{ 1}}(或任何适合您的需求)并在处理容器时跳过null

答案 3 :(得分:0)

您可以更改数组的值,如下所示:

代码:     

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

输出:

Room Object
(
    [users:Room:private] => Array
        (
            [0] => Tom
            [1] => AAA123
        )

)

Array
(
    [1] => Tom
    [2] => Hank
    [3] => AAA123
    [4] => John
)

但是以这种方式删除是不可能的...我不知道该怎么解释,所以举个例子:

$a = 10;
$b = &$a;
unset($a);
echo $b; // 10

然后删除变量名,直到refcount达到0为止,才删除zval(container)...然后“垃圾回收”将全部工作并删除zval ...

因此,仅在这种情况下,方法unset()才删除变量名称...

答案 4 :(得分:-1)

小心。

您正在向addUser()传递对构建数组'Tom'时分配的字符串$users的引用。

首先,addUser()应该是$this->users[] =& $user;,否则您将把值复制到$this->users[]而不是共享参考。

现在,$usersRoom::$users共享相同的对象,但unset($users[3]) 会删除索引3 映射的元素从数组,它不会破坏映射对象。