为什么 PHP 认为0等于一个字符串?

我有以下代码:

$item['price'] = 0;
/* Code to get item information goes in here */
if($item['price'] == 'e') {
$item['price'] = -1;
}

它的目的是将商品价格初始化为0,然后获取有关它的信息。如果价格被告知为“ e”,它意味着一个交易所而不是一个卖出,这是存储在数据库中的一个负数。

也有可能将价格保留为0,或者因为该项目是一个奖金,或者因为价格将在稍后设置。

但是,如果没有设置 price,使其初始值为0,则上面指示的 if循环的计算结果为 true,price 被设置为 -1。也就是说,它认为0等于“ e”。

这怎么解释呢?

当价格提供为0(在初始化之后)时,行为是不稳定的: 有时 if 计算为 true,有时它计算为 false。*

37019 次浏览

应该使用 ===而不是 ==,因为普通运算符不会比较类型。相反,它将尝试对项进行类型转换。

同时,===考虑项目的类型。

  • ===的意思是“等于”
  • ==的意思是“有点像”

= = 运算符将尝试匹配值,即使它们属于不同的类型。例如:

'0' == 0 will be true

如果还需要类型比较,请使用 = = = 运算符:

'0' === 0 will be false

你正在做的 ==为你分类的类型。

0是一个整型数,因此在这种情况下,它将把 'e'强制转换为一个整型数。它不能解释为一个,将成为 0。字符串 '0e'将成为 0,并将匹配!

使用 ===

来自 PHP.net:

使用 = = 和其他非严格字符串和数字之间的比较 比较运算符当前的工作方式是将字符串强制转换为数字, 然后对整数或浮点数执行比较 导致了许多令人惊讶的比较结果,其中最值得注意的是 也就是0 = = “ foobar”返回 true。

然而,这种行为在 PHP 8.0中发生了改变:

当与数字字符串进行比较时,PHP8使用数字比较。 否则,它将数字转换为字符串并使用字符串 比较。

PHP 7

0 == 'foobar' // true
0 == '' // true
4 == '4e' // true (4e is cast as a number and becomes 4)

PHP8在进行比较之前将数字转换为字符串

0 == 'foobar' // false
0 == '' // false
4 == '4e' // false ('4e' is considered non-numeric therefore 4 is cast as a string and becomes '4')

这是一个重大的变化,因此它是在一个新的主要 PHP 版本中实现的。这个变化打破了依赖于旧行为的脚本的向下兼容。

这是由于 PHP 如何执行 ==比较运算符表示的比较操作:

如果您将数字与字符串进行比较,或者比较涉及到数字字符串,那么每个字符串都将转换为一个数字,并执行数字比较。[ ... ]当比较是 ===!==时,类型转换不会发生,因为这涉及到比较类型和值。

由于第一个操作数是一个数字(0) ,第二个操作数是一个字符串('e') ,所以字符串也被转换为一个数字(另见 表 < em > 与不同类型的比较)。关于字符串数据类型的手册页定义了如何执行 字符串到数字的转换:

在数值上下文中计算字符串时,将按如下方式确定结果值和类型。

如果字符串不包含任何字符‘ .’、‘ e’或‘ E’,并且数值符合整数类型限制(由 PHP_INT_MAX定义) ,则字符串将被计算为整数。在所有其他情况下,它将被评估为一个浮点数。

在这种情况下,字符串是 'e',因此它将被计算为浮点数:

该值由字符串的初始部分给出。如果字符串以有效的数值数据开始,则这将是所使用的值。否则,该值将为 0(零)。有效的数值数据是一个可选的符号,后跟一个或多个数字(可选包含一个小数点) ,后跟一个可选的指数。指数是一个“ e”或“ E”后跟一个或多个数字。

由于 'e'不以有效的数值数据开始,因此计算结果为浮点 0

在 PHP 中有一个相当方便的方法来验证“0”、“ false”、“ off”作为 = = false 和“1”、“ on”、“ true”作为 = = true 的组合,而这些常常被忽略。它对于解析 GET/POST 参数特别有用:

