## 比較演算子

比較演算子は、その名前が示すように、二つの値を比較します。 型の比較表 に、型に関連するさまざまな比較の例があります。

比較演算子
例 | 名前 | 結果
--- | --- | ---
$a == $b | 等しい | 型の相互変換をした後で $a が $b に等しい時に `true`。
$a === $b | 等しい | $a が $b に等しく、および同じ型である場合に `true` 。
$a != $b | 等しくない | 型の相互変換をした後で $a が $b に等しくない場合に `true`。
$a <> $b | 等しくない | 型の相互変換をした後で $a が $b に等しくない場合に `true`。
$a !== $b | 等しくない | $a が $b と等しくないか、同じ型でない場合に `true` 。
$a < $b | より少ない | $a が $b より少ない時に `true`。
$a > $b | より多い | $a が $b より多い時に `true`。
$a <= $b | より少ないか等しい | $a が $b より少ないか等しい時に `true`。
$a >= $b | より多いか等しい | $a が $b より多いか等しい時に `true`。
$a <=> $b | 宇宙船演算子 | $a が $b より小さい場合は、0より小さい整数。 $a が $b と等しい場合は、0。 $a が $b より大きい場合は、0より大きい整数。

オペランドが両方 数値形式の文字列 の場合、 もしくは一方が数値で、もう一方が 数値形式の文字列 の場合、 比較は数値として行われます。 これらのルールは switch 文にも適用されます。 型の変換は 演算子が `===` や `!==` の場合は行われません。 なぜなら、これらの演算子は、値と型を両方比較するものだからです。

警告

PHP 8.0.0 より前のバージョンでは、 文字列が数値または数値形式の文字列の場合、文字列は比較する前に数値に変換されていました。 これによって、以下の例で見られるような驚きの結果が生じる場合があります:

    <?php
    var_dump(0 == "a");
    var_dump("1" == "01");
    var_dump("10" == "1e1");
    var_dump(100 == "1e2");

    switch ("a") {
    case 0:
      echo "0";
      break;
    case "a":
      echo "a";
      break;
    }
    ?>

上の例の PHP 7 での出力は、このようになります。

    bool(true)
    bool(true)
    bool(true)
    bool(true)
    0

上の例の PHP 8 での出力は、このようになります。:

    bool(false)
    bool(true)
    bool(true)
    bool(true)
    a

例1 比較演算子

    <?php
    // Integer
    echo 1 <=> 1, ' '; // 0
    echo 1 <=> 2, ' '; // -1
    echo 2 <=> 1, ' '; // 1

    // Float
    echo 1.5 <=> 1.5, ' '; // 0
    echo 1.5 <=> 2.5, ' '; // -1
    echo 2.5 <=> 1.5, ' '; // 1

    // 文字列
    echo "a" <=> "a", ' '; // 0
    echo "a" <=> "b", ' '; // -1
    echo "b" <=> "a", ' '; // 1

    echo "a" <=> "aa", ' ';  // -1
    echo "zz" <=> "aa", ' '; // 1

    // 配列
    echo [] <=> [], ' ';               // 0
    echo [1, 2, 3] <=> [1, 2, 3], ' '; // 0
    echo [1, 2, 3] <=> [], ' ';        // 1
    echo [1, 2, 3] <=> [1, 2, 1], ' '; // 1
    echo [1, 2, 3] <=> [1, 2, 4], ' '; // -1

    // オブジェクト
    $a = (object) ["a" => "b"];
    $b = (object) ["a" => "b"];
    echo $a <=> $b, ' '; // 0

    $a = (object) ["a" => "b"];
    $b = (object) ["a" => "c"];
    echo $a <=> $b, ' '; // -1

    $a = (object) ["a" => "c"];
    $b = (object) ["a" => "b"];
    echo $a <=> $b, ' '; // 1

    // 比較するのは値だけではない; キーも一致しなければならない
    $a = (object) ["a" => "b"];
    $b = (object) ["b" => "b"];
    echo $a <=> $b, ' '; // 1

    ?>

