基礎

PHPの参照渡し|関数で外部変数を直接変更する方法

PHPの関数では通常、引数は値渡し(pass by value)で受け取ります。つまり、関数に渡された値のコピーが関数内で使われ、元の変数には影響しません。しかし、参照渡し(pass by reference)を使うと、関数内で引数の値を変更した場合、呼び出し元の変数も直接変更されます。

参照渡しは、関数から複数の値を返したい場合や、大きなデータ構造を効率的に処理したい場合に使われるテクニックです。

基本的な使い方

引数名の前に &(アンパサンド)を付けることで、その引数を参照渡しにできます。

PHP
<?php
function addTen(&$value) {
    $value += 10;
}

$num = 5;
echo "変更前: " . $num . "\n";

addTen($num);
echo "変更後: " . $num . "\n";
実行結果
変更前: 5
変更後: 15

addTen() 関数の引数に & を付けているため、関数内で $value を変更すると、呼び出し元の $num も直接変更されます。もし & を付けなければ、$num5 のままです。

値渡しとの比較

違いを明確にするため、値渡しと参照渡しを並べて比較してみましょう。

PHP
<?php
// 値渡し(コピーが渡される)
function doubleByValue($x) {
    $x *= 2;
    echo "関数内: " . $x . "\n";
}

// 参照渡し(元の変数を直接操作)
function doubleByRef(&$x) {
    $x *= 2;
    echo "関数内: " . $x . "\n";
}

$a = 10;
doubleByValue($a);
echo "呼び出し後(値渡し): " . $a . "\n";

$b = 10;
doubleByRef($b);
echo "呼び出し後(参照渡し): " . $b . "\n";
実行結果
関数内: 20
呼び出し後(値渡し): 10
関数内: 20
呼び出し後(参照渡し): 20

値渡しでは関数内の変更が元の変数に反映されませんが、参照渡しでは元の変数 $b 自体が 20 に変わっていることがわかります。

配列を参照渡しで操作する

配列に対して参照渡しを使うと、関数内で配列の要素を直接追加・変更できます。

PHP
<?php
function addItem(&$cart, $item, $price) {
    $cart[] = ["item" => $item, "price" => $price];
}

function calcTotal($cart) {
    $total = 0;
    foreach ($cart as $entry) {
        $total += $entry["price"];
    }
    return $total;
}

$cart = [];
addItem($cart, "りんご", 150);
addItem($cart, "バナナ", 200);
addItem($cart, "みかん", 100);

foreach ($cart as $entry) {
    echo $entry["item"] . ": " . $entry["price"] . "円\n";
}
echo "合計: " . calcTotal($cart) . "円\n";
実行結果
りんご: 150円
バナナ: 200円
みかん: 100円
合計: 450円

実用的な例

PHPの組み込み関数にも参照渡しを使うものが多数あります。例えば sort() は配列を参照渡しで受け取り、元の配列自体を並べ替えます。

PHP
<?php
// エラー情報を参照渡しで取得するパターン
function divide($a, $b, &$error) {
    if ($b == 0) {
        $error = "ゼロで割ることはできません";
        return false;
    }
    $error = null;
    return $a / $b;
}

$error = null;
$result = divide(10, 3, $error);
echo "10 / 3 = " . $result . "(エラー: " . ($error ?? "なし") . ")\n";

$result = divide(10, 0, $error);
echo "10 / 0 = " . var_export($result, true) . "(エラー: " . ($error ?? "なし") . ")\n";
実行結果
10 / 3 = 3.3333333333333(エラー: なし)
10 / 0 = false(エラー: ゼロで割ることはできません)
注意

参照渡しの引数にはリテラル値を直接渡すことはできません。addTen(5) のような呼び出しはエラーになります。必ず変数を渡してください。また、参照渡しを多用するとコードの追跡が難しくなるため、本当に必要な場面でのみ使用することを推奨します。

ポイント

PHP 5以降、オブジェクトは常に参照のように振る舞います(正確にはオブジェクトハンドルの値渡し)。そのため、オブジェクトを関数に渡すとき、明示的に & を付けなくても、関数内でのプロパティ変更は元のオブジェクトに反映されます。

まとめ

  • 引数名の前に & を付けると参照渡しになる
  • 参照渡しでは関数内の変更が呼び出し元の変数に直接反映される
  • 値渡しでは変数のコピーが渡されるため、元の変数は変更されない
  • 配列の操作やエラー情報の取得など、実用的な場面で活用できる
  • リテラル値は参照渡しに渡せない。多用はコードの可読性を下げるので注意