LoginSignup
2
3

More than 5 years have passed since last update.

初心者向け PHPの不可解な動きをまとめました。

Posted at

はじめに

春ですね。
街を歩いていると、桜が目にとまり気分がウキウキしてくると同時に、
新入社員と思われる人もチラホラ見かけ懐かしい気分になります。

今回は、そんな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
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3