比較演算子
比較演算子は、その名前が示すように、二つの値を比較します。 型の比較表 に、型に関連するさまざまな比較の例があります。
例 | 名前 | 結果 |
---|---|---|
$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
<?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 のほうが常に大きくなります |
例1 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
?>
例2 一般的な配列の比較
<?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
}
?>
注意: PHP における型の相互変換の動作は、 異なる型同士を比較する時には必ずしも自明でないことに注意して下さい。 整数型 と boolean を比較したり、 整数型 を 文字列 と比較する場合は特にそうです。 よって一般的には、
==
や!=
ではなく===
と!==
を使う方がほとんどの場合は好ましいです。
比較できない値
(===
や !==
を使った)
同一性の比較は、あらゆる値に適用できますが、
それ以外の演算子は、比較できる値同士の場合にのみ適用可能です。
比較できない値同士を比較した場合の結果は未定義であり、
その結果に依存すべきではありません。
三項演算子
もうひとつの条件演算子として "?:"(あるいは三項)演算子があります。
例3 デフォルト値を設定する
<?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 以降は、三項演算子はどの演算とも結合しなくなっています。
例4 三項演算子のわかりにくい挙動
<?php
// ぱっと見た感じでは、これは 'true' と表示されると思うでしょう。
echo (true ? 'true' : false ? 't' : 'f');
// しかし、PHP 8.0.0 より前のバージョンでは、実際には上の出力結果は 't' です。
// なぜなら、三項演算子は左結合だったからです。
// 上のコードをもう少しわかりやすく書くと、このようになります。
echo ((true ? 'true' : false) ? 't' : 'f');
// まず、最初の式が 'true' と評価されます。この 'true' は
// (bool)true と評価されるので、それをもとに二番目の三項
// 演算子が評価されます。
?>
注意:
一方で、三項演算子の短縮形の挙動は安定しており、 理にかなった動作をします。 これは、
false
と評価されない最初の引数を評価します。 但し、未定義の値については未だ警告が出るので注意して下さい。例5 三項演算子の短縮形
<?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 合体演算子) を使うことが出来ます。
例6 デフォルト値の代入
<?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 合体演算子はネストさせることもできます。
例7 Null 合体演算子のネスト
<?php
$foo = null;
$bar = null;
$baz = 1;
$qux = 2;
echo $foo ?? $bar ?? $baz ?? $qux; // 出力は 1 です
?>
User Contributed Notes 14 notes
I couldn't find much info on stacking the new ternary operator, so I ran some tests:
<?php
echo 0 ?: 1 ?: 2 ?: 3; //1
echo 1 ?: 0 ?: 3 ?: 2; //1
echo 2 ?: 1 ?: 0 ?: 3; //2
echo 3 ?: 2 ?: 1 ?: 0; //3
echo 0 ?: 1 ?: 2 ?: 3; //1
echo 0 ?: 0 ?: 2 ?: 3; //2
echo 0 ?: 0 ?: 0 ?: 3; //3
?>
It works just as expected, returning the first non-false value within a group of expressions.
Extending from here: https://www.php.net/manual/en/language.operators.comparison.php#121907
$a = ['a' => 1, 'b' => 2, 'c' => 3, 'e' => 4];
$b = ['a' => 1, 'b' => 2, 'd' => 3, 'e' => 4];
echo $a > $b; // 0
echo $b > $a; // 0
echo $a <$b; // 0
echo $b < $a; // 0
If using spaceship operator then it is returning true like :
echo $a <=> $b; //1
echo $b <=> $a; //1
echo $a <=> $b; //1
echo $b <=> $a; //1
Note: according to the spec, PHP's comparison operators are not transitive. For example, the following are all true in PHP5:
"11" < "a" < 2 < "11"
As a result, the outcome of sorting an array depends on the order the elements appear in the pre-sort array. The following code will dump out two arrays with *different* orderings:
<?php
$a = array(2, "a", "11", 2);
$b = array(2, "11", "a", 2);
sort($a);
var_dump($a);
sort($b);
var_dump($b);
?>
This is not a bug report -- given the spec on this documentation page, what PHP does is "correct". But that may not be what was intended...
Between the "shortcut ternary" (aka "elvis") and "spaceship" operators, you can write some quite compact comparison functions for usort and its ilk.
If you want to sort an array of associative arrays by several different keys you can chain them in the same way that you can list column names in an SQL ORDER BY clause.
<?php
usort($array, fn($a, $b) => $a['a'] <=> $b['a']
?: $b['b'] <=> $a['b']
?: $a['c'] <=> $b['c']);
?>
Will sort the array by column 'a', then by column 'b' descending, then by column 'c'; or in SQL-speak 'ORDER BY a, b DESC, c".
Very careful when reading PHP documentation, Here's a lot of miss information.
According to documentation, They say's (int) 0 == (string) "a" is true. But it is not in PHP 8.
var_dump(0 == "a"); // 0 == 0 -> true
Now In PHP 8 it's False.
Please be careful when you try to compare strings that have a plus sign `+` at the beginning (such as phone number, etc). When you use the Equal operator `==` PHP will ignore the plus sign. Use Identical operator `===` instead
Example:
$str1 = "62";
$str2 = "+62";
var_dump($str1 == $str2); // bool(true)
var_dump($str1 === $str2); // bool(false)
You can't just compare two arrays with the === operator
like you would think to find out if they are equal or not. This is more complicated when you have multi-dimensional arrays. Here is a recursive comparison function.
<?php
/**
* Compares two arrays to see if they contain the same values. Returns TRUE or FALSE.
* usefull for determining if a record or block of data was modified (perhaps by user input)
* prior to setting a "date_last_updated" or skipping updating the db in the case of no change.
*
* @param array $a1
* @param array $a2
* @return boolean
*/
function array_compare_recursive($a1, $a2)
{
if (!(is_array($a1) and (is_array($a2)))) { return FALSE;}
if (!count($a1) == count($a2))
{
return FALSE; // arrays don't have same number of entries
}
foreach ($a1 as $key => $val)
{
if (!array_key_exists($key, $a2))
{return FALSE; // uncomparable array keys don't match
}
elseif (is_array($val) and is_array($a2[$key])) // if both entries are arrays then compare recursive
{if (!array_compare_recursive($val,$a2[$key])) return FALSE;
}
elseif (!($val === $a2[$key])) // compare entries must be of same type.
{return FALSE;
}
}
return TRUE; // $a1 === $a2
}
?>
Please note that using the null coalescing operator to check properties on a class that has the __get magic method (without an __isset magic method) invokes the magic method.
For example:
<?php
class A
{
public function __get($property)
{
echo 'Called __get for ' . $property . PHP_EOL;
}
}
$a = new A();
echo 'Trying null coalescing operator' . PHP_EOL;
$b = $a->test ?? 5;
echo 'Trying isset()' . PHP_EOL;
if (isset($a->test)) {
$b = $a->test;
} else {
$b = 5;
}
?>
When you want to know if two arrays contain the same values, regardless of the values' order, you cannot use "==" or "===". In other words:
<?php
(array(1,2) == array(2,1)) === false;
?>
To answer that question, use:
<?php
function array_equal($a, $b) {
return (is_array($a) && is_array($b) && array_diff($a, $b) === array_diff($b, $a));
}
?>
A related, but more strict problem, is if you need to ensure that two arrays contain the same key=>value pairs, regardless of the order of the pairs. In that case, use:
<?php
function array_identical($a, $b) {
return (is_array($a) && is_array($b) && array_diff_assoc($a, $b) === array_diff_assoc($b, $a));
}
?>
Example:
<?php
$a = array (2, 1);
$b = array (1, 2);
// true === array_equal($a, $b);
// false === array_identical($a, $b);
$a = array ('a' => 2, 'b' => 1);
$b = array ('b' => 1, 'a' => 2);
// true === array_identical($a, $b)
// true === array_equal($a, $b)
?>
(See also the solution "rshawiii at yahoo dot com" posted)
Care must be taken when using the spaceship operator with arrays that do not have the same keys:
- Contrary to the notes above ("Example #2 Transcription of standard array comparison"), it does *not* return null if the left-hand array contains a key that the right-hand array does not.
- Because of this, the result depends on the order you do the comparison in.
For example:
<?php
$a = ['a' => 1, 'b' => 2, 'c' => 3, 'e' => 4];
$b = ['a' => 1, 'b' => 2, 'd' => 3, 'e' => 4];
var_dump($a <=> $b); // int(1) : $a > $b because $a has the 'c' key and $b doesn't.
var_dump($b <=> $a); // int(1) : $b > $a because $b has the 'd' key and $a doesn't.
?>
Searching for "double question mark" operator should find this page (and hopefully after this comment the crawlers will agree)
In the table "Comparison with Various Types", please move the last line about "Object" to be above the line about "Array", since Object is considered to be greater than Array (tested on 5.3.3)
(Please remove my "Anonymous" post of the same content before. You could check IP to see that I forgot to type my name)