0
0

More than 1 year has passed since last update.

PHPの値渡し、参照渡しについて

Last updated at Posted at 2022-07-14

最近学んだのでまとめます。

値渡しとは??

$a = 1;
$b = $a;
echo $b;
// 1

僕含め、よく皆さんが使っているであろうデフォルトの変数の代入です。
想定通りの挙動ですね。
内部的には、$a中にある1という値を複製して$bに渡しています。
文字通り、「値渡し」ですね。

参照渡しとは??

$a = 1;
$b = &$a; // &を使うことで、$aを$bに参照渡しできる
$a = 2;
echo $b;
// 2

あれ?$bには「1が入ってる$a」を代入してるから、$bをechoしたら1じゃないの??
しかし、2が出力されます。
これは、内部的には「変数$aの場所を$bに渡して」います。
$a$bに参照渡しした結果、$aの値を変えると$bの値も変わります。
なので、$a$bも2になってしまう仕組みです。

それを踏まえて下記コード。

<?php

$articles = [
    ['name' => 'アイス', 'price' => '100'],
    ['name' => 'ジュース', 'price' => '150'],
    ['name' => '唐揚げ', 'price' => '400'],
];


$object = new stdClass();

$array = [];

foreach ($articles as $article) {
    $object->name = $article['name'];
    $object->price = $article['price'];
    $array[] = $object;
}
var_dump($array);
exit;

の結果が下記。

実行結果
array(3) {
  [0]=>
  object(stdClass)#1 (2) {
    ["name"]=>
    string(9) "唐揚げ"
    ["price"]=>
    string(3) "400"
  }
  [1]=>
  object(stdClass)#1 (2) {
    ["name"]=>
    string(9) "唐揚げ"
    ["price"]=>
    string(3) "400"
  }
  [2]=>
  object(stdClass)#1 (2) {
    ["name"]=>
    string(9) "唐揚げ"
    ["price"]=>
    string(3) "400"
  }
}
期待値
array(3) {
  [0]=>
  object(stdClass)#2 (2) {
    ["name"]=>
    string(9) "アイス"
    ["price"]=>
    string(3) "100"
  }
  [1]=>
  object(stdClass)#3 (2) {
    ["name"]=>
    string(12) "ジュース"
    ["price"]=>
    string(3) "150"
  }
  [2]=>
  object(stdClass)#4 (2) {
    ["name"]=>
    string(9) "唐揚げ"
    ["price"]=>
    string(3) "400"
  }
}
    $object->name = $article['name'];
    $object->price = $article['price'];
    $array[] = $object;

(@shiracamus様より)
オブジェクトの場合は、オブジェクトへの参照を値(参照値)として変数に保持します。
オブジェクトへの参照値を渡すと「オブジェクト共有」することになります。

で、$objectの中身を作ったタイミングで $array[] に代入しています。
しかし、オブジェクトへの参照値を渡しているため、次のループで、$array[]の中の$objectを書き換えてしまいます。
つまり、$array[] の中の$objectと、$objectは一心同体状態になります。
$objectに変更があったら、$array[]内の$objectにも同じように変更が加わります。

おそらく内部的な挙動はこうです。

1回目のループ

$array = [
(object)['name' => 'アイス', 'price' => '100'],
];

2回目のループ

$array = [
(object)['name' => 'ジュース', 'price' => '150'], --> 本来はアイス100が入るはず
(object)['name' => 'ジュース', 'price' => '150'],
];

3回目のループ

$array = [
(object)['name' => '唐揚げ', 'price' => '400'], --> 本来はアイス100が入るはず
(object)['name' => '唐揚げ', 'price' => '400'], --> 本来はジュース150が入るはず
(object)['name' => '唐揚げ', 'price' => '400'],
];

回避策

参照値をそのまま渡すのではなく、参照先のオブジェクトの中身をコピーして渡してあげます。

<?php

$articles = [
    ['name' => 'アイス', 'price' => '100'],
    ['name' => 'ジュース', 'price' => '150'],
    ['name' => '唐揚げ', 'price' => '400'],
];


$object = new stdClass();

$array = [];

foreach ($articles as $article) {
    $object->name = $article['name'];
    $object->price = $article['price'];
    $array[] = clone $object;
}
var_dump($array);
exit;

cloneを使うことによって、$object値だけをコピーして $array[]の中に代入することができます(値渡し)

結果、期待値通りの挙動となります。

0
0
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0