19

我明白为什么

$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

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

什么时候$b改变$a['ID']$c['ID']改变,因为分配$b改变了引用的值$a['ID']

$c['ID']更改时,一个新的 int 被分配给$a['ID']$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)

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

4

2 回答 2

3

让我们以此代码为基础:(引用文档

$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']$b: refcount=2, is_ref=1) 的引用,并且 $c是作为引用的复制(从 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']被分配了一个新的intso =>

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

BUT as $cand aredependent $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

而不是:(如果$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 所以:

如评论中所述在此处记录

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

一个可能的答案:

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

refcount并且is_ref不受影响,因此(在我们的例子中)所有相关变量的值都会被修改。( $c['ID']=> $a['ID']=>$b)

于 2013-05-22T09:22:15.577 回答
2

它或多或少是已定义(但有时未记录)的行为;主要是因为$ais not an arraybut an 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']本注释中所述。因为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";

输出

Destruct of MyLoggerObj
37

如您所见,析构函数被调用MyLoggerObj;那是因为在这种情况下,不再有变量保留该值。

奖金

如果您试图找出何时offsetGet()offsetSet()通过扩展来调用,ArrayObject您会失望地发现您无法正确模仿mixed &offsetGet($key);。事实上,这样做会改变 的行为,ArrayObject以至于无法证明此类的行为。

于 2013-05-22T09:32:41.317 回答