2012-12-28 6 views
6

私は非常に奇妙なPHPの動作(ubuntu 10.04では5.3.2)に遭遇しました。ローカルスコープ内で発生するはずの設定が、呼び出し側関数のスコープに影響します。次のスニペットは、バグです私は想定できるものを表示する私のコードを簡略化したものである: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がスコープとリファレンスを通常の(配列ではない)変数で扱う方法と矛盾するためです。予想通り

<?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両方:例えば、配列関数should_only_unset_locally()ではなく、文字列を使用すると、グローバルスコープに影響を及ぼしません。実際には$dataが配列だが$in_refが配列全体(つまり$in_ref =& $in;)を参照していても、バグの振る舞いは消えてしまいます。

更新

i have submitted a bug report

+0

私はそれがそれを閉鎖するために割り当てられたdevを参照してください。彼は問題を正しく解釈していないようです。あなたがすでに何らかのフォローアップを書いている、あるいは書いていると思っているのであれば、興味がありますか? – Corbin

+0

私もそれを見ました。私は歴史の中であなたのコメントを見ました。デベロッパーが私が何を表示しようとしているのか理解できなかったようです。おそらく彼は実際に私のコードを実行していない可能性がありますので、彼はラインをコメントアウトすることは何の違いもなく、明らかにそうです。 – mulllhausen

答えて

0
$data = should_not_alter($data) 

この行は$inあるshould_not_alterの戻り値と$dataアレイを、上書きされます。これは正常な動作です。

また、参照番号$in_ref =& $in['level1'];を作成していても、何もしません。プログラムの出力には影響しません。

短い答え:

should_only_unset_locally()関数を呼び出す前にunset($in_ref)を介して基準変数を削除します。

ロング答えは:配列要素への参照が作成されると

、配列要素を参照して交換されます。この動作は異常ですが、バグではありません。これは言語の機能であり、設計通りのものです。

は、次のPHPプログラムを考える:彼らの両方が同じ値を基準として$a['key1']に値を割り当てる

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

$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 =&$ ['level1'];では確かに効果があります。コメントアウトすると、テスト1またはテスト2の間に、「最初の値」がグローバルスコープ内で設定されません。 – mulllhausen

+0

あなたは正しいです。しかし、グローバルスコープは '$ data'が代入されたときにのみ有効です。 – leepowers

+2

実際に設計されていることを確認するソースがありますか?これはバグではありませんか? – eis

1

うん、バグのように見えます。

関数の名前が示すように、should_not_alterは値渡しのため配列を変更しないでください。 (私はもちろんその名前からちょうどその名前に基づいていない - それはその定義に基づいて何も変更するべきではありません)$in_ref =& $in['level1'];は、それだけのことをさらに証拠になると思われることは、$in_ref =& $in['level1'];バグ。それはかなり変わった小さな変わったものです。それを引き起こすために内部的に何が起こっているのかは考えられません。

PHPバグトラッカーに関するバグレポートを提出したいと思います。それが価値あるものについては、5.4.6にはまだ存在しています。

+0

、ありがとう。 – mulllhausen

+0

これはバグではありません。それは設計によるもので、私の改訂された答えを見てください:http://stackoverflow.com/a/14064555/212700 – leepowers

関連する問題