2013-05-20 37 views
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

在這兩種情況下$b是對$a['ID']的引用,而$c是指向與0123相同的對象的指針。

$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)

這是定義的行爲? 我沒有看到有關的任何文檔中......

+3

是的,我不認爲這是記錄。手冊只說++和 - 執行間接修改,從而觸發'ArrayAccess :: offsetGet()'而不是'ArrayAccess :: offsetSet()'。爲了一致起見,我想+ =, - =,和堂兄弟是以相同的方式實現的。 – cleong

+0

@cleong我似乎無法在文檔中找到它。你能指出哪裏? – webmaster777

回答

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是$一個參考(它現在是相同的對象: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所以=>

$b成爲獨立的(refcount=1is_ref=0),以及$a['ID']$c['ID']

但作爲$c$a依賴,$a['ID']$c['ID']取相同的值37。


現在,讓我們的基本代碼,我們做到:$c['ID'] &= 0;

UPDATE: 出乎意料的是,我們得到:

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這樣:

正如在註釋所述documented here

直接修改是一個在$ OBJ完全替換陣列維度的值,如[6] = 7.另一方面,間接修改只改變部分維度,或嘗試通過引用另一個變量來指定維度,如$ obj [6] [7] = 7或$ var = & $ obj [ 6]。用++進行增量並用 - 進行遞減也需要間接修改。

一個可能的答案:

「組合運算符(+ =, - =,& =,| =)可以擔任相同的方式(間接修改。)」:

refcountis_ref不受影響,因此(在我們的情況下)所有 相關變量的值都會被修改。 ($c['ID'] =>$a['ID'] =>$b)

+1

與示例1相比,您看不到_unexpected_這是怎麼回事? – webmaster777

+1

真的,你應該試試這個代碼。 ('$ c ['ID'] = $ c ['ID']&0') – webmaster777

+0

@ webmaster777,確實...我錯了 – antoox

2

它或多或少是確定的(但有時無證)行爲,主要是因爲$a不是array而是ArrayObject

讓我們來看看你的第三個代碼片段。第一:

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

最後分配轉化爲:

這裏

外賣點只有offsetGet()被調用並返回一個參考$c['ID'],爲this comment指出。由於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"; 

Output

Destruct of MyLoggerObj 
37 

正如你所看到的,調用析構函數上MyLoggerObj;這是因爲在這種情況下,不再有變量持有這個值。

獎金

如果你嘗試找出當offsetGet()offsetSet()通過擴展ArrayObject叫你會失望地發現,你不能正確地模仿mixed &offsetGet($key);。實際上,這樣做會改變ArrayObject的行爲,以至於無法證明此類的行爲。

+1

仍然不服氣:P,怎麼沒有在例子1中更新b ?根據你的offsetset的_expected_行爲,它改變了引用(間接修改),但是b也應該是37。 – webmaster777

+0

我只能猜測其他語義在這裏工作,但一旦我到達電腦,我會給這個方面一些注意。 –

+1

這會使Ex.3導致37,42,37(因爲引用$ c ['id']未被您的標準設置,並且之後設置爲0)。 – webmaster777