PHPのイテレーションの話とか
4. if文は「===」を使う
PHP勉強会で、Dinoのhnwさんに教わったことですが、'0.00' == '0'は真です。
もし、仮に、パスワード処理でデータ側でMD5などのハッシュ化してない場合に、上記が真になっては非常にまずいのではないでしょうか。
さらにいえば、もっといろいろな形で、真となる場合があるので「==」は使わず、「===」を使いましょう。if ($str === 'hoge') { if (! ($str === 'hoge')) { if (is_null($null) === false) {
書いてあること自体はまったくそのとおりなのだが、例がちょっと変。普通に以下のように書けばいい。
if ($str !== 'hoge') { if (!is_null($null)) {
真偽値を返す関数の結果は比較しないほうが見やすいんじゃないかな。PHP には and, or, xor はある癖に not が無いし unless もないので可読性が少し悪いが。
5. countは配列数を毎回数えてる
これを知ったのは、PukiwkiPukiWiki のクリーンアップの記事を見たときなのですが。
countは配列の件数を数えるらしいので次のような書き方に直したほうが無難です。// if(count($array)) if(! empty($array)) // for($i = 0; $i < count($array); $i++) $count = count($array); for($i = 0; $i < $count; $i++)
なんか count が配列の要素数に対して線形時間かかるみたいな書き方だが、そのような事実はない。
元のPukiwikiのクリーンアップのほうの話は empty との比較の話なので、そっちのコードも一緒にベンチマーク。
% cat bench.php require_once 'Benchmark/Timer.php'; function test_count($array, $iterate) { for ($i = 0; $i < $iterate; $i++) { count($array); } } function test_empty($array, $iterate) { for ($i = 0; $i < $iterate; $i++) { empty($array); } } $a = range(1, 10); $b = range(1, 10000); $timer = new Benchmark_Timer(); $timer->start(); test_count($a, 1000000); $timer->setMarker('count10'); test_count($b, 1000000); $timer->setMarker('count10000'); test_empty($b, 1000000); $timer->setMarker('empty'); $timer->stop(); $timer->display(); ?> % php bench.php --------------------------------------------------------- marker time index ex time perct --------------------------------------------------------- Start 1161924627.81220400 - 0.00% --------------------------------------------------------- count10 1161924629.24052200 1.428318 36.81% --------------------------------------------------------- count10000 1161924630.64644100 1.405919 36.23% --------------------------------------------------------- empty 1161924631.69277700 1.046336 26.96% --------------------------------------------------------- Stop 1161924631.69281400 0.000037 0.00% --------------------------------------------------------- total - 3.880610 100.00% ---------------------------------------------------------
要素数に100倍の差があっても、コストは一緒。empty も早いとはいえ性能向上は30%程度なので、よほど繰り返されるわけでなければ、わざわざcountから置換するほどのことも無い。
そもそも、PHP は連想配列と配列を区別できないので、インデックスが0から順に始まっているとは限らない訳で、素直に foreach 使っておいたほうがいいんじゃないかな。連想配列でも要素の順序は保存されているし。
どうせなので、ループの場合のベンチマークも。
<?php require_once 'Benchmark/Timer.php'; function test_count_loop($array, $iterate) { for ($j = 0; $j < $iterate; $j++) { $n = 0; $count = count($array); for ($i = 0; $i < $count; $i++) { $n += $array[$i]; } } } function test_foreach($array, $iterate) { for ($j = 0; $j < $iterate; $j++) { $n = 0; foreach ($array as $value) { $n += $value; } } } function test_foreach_with_key($array, $iterate) { for ($j = 0; $j < $iterate; $j++) { $n = 0; foreach ($array as $i => $value) { $n += $value; } } } $a = range(0, 9999); $timer = new Benchmark_Timer; $timer->start(); test_count_loop($a, 100); $timer->setMarker('count_loop'); test_foreach($a, 100); $timer->setMarker('foreach'); test_foreach_with_key($a, 100); $timer->setMarker('foreach_with_key'); $timer->stop(); $timer->display(); ?>
--------------------------------------------------------------- marker time index ex time perct --------------------------------------------------------------- Start 1161927045.52569000 - 0.00% --------------------------------------------------------------- count_loop 1161927046.91695700 1.391267 34.92% --------------------------------------------------------------- foreach 1161927047.63913600 0.722179 18.12% --------------------------------------------------------------- foreach_with_key 1161927049.51007500 1.870939 46.96% --------------------------------------------------------------- Stop 1161927049.51014100 0.000066 0.00% --------------------------------------------------------------- total - 3.984451 100.00% ---------------------------------------------------------------
--------------------------------------------------------------- marker time index ex time perct --------------------------------------------------------------- Start 1161927023.92807000 - 0.00% --------------------------------------------------------------- count_loop 1161927024.26778700 0.339717 37.76% --------------------------------------------------------------- foreach 1161927024.50450100 0.236714 26.31% --------------------------------------------------------------- foreach_with_key 1161927024.82780600 0.323305 35.93% --------------------------------------------------------------- Stop 1161927024.82786100 0.000055 0.01% --------------------------------------------------------------- total - 0.899791 100.00% ---------------------------------------------------------------
あれ、同じマシンで実行したのに随分性能が違うなぁ。まぁ、どっちにしても、単純ループなら key なしの foreach が最速ってことで。
*1:といっても学生のアルバイトだが