多くの型では、以下の表にしたがって（上から順に）比較が行われます。

さまざまな型の比較
第 1 オペランドの型 | 第 2 オペランドの型 | 結果
--- | --- | ---
null または string | string | `null` を "" に変換し、数値または文字として比較します
bool または null | あらゆる型 | 両辺を bool に変換し、`false` < `true` と判断します
object | object | 組み込みクラスには独自の比較基準が定義されています。 異なるクラスは比較できません。同じクラスであるかどうかは ここで説明されています。
string, resource, int または float | string, resource, int または float | 文字列やリソースを数値に変換し、算術演算を行います
array | array | 要素数の少ない配列のほうが小さくなります。オペランド 1 のキーが オペランド 2 に存在しない場合、配列は比較できません。そうでない場合は 個々の要素の値を比較します（以下の例を参照ください）
object | あらゆる型 | object のほうが常に大きくなります
array | あらゆる型 | array のほうが常に大きくなります

例2 Boolean/null の比較

    <?php
    // Bool and null are compared as bool always
    var_dump(1 == TRUE);  // TRUE - same as (bool) 1 == TRUE
    var_dump(0 == FALSE); // TRUE - same as (bool) 0 == FALSE
    var_dump(100 < TRUE); // FALSE - same as (bool) 100 < TRUE
    var_dump(-10 < FALSE);// FALSE - same as (bool) -10 < FALSE
    var_dump(min(-100, -10, NULL, 10, 100)); // NULL - (bool) NULL < (bool) -100 is FALSE < TRUE
    ?>

例3 一般的な配列の比較

    <?php
    // 標準の比較演算子や、宇宙船演算子を用いて、配列はこのように比較されます
    function standard_array_compare($op1, $op2)
    {
      if (count($op1) < count($op2)) {
          return -1; // $op1 < $op2
      } elseif (count($op1) > count($op2)) {
          return 1; // $op1 > $op2
      }
      foreach ($op1 as $key => $val) {
          if (!array_key_exists($key, $op2)) {
              return 1;
          } elseif ($val < $op2[$key]) {
              return -1;
          } elseif ($val > $op2[$key]) {
              return 1;
          }
      }
      return 0; // $op1 == $op2
    }
    ?>

警告

# 浮動小数点数値の比較

ふたつの float 値が等しいかどうかを調べてはいけません。 float の内部的な表現方法がその理由です。

詳細な情報は float のドキュメントを参照ください。

> 注意: PHP における型の相互変換の動作は、 異なる型同士を比較する時には必ずしも自明でないことに注意して下さい。 整数型 と boolean を比較したり、 整数型 を 文字列 と比較する場合は特にそうです。 よって一般的には、 `==` や `!=` ではなく `===` と `!==` を使う方がほとんどの場合は好ましいです。

### 比較できない値

(`===` や `!==` を使った) 同一性の比較は、あらゆる値に適用できますが、 それ以外の演算子は、比較できる値同士の場合にのみ適用可能です。 比較できない値同士を比較した場合の結果は未定義であり、 その結果に依存すべきではありません。

### 参考

- strcasecmp()
- strcmp()
- Array operators
- Types

### 三項演算子

もうひとつの条件演算子として "?:"（あるいは三項）演算子があります。

例4 デフォルト値を設定する

    <?php
    // 三項演算子の使用例
    $action = (empty($_POST['action'])) ? 'default' : $_POST['action'];

    // 上記は以下の if/else 式と同じです。
    if (empty($_POST['action'])) {
      $action = 'default';
    } else {
      $action = $_POST['action'];
    }
    ?>

`(expr1) ? (expr2) : (expr3)` という式は、式1 が `true` の場合に 式2 を、 式1 が `false` の場合に 式3 を値とします。

三項演算子のまんなかの部分をなくすこともできます。 式 `expr1 ?: expr3` の結果は、expr1 が `true` と同等の場合は expr1、 それ以外の場合は expr3 となります。 この場合、expr1 は一度だけ評価されます。

