StupidDog's blog

IT関連の手近な所で、疑問に思った事を調べた記録

「PHPのforeach(range())は、forループの代用にはならないよ」

はじめに

添字などのループ処理に「for」ではなく「foreachとrange」を組み合わせたループで添字をカウントアップしているコードを見かけました。
見た目は、他の言語のイテレータのような雰囲気を出しているが、これはPHPでダメな書き方なのでメモ。

説明

何がダメかと言うと、range()はイテレータ(or ジェネレータ)を生成するのでなく、配列を生成する。
10回くらいのループならば気が付かないかもしれません。

<?php
foreach(range(1, 10) as $i) {
  echo $i . PHP_EOL;
}

でも、100万回のループならば・・・

<?php
foreach(range(1, 1000000) as $i) {
  echo $i . PHP_EOL;
}

メモリーを確保出来ずにエラーとなります。
php.iniのmemory_limitをデフォルト値の128Mから大きくしても、根本の問題は解決しません。

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in xxx.php

まとめ

シンタックスが似ているだけで、RubyPerlのrange表現とは別物です。
この処理の問題は、処理速度ではなくRangeで指定した分の配列をメモリに確保することです!ただの添字をループさせるだけなら素直にforをつかいましょう。
古いブログで処理速度だけの検証で優劣をつけて紹介されているため、意外と多く見かけます。
稼働後に負荷かが大きくなった時に障害になるのでコードレビューで気を付けたいところです。