filter_var( $item['price'], FILTER_VALIDATE_BOOLEAN );

它与这个用例并不完全相关,但考虑到相似性和事实,这是搜索结果往往会发现,当问验证(字符串)“0”为假的问题时,我认为这将有助于其他人。

Http://www.php.net/manual/en/filter.filters.validate.php

您的问题是双等运算符,它会将右成员类型转换为左成员类型。如果你愿意,可以用严格的。

if($item['price'] == 'e') {
$item['price'] = -1;
}

让我们回到你的代码(复制上面)。在这种情况下,在大多数情况下,$item [‘ price’]是一个整数(当然,除非它等于 e)。因此,根据 PHP 的法则,PHP 将 "e"类型化为整数,从而生成 int(0)。(不相信我?<?php $i="e"; echo (int)$i; ?>).

要轻松避免这种情况,可以使用三重等价(精确比较)运算符,它将检查类型,而不会隐式转换类型。

P.S: 一个 PHP 有趣的事实: a == b并不意味着 b == a。举个例子,反过来看: 如果 $item [‘ price’]总是一个整数,那么 if ("e" == $item['price'])永远不会实现。

"ABC" == 0

计算 true,因为 第一 "ABC"被转换成整数,变成 0 那么,它被比作 0

这是 PHP 语言的 奇怪行为: 通常人们会期望 0被提升为字符串 "0",然后与 "ABC"进行比较,得到结果 false。 也许在其他语言中也是这样,比如 JavaScript,弱比较 "ABC" == 0计算 false

进行严格的比较可以解决这个问题:

"ABC" === 0

评估 false

但是 如果我需要将数字作为字符串与数字进行比较呢?

"123" === 123

计算 false,因为左项和右项的类型不同。

实际上需要的是一个没有 PHP 类型杂乱陷阱的弱比较。

解决方案是将术语显式提升为字符串,然后进行比较(严格或弱不再重要)。

(string)"123" === (string)123

true

同时

(string)"123" === (string)0

false


应用于原始代码:

$item['price'] = 0;
/*code to get item information goes in here*/
if((string)$item['price'] == 'e') {
$item['price'] = -1;
}

我认为最好是通过我做过的例子来证明,同时遇到同样奇怪的行为。看看我的测试用例,希望它能帮助您更好地理解这种行为:

// Normal comparison using the == Operator
echo (0 == "0"); // true
echo (0 == "a"); // true
echo (0 == "safta!"); // true
echo (1000 == "bla"); // false. It appears that PHP has a weird behavior only with the number / string 0 / "0" according to the past 3 examples.
echo (23 == "23"); // true. So as we said, PHP has a problem (not a problem but weird behavior) only when the number / string 0 (or "0") is present
echo (23 == "24"); // false. values aren't equal (unlike last example). The type is less relevant with the == operator as we can see.


// Now using the === and !== Operators
echo ("0" === 0); // false, since === requires both value and type to be the same. Here, type is different (int vs string)
echo ("0" !== 0); // true because they aren't the same in terms of === comparison (type is different and that's why it's true)
echo ("bla" === "blaa"); // false because the values are not the same. The type is the same, but === checks for both equal type and equal value.


//Now using casting and === Operator:
echo ((string)123 === "123"); // true. The casting of the int 123 to string changed it to "123" and now both variables have same value and are of same type
echo ((int)"123" === 123); // true. The casting of the string 123 to int, changed it to int, and now both variables are of same value and type (which is exactly what the === operator is looking for)


// Now using casting and == Operator. Basically, as we've seen above, the == care less for the
// type of var, but more to the value. So the casting is less relevant here, because even
// without casting, like we saw earlier, we can still compare string to int with the == operator
// and if their value is same, we'll get true. Either way, we will show that:
echo ((string)123 == "123"); // true. The casting of the int 123 to string changed it to "123" and now both vars have same value and are of same type
echo ((int)"123" == 123); // true. The casting of the string 123 to int, changed it to int, and now both vars are of same value and type (which is exactly what the === operator is looking for)

基本上,始终使用 ===操作符,以保证类型安全。

Enter image description here

Enter image description here