PHPのif文が効かないときに見直したい書き方(否定と括弧・PHP8の比較の変更)

PHPでif文を書いたのに、条件に合っているはずの処理が動かない、否定の「!」を付けたら逆にならない、ということがありました。こういうときは、if文そのものよりも条件式の書き方に原因があるかもしれません。

if文の基本の書き方

if文は、条件式がtrueのときだけ中の処理を実行する構文です。

PHP
<?php
  $a = 3;
  $b = 1;
  if ($a > $b) {
    echo 'aはbより大きい';
  }

条件によって処理を分けたいときは、elseifやelseを続けます。

PHP
<?php
  $score = 70;
  
  if ($score >= 80) {
    echo '合格';
  } elseif ($score >= 60) {
    echo '再テスト';
  } else {
    echo '不合格';
  }

処理が1行だけなら{}を省略することもできますが、あとから処理を足したときに2行目がif文の外に出てしまう原因になるので、基本的には{}を付けておくほうが安心です。

PHP
<?php
// 2行目の echo はif文の外なので、条件に関係なく実行される
if ($a > $b)
  echo 'aはbより大きい';
  echo 'この行は常に表示される';

出典:PHP: if – Manual

否定の「!」はどこまでかかるか

広告

「〜ではないとき」という条件を書くときに使う「!」は、すぐ後ろの値だけにかかります。比較式全体を否定したいときは、括弧で囲む必要があります。

比較式を否定するときは括弧で囲む

たとえば「$aと$bが等しくないとき」のつもりで、次のように書くと意図どおりに動きません。

PHP
<?php
  $a = 5;
  $b = 3;
  
  if (!$a == $b) {
    echo '等しくない'; // 表示されない
  }

「!」は「==」より優先順位が高いので、この条件式は(!$a) == $bとして扱われます。!$aはfalseになり、false == 3を比較することになるので結果はfalseです。

比較式全体を否定するなら、括弧で囲みます。

PHP
<?php
  if (!($a == $b)) {
    echo '等しくない'; // 表示される
  }

もっとも、この場合は括弧を使うより、はじめから否定の比較演算子を使うほうが読みやすくなります。

広告
PHP
<?php
  if ($a != $b) {
    echo '等しくない';
  }
  
  // 型まで比べる場合
  if ($a !== $b) {
    echo '等しくない';
  }

関数の戻り値を否定する!in_array()や!empty()、!isset()は、「!」が関数の結果全体にかかるので括弧を足さなくても問題ありません。

PHP
<?php
  $fruits = ['apple', 'banana'];
  
  if (!in_array('melon', $fruits, true)) {
    echo 'melonは含まれていない';
  }

instanceofは例外的に括弧なしでも全体が否定される

instanceof(オブジェクトが特定のクラスのものかどうかを調べる演算子)は「!」より優先順位が高いので、!$obj instanceof Fooは!($obj instanceof Foo)として扱われます。

PHP
<?php
  if (!$obj instanceof Foo) {
    // $objがFooクラスのオブジェクトでないとき
  }

動作としては問題ありませんが、ぱっと見で「どこまでが否定されているのか」がわかりにくいので、括弧を付けて!($obj instanceof Foo)と書いておくほうが読み返したときに迷いません。

出典:演算子の優先順位(PHPマニュアル)

「AでもBでもないとき」の書き方

複数の条件をまとめて否定するときも、つまずきやすいポイントです。「$colorが’red’でも’blue’でもないとき」のつもりで、次のように書くと、どんな値でも条件がtrueになってしまいます。

PHP
<?php
  $color = 'red';
  
  if ($color != 'red' || $color != 'blue') {
    echo 'redでもblueでもない'; // 'red'でも表示される
  }

$colorが’red’のときは$color != 'blue'がtrueになり、’blue’のときは$color != 'red'がtrueになります。「||」(または)はどちらかがtrueなら全体がtrueになるので、結果的にいつでも条件を満たしてしまいます。

「AでもBでもない」は「Aではない、かつBではない」なので、「&&」でつなぎます。

PHP
<?php
  if ($color !== 'red' && $color !== 'blue') {
    echo 'redでもblueでもない';
  }

「AかBのどちらか」を丸ごと否定する書き方でも同じ意味になります。この場合は括弧が必須です。

PHP
<?php
  if (!($color === 'red' || $color === 'blue')) {
    echo 'redでもblueでもない';
  }

比べたい値が多いときは、配列とin_array()を使うとすっきり書けます。

PHP
<?php
  if (!in_array($color, ['red', 'blue'], true)) {
    echo 'redでもblueでもない';
  }

PHP8から文字列と数値の比較結果が変わった

PHP7までは問題なく動いていたif文が、PHP8にしたら効かなくなったという場合は、文字列と数値の比較を疑ってみてください。

PHP8では、数値と「数値として扱えない文字列」を==で比較したとき、数値のほうを文字列に変換して比べるようになりました。PHP7までは文字列のほうを数値に変換していたので、同じコードでも結果が変わることがあります。

比較PHP7までPHP8以降
0 == "0"truetrue
0 == "foo"truefalse
0 == ""truefalse
42 == " 42"truetrue
42 == "42foo"truefalse

出典:PHP 8.0.x で推奨されなくなる機能・後方互換性のない変更点(PHPマニュアル)

たとえば、フォームから送られてきた値が空文字かどうかを0と比べて判定していた場合、PHP7まではtrueになっていた条件がPHP8ではfalseになります。

PHP
<?php
  $input = '';
  if ($input == 0) {
    echo '未入力'; // PHP7では表示、PHP8では表示されない
  }

未入力かどうかを調べたいなら、0と比べるのではなく、空文字と比べるか、trim()などで整えてから判定するほうが確実です。

