PHP Conference Japan 2024

比较运算符

顾名思义,比较运算符允许您比较两个值。您可能也希望查看类型比较表,因为它们显示了各种类型相关比较的示例。

比较运算符
示例 名称 结果
$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 时,一个小于、等于或大于零的int

如果两个操作数都是数字字符串,或者一个操作数是数字而另一个操作数是数字字符串,则比较按数值进行。这些规则也适用于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
// 整数
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1

// 浮点数
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 的类型 结果
nullstring string null 转换为 "",进行数字或词法比较
boolnull 任何类型 将两侧都转换为 boolfalse < true
object object 内置类可以定义自己的比较方式,不同类之间不可比较,相同类请参考 对象比较
stringresourceintfloat stringresourceintfloat 将字符串和资源转换为数字,进行常规数学运算
array array 成员较少的数组较小,如果操作数 1 中的键在操作数 2 中找不到,则数组不可比较,否则 - 按值比较(参见以下示例)
object 任何类型 object 总是大于
array 任何类型 array 总是大于

示例 #1 布尔/null 比较

<?php
// 布尔值和 null 始终作为布尔值进行比较
var_dump(1 == TRUE); // TRUE - 与 (bool) 1 == TRUE 相同
var_dump(0 == FALSE); // TRUE - 与 (bool) 0 == FALSE 相同
var_dump(100 < TRUE); // FALSE - 与 (bool) 100 < TRUE 相同
var_dump(-10 < FALSE);// FALSE - 与 (bool) -10 < FALSE 相同
var_dump(min(-100, -10, NULL, 10, 100)); // NULL - (bool) NULL < (bool) -100 为 FALSE < TRUE
?>

示例 #2 标准数组比较的转录

<?php
// 使用标准比较运算符以及 spaceship 运算符比较数组的方式如下所示。
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 的类型转换并不总是很明显,尤其是在比较 intboolintstring 时。因此,在大多数情况下,建议使用 ===!== 比较,而不是 ==!=

不可比较的值

虽然身份比较运算符(===!==)可以应用于任意值,但其他比较运算符应该仅应用于可比较的值。比较不可比较的值的结果是未定义的,不应依赖于它。

三元运算符

另一个条件运算符是 "?:"(或三元)运算符。

示例 #3 赋值默认值

<?php
// 三元运算符示例用法
$action = (empty($_POST['action'])) ? 'default' : $_POST['action'];

// 上述代码等价于以下 if/else 语句
if (empty($_POST['action'])) {
$action = 'default';
} else {
$action = $_POST['action'];
}
?>
表达式 (expr1) ? (expr2) : (expr3) 如果 expr1 的值为 true,则计算结果为 expr2;如果 expr1 的值为 false,则计算结果为 expr3

可以省略三元运算符的中间部分。表达式 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',它
// 反过来计算为 (bool) true,因此返回第二个三元表达式的 true 分支。
?>

注意:

然而,短三元运算符 (?:) 的链接是稳定的,并且表现得合理。它将计算为第一个计算结果为非假值的变量。请注意,未定义的值仍将引发警告。

示例 #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
?>

空值合并运算符

另一个有用的简写运算符是 "??"(或空值合并)运算符。

示例 #6 赋值默认值

<?php
// 空值合并运算符示例用法
$action = $_POST['action'] ?? 'default';

// 上述代码等价于以下 if/else 语句
if (isset($_POST['action'])) {
$action = $_POST['action'];
} else {
$action = 'default';
}
?>
表达式 (expr1) ?? (expr2) 如果 expr1null,则计算结果为 expr2;否则计算结果为 expr1

特别是,如果左侧的值不存在,此运算符不会发出通知或警告,就像 isset() 一样。这在数组键上特别有用。

注意: 请注意,空值合并运算符是一个表达式,它不计算为变量,而是计算为表达式的结果。如果您想通过引用返回一个变量,这一点很重要。因此,在通过引用返回的函数中,语句 return $foo ?? $bar; 将不起作用,并且会发出警告。

注意:

空值合并运算符的优先级较低。这意味着如果将其与其他运算符(如字符串连接或算术运算符)混合使用,则可能需要括号。

<?php
// 引发 $name 未定义的警告。
print 'Mr. ' . $name ?? 'Anonymous';

// 输出 "Mr. Anonymous"
print 'Mr. ' . ($name ?? 'Anonymous');
?>

注意:

请注意,空值合并运算符允许简单嵌套

示例 #7 嵌套空值合并运算符

<?php

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

echo
$foo ?? $bar ?? $baz ?? $qux; // 输出 1

?>

添加注释

用户贡献的注释 13 条注释

173
crazy888s at hotmail dot com
14 年前
我找不到关于堆叠新三元运算符的太多信息,所以我运行了一些测试

<?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
?>

它按预期工作,返回一组表达式中的第一个非假值。
8
Sumon Mahmud
4 年前
从这里扩展: https://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

如果使用太空船运算符,则它将返回 true,例如

echo $a <=> $b; //1
echo $b <=> $a; //1
echo $a <=> $b; //1
echo $b <=> $a; //1
3
Hayley Watson
1 年前
在“快捷三元”(又名“Elvis”)和“太空船”运算符之间,您可以为 usort 及其同类编写一些非常紧凑的比较函数。



如果您想根据多个不同的键对关联数组的数组进行排序,您可以像在 SQL ORDER BY 子句中列出列名一样将它们链接起来。

