PHP 类型比较
下面对 PHP 中的 类型比较 做一次深入详解,涵盖各种比较运算符、松散比较(type juggling)规则、特殊情况及最佳实践。
一、比较运算符概览
运算符 | 描述 |
---|---|
== | 等于(松散比较) |
=== | 全等(严格比较,类型与值都相同) |
!= | 不等于(松散比较) |
!== | 不全等(严格比较) |
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
<=> | 太空船操作符(PHP 7+),返回–1/0/1 |
// 示例
var_dump( 1 == "1" ); // true (值相同,类型可转换)
var_dump( 1 === "1" ); // false (类型不同)
var_dump(2 < 3); // true
var_dump(2 <=> 3); // -1
var_dump(3 <=> 3); // 0
var_dump(4 <=> 3); // 1
二、松散比较(==
/ !=
)规则
松散比较时,PHP 会按以下优先级进行类型转换,再比较:
- 若两者都是数值或数值字符串,都转换为数值后比较
- 若其中一个是布尔值,另一方也转换为布尔后比较
- 若其中一个是
NULL
,另一方转换为NULL
后比较 - 数组与对象 按各自规则(见下文)
- 其它情况,将两者都转换为字符串后比较
1. 数字与字符串
var_dump("123" == 123); // true ("123" → 123)
var_dump(" 10px" == 10); // true (前导空白可,遇非数字停止:10)
var_dump("foo" == 0); // true (非数字字符串转为 0)
var_dump("0" == false); // true ("0"→0, false→0)
var_dump("" == 0); // true (""→0)
2. 布尔比较
var_dump(0 == false); // true
var_dump("" == false); // true
var_dump([] == false); // true
var_dump("php" == true); // true (非空非“0”字符串→true)
var_dump("0" == false); // true
3. NULL
比较
var_dump(NULL == false); // true (NULL→false)
var_dump(NULL == 0); // true (NULL→0)
var_dump(NULL == ""); // true (NULL→"")
var_dump(NULL == []); // true (NULL→[]?)
注意:
NULL
在松散比较时,会和大多数空值都等价。
4. 数组与对象
- 空数组 vs 空字符串、0、false、NULL
var_dump([] == false); // true var_dump([] == ""); // true var_dump([] == null); // true
- 非空数组之间,按元素逐一比较长度和值
- 对象 vs 对象,要求同一类且所有属性松散相等
- 数组 vs 对象,会先转换对象为数组再比较
三、严格比较(===
/ !==
)
严格比较无需类型转换,只有类型和值都完全相同时才为 true。
var_dump(0 === false); // false (类型不同)
var_dump("" === false); // false
var_dump(NULL === false); // false
var_dump([] === false); // false
var_dump([1,2] === [1,2]); // true
最佳实践:尽量在业务逻辑中使用严格比较,避免因隐式转换导致意外。
四、太空船操作符 <=>
(PHP 7+)
- 用于整洁地实现三向比较,常用于排序函数中。
- 语义:
- 若左 < 右,返回 -1
- 相等返回 0
- 左 > 右,返回 1
echo 1 <=> 2; // -1
echo 2 <=> 2; // 0
echo 3 <=> 2; // 1
// 排序示例
$arr = [3,1,2];
usort($arr, function($a,$b){ return $a <=> $b; });
print_r($arr); // [1,2,3]
五、特殊与边缘案例
比较 | 结果 | 原因 |
---|---|---|
"php" == true | true | 非空非“0”字符串 → true |
"php" === true | false | 类型不同 |
" 123abc" == 123 | true | 前导空白忽略,遇非数字停在 abc |
" " == 0 | true | 仅空白 → 转为 "" → 0 |
" " == false | true | 空字符串 → false |
0 == "a" | true | 非数字字符串 → 0 |
[] == "" | true | 空数组 → “” |
[0] == false | true | 数组转换为单元素字符串 “0”? → 0? 与 false 相等 |
new A == new B | false | 不同类对象 |
六、常见陷阱与建议
- 不要用
==
比较浮点数
浮点精度问题,建议使用abs($a - $b) < $epsilon
。 - 避免将数字与用户输入的任意字符串直接比较
比如$_GET['id'] == 0
,会把"foo"
误判为0
。 - 对空值用
===
或 显式函数if (is_null($x)) { … } if ($x === "") { … } if (empty($x)) { … } // 但 empty() 会将 0 视为空
- 推荐开启严格模式
declare(strict_types=1);
并在函数中使用类型声明,减少类型错误。
七、实践示例
declare(strict_types=1);
function compare($a, $b) {
echo var_export($a, true) . " == " . var_export($b, true) . " ? ";
var_dump($a == $b);
echo var_export($a, true) . " === " . var_export($b, true) . " ? ";
var_dump($a === $b);
echo "\n";
}
compare("0", false);
compare("", 0);
compare("php", true);
compare(null, []);
compare([1,2], [1,2]);
compare(1.0, 1);
输出:
'0' == false ? true
'0' === false ? false
'' == 0 ? true
'' === 0 ? false
'php' == true ? true
'php' === true ? false
NULL == array () ? true
NULL === array () ? false
array (1,2) == array (1,2) ? true
array (1,2) === array (1,2) ? true
1.0 == 1 ? true
1.0 === 1 ? false
小结
- 松散比较 会进行类型转换,易出意外;
- 严格比较 既比较类型又比较值,更安全;
- 对于排序场景,可用
<=>
; - 业务逻辑中 推荐 一律使用
===
/!==
,并结合类型声明与严格模式,避免隐式类型陷阱。