为什么 PHP 8 将 42 == " 42" 视为真?

Why does PHP 8 treat 42 == " 42" as true?

PHP 8 已发布。它对相等运算符的行为进行了更改。根据 the documentation,这是它现在的行为方式:

Non-strict comparisons between numbers and non-numeric strings now work by casting the number to string and comparing the strings.

但是,此表达式在 PHP 8 和 PHP 的早期版本中的计算结果为真:

42 == " 42" // true

这对我来说没有意义。根据文档,数字应该转换为字符串 "42",这肯定不等于字符串 " 42",对吧?

简答:

在PHP 8和PHP 7中,42 == " 42"为真,在PHP 7和PHP 8中,"42" == " 42" 是真的。

长答案:

我认为您对相等运算符 == 在 PHP 中的工作方式有误解。在 PHP 8 和 PHP 的早期版本中,相等运算符对待数字字符串的方式与其他字符串不同:

<?php
// the == operator does type juggling and also handles numerical strings differently:
var_export("42" == "42"); // true
var_export(42 == "42"); // true
var_export("42" == " 42"); // true
var_export("42" == "42 "); // false in PHP 7 and true in PHP 8
var_export("42" == "042"); // true
var_export("10" == "1e1"); // true
var_export("abc" == " abc"); // false
var_export("42" == "abc 42"); // false

echo "\n";

// the === operator does not do type juggling and does not handle numerical strings differently:
var_export("42" === "42"); // true
var_export(42 === "42"); // false
var_export("42" === " 42"); // false
var_export("42" === "42 "); // false
var_export("42" === "042"); // false
var_export("10" === "1e1"); // false
var_export("abc" === " abc"); // false
var_export("42" === "abc 42"); // false

echo "\n";

即使在 PHP 8 中,== 运算符也会进行类型转换,并且它以不同的方式处理数字字符串。这个原则没有改变。 PHP 8 中发生的变化是:

  • 相等运算符 == 在 PHP 8 中的行为与在 PHP 7 中的行为不同,用于比较数字和非数字字符串
  • 什么字符串被认为是数字字符串在PHP8
  • 中是不同的

关于后一点,请阅读:

Numeric string handling has been altered [in PHP 8] to be more intuitive and less error-prone. Trailing whitespace is now allowed in numeric strings for consistency with how leading whitespace is treated. This mostly affects:

  • The is_numeric() function
  • String-to-string comparisons
  • Type declarations
  • Increment and decrement operations

您引用的文档实际上是正确的,但可能会更清楚:

Non-strict comparisons between numbers and non-numeric strings now work by casting the number to string and comparing the strings.

我将关键短语“非数字字符串”加粗了。

在 PHP 的各个部分中,有一个“数字字符串”的概念作为一种伪类型:任何 看起来像 数字的字符串可以被语言的一部分视为一个。这是为了帮助 PHP 的主要用例,即从网络请求中获取数据(始终是字符串)并在操作中使用它(可能希望将其视为数字)。

它发挥作用的地方之一是 == 运算符,它试图对您要比较的内容“聪明”。

如果 PHP 确定 == 运算符的两边都是数字 数字字符串,它将通过将它们都转换为来执行比较整数(或浮点数,如果适用)并查看它们是否具有相同的 numeric 值。 “数字字符串”的定义非常广泛,并且允许有前导空格;从 PHP 8 开始,它现在还允许尾随空格,以保持一致性。

因此在您的示例中," 42" 是一个“数字字符串”,42 是一个实际数字,因此执行的比较是 (int)" 42" === 42

你的报价中描述的变化是针对一侧是实际数字(整数或浮点数)而另一侧是非数字字符串的情况,即PHP 认为“看起来不像数字”。在 PHP 的旧版本中,这会 转换为 int,因此 "hello" == 0 会评估 (int)"hello" === 0,即 true。在 PHP 8 中,this case 转换为字符串,因此 "hello" == 0 将计算 "hello" === (string)0,即 false.