<?php
usort
($array, fn($a, $b) => $a['a'] <=> $b['a']
?:
$b['b'] <=> $a['b']
?:
$a['c'] <=> $b['c']);
?>
将对数组按 'a' 列排序,然后按 'b' 列降序排序,最后按 'c' 列排序;或者用 SQL 的说法就是 'ORDER BY a, b DESC, c'。
23
adam at caucho dot com
18 年前
注意:根据规范,PHP 的比较运算符不是传递的。例如,以下在 PHP5 中都为真

"11" < "a" < 2 < "11"

因此,排序数组的结果取决于元素在预排序数组中出现的顺序。以下代码将输出两个具有*不同*排序的数组

<?php
$a
= array(2, "a", "11", 2);
$b = array(2, "11", "a", 2);
sort($a);
var_dump($a);
sort($b);
var_dump($b);
?>

这不是错误报告——鉴于此文档页面上的规范,PHP 的行为是“正确的”。但这可能不是预期的结果……
9
Tahazzot
3 年前
阅读 PHP 文档时要非常小心,这里有很多错误信息。

根据文档,他们说 (int) 0 == (string) "a" 为真。但在 PHP 8 中并非如此。

var_dump(0 == "a"); // 0 == 0 -> true

现在在 PHP 8 中它是假。
8
admin at zeros dot co dot id
2 年前
当您尝试比较以加号 `+` 开头的字符串(例如电话号码等)时,请小心。当您使用等号运算符 `==` 时,PHP 将忽略加号。请改用全等运算符 `===`

示例

$str1 = "62";
$str2 = "+62";

var_dump($str1 == $str2); // bool(true)
var_dump($str1 === $str2); // bool(false)
18
rshawiii at yahoo dot com
18 年前
您不能仅仅使用 === 运算符比较两个数组
就像您认为的那样,找出它们是否相等。当您有多维数组时,这会变得更加复杂。这是一个递归比较函数。

<?php
/**
* 比较两个数组以查看它们是否包含相同的值。返回 TRUE 或 FALSE。
* 用于确定记录或数据块是否被修改(可能由用户输入)
* 在设置“date_last_updated”或在没有更改的情况下跳过更新数据库之前。
*
* @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; // 数组没有相同数量的条目
}

foreach (
$a1 as $key => $val)
{
if (!
array_key_exists($key, $a2))
{return
FALSE; // 不可比较的数组键不匹配
}
elseif (
is_array($val) and is_array($a2[$key])) // 如果两个条目都是数组,则递归比较
{if (!array_compare_recursive($val,$a2[$key])) return FALSE;
}
elseif (!(
$val === $a2[$key])) // 比较条目必须是相同类型。
{return FALSE;
}
}
return
TRUE; // $a1 === $a2
}
?>
3
gfilippakis at sleed dot gr
1 年前
请注意,在具有 __get 魔术方法(没有 __isset 魔术方法)的类上使用空合并运算符检查属性会调用魔术方法。

例如

<?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;
}

?>
13
bishop
18 年前
当您想知道两个数组是否包含相同的值(无论值的顺序如何)时,您不能使用“==”或“===”。换句话说

<?php
(array(1,2) == array(2,1)) === false;
?>

要回答这个问题,请使用

<?php
function array_equal($a, $b) {
return (
is_array($a) && is_array($b) && array_diff($a, $b) === array_diff($b, $a));
}
?>

一个相关但更严格的问题是,如果您需要确保两个数组包含相同的键=>值对,而不管这些对的顺序如何。在这种情况下,请使用

<?php
function array_identical($a, $b) {
return (
is_array($a) && is_array($b) && array_diff_assoc($a, $b) === array_diff_assoc($b, $a));
}
?>

示例
<?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)
?>

(另请参阅“rshawiii at yahoo dot com”发布的解决方案)
4
niall at maranelda dot org
7 年前
在将 spaceship 运算符用于没有相同键的数组时必须小心

- 与上面的注释相反(“示例 #2 标准数组比较的转录”),如果左侧数组包含右侧数组没有的键,它*不会*返回 null。
- 因此,结果取决于您进行比较的顺序。

例如

<?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 因为 $a 包含键 'c' 而 $b 没有。

var_dump($b <=> $a); // int(1) : $b > $a 因为 $b 包含键 'd' 而 $a 没有。
?>
3
Ryan Mott
5 年前
搜索“双问号”运算符应该可以找到此页面(希望在此评论之后,爬虫会同意)
6
Cuong Huy To
13 年前
在“与各种类型的比较”表中,请将关于“对象”的最后一行移到关于“数组”的行的上面,因为对象被认为大于数组(在 5.3.3 上测试)

(请删除我之前发布的相同内容的“匿名”帖子。您可以检查 IP 以查看我忘记输入我的姓名)
2
Marcin Kuzawiski
9 年前
A < B 并且 B < A...

$A = [1 => 1, 2 => 0, 3 => 1];
$B = [1 => 1, 3 => 0, 2 => 1];

var_dump($A < $B); // TRUE
var_dump($B < $A); // TRUE

var_dump($A > $B); // TRUE
var_dump($B > $A); // TRUE

接下来 - C 和 D 是可比较的,但既不是 C < D 也不是 D < C(并且仍然 C != D)...

$C = [1 => 1, 2 => 1, 3 => 0];
$D = [1 => 1, 3 => 1, 2 => 0];

var_dump($C < $D); // FALSE
var_dump($D < $C); // FALSE

var_dump($C > $D); // FALSE
var_dump($D > $C); // FALSE

var_dump($D == $C); // FALSE
To Top