PHP 参照渡しまとめ


はじめに

PHPには参照渡しという変数を共有する方法があります。
参照渡しの各型のパターンから、よく間違えやすい値渡しとの違いについても紹介していきます。

参照渡しとは

基本

PHPでは、「&」をつけることで参照渡しができます。

# 下記はどちらも同じ挙動になります
$result =& $value;
$result = &$value;

参照渡しと値渡しの違い

「値渡し」とは、変数に代入した値の"コピー"を渡します。

$value = 1;
$result = $value; // 1をコピーして代入

「参照渡し」とは、変数に代入した値の”参照先”を渡します。


$value = 1;
$result =& $value; // 値の入れ物を参照先として代入

参照渡しは、変数のコピーではなく変数のシンボルテーブル上の位置を渡してエイリアスのようにしているようです。すなわち、元の変数が書き換わってしまうようです。

リファレンスより

最も良く似ているのは、Unix のファイル名とファイルの 関係です。この場合、変数名はディレクトリエントリ、変数の内容は ファイル自体に対応します。リファレンスは、Unix ファイルシステムの ハードリンクのようなもの

サンプル

変数

値渡し
変数bに代入した後に変数aの値を変えても変数bは変わりありません。

$a = 2;
$b = $a;  // 変数aを代入
$a = 10;  // 変数aの値を変更
echo $b;

# 結果
2

参照渡し
変数bに代入した後に変数aの値を変えたところ、変数bの値も変わります。

$a = 2;
$b =& $a;  // 変数aを&をつけて代入
$a = 10;    // 変数aの値を変更
echo $b;

# 結果
10

配列

変数と同様な挙動になります。

値渡し

$a = array(1, 2, 3);
$b = $a;                  // 配列を代入
$a[1] = 0;                // 配列aを変更
var_dump($b);

# 結果
array(3) {
  [0] => int(1)
  [1] => int(2)  // $b[1]の値は2のまま
  [2] => int(3)
}

参照渡し

$a = array(1, 2, 3);
$b =& $a;                // 配列を&をつけて代入
$a[1] = 0;               // 配列aを変更
var_dump($b);

# 結果
array(3) {
  [0] => int(1)
  [1] => int(0)  // $b[1]の値が0に変わっている
  [2] => int(3)
}

オブジェクト

オブジェクトは元々参照型なので、「&」をつけなくても参照されます。
参照させたくない場合は、「clone」を使います。実際に見てみましょう。

参照渡し

$a = new stdClass();
$a->property = 1;
$b = $a;              // オブジェクトを代入
$a->property = 3;    // オブジェクトaを変更

var_dump($b);

# 結果
stdClass Object
(
    [property] => 3
)

値渡し

$a = new stdClass();
$a->property = 1;
$b = clone $a;       // cloneをつけて代入
$a->property = 3;    // オブジェクトaを変更

var_dump($b);

# 結果
stdClass Object
(
    [property] => 1
)

clone(複製)されることによって、別々のオブジェクトになります。
PHPでは、オブジェクトは参照されますので注意が必要です。

参照渡しを関数で使うことも

参照渡しは関数の引数として使用されることもありますので確認しておきましょう。

値渡し

function add ($number) {
    return ++$number;
}

$a = 1;
$b = add($a);

echo $a;
echo $b;

# 結果
1 // $a
2 // $b

参照渡し

function add (&$number) {    // 「&」を引数につける
    return ++$number;
}

$a = 1;
$b = add($a);

echo $a;
echo $b;

# 結果
2 // $a
2 // $b

まとめ

ある変数を別の変数に代入しても、参照しているだけならコピーしない。値の編集が発生する際に初めてコピーを行う。という使い分けができると無駄なメモリコピーを減らすことができます(Copy on Write)
ぜひ、参考にしてみてください。