リファレンスが行うことは何ですか?

リファレンスを使う基本操作には三通りあります。 リファレンスの代入リファレンス渡し、 そして リファレンスを返すことです。 この節では、これらの操作に関する基本的な解説をします。 そして、詳細な説明へのリンクを示します。

リファレンスの代入

まず最初の操作です。PHP のリファレンスを使うと、 ふたつの変数が同じ内容を指すようにできます。 次の例を考えてみましょう。

<?php

$a =& $b;

?>
この場合、$a$b は同じ内容を指します。

注意:

ここで、$a$b は完全に 同じで、$a$b を 指しているわけではなく、その逆でもありません。$a$b は同じ場所を指しているのです。

注意:

未定義の変数のリファレンスに対して代入したり 渡したり返したりすると、そこで変数が作成されます。

例1 未定義の変数のリファレンスの使用

<?php

function foo(&$var) {}

foo($a); // $a が作成され、null が代入されます

$b = array();
foo($b['b']);
var_dump(array_key_exists('b', $b)); // bool(true)

$c = new stdClass();
foo($c->d);
var_dump(property_exists($c, 'd')); // bool(true)

?>

リファレンスを返す関数でも 同じ構文が使用可能です

<?php

$foo =& find_var($bar);

?>

リファレンスを返さない関数で、同じ構文を使うとエラーになります。 new 演算子の結果に対して同じ構文を使った場合もエラーになります。 オブジェクトはポインタで渡されますが、 これはリファレンスとは異なります。 詳細な情報は オブジェクトと参照 にあります。

警告

関数の内部で global 宣言された変数にリファレンスを 代入すると、そのリファレンスは関数の内部でのみ参照可能となります。 これを避けるには、$GLOBALS 配列を使用します。

例2 関数内でのグローバル変数の参照

<?php

$var1 = "Example variable";
$var2 = "";

function global_references($use_globals)
{
    global $var1, $var2;

    if (!$use_globals) {
        $var2 =& $var1; // 関数の内部でのみ参照可能
    } else {
        $GLOBALS["var2"] =& $var1; // 関数の外部でも参照可能
    }
}

global_references(false);
echo "var2 の値は '$var2'\n"; // var2 の値は ''

global_references(true);
echo "var2 の値は '$var2'\n"; // var2 の値は 'Example variable'

?>
global $var; は、$var =& $GLOBALS['var']; の短縮版だと考えてください。 これにより、他のリファレンスを $var に代入し、 ローカル変数のリファレンスのみを変更します。

注意:

foreach ステートメントの内部でリファレンス変数に値を代入すると、リファレンスも変更されます。

例3 リファレンスと foreach ステートメント

<?php

$ref = 0;
$row =& $ref;

foreach (array(1, 2, 3) as $row) {
    // 何かを実行します
}

echo $ref; // 3 - 配列の最後の要素

?>

厳密にリファレンスでの代入をしなくても、 array() で作成した式は、配列の要素の先頭に & を追加したのと同じような動作をします。たとえば、次のようになります。

<?php

$a = 1;
$b = array(2, 3);

$arr = array(&$a, &$b[0], &$b[1]);
$arr[0]++;
$arr[1]++;
$arr[2]++;
/* $a == 2, $b == array(3, 4); */

?>

しかし、配列の内部のリファレンスは危険もあるということに気をつけましょう。 通常の (リファレンスではない) 代入の右辺にリファレンスを使っても 左辺がリファレンスに変わることはありませんが、配列の内部のリファレンスは通常の代入においても維持されます。 これは、関数をコールする際に配列をリファレンスで渡すときも同じです。 たとえば、次のようになります。

<?php

/* スカラー変数への代入 */
$a = 1;
$b =& $a;
$c = $b;
$c = 7; // $c はリファレンスではないので $a や $b の値は変わりません

/* 配列変数への代入 */
$arr = array(1);
$a =& $arr[0]; // $a および $arr[0] は同じリファレンスセットになります
$arr2 = $arr; // これは、リファレンス代入ではありません!
$arr2[0]++;
/* $a == 2, $arr == array(2) */
/* $arr の内容は、たとえリファレンスでなくても変わります! */

?>
言い換えると、配列内のリファレンスの挙動はその要素ごとに決まるということです。 個々の要素のリファレンスに関する動きは、 配列コンテナがリファレンスであるかどうかとは独立しています。

リファレンス渡し

リファレンスの第 2 の使用法は、変数のリファレンス渡しです。この場合、 関数でローカル変数が作成され、コール側の変数が、それと同じ内容への リファレンスとなります。例を示します。

<?php

function foo(&$var) 
{
  $var++;
}

$a=5;
foo($a);

?>
この結果、$a は 6 となります。これは、関数 foo の中では、変数 $var$a と同じ内容を指しているためです。 より詳細な説明は、 リファレンス渡し を参照ください。

リファレンスによる戻り値

リファレンスの第 3 の使用法は、 リファレンスによる戻り値 です。