【ゼロからわかる】PHP・Laravelのforeachでなぜ値が変わらないのか・参照渡しと値渡し

Laravel
はむ
はむ

プログラミングにも結構慣れてきました。でもforeachで配列を展開したとき、値をうまく書き換えられないんです。

タック
タック

値をうまく書き換えるには、値渡しと参照渡しの違いを知っていないとわからないので、見ていきましょう!

 

この記事にぴったりな方

  • プログラミングをすることに慣れてきた
  • foreachしたとき、値が変わらない理由がわからない
  • 値渡しと参照渡しの違いがわからない

 

この記事で得られること

  • 配列を展開したとき、スムーズに値の変更をすることができるようになる
  • 値渡しと参照渡しの違いがわかる
  • 配列を展開したとき、値を変更する複数の方法を知ることができる

 

 

スポンサーリンク

結論

foreachしたときに、値が変更されない理由は「展開されている時の値は値渡しとなっており、元の変数ではなく、新たな変数を書き換えているから」です。

 

耳慣れない言葉がでてきました。値渡しと参照渡し。うーん。心が砕かれそうになりますね。ゆっくり解説していきます!

 

まず、値が書き換わる書き方と書き換わらない書き方の結論を見ておきます。

 

まずは、配列を用意します。今回は、$usersにあるtackのemailを書き換えたいとします。

$users = [
['id' => 1, 'name' => 'tack', 'email' => 'tack@gmail.com'],
['id' => 2, 'name' => 'hamu', 'email' => 'hamu@gmail.com'],
['id' => 3, 'name' => 'tanaka', 'email' => 'tanaka@gmail.com'],
];

値が書き換わる場合

foreach ($users as $key => $user) {
if ($user['name'] = 'tack') {
$users[$key]['email'] = 'tack-change@gmail.com';
}
}
var_dump($users);

//出力結果
array(3) {
[0]=>
array(3) {
["id"]=>
int(1)
["name"]=>
string(4) "tack"
["email"]=>
string(21) "tack-change@gmail.com"
}
・・・以下省略

 

値が書き換わらない場合

foreach ($users as $user) {
if ($user['name'] = 'tack') {
$user['email'] = 'tack-change@gmail.com';
}
}

var_dump($users);

//出力結果
array(3) {
[0]=>
array(3) {
["id"]=>
int(1)
["name"]=>
string(4) "tack"
["email"]=>
string(14) "tack@gmail.com"
}
・・・以下省略

 

2つを見比べると、一瞬違いがわからないですよね。2つの違いは元の配列を直接書き換えているか、foreach内で新たにできた変数を書き換えに行っているかの差です。

 

ここで必要になる知識が「値渡し」「参照渡し」です。

「値渡し」と「参照渡し」とは何か

値渡し

値渡しとは、引数で値を受けるとき、前の値を直接受け取っている状態です。友達から画像をもらったとき、一旦コピーして、コピーした画像を編集するイメージです。

 

コピーした値を利用しているので、値を渡している元の値には影響を与えません。コピーを編集しても、友達から直接もらった画像には全く変化がないのと同じです。

 

参照渡し

値渡しとは、引数で値を受けるとき、前の値を直接受け取っている状態です。友達から画像をもらったとき、コピーせずに画像をそのまま編集するイメージです。

 

元の値を直接参照しているので、参照渡しといいます。値を変更したときは、元の値を直接書き換えにいっています。友達からもらった画像を直接編集しているようなものです。

 

「参照渡し」と「値渡し」のどちらを使うか

PHPでは原則、値渡しとなっています。

メソッド内で元の値を書き換えてしまうことによる、意図せぬ挙動を防ぐことができるからです。

 

スポンサーリンク

なぜforeachで値が書き変わらないのか

ここまでの値渡しと参照渡しの知識を元に、変更されない場合を再度見てみましょう。

foreach ($users as $user) {
if ($user['name'] = 'tack') {
$user['email'] = 'tack-change@gmail.com';
}
var_dump($user);
}

var_dump($users);


//$userの出力結果
array(3) {
["id"]=>
int(1)
["name"]=>
string(4) "tack"
["email"]=>
string(21) "tack-change@gmail.com"
}
・・・以下省略

//$usersの出力結果
array(3) {
[0]=>
array(3) {
["id"]=>
int(1)
["name"]=>
string(4) "tack"
["email"]=>
string(14) "tack@gmail.com"
}
・・・以下省略

上の出力結果を見るとわかるように、$usersを展開するときに引数として受けた$userの値は変更されています。しかし、元の値の$usersは変更されていません。

 

その理由は$userの値が値渡しで別の値となっているため、元の変数$usersには影響を与えないためです。

 

foreachで値を書き換える方法

このとき、値を変更する方法は3つあります。

  1. $usersの値を直接書き換える
  2. $userを参照渡しにすることで、元の$usersを書き換える
  3. $usersに編集後の新たな配列を入れる

 

$usersの値を直接書き換える

これは冒頭で示した方法ですね。

foreach ($users as $key => $user) {
if ($user['name'] = 'tack') {
$users[$key]['email'] = 'tack-change@gmail.com';
}
}

$usersの配列の$key番目(今回だと配列の0番目、わからない方は$keyをvar_dumpしてみるといいかと思います。)にあるemailを書き換えています。

 

$userを参照渡しにすることで、元の$usersを書き換える

foreach ($users as &$user) {
if ($user['name'] = 'tack') {
$user['email'] = 'tack-change@gmail.com';
}
}

 

PHPでは引数を渡すときに、&をつけると参照渡しになります。&$userとして値を渡すことで、$usersの値を直接編集しています。

 

しかし、この方法は意図せぬバグを発生させる可能性があるので、極力使用しないほうがいいかと思います。

 

$usersに編集後の新たな配列を入れる

$changeUsers = [];
foreach ($users as $user) {
if ($user['name'] = 'tack') {
$user['email'] = 'tack-change@gmail.com';
}

$changeUsers[] = $user;
}

$users = $changeUsers;

$changeUsersに新たな配列を用意しておき、$userを格納していきます。

最後に$userに$changeUsersを入れています。

 

Laravelだとcollectionという便利な形があるので、collectionのmapメソッドを使って新たな配列をつくって値を書き換えるという手法も便利です。

 

まとめ

foreachで値が変更されない理由は、「値渡しされた別の変数を書き換えており、元の変数が書き換わっていないため」でした。

 

変数を書き換える手法は大きく3つあります。

$usersの値を直接書き換える
$userを参照渡しにすることで、元の$usersを書き換える
$usersに編集後の新たな配列を入れる

 

はむ
はむ

これで配列があっても安心して値を変更することができそうです!

タック
タック

プログラミングでは配列の値を操作することが多々あるので、「値渡し」と「参照渡し」の差を理解して、どんどん使いこなしていきましょう!

 

 

コメント

タイトルとURLをコピーしました