玩引用

时间:2013-05-20 23:43:02

标签: php reference binary-operators

我明白为什么

$a = new ArrayObject();
$a['ID'] = 42;
$b = &$a['ID'];
$c = $a;
$c['ID'] = 37;
echo $a['ID']."\n";
echo $b."\n";
echo $c['ID']."\n";

输出37,42,37

,而

$a = new ArrayObject();
$a['ID'] = 42;
$b = &$a['ID'];
$c = $a;
$b = 37;
echo $a['ID']."\n";
echo $b."\n";
echo $c['ID']."\n";

输出37,37,37

在这两种情况下,$b都是对$a['ID']的引用,而$c是指向与$a相同的对象的指针。

$b更改$a['ID']$c['ID']更改时,因为分配$b会更改$a['ID']引用的值。

$c['ID']更改时,会为$a['ID']分配新的int,$b不再引用$a['ID']

但这让我感到高兴

$a = new ArrayObject();
$a['ID'] = 42;
$b = &$a['ID'];
$c = $a;
$c['ID'] &= 0;
$c['ID'] |= 37;
echo $a['ID']."\n";
echo $b."\n";
echo $c['ID']."\n";

(输出37,37,37)

这是定义的行为吗? 我在文档中没有看到任何相关内容......

2 个答案:

答案 0 :(得分:3)

让我们以此代码为基础:(refcounting documentation

$a = new ArrayObject();
$a['ID'] = 42;
$b = &$a['ID'];
$c = $a;

xdebug_debug_zval('a');
xdebug_debug_zval('b');
xdebug_debug_zval('c');

这给出了:

a:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
  public 'ID' => (refcount=2, is_ref=1),int 42
b:
(refcount=2, is_ref=1),int 42
c:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
  public 'ID' => (refcount=2, is_ref=1),int 42

正如你所说: $a是一个对象,$b$a['ID']的引用($a['ID']$brefcount=2, is_ref=1) 和$ c is copy as a reference (since PHP5),所以$ c是$ a的引用(它现在是同一个对象:refcount=2, is_ref=0


如果我们这样做:$c['ID'] = 37;

我们得到:

a:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
  public 'ID' => (refcount=1, is_ref=0),int 37
b:
(refcount=1, is_ref=0),int 42
c:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
  public 'ID' => (refcount=1, is_ref=0),int 37

$c['ID']被分配了一个新的int so =>

$b变得独立(refcount=1is_ref=0),以及$a['ID']$c['ID']

但由于$c$a是相关的,$a['ID']$c['ID']采用相同的值37。


现在,我们来看看基本代码:$c['ID'] &= 0;

<强>更新: 出乎意料的是,我们得到了:

a:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
  public 'ID' => (refcount=2, is_ref=1),int 0
b:
(refcount=2, is_ref=1),int 0
c:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
  public 'ID' => (refcount=2, is_ref=1),int 0

而不是:(if:$c['ID'] = $c['ID'] & 0;

a:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
  public 'ID' => (refcount=1, is_ref=0),int 0
b:
(refcount=1, is_ref=0),int 42
c:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
  public 'ID' => (refcount=1, is_ref=0),int 0

ArrayObject实现ArrayAccess所以:

如评论中所述documented here

直接修改是完全替换数组维度的值,如$ obj [6] = 7.另一方面,间接修改只会更改维度的一部分,或尝试通过引用另一个变量来指定维度,如$ obj [6] [7] = 7或$ var =&amp; $ OBJ [6]。使用++的增量和使用 - 的减量也以需要间接修改的方式实现。

可能的答案:

“组合运算符(+ =, - =,&amp; =,| =)可以以相同的方式工作(间接修改。)”:

  

refcountis_ref因此(在我们的例子中)不会影响所有人的价值   相关变量被修改。 ($c['ID'] =&gt; $a['ID'] =&gt; $b)

答案 1 :(得分:2)

它或多或少地定义了(但有时没有记录)行为;主要是因为$a不是array而是ArrayObject

让我们先来看看你的第三个代码片段:

$a = new ArrayObject();
$a['ID'] = 42;
$b = &$a['ID'];
$c = $a;
$c['ID'] &= 0;

最后一项任务转换为:

$tmp = &$c->offsetGet('ID');
$tmp &= 0; // or: $tmp = $tmp & 0;

这里的外卖点是仅调用 offsetGet(),它会将引用返回到$c['ID'],如{{3}所述}}。由于未调用offsetSet(),因此$b的值也会发生变化。

顺便说一句,增量(++)和减量运算符( - )以类似的方式工作,不会调用offsetSet()

<强>差异

这与你的第一个例子不同:

$a = new ArrayObject();
$a['ID'] = 42;
$b = &$a['ID'];
$c = $a;
$c['ID'] = 37;

最后一个语句具有以下等价物:

$c->offsetSet('ID', 37);

在将新值分配给$c['ID']之前,之前的值实际为unset();这就是为什么$b是唯一仍然保留42的变量。

当您使用对象而不是数字时,可以看到此行为的证据:

class MyLoggerObj
{
        public function __destruct()
        {
                echo "Destruct of " . __CLASS__ . "\n";
        }
}

$a = new ArrayObject();
$a['ID'] = new MyLoggerObj();
$a['ID'] = 37;

echo $a['ID']."\n";

this comment

Destruct of MyLoggerObj
37

如您所见,析构函数在MyLoggerObj上调用;那是因为在这种情况下,没有任何变量可以保留该值。

<强>加成

如果您试图通过延长offsetGet()来查找何时调用offsetSet()ArrayObject,那么您会发现自己无法正确模仿mixed &offsetGet($key);而感到失望。实际上,这样做会改变ArrayObject的行为,从而无法证明这个类的行为。