> 注意: 三項演算子は式であり、値としては評価されずに式の結果として評価される ことに注意してください。演算結果をリファレンスとして返したい場合に、 これを知っておくことが大切です。結果をリファレンスとして返す関数で `return $var == 42 ? $a : $b;` とすることはできず、 警告が発生します。

> 注意:

> 三項演算子を "積み重ねて" 使用することは避けましょう。 ひとつの文の中で括弧で囲わずに複数の三項演算子を使用した際の PHP の振る舞いは、 他のプログラミング言語のそれと比べて、少々わかりにくいものです。 PHP 8.0.0 より前のバージョンでは、三項演算子は左から右に評価されていました。 他の殆どのプログラミング言語では、右から左に評価されます。 左から右に評価される振る舞いに依存することは、PHP 7.4.0 以降は推奨されません。 PHP 8.0.0 以降は、三項演算子はどの演算とも結合しなくなっています。

> 例5 三項演算子のわかりにくい挙動

>     <?php
>     // ぱっと見た感じでは、これは 'true' と表示されると思うでしょう。
>     echo (true ? 'true' : false ? 't' : 'f');

>     // しかし、PHP 8.0.0 より前のバージョンでは、実際には上の出力結果は 't' です。
>     // なぜなら、三項演算子は左結合だったからです。

>     // 上のコードをもう少しわかりやすく書くと、このようになります。
>     echo ((true ? 'true' : false) ? 't' : 'f');

>     // まず、最初の式が 'true' と評価されます。この 'true' は
>     // (bool) true と評価されるので、それをもとに二番目の三項
>     // 演算子が評価されます。
>     ?>

> 注意:

> 一方で、三項演算子の短縮形の挙動は安定しており、 理にかなった動作をします。 これは、`false` と評価されない最初の引数を評価します。 但し、未定義の値については未だ警告が出るので注意して下さい。

> 例6 三項演算子の短縮形

>     <?php
>     echo 0 ?: 1 ?: 2 ?: 3, PHP_EOL; //1
>     echo 0 ?: 0 ?: 2 ?: 3, PHP_EOL; //2
>     echo 0 ?: 0 ?: 0 ?: 3, PHP_EOL; //3
>     ?>

### Null 合体演算子

別の便利な短縮形式の演算子として、 "??" 演算子 (Null 合体演算子) を使うことが出来ます。

例7 デフォルト値の代入

    <?php
    // Null 合体演算子の使用例
    $action = $_POST['action'] ?? 'default';

    // 上の文は、この if/else 文と同じ意味です
    if (isset($_POST['action'])) {
      $action = $_POST['action'];
    } else {
      $action = 'default';
    }
    ?>

式 `(expr1) ?? (expr2)` は、 expr1 が `null` である場合は expr2 と評価され、それ以外の場合は expr1 と評価されます。

この演算子は、左側の値が存在しない場合でも notice や warning が発生しません。 isset() と同じ挙動です。 これは、配列のキーを扱う場合に便利です。

> 注意: Null 合体演算子は式であることに注意しましょう。変数として評価されるのではなく、式の結果として評価されます。 変数を参照で返そうとするときには、これを意識しておくことが重要です。 参照返しの関数で `return $foo ?? $bar;` のように書いてもうまく動かずに、 警告が発生します。

> 注意:

> Null 合体演算子の優先順位は低いです。 (文字列結合や、算術演算子のような) 他の演算子と組み合わせる場合には、おそらくカッコが必要になるでしょう。

>     <?php
>     // $name が未定義の場合、警告が発生します。
>     print 'Mr. ' . $name ?? 'Anonymous';
>     // "Mr. Anonymous" と出力
>     print 'Mr. ' . ($name ?? 'Anonymous');
>     ?>

> 注意:

> Null 合体演算子はネストさせることもできます。

> 例8 Null 合体演算子のネスト

>     <?php

>     $foo = null;
>     $bar = null;
>     $baz = 1;
>     $qux = 2;

>     echo $foo ?? $bar ?? $baz ?? $qux; // 出力は 1 です

>     ?>

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open