2012-12-28 75 views
6

我遇到了一些非常奇怪的PHP行爲(5.3.2在Ubuntu 10.04上)。在本地範圍內發生的未設置會影響呼叫者功能的範圍。下面的代碼片段是我的代碼的簡化,顯示什麼,我只能認爲是一個錯誤:php未設置影響全局範圍的本地參考

<?php 
function should_not_alter($in) 
{ 
    $in_ref =& $in['level1']; 
    should_only_unset_locally($in); 
    return $in; 
} 
function should_only_unset_locally($in) 
{ 
    unset($in['level1']['level2_0']); 
} 
$data = array('level1' => array('level2_0' => 'first value', 'level2_1' => 'second value')); 
$data = should_not_alter($data); //test 1 
//should_only_unset_locally($data); //test 2 
print_r($data); 
?> 

如果你運行上面,你會看到該值'first value'在全球範圍內一直未設置從$data陣列。但是,如果您註釋掉test 1並運行test 2這不會發生。

我只能假設PHP不喜歡引用數組的元素。在我的代碼中,我需要更改$in_ref - 因此在上面的代碼中$in_ref =& $in['level1'];行的原因。我意識到,刪除這條線將解決在全球範圍內未被設置的'first value'的問題,但這不是一個選項。

任何人都可以確認,如果這是PHP的預期行爲?

我懷疑它是一個錯誤,而不是一個功能,因爲這種行爲與php處理範圍和引用與正常(非數組)變量的方式不一致。例如,使用字符串,而不是陣列功能should_only_unset_locally()對全球範圍沒有影響:

<?php 

function should_not_alter($in) 
{ 
    $in_ref =& $in; 
    should_only_unset_locally($in); 
    return $in; 
} 
function should_only_unset_locally($in) 
{ 
    unset($in); 
} 
$data = 'original'; 
$data = should_not_alter($data); //test 1 
//should_only_unset_locally($data); //test 2 
print_r($data); 

?> 

兩者TEST1或TEST2輸出original如預期。實際上,即使$data是一個數組,但$in_ref被引用到整個數組(即$in_ref =& $in;),那麼車的行爲就會消失。

更新

i have submitted a bug report

+0

我看到它被分配給它的開發關閉它。儘管如此,他似乎沒有正確解釋問題。我很好奇,如果你已經寫或考慮寫一些跟進? – Corbin

+0

是的,我也看到了。我在歷史中看到你的評論。似乎開發人員不明白我想表現出什麼。也許他實際上沒有運行我的代碼,因爲他說註釋掉這條線沒有什麼區別,顯然它確實如此。 – mulllhausen

回答

0
$data = should_not_alter($data) 

這條線被重寫$data陣列的should_not_alter返回值,這是$in。這是正常的行爲。

此外,雖然你正在創建一個參考$in_ref =& $in['level1'];,但你沒有做任何事情。它對程序輸出沒有影響。

簡短的回答:

調用should_only_unset_locally()函數之前通過unset($in_ref)刪除引用變量。

龍答案:

當創建一個數組元素的引用,所述陣列元件被替換爲參考。這種行爲很奇怪,但它不是一個錯誤 - 它是語言的一個特徵,並且是設計的。

考慮下面的PHP程序:

<?php 
$a = array(
    'key1' => 'value1', 
    'key2' => 'value2', 
); 
$r = &$a['key1']; 
$a['key1'] = 'value3'; 
var_dump($a['key1']); 
var_dump($r); 
var_dump($a['key1'] === $r); 

Output: 
string(6) "value3" 
string(6) "value3" 
bool(true) 

分配一個值,以改變$a['key1']$r值,因爲它們都引用相同的值。相反更新$r將更新數組元素:

$r = 'value4'; 
var_dump($a['key1']); 
var_dump($r); 

Output: 
string(6) "value4" 
string(6) "value4" 

的值不會住在$r$a['key'] - 這些只是參考。這就像他們都參考了一些幽靈般的隱藏價值。奇怪,是吧?

對於大多數用例來說,這是期望和有用的行爲。

現在將其應用於您的程序。以下行修改本地$in陣列和與參考替換'level1'元件:

$in_ref = &$in['level1']; 

$in_ref不是$in['level1']基準 - 代替它們都引用相同的鬼值。所以,當這條線惡有惡報:

unset($in['level1']['level2_0']); 

PHP認爲$in['level1']作爲一個怪異值的引用,並刪除'level2_0'元素。因爲這是一個參考,所以在should_not_alter()函數的範圍內也會看到這種移除。

解決您的具體問題是摧毀參考變量,它將自動恢復$in['level1']恢復正常行爲:

function should_not_alter($in) { 
    $in_ref =& $in['level1']; 
    // Do some stuff with $in_ref 
    // After you're done with it delete the reference to restore $in['level1'] 
    unset($in_ref); 
    should_only_unset_locally($in); 
    return $in; 
} 
+0

'$ in_ref =&$ in ['level1'];'肯定有效果。如果你將它評論出來,那麼在測試1或測試2期間,'第一個值'在全局範圍內不會被取消設置。 – mulllhausen

+0

你是對的。但是,全局範圍僅在分配了'$ data'時纔會生效。 – leepowers

+2

你會有一些來源確認它確實是設計的,這不是一個錯誤? – eis

1

是啊,看起來像一個錯誤。

正如函數的名字暗示的那樣,should_not_alter不應該因數值傳遞而改變數組。 (我當然不會僅僅基於它的名字 - 它也不應該基於它的定義來改變任何東西)。

評論$in_ref =& $in['level1'];使它離開$in的事實似乎進一步證明了它是一個錯誤。這是一個很奇怪的小怪癖。不知道內部會發生什麼情況。

我會提交一個關於PHP錯誤跟蹤器的錯誤報告。對於它的價值,它仍然存在於5.4.6中。

+0

會做,謝謝。 – mulllhausen

+0

這不是一個錯誤。這是設計,請參閱我的修訂答案:http://stackoverflow.com/a/14064555/212700 – leepowers