https://www.amazon.co.jp/WEB-DB-PRESS-Vol-103-%E8%A5%BF%E6%9D%91/dp/4774195545/ref=pd_sim_14_2?_encoding=UTF8&psc=1&refRID=BTDQPWNWFY136VE48HCK
「WEB+DB PRESS Vol.103」の連載、「事業を支えるPHP」を読んでの突っ込みです。
※記事の内容は全体的に素晴らしいと思います。筆者と記事を貶めるような意図はありません。誤解が広まってしまうのは良くないな〜&自分の知識を確認するのが目的です。
指摘したい箇所
「配列の代入や関数へ引数で渡すときには参照は渡されず、すべて配列がコピーされる挙動になります。」
$array1 = ['hoge' => 'fuga'];
$array2 = $array1; // 配列がコピーされる
$array2['hoge'] = 'piyo'; // 変更は$array2に対してのみ行われる
指摘内容
問題としているのは、「コピー」についてです。
PHPにおいては、コピーオンライトという仕組みがあって、値が変更されるタイミングで初めて値がコピーされる、という仕組みがあります。
これを理解しないと、以下のような誤解が生まれがちです。
// 巨大な配列
$array1 = [];
for ($i = 0; $i < 1000000; $i++) {
$array1[$i] = $i;
}
$array2 = $array1; // 巨大な配列がコピーされるのでメモリをたくさん食う
実際に計測してみます。
PHPのバージョンは7.1.11です。
<?php
echo "first:".memory_get_usage() / (1024 * 1024)."MB\n";
// 巨大な配列
$array1 = [];
for ($i = 0; $i < 1000000; $i++) {
$array1[$i] = $i;
}
echo "second:".memory_get_usage() / (1024 * 1024)."MB\n";
$array2 = $array1; // 巨大な配列がコピーされるのでメモリをたくさん食う?
echo "thrid:".memory_get_usage() / (1024 * 1024)."MB\n";
結果
first:0.34778594970703MB
second:32.35179901123MB
thrid:32.35179901123MB
値がコピーされてない事がわかります。
次に配列の値を変更してみます。
<?php
echo "first:".memory_get_usage() / (1024 * 1024)."MB\n";
// 巨大な配列
$array1 = [];
for ($i = 0; $i < 1000000; $i++) {
$array1[$i] = $i;
}
echo "second:".memory_get_usage() / (1024 * 1024)."MB\n";
$array2 = $array1; // 巨大な配列がコピーされるのでメモリをたくさん食う?
echo "thrid:".memory_get_usage() / (1024 * 1024)."MB\n";
$array1[0] = "test";
echo "fourth:".memory_get_usage() / (1024 * 1024)."MB\n";
結果
first:0.34822082519531MB
second:32.352233886719MB
thrid:32.352233886719MB
fourth:64.356216430664MB
値が変更されたタイミングで配列がコピーされている事がわかります。
指摘修正版
というわけで、以下が正しいと思います。
「配列の代入や関数へ引数で渡すときには参照は渡されず、値が渡されます。コピーオンライトという仕組みにより、値が変更されたタイミングで配列がコピーされる挙動になります。」
$array1 = ['hoge' => 'fuga'];
$array2 = $array1; // 値はまだコピーされない
$array2['hoge'] = 'piyo'; // 配列がコピーされ、変更は$array2に対してのみ行われる
所感等
マニュアルみても明示的に書いてないのと、他の言語ではあまり見られない特徴なので、誤解されがちだな〜と思います。
結論の、「参照を渡したくなるケースはほとんどないでしょう。プログラムを複雑にする原因にもなり得るので可能な限り避けると良いです。」には完全同意です。↓の記事にあるように、扱いを間違えるとパフォーマンス的にもデメリットが発生しますし、、、、
http://tanakahisateru.hatenablog.jp/entry/2013/12/12/012728
PHP5とPHP7
今回は配列の扱いなので関係ないですが、整数等の場合はPHP5とPHP7で挙動が違います。
PHP5までは全ての値について、今回検証した配列のような動き(コピーオンライト)をしますが、PHP7では普通に値がコピーされます。
https://www.slideshare.net/hnw/php7
参考
http://php.net/manual/ja/features.gc.refcounting-basics.php
http://keicode.com/cgi/object-in-php-5.php
2018/03/31追記
公式で捕捉いただけました
http://gihyo.jp/magazine/wdpress/archive/2018/vol103/support#supportInformation