ホームページ >バックエンド開発 >PHPチュートリアル >PHPリファレンス(&)の詳しい説明と注意点_PHPチュートリアル
phpのリファレンス(変数、関数、オブジェクトなどの前にアンパサンドを追加する)
PHP での引用とは、異なる名前が同じ変数の内容にアクセスすることを意味します。
C言語のポインタとは異なります。 C言語におけるポインタは変数の内容とメモリ上に格納されているアドレスを格納します。
1. 変数の参照
PHP 参照を使用すると、2 つの変数を使用して同じコンテンツを指すことができます
$a = " ABC " ;
$b =& $a ;
Echo $a ; // ここに出力: ABC
Echo $b; // ここに出力: ABC
$b = " EFG " ;
echo $a ; // ここでの$aの値がEFGになるのでEFGが出力されます
Echo $b; // ここにEFGを出力します
?>
2.関数参照転送(アドレスによる呼び出し)
アドレスによる呼び出しについては詳しく説明しません
コードはすぐ下にあります。
関数テスト(& $a )
{
$a = $a + 100 ;
}
$b = 1 ;
Echo $ b // 出力 1
Test($b); // ここで関数に渡される $b は、実際には $b の変数の内容が配置されているメモリ アドレスです。関数内の $a の値を変更することで、$b の値を変更できます。変わりました
エコー "
" ;
Echo $b; // 101 を出力します
?>
ここでtest(1);を使用するとエラーが発生するので注意してください。
注:
上記の「test($b);」では $b の前に & 記号を追加しないでください。ただし、関数「call_user_func_array」では、渡されたパラメーターを参照したい場合は、次のように & 記号が必要です。次のコード内:
関数 a( & $b ){
$b++ ;
}
$c = 0 ;
call_user_func_array ( ' a ' , array ( & $c ));
エコー $c ;
//出力 1
?>
3. 関数参照が返されます
まずコードを見てください
関数とテスト()
{
static // 静的変数を宣言します
;
$b = $b + 1 ;
$b をエコーします ;
$b を返します ;
}
$a = test(); // このステートメントは $b の値を 1 として出力します
$a = 5 ;
$a = test(); // このステートメントは $b の値を 2
$a =& test(); // このステートメントは $b の値を 3 として出力します
$a = 5 ;
$a = test(); // このステートメントは $b の値を 6
として出力します
?>
以下で説明します:
このように、$a=test(); は実際には関数からの参照を取得しません。理由としては、これは PHP
の規定です。
PHP では、$a=&test(); を通じて取得されるものは関数の参照戻りであると規定されています
参照リターンとは何かというと(PHPマニュアルには「参照リターンは、関数を使用して参照をバインドする変数を見つけたいときに使用します。」と書かれています。)このナンセンスなため、私は長い間理解できませんでした
上記の例を使用して説明します
$a=test() を使用して関数を呼び出すと、関数の値が $a に代入されるだけであり、$a を変更しても関数内の $b
には影響しません。
$a=&test() を通じて関数を呼び出すと、その関数は $b を返す $b 変数のメモリ アドレスと、$a 変数のメモリ アドレスが同じ場所を指すようになります
つまり、これと同等の効果($a=&$b;)なので、$aの値を変更すると$bの値も変更されるので、
を実行した後、
$a=&test();
$a=5;
これから$bの価値は5になります
ここで静的変数を使用するのは、関数の参照戻りを誰もが理解できるようにするためです。実際、関数の参照戻りは主にオブジェクトで使用されます
。
公式の PHP サンプルを添付します:
これは、ポインタを使用してクラス内の変数にアクセスする方法です。
クラスの講師{
プライベート $data = ' こんにちは ' ;
パブリック関数 & get(){
戻る
}
パブリック関数 out(){
$this -> データをエコー;
}
$aa = 新しい話者();
$d = & $aa ->
$aa -> アウト();
$d = ' アレ ' ;
$aa -> アウト();
$d = 'あなた' ;
$aa -> アウト();
?>
出力は「HiHowAreYou」です
4. オブジェクトへの参照
クラスA{
var $abc = " ABC " ;
}
$b = 新しい a;
$c = $b ;
echo $b -> // ここに ABC が出力されます
;
echo $c -> // ここに ABC が出力されます
;
$b -> abc = " DEF " ;
echo $c -> abc; // DEF がここに出力されます
;
?>
上記のコードはPHP5での実行エフェクトです
PHP5では、オブジェクトの代入は参照処理です。上の列では、 $b=new a; $c=$b; は $b=&$b; と同等です。 PHP5 のデフォルトでは、参照によってオブジェクトを呼び出しますが、オブジェクトのコピーを作成し、元のオブジェクトへの変更がコピーに影響しないようにしたい場合があります。この目的のために、PHP5 は __clone と呼ばれる特別なメソッドを定義しています。
PHP 5 では、new は自動的に参照を返すため、ここでの =& の使用は廃止され、E_STRICT レベルのメッセージが生成されます。
例: $b=new a、new a は匿名のオブジェクト インスタンスを生成し、この時点の $b はこの匿名オブジェクトのコピーです。同様に、$c=$b も $b の内容のコピーです。したがって、php4 では、メモリ領域を節約するために、$b=new a を参照モード、つまり $b=& new a に変更するのが一般的です。
別の公式の例を次に示します:
php5 では、「オブジェクト参照」機能を実現するために他に何も追加する必要はありません。
クラスフー{ 保護された $ 名
関数 __construct( $str ){
$ この -& gt; 名前 = $ str;
}
関数 __toString(){
誰が「誰だ」という名前
}
関数 setName( $str ){
$ この -& gt; 名前 = $ str;
}
}
クラスマスターワン{
保護されています
関数 __construct( $f ){
$this -> foo = $f ;
}
関数 __toString(){
関数 setFooName( $str ){
$this -> foo -> setName( $str );
}
}
クラスマスターツー{
保護済み $foo ;
関数 __construct( $f ){
$this -> foo = $f ;
}
関数 __toString(){
return ' マスター: ' 。 __クラス__ 。 ' | foo: ' . $this ->ふー . 「ん」 ;
}
関数 setFooName( $str ){
$this -> foo -> setName( $str );
}
}
$bar = 新しい foo( ' bar ' );
print ( " n " );
print ( " $bar の作成と $barn の印刷のみ " );
print ( $bar );
print ( " n " );
print ( " $baz が $bar を参照し、 $bar と $bazn を出力するようになりました " );
$baz =& $bar ;
print ( $bar );
print ( " n " );
print ( " MasterOne と Master Two を作成し、$bar を両方のコンストラクターに渡しますn " );
$m1 = 新しい MasterOne( $bar );
$m2 = 新しい MasterTwo( $bar );
print ( $m1 );
print ( $m2 );
print ( " n " );
print ( " $bar の値を変更して $bar と $bazn を出力します " );
$bar -> setName( ' baz ' );
print ( $bar );
print ( $baz );
print ( " n " );
print ( " MasterOne と Twon を再度印刷します " );
print ( $m1 );
print ( $m2 );
print ( " n " );
print ( " MasterTwo の foo 名を変更して、MasterOne と Twon を再度印刷します " );
$m2 -> setFooName( ' MasterTwo の Foo ' );
print ( $m1 );
print ( $m2 );
print ( " $bar と $bazn も印刷します " );
print ( $bar );
print ( $baz );
?>
出所:
$bar の作成と $bar の印刷のみ
私の名前は「 bar 」で、「 foo 」に住んでいます。
現在、$baz は $bar を参照し、$bar と $baz を出力します
私の名前は「 bar 」で、「 foo 」に住んでいます。
MasterOne と Master Two を作成し、 $bar を両方のコンストラクターに渡します
マスター : MasterOne | foo : 私の名前は「 bar 」で、「 foo 」に住んでいます。
マスター : マスターツー | foo : 私の名前は「 bar 」で、「 foo 」に住んでいます。
$bar の値を変更し、$bar と $baz を出力します
私の名前は「 baz 」で、「 foo 」に住んでいます。
私の名前は「 baz 」で、「 foo 」に住んでいます。
MasterOne と Two を再度印刷中です
マスター : MasterOne | foo : 私の名前は「 baz 」で、「 foo 」に住んでいます。
マスター : マスターツー | foo : 私の名前は「 baz 」で、「 foo 」に住んでいます。
MasterTwo の foo 名を変更して、MasterOne と Two を再度印刷します
マスター: マスターワン | foo: 私の名前は「MasterTwo の Foo」で、「foo」に住んでいます。
マスター: マスターツー | foo: 私の名前は「MasterTwo 's Foo」で、「foo」に住んでいます。
$bar と $baz も印刷します
私の名前は「MasterTwo の Foo」で、「foo」に住んでいます。
私の名前は「MasterTwo 's Foo」で、「foo」に住んでいます。
上の例子解析:
$bar = 新しい foo( ' bar ' );
$m1 = 新しい MasterOne( $bar );
$m2 = 新しい MasterTwo( $bar );
インスタンスオブジェクト$m1と$m2の$barはコピーではなくインスタンス$barへの参照です、これがphp5のオブジェクト参照の特徴です
。
1. $m1 または $m2 内で $bar に対する操作は、外部オブジェクト インスタンス $bar の関連値に影響します。
2. 外部オブジェクトインスタンス $bar への変更は、$m1 および $m2 内の $bar の参照値にも影響します。
php4 では、オブジェクト インスタンスを使用して別のオブジェクトのプロパティを参照する上記のメソッドを実装するための、同等のコード (つまり、参照呼び出し) は次のようになります。
クラスフー{
var $bar ;
関数 setBar( & $newBar ){
$this -> バー =& newBar;
}
}
5. 引用の役割
プログラムが比較的大きく、同じオブジェクトを参照する変数が多数あり、オブジェクトを使用した後に手動でクリアしたい場合は、個人的には「&」メソッドを使用し、それ以外の場合は $var=null を使用してクリアすることをお勧めします。また、php5 で大きな配列を転送する場合は、メモリ領域を節約できるため、「&」メソッドを使用することをお勧めします。
6. 参照しない
参照の設定を解除すると、変数名と変数の内容の間のバインドが解除されるだけです。これは、変数の内容が破壊されることを意味するものではありません。例:
$a = 1 ;
$b =& $a ;
設定を解除 ( $a );
?>
$b の設定は解除されず、$a のみが解除されます。
7.グローバル引用
global $var を使用して変数を宣言すると、実際にはグローバル変数への参照が作成されます。それはこれを行うのと同じです:
これは、たとえば、unset $var ではグローバル変数の設定が解除されないことを意味します。
関数内でグローバルとして宣言された変数に参照が割り当てられている場合、その参照は関数内でのみ表示されます。これは、$GLOBALS 配列を使用することで回避できます。
関数内でグローバル変数 www.2cto.com を参照する例
$var1 = "変数の例" ;
{
グローバル $var1、$var2 ;
If ( ! $use_globals ) {
$var2
} その他 {
$GLOBALS [ " var2 " ] =& $var1 // グローバルコンテキストでも表示されます
;
}
}
global_references( false );
に設定されます
global_references( true );
echo " var2 は ' $var2 'n " に設定されます // var2 は 'サンプル変数' に設定されます
?>
global $var; は $var =& $GLOBALS['var']; の短縮形と考えてください。したがって、別の参照を $var に代入しても、ローカル変数への参照が変更されるだけです。
オブジェクト メソッドでは、$this は常に、それを呼び出すオブジェクトへの参照になります。
PHP のアドレス ポインティング (ポインタに似た) 関数は、ユーザー自身によって実装されるのではなく、Zend コアによって実装されます。PHP の参照は、書き込みが行われない限り、コピーオンライトの原則を採用しています。操作が発生した場合、同じアドレスを指す変数またはオブジェクトはコピーされません。
平たく言うと
$b =& $a ;
?>
実際、この時点では、$a と $b が異なるメモリを占有するのではなく、両方とも同じメモリ アドレスを指します
2: 上記コードに以下のコードを追加すると
$a = " EFG " ;
?>
$a と $b が指すメモリ内のデータは書き換える必要があるため、この時点で Zend コアは $b 用の $a のデータ コピーを自動的に生成し、メモリの一部を再適用するかどうかを自動的に決定します。保管用
多くの人は、PHP の参照が C のポインターと同じであると誤解しています。実際には、それらは異なり、大きく異なります。配列転送プロセス中に明示的に宣言する必要のない C 言語のポインターを除き、他のポイントは * を使用して定義する必要があります。ただし、PHP のアドレスへのポインター (ポインターと同様) 関数は、はい、PHP の参照は「コピーオンライト」の原則を採用しています。つまり、書き込み操作が発生しない限り、同じアドレスを指す変数やオブジェクトはコピーされません。 、次のコードのように:
$a = 配列 (' a ' , ' c ' ... ' n ' );
$b = $a ;
ここでプログラムを実行するだけであれば $a と $b は同じですが、C のように異なるメモリ空間を占有するのではなく、同じメモリを指すのです。 これが php と c の違いです。 $b=&$a は、$b が $a のメモリを指すことを意味します。zend はすでに参照を実装しており、zend は、いつこれを行うべきか、いつ行うべきでないかを判断するのに非常に役立ちます。対処する。
後で次のコードを書き続ける場合は、関数を追加し、パラメーターを参照渡しして、配列のサイズを出力します。
function printArray( & $arr ) // 参照渡し
{
Print ( count ( $arr ));
}
printArray( $a );
上記のコードでは、$a 配列を参照によって printArray() 関数に渡します。zend エンジンは、printArray() によって $a が変更される可能性があると判断し、$b 用の $a のデータ コピーを自動的に生成します。 、ストレージ用にメモリを再適用します。これが、前述した「コピーオンライト」の概念です。
上記のコードを次のように変更すると:
function printArray( $arr ) // 値の受け渡し
{
Print ( count ( $arr ));
}
printArray( $a );
上記のコードは $a 値を printArray() に直接渡します。この時点では参照転送がないため、コピーオンライトは行われません。
上記の 2 行のコードの実行効率をテストできます。たとえば、ループを 1000 回外に追加して、実行にかかる時間を確認します。その結果、参照を不適切に使用するとパフォーマンスが低下することがわかります。 30%以上。
自己理解: 値渡しの場合は関数内のパラメータとは無関係でローカル変数の役割に相当しますが、アドレス(参照)渡しの場合は関数内のパラメータに関係します、これはグローバル変数の役割と同等です。パフォーマンスの観点からは、上記の分析を見るだけで十分です。 .
引用符をどうするか
PHP の参照では、2 つの変数が同じコンテンツを指すことができます。つまり、これを行うとき:
$a =& $b;
?>
これは、$a と $b が同じ変数を指していることを意味します。
注:
$a と $b はここではまったく同じです。$a が $b を指しているわけではなく、その逆でもありません。$a と $b が同じ場所を指しているだけです。
注:
参照を持つ配列がコピーされた場合、その値は逆参照されません。配列値を関数に渡す場合も同様です。
注:
未定義の変数が参照によって割り当てられた場合、参照によって渡された場合、または参照によって返された場合、変数は自動的に作成されます。
例 #1 未定義の変数への参照を使用する
関数 foo(&$var) { }
foo($a); // $a が「作成」され、null に割り当てられます
$b = 配列();
foo($b['b']);
var_dump(array_key_exists('b', $b)) // bool(true)
$c = 新しい StdClass;
foo($c->d);
var_dump(property_exists($c, 'd')) // bool(true)
;
?>
同じ構文は、参照を返す関数と new 演算子 (PHP 4.0.4 以降) で使用できます。
@new などの @ 演算子を使用してコンストラクター内のエラー メッセージを抑制することは可能ですが、これは &new ステートメントを使用する場合には効果がありません。これは Zend エンジンの制限であり、解析エラーが発生します。
警告
関数内でグローバルに宣言された変数に参照が割り当てられている場合、その参照は関数内でのみ表示されます。これは、$GLOBALS 配列を使用することで回避できます。
$var1 = "変数の例";
関数 global_references($use_globals)
{
グローバル $var1、$var2;
If (!$use_globals) {
$var2 =& $var1; // 関数内でのみ表示されます
} その他 {
$GLOBALS["var2"] =& $var1; // グローバルコンテキストでも表示可能
}
}
global_references(false);
echo "var2 は '$var2'n に設定されます" // var2 は ''
に設定されます
global_references(true);
echo "var2 は '$var2'n に設定されます"; // var2 は 'サンプル変数' に設定されます
?>
global $var; は $var =& $GLOBALS['var']; の短縮形と考えてください。したがって、別の参照を $var に代入しても、ローカル変数への参照が変更されるだけです。
注:
foreach ステートメントで参照を含む変数に値を代入すると、参照されるオブジェクトも変更されます。
例 #3 引用符と foreach ステートメント
$ref = 0;
$row =& $ref;
foreach (array(1, 2, 3) as $row) {
// 何かをしてください
}
echo $ref; // 3 - 反復された配列の最後の要素
?>
参照が行う 2 番目のことは、変数を参照によって渡すことです。これは、関数内にローカル変数を作成し、その変数が呼び出しスコープ内の同じコンテンツを参照することによって実現されます。例:
関数 foo(&$var)
{
$var++;
}
$a=5;
foo($a);
?>
$a を 6 に変更します。これは、関数 foo 内で変数 $var が $a が指すものと同じものを指しているためです。詳細な説明については、「参照による受け渡し」を参照してください。
参照が行う 3 番目のことは、参照の戻りです
配列参照のバグ (慎重に検討した結果、実際にはバグではありませんでした)
参照には副作用があるようです。以下に 2 つの例を示します。どちらも、ある配列を別の配列にコピーするだけです。最初の例では、値をコピーする前に、最初の配列の値への参照が行われます。 2 番目の例では、インデックス 0 の値は同じメモリ位置を指します。
PHP の設計された動作がどのようなものであるかわからないので、これがバグであるとは言いませんが、この動作を期待している開発者はいないと思いますので、注意してください。
これが問題を引き起こす可能性がある例としては、スクリプトで配列のコピーを実行し、動作のタイプを期待していたが、後からスクリプトの前半で配列の値への参照を追加し、配列のコピーが間違っていることが判明した場合です。動作が予期せず変化しました。
// 例 1
$arr1 = 配列 (1);
echo " nbefore:n " ;
echo " $arr1[0] == { $arr1 [0]}n " ;
$arr2 = $arr1 ;
$arr2 [ 0 ] ++ ;
echo " naafter:n " ;
echo " $arr1[0] == { $arr1 [0]}n " ;
echo " $arr2[0] == { $arr2 [0]}n " ;
出力:
前:
$arr1[0] == 1
後:
$arr1[0] == 1
$arr2[0] == 2
//例 2
$arr3=配列(1);
$a=&$arr3[0];
echo"nbefore:n";
echo "$a == $an";
echo "$arr3[0] == {$arr3[0]}n";
$arr4=$arr3;
$arr4[0]++;
echo"その後:n";
echo "$a == $an";
echo "$arr3[0] == {$arr3[0]}n";
echo "$arr4[0] == {$arr4[0]}n";
出力:
前:
$a==1
$arr3[0] == 1
後:
$a==2
$arr3[0] == 2
$arr4[0] == 2
?>
分析の説明:
「例 2」については、最初はバグだと思いましたが、実際、よく考えてみると、次のとおりです。
課題(コピー)$arr4 = $arr3 ;
への参照を確立するプロセスがありました。
$a =& $arr3 [ 0 ];
$a、$arr3[0]、$arr4[0] は実際には参照関係であり、同じ場所を指します。
PPP より抜粋