ホームページ >php教程 >php手册 >PHP を慎重に使用する理由

PHP を慎重に使用する理由

WBOY
WBOYオリジナル
2016-06-13 11:58:46975ブラウズ

引用类型(Reference)在许多计算机语言中都被使用,而且是作为一个非常强大而实用的特性存在。它有类似指针(Pointer)的实现,却又有不同于指针的表现。例如C++的引用,可以让不同变量指向同一个对象,同时又保有直接使用dot来获取对象成员,不用繁琐的使用dereference运算符(*)和Pointer to Member运算符(->)。Java和C#中就直接以引用为主要类型,尽量让开发人员避免使用指针。

PHP中也引入了引用类型,在对对象赋值传递上,基本可视为是同于Java/C#的引用传递(具体请见Objects and references)。但同时又支持在基础类型上通过引用运算符(&)来获得内容的引用。不过在实际的使用中,PHP的引用类型因为整个PHP设计结构而存在着许多的问题,使得在程序出现非预计的结果。


引用变量可被赋予新的引用

在C++中,引用类型的变量只能在其定义时被赋予引用值,所以我们只要追踪到变量的定义处就可以知道变量是在操作哪个内容。

但是PHP不同,PHP里模糊了变量的定义,可以不定义就使用的变量。所以可以让变量被多次赋予引用值。

复制代码 代码如下:


$x = 21;
$y = 7;

$z = &$x;
$z = &$y;

var_dump($x,$y,$z);


初次看起来,让人的感觉是$z变成了对$x的引用,然后让$z的内容变成了对$y的引用,也就是说$x和$z都成对$y的引用。但是实际输出结果是:

复制代码 代码如下:


int(21)
int(7)
int(7)


从结果上看出,$x保持不变,只是$z被改变成了对$y的引用。相当于先unset了$z变量然后赋予了新值。

复制代码 代码如下:


$z = &$x;
unset($z);
$z = &$y;


这其实是比较合理逻辑,就比如下边的代码,我们并不是得到类似于“指向指针的指针(Pointer point to a Pointer)”那样的“引用引用的引用(Reference refer to a Referenece)”,只是多个引用到同一块内容的引用变量。

复制代码 代码如下:


$x = 21;
$y = &$x;
$z = &$y



引用数组元素会让该元素变成引用类型

对于变量上取引用,并不会造成原变量类型的改变,但是如果取的是数组中的元素,却会让该元素也变成引用类型。

在看问题代码前,首先要指出的是:
Array assignment always involves value copying. Use the reference operator to copy an array by reference.

也就是说PHP的数组赋值是copy而非引用,赋值过程会创建新的数组赋予被赋值的变量。在新变量上的数组操作并不会影响到原数组变量中的内容。

复制代码 代码如下:


$a = array(21, 7);
$b = $a;
$b[0] = 7;
var_dump($a);
echo '
';
var_dump($b);

//Output:
//array(2) { [0]=> int(21) [1]=> int(7) }
//array(2) { [0]=> int(7) [1]=> int(7) }


下边我们再来看看如果引用数组中的元素,会有什么异常。

复制代码 代码如下:


$a = array(21, 7);
$c = & $a[0];
$b = $a;
$b[0]= "21";
$b[1]= "7";

var_dump($a);
echo '
';
var_dump($b);
echo '
';
var_dump($c);
echo '
';

// Output:
// array(2) { [0]=> &string(2) "21" [1]=> int(7) }
// array(2) { [0]=> &string(2) "21" [1]=> string(1) "7" }
// string(2) "21"


代码中$b跟之前的只是简单的赋值,只是在之前多了一部取第一个元素的引用,但理应还是拷贝了一个新的数组。可是结果却是对$b的修改,同时也改变了$a的第一个元素,而第二个元素没有影响。

出力からは異常な点もわかります。つまり、配列の最初の要素の型に余分な '&' 記号が含まれています。そしてこれが参照演算子です。つまり、配列の最初の要素が参照型になります。したがって、割り当ても値のコピーではなく参照コピーになります。

この問題は非常に奇妙で、開発中に多くの無用なトラブルを引き起こしました。当初、コピーされた配列は元の配列に関連していないと考えていましたが、この予期しない参照型のせいで、操作中に混乱してしまいました。元の配列が影響を受けます。

これが PHP のバグなのか、それとも意図的にこのように設計されているのかはわかりません。長い間オンラインで検索していましたが、この利便性について関連する説明はありません。これについては、Float Middle の「PHP: 配列要素への参照は危険です」と Symmetric Designs の「参照による PHP 配列へのアクセスに関する問題」だけです。 , しかし理由は明かされませんでした。

その後、PHP バグ レポートでいくつかの関連レポート (Bug6417、Bug7412、Bug15025、Bug20993) を確認しました。これはバグであり、後のバージョンでは修正されているという人もいます。詳細はわかりませんが、配列への参照の使用を避けることしかできません。

さらに興味深いのは、これらの参照の設定を解除して 1 つだけを残すと、配列要素は参照のない通常の型になるということです。

コードをコピーします コードは次のとおりです。


unset($b); c);
var_dump($a);

// 出力:
//array(2) { [0]=> string(2) "21"=> ; int( 7) }



PHP 参照の使用を避ける これは、実際には PHP 配列マニュアルに記載されている注意すべき点です。ほとんどの場合、参照を通じて far 配列の値を変更したい場合に発生します (この記事を参照)。

実際、参照付きの foreach を使用して配列要素の値を変更したいのですが、これは主に PHP の配列が連想配列であるためです。この種の配列は長さが無限で、インデックスが不連続である可能性があり、文字列と整数が含まれるためです。同時にインデックスとして使用できます。」したがって、for ループを使用して整数インデックスを単純にインクリメントすることはできません。

もちろん、以下のコードのように $key を介して配列要素の値を直接変更することもできますが、これには効率上の問題が生じる可能性があります。


コードをコピーします コードは次のとおりです:

foreach ($array_var as $key => $value)
$array_var [$key] = $newValue

参照のもう 1 つの一般的な場所は、関数呼び出しでパラメーターを参照によって渡すことです。主な理由は、このメソッドを使用して関数が複数の戻り値を返せるようにするためです。たとえば、関数の実行中にエラーが発生し、戻り値が無効かどうかを表現を使用して示したいとします。

しかし、PHP 関数はさまざまな型を返すことができるため、表現として参照パラメーターを渡す必要はありません。本当に複数の戻り値が必要な場合でも、解決策として「主キーとして文字列を持つ配列」を返すことができますが、各要素がその結果に対応することをドキュメントで指摘する必要がある場合があります。

これを操作するより良い方法は、参照変数

を使用する必要がなくなったときに、すぐに変数に対して unset を使用してコンテンツとの接続を切り替えることです。また、変数が参照型でない場合でも、その変数が使用されていないことを 確認します 。その変数に対して unset を呼び出しても問題はありません。少なくとも、後で変数が再割り当てされたときに、前の結果に影響を与えないことが保証されます。

参照による PHP 配列へのアクセスに関する問題 - 対称デザイン
    PHP: 配列要素への参照は危険です – Float Middle
  1. 参考文献と foreach - Johannes Schlüter
  2. 参照の説明 - PHP マニュアル
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。