7

非常に奇妙なphp動作に遭遇しました(ubuntu 10.04の5.3.2)。ローカル スコープ内で発生するはずの unset が、呼び出し元関数のスコープに影響を与えています。次のスニペットは、バグであるとしか思えないコードを簡略化したものです。

<?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;) を参照している場合でも、バグのある動作はなくなります。

アップデート

バグレポートを提出しました

4

2 に答える 2

1

うん、バグのようだ。

関数の名前が示すように、should_not_alter値によって渡されるため、配列を変更しないでください。(もちろん、名前だけに基づいているわけではありません。また、その定義に基づいて何も変更するべきではありません。)

コメント$in_ref =& $in['level1'];がそれを放っておくという事実は$in、それがバグであることのさらなる証拠のようです。それはかなり奇妙なちょっとした癖です。それを引き起こすために内部で何が起こっているのか分かりません。

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

于 2012-12-28T04:36:58.803 に答える
1
$data = should_not_alter($data)

$dataこの行は、配列を の戻り値で上書きしています。should_not_alterこれは$inです。これは正常な動作です。

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

簡潔な答え:

unset($in_ref)関数を呼び出す前に参照変数を削除しshould_only_unset_locally()ます。

長い答え:

配列要素への参照が作成されると、配列要素は参照に置き換えられます。この動作は奇妙ですが、バグではありません。これは言語の機能であり、設計によるものです。

次の 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値はorには存在しません$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;
}
于 2012-12-28T04:25:36.723 に答える