phpでの値渡し、参照渡しを理解する

4 min read読了の目安(約3800字

言語による大きな違いはないと思うが、php独自の部分もあるかもしれないのでタイトルはphpでのとつけました。

○○渡しとはどういう時に使う用語?

関数やメソッドに引数の渡し方の種類を表す用語

  • 値渡し
    • 変数の値をコピーする渡し方
  • 参照渡し
    • 変数を共有するような渡し方

※変数の実体はメモリ領域にある。

挙動の違い

挙動の確認は

PHP Sandbox のphp8で試した

変数

値渡しでは

bに代入した後にaが変わっても$bの中身は変わらない

参照渡しでは

bに代入した後にaが変わったら$bの中身は変わる事が確認できた

値渡し

  1. aの値をbに代入する
  2. $aの値を変更する
  3. $bの値は変わらない
<?php 
$a = 1;
$b = $a;
$a = $a + 1;

//結果
int(1)

参照渡し

  1. aの値をbに代入する
  2. $aの値を変更する
  3. $bの値も変わる
<?php 
$a = 1;
$b =& $a;
$a = $a + 1;

//結果
int(2)

配列

変数の時と同じ。

値渡しでは

  1. newArrayにarrayを代入する
  2. $array[1]の値を変更する
  3. $newArray[1]の中身が変わらない

参照渡しでは

  1. newArrayにarrayを代入する
  2. $array[1]の値を変更する
  3. $newArray[1]の中身が変わる

値渡し

<?php 
$array = [1, 2, 3];
$newArray = $array;
$array[1] = 0;   

var_dump($newArray);

//結果
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2) //変わっていない
  [2]=>
  int(3)
}

参照渡し

<?php 
$array = [1, 2, 3];
$newArray =& $array;
$array[1] = 0;   

var_dump($newArray);

//結果
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(0) //変わっている
  [2]=>
  int(3)
}

オブジェクト

phpのオブジェクトは参照型のため、&がなくても参照される。

オブジェクトと参照

<php
$carbon = new Carbon();
[]

値渡し

<?php 
$stdClass = new stdClass();
$stdClass->property = 1;

$newStdClass = $stdClass;       
$stdClass->property = 3;

var_dump($newStdClass);

// 結果
object(stdClass)#1 (1) {
  ["property"]=>
  int(3)
}

参照渡し

参照させたくなければ cloneをつける。

オブジェクトのクローン作成

<?php 
$stdClass = new stdClass();
$stdClass->property = 1;

$newStdClass = clone $stdClass;
$stdClass->property = 3;

var_dump($newStdClass);

// 結果
object(stdClass)#2 (1) {
  ["property"]=>
  int(1)
}

自分はこの挙動がわかっていなくて詰まった経験がある。

例えば日付の配列を作るときに次のように実装したが、日付が同じになって戸惑った経験があるw

<?php 
$dateTime = new \DateTime();

$dateArray = [
    $dateTime,//今日
    $dateTime->modify('+1 days'), //明日
    $dateTime->modify('+2 days'),//明後日
    ];
var_dump($dateArray);

//期待する結果
array(3) {
  [0]=>
  object(DateTime)#1 (3) {
    ["date"]=>
    string(26) "2021-05-26 16:35:19.007567"
    ["timezone_type"]=>
    int(3)
    ["timezone"]=>
    string(10) "US/Pacific"
  }
  [1]=>
  object(DateTime)#1 (3) {
    ["date"]=>
    string(26) "2021-05-27 16:35:19.007567"
    ["timezone_type"]=>
    int(3)
    ["timezone"]=>
    string(10) "US/Pacific"
  }
  [2]=>
  object(DateTime)#1 (3) {
    ["date"]=>
    string(26) "2021-05-28 16:35:19.007567"
    ["timezone_type"]=>
    int(3)
    ["timezone"]=>
    string(10) "US/Pacific"
  }
}

//実際の結果は全て同じ日付になっている
array(3) {
  [0]=>
  object(DateTime)#1 (3) {
    ["date"]=>
    string(26) "2021-05-28 16:35:19.007567"
    ["timezone_type"]=>
    int(3)
    ["timezone"]=>
    string(10) "US/Pacific"
  }
  [1]=>
  object(DateTime)#1 (3) {
    ["date"]=>
    string(26) "2021-05-28 16:35:19.007567"
    ["timezone_type"]=>
    int(3)
    ["timezone"]=>
    string(10) "US/Pacific"
  }
  [2]=>
  object(DateTime)#1 (3) {
    ["date"]=>
    string(26) "2021-05-28 16:35:19.007567"
    ["timezone_type"]=>
    int(3)
    ["timezone"]=>
    string(10) "US/Pacific"
  }
}

どう使い分ける?

挙動としては理解できた!

参照渡しを使うメリットがパフォーマンスが向上するくらいしか把握できていない。

値渡しだと変数から別の変数に値をコピーするため、メモリ上に新たにデータが追加される。

参照渡しでは別の変数に入ってはいるが、メモリ上使っているところは同じと理解している。

参照渡しを使う事はあまりないけど、オブジェクトがどういう挙動をするかという意味では勉強になった😇

参考

値渡しと参照渡しの違いを理解する

PHP 参照渡しまとめ - Qiita