はじめに
春ですね。
街を歩いていると、桜が目にとまり気分がウキウキしてくると同時に、
新入社員と思われる人もチラホラ見かけ懐かしい気分になります。
今回は、そんなPHPをこれから始める方に、
誰しもが一度は通ると思われるPHPの罠についてお伝えいたします。
empty 関数
empty は、変数が空かどうかを判定するのに利用されます。
ただ、この「空かどうかの判定」がくせ者で、きちんと把握していなければ思わぬバグとなります。
たとえば、画面から入力された値が空かどうかの判定に使ったとしましょう。
empty($value)
\$value がどんな場合に、これはTrueとなるのでしょうか?
// case 1. value値が空文字列宇
$value="";
var_dump( empty($value) ); // true
// case 2. value値が文字列 0
$value="0";
var_dump( empty($value) ); // true
case 1 は、空文字列なので true となる、誰もが期待する動作ですね。
case 2 はどうでしょうか。なんと "0" という文字列が空判定されました。
もし画面からの入力チェックに使っていると、0 と入力したにもかかわらず、
未入力と判定され、もしかすると「必須項目が未入力です。」的なエラーが表示されるかもしれません。
実害はないかもしれませんが(?)、かっこわるいですよね。
また別のケースで、NULLと空配列で処理が分かれる場合の判定に使い、バグってしまう事もよくあります。
※空配列も 「空」と判定されます。
empty 関数を使う場合は、「思わぬ値が空と判定されないか」を常に気をつけて使ってください。
多段三項演算子
プログラムにも慣れ、見やすい/綺麗なソースコードを書こうとする頃に顔を出してくる、
多段三項演算子問題です。
三項演算子自体はとても便利で、うまく使うとプログラムを簡潔に書くことが出来ます。
// $a が Trueの場合は $bに1を、Falseの場合は2を代入
if ($a) {
$b=1;
} else {
$b=2;
}
// 予め初期値として設定しておくやり方
$b = 1;
if (!$a) $b=2;
// 三項演算子を使うやり方。
$b = $a ? 1 : 2;
ただ、三項演算子で複雑な事を行うと思わぬ動きをします。
たとえば、下記 if 文を三項演算子で記述するとどのようになるのでしょうか?
// $a が Trueの場合は $bに1を、Falseの場合は、さらに$cを確認し$bの値を決定する。
if ($a) {
$b=1;
} else {
if ($c) {
$b=2;
} else {
$b=3;
}
}
おそらく、大半の人が以下のようなコーディングを行うとおもいます。
// $a が Trueの場合は $bに1を、Falseの場合は、さらに$cを確認し$bの値を決定する。
$b = $a ? 1 : $c ? 2 : 3;
では実際に、\$aと\$cに値を入れてみましょう
$a = $b = true;
$b = $a ? 1 : $c ? 2 : 3;
echo $b; // 2
期待値としては、\$a がtrue なので \$b が 1 であることです。
しかし、実際は \$b に 2 がはいます。一体なぜでしょうか??
これはphpの三項演算子は左側から判定されるため、以下のように解釈されるためです。
$b = ($a ? 1 : $c) ? 2 : 3;
1.「\$a ? 1 : $c」 がはじめに判定され、結果 「1」となります。
2.「1 ? 2 : 3」が判定され、「2」となります。
そのため三項演算子を重ねる場合、優先度を明示するようにしましょう。
$b = $a ? 1 : ($c ? 2 : 3);
※ちなみに大体数の言語では、カッコなしでも上記のような解釈がされます。
比較演算子 ==
「==」は型変換を行った後に比較する演算子です。
しかし型変換がくせ者で、例えば以下のような比較はTrueとなります。
"hogehoge" == 0; // true
これは "hogehoge" を int型に変換すると 0 になるためです。
特に必要がないかぎりは、比較演算子は「===」を使うようにしましょう。
参照
余り使う機会が無いとは思いますが、
参照を使う場合、使った後にかならず unset を行うよう心がけましょう。
たとえば下記のように参照として使った変数を流用する場合、意図せぬ動きをします。
$array = [1, 2, 3];
foreach($array as &$v) {}
foreach($array as $v) { echo $v; } // 1,2,2
1つ目の foreach を抜けると、「\$v」は \$array の末尾「3」の参照を示した状態となります。
この状態で2つ目のforeachに入ると、ループの度に「\$v」が示す $arrayの末尾を更新する動きとなります。
$array = [1, 2, 3];
foreach($array as &$v) {}
foreach($array as $v) { var_dump($array) }
// 1ループ目 => [1,2,1]
// 2ループ目 => [1,2,2]
// 3ループ目 => [1,2,2]
この動きを回避するには、1つ目のループの末尾で unset してあげる必要があります。
$array = [1, 2, 3];
foreach($array as &$v) {}
unset($v);
foreach($array as $v) { echo $v; } // 1,2,3