こんにちは!エンジニアのオータケです!
今回は「値渡しと参照渡し」、「参照」について解説をしていきます。
引数に値を渡すときの方法に値渡しと参照渡しがありますが、それぞれどんなものか知っていますか?
PHPでプログラムを組む以上、それぞれどんなものか違いを知って置かなければなりません。
この記事では
・値渡しと参照渡しの違いについて
・配列やオブジェクトの参照渡しについて
といった基本的な内容から
・参照を使う上で注意すべき点
といった実践で役立つ情報などを解説していきます!
参照渡しとは
引数付きの関数を定義する時に引数の値を変更するとどうなるでしょうか?
例えば次のようなプログラムを見てみましょう。
<?php function hoge($foo) { $foo = null; } $piyo = 1; hoge($piyo); var_dump($piyo); ?>
このプログラムでは引数foo付きの関数hogeを定義しています。
関数hogeを呼び出したあとに$piyoの中身を出力しています。
この関数の中ではfooにnullを代入していますが、関数を呼んだあとのvar_dumpではどのような値が表示されるのでしょうか?
実行結果:
int(1)
1と表示されました。
つまり、関数の中でnullを代入しても$piyoには影響せず値の変更は行われません。
このような引数への渡し方を値渡しといいます。
文字通り値をコピーして渡しているだけのためなにか変更を行っても元の変数に影響を与えません。
では逆にこのサンプルコードが本来したかったことを実現するにはどうすればよいでしょうか?
次のサンプルコードを見てみましょう!
<?php function hoge(&$foo) { $foo = null; } $piyo = 1; hoge($piyo); var_dump($piyo); ?>
実行結果:
NULL
今回は引数$fooの前に&を付けています。
この&をつけることにより関数の中でnullを代入した時にhoge関数の引数として渡した変数$piyoの値も変更されることになります。
このような引数への渡し方を参照渡しといいます。
様々な値を参照渡ししてみる
この章では様々なタイプの引数を参照渡しした場合にどういった動きをするのかを解説していきます。
値や文字列については先程の章をご覧いただけると幸いです。
配列
この項では配列を渡してみます。
今回は値を書き換えるだけでなくarray_pop関数を使って配列の末尾を削除した場合どうなるか試してみます。
<?php function hoge(&$foo) { $foo[2] = 1; array_pop($foo); } $piyo = [1, 2, 3, 4]; hoge($piyo); var_dump($piyo); ?>
実行結果:
array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(1) }
2番目の要素の3が1に書き換わっているだけでなく、配列の末尾も削除されていることがわかりました。
参照渡しで配列を渡して操作を行った場合でも値の変更がされるようになっています。
では続いてオブジェクトの場合はどうでしょうか?
次の項で見てみましょう。
オブジェクト
<?php class Hoge { protected $piyo = 1; public function setValue($value) { $this->piyo = $value; } public function getValue() { return $this->piyo; } } function foo(&$qux) { $qux->setValue(5); } $object = new Hoge(); foo($object); echo $object->getValue(); ?>
実行結果:
5
Hogeクラスを定義して値を変更するためのsetValueメソッドと値を取得するためのgetValueメソッドの両方を定義しています。
変数$objectにはHogeクラスからオブジェクトを生成して保持しています。
このオブジェクトを関数fooに渡していますが、関数foo内ではsetValueメソッドを呼び出して値を変更しています。
最後にechoでオブジェクトの値を取得していますがここでもしっかりと変更された値である5が表示されています。
参照を使う際の注意点
ここまで参照渡しについて見てきました。
参照自体は便利ではありますが、扱う際に注意しないととんでもないバグの元になってしまうようなこともあります。
例えば、foreachを使う場合がわかりやすいでしょう。
問題のあるコード:
<?php $hoge = [1, 2, 3, 4, 5]; $piyo = [5, 4, 3, 2, 1]; foreach($hoge as &$value) { // 何らかの処理を行う } foreach($piyo as &$value) { // 何らかの処理を行う } $value = 9; // hoge, piyo を出力 var_dump($hoge, $piyo); ?>
問題のあるコードの実行結果:
array(5) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> int(5) } array(5) { [0]=> int(5) [1]=> int(4) [2]=> int(3) [3]=> int(2) [4]=> &int(9) }
各要素を出力した際にintと表示されていますが、最後を見ていただくと&intとなっているかと思います。
同時に値が9に変わってしまっています。
foreachでループを回す際に使用した変数$valueを参照扱いしたためにforeachのループを抜けても$valueが参照扱いされているため値を変更することで配列の値を書き換えてしまうという問題が発生します。
このような問題が起きないにはどのようにすべきでしょうか?
答えは、unset関数を使うことです。
次のようなコードを書けば今回のような問題はおきなくなります。
正しいコード:
<?php $hoge = [1, 2, 3, 4, 5]; $piyo = [5, 4, 3, 2, 1]; foreach($hoge as &$value) { // 何らかの処理を行う } foreach($piyo as &$value) { // 何らかの処理を行う } // ここで参照状態を解く unset($value); $value = 9; // hoge, piyo を出力 var_dump($hoge, $piyo); ?>
変数$valueに値を代入する前にunset関数を呼び出して変数を破棄しています。
これを入れることによって$valueの参照状態を解除しています。
まとめ
いかがでしょうか?
今回の記事では値渡しと参照渡しの違いについて説明しました。
また、参照変数が使い方次第ではバグのもとになる可能性があることについても解説しました。
使い方を間違えないようunset関数を使いつつ使っていくようにしましょう。