PHP
<?php
  if ($input === '') {
    echo '未入力';
  }

同じように、1 == "1円"もPHP7まではtrueでしたが、PHP8ではfalseになります。古い記事では「==だと型が変換されて一致してしまう」という例として紹介されていることがあるので、PHP8で試したときに結果が違っても慌てなくて大丈夫です。

==と===の違い

比較の結果が思ったとおりにならないときは、==と===のどちらを使っているかも確認しておきます。

演算子比べるもの
==値が等しいか(必要に応じて型を変換してから比べる)
===値と型の両方が等しいか

PHP8で文字列と数値の比較が変わったとはいえ、==で型の変換が起こること自体はなくなっていません。数値として扱える文字列同士は、数値として比較されます。

PHP
<?php
  var_dump('1' == '01');    // true
  var_dump('10' == '1e1');  // true
  var_dump(100 == '1e2');   // true
  var_dump(null == false);  // true

意図しない一致を避けたいときは、===や!==を使うのが基本です。in_array()も、第3引数にtrueを渡すと型まで比較してくれます。

PHP
<?php
  $ids = [1, 2, 3];
  var_dump(in_array('1e0', $ids));        // true
  var_dump(in_array('1e0', $ids, true));  // false

そのほかの見落としやすいポイント

=(代入)と==(比較)を書き間違えている

条件式の中で=を1つだけ書くと、比較ではなく代入になります。代入した値がtrueとして扱われる値なら、if文の中は常に実行されてしまいます。

PHP
<?php
  $status = 0;
  if ($status = 1) {
    echo '実行される'; // $statusに1が代入され、常にtrue
  }

エラーにはならないので、気づきにくい書き間違いです。

andとorは&&と||より優先順位が低い

andとorは、&&や||と同じ意味の演算子ですが、優先順位が=(代入)より低くなっています。そのため、結果を変数に代入すると思わぬ値になることがあります。

PHP
<?php
  $result = true && false;
  var_dump($result); // false
  
  $result = true and false;
  var_dump($result); // true($result = true が先に実行される)

if文の条件式の中でも、andとor、&&と||を混ぜると評価の順番がわかりにくくなるので、&&と||にそろえて、必要な部分は括弧でまとめておくと安全です。

“0”や空の配列もfalseとして扱われる

if文の条件式に変数をそのまま書いた場合、次の値はfalseとして扱われます。

  • false
  • 0、0.0
  • 空文字''と、文字列の'0'
  • 空の配列[]
  • null

文字列の'0'がfalseになるのは見落としやすいポイントです。フォームで「0」と入力された値をif ($value)で判定すると、未入力と同じ扱いになってしまいます。一方で、'0.0'や半角スペース' '、文字列の'false'はtrueとして扱われます。

PHP
<?php
  $value = '0';
  if ($value) {
    echo '入力あり';
  } else {
    echo '未入力'; // こちらが表示される
  }

empty()も同じ基準で判定するので、'0'を入力値として受け付けたい場合は$value !== ''のように明示的に比較します。

出典:PHP: 論理型 (boolean) – Manual

コロン構文ではelse ifと書けない

HTMLの中でif文を使うときに便利なコロン構文({}の代わりに:とendif;を使う書き方)では、elseifを1語で書く必要があります。else ifと2語に分けると構文エラーになります。

PHP
<?php if ($a > 1): ?>
  <p>1より大きい</p>
<?php elseif ($a === 1): ?>
  <p>1と等しい</p>
<?php else: ?>
  <p>1より小さい</p>
<?php endif; ?>

{}を使う書き方ではelse ifでもelseifでも動きますが、どちらかにそろえておくと読みやすくなります。

出典:PHP: elseif/else if – Manual

if文が効かないときの確認方法

条件式のどこがおかしいのか見当がつかないときは、条件式の結果をvar_dump()で出力してみると原因を見つけやすくなります。

PHP
<?php
  var_dump($a, $b);        // 値と型を確認
  var_dump(!$a == $b);     // 条件式全体の結果を確認
  var_dump(!($a == $b));

echoではfalseが何も表示されず、trueは1と表示されるだけなので、var_dump()で型まで確認するのがおすすめです。

まとめ

if文が思ったとおりに動かないときは、まず否定の「!」がどこまでかかっているかを確認します。「!」はすぐ後ろの値だけにかかるので、比較式全体を否定したいときは括弧で囲むか、はじめから!==を使います。「AでもBでもない」という条件は、||ではなく&&でつなぎます。

PHP
<?php
  // NG:(!$a) == $b として扱われる
  if (!$a == $b) {}
  
  // OK
  if (!($a == $b)) {}
  if ($a !== $b) {}
  
  // NG:どんな値でも true になる
  if ($color != 'red' || $color != 'blue') {}
  
  // OK
  if ($color !== 'red' && $color !== 'blue') {}
  if (!in_array($color, ['red', 'blue'], true)) {}

PHP8にしてから効かなくなったif文は、数値と文字列を==で比べている箇所が原因になっていることがあります。意図しない一致や不一致を避けるためにも、比較は===や!==で型までそろえておくと安心です。

PHP
<?php
  $input = '';
  
  // PHP7まではtrue、PHP8からはfalse
  if ($input == 0) {}
  
  // OK
  if ($input === '') {}

そのほか、=と==の書き間違いや、文字列の'0'がfalseとして扱われる点も見落としやすいポイントです。原因がわからないときは、var_dump()で条件式の結果を出力して確かめてみてください。

PHP
<?php
  // NG:代入になり、常に true
  if ($status = 1) {}
  
  // '0' は false として扱われる
  $value = '0';
  var_dump((bool) $value); // false
  
  // 条件式の結果を確認する
  var_dump(!$a == $b);
広告

カテゴリー : WordPress

タグ :