项目 | 描述 |
---|---|
搜索引擎 | Bing、Google |
AI 大模型 | 文心一言、通义千问、讯飞星火认知大模型、ChatGPT |
PHP 手册 | PHP Manual |
wsfgrdgh | PHP8中字符串与数字的比较更智能 |
PHP RFC 文档 | Saner string to number comparisons |
项目 | 描述 |
---|---|
PHP | 5.5.0 、5.6.8 、7.0.0 、7.2.5 、7.4.9 、8.0.0 、8.2.9 |
注:
本篇文章在除大板块 PHP8 在字符串与数值的弱比较方面做出的改动
外均不使用 8.x.x
版本的 PHP 解释器。
本篇文章所介绍的 弱比较
适用于任何执行 非严格比较
的操作,包括但不限于 ==
、 !=
、 >
、 >=
、in_array()
、array_search()
。为使文章更为 精简
,本文将专注于 ==
的弱比较讲解。
在 PHP 中,隐式类型转换(Implicit Type Conversion)
是指在某些操作中,PHP 会 自动
将数据 由一种数据类型转换为另一个数据类型
,而 无需显式
地编写 类型转换
代码。
PHP 的隐式类型转换会按照一定规则(具体情况具体分析)对操作数进行转换,以使得相关操作 能够正常进行
下去。
在通过使用句点运算符 .
进行字符串连接操作时,PHP 将会尝试将其他数据类型 转换为字符串数据类型
。对此,请参考如下示例:
// 尝试将两个字符串进行拼接
var_dump('Hello ' . 'World');
// 尝试将数值与字符串进行拼接
var_dump('1 + 1 = ' . 2);
// 尝试将两个数值进行拼接
var_dump(1 . 1);
执行效果
string(11) "Hello World"
string(9) "1 + 1 = 2"
string(2) "11"
在通过 数学运算符
进行数学运算时,PHP 将会尝试将其他数据类型的数据 转换为数值类型
。对此,请参考如下示例:
// 尝试对布尔值 true 与数值 1 进行减法运算
var_dump(true - 1);
// 尝试对布尔值 true 与 false 进行加法运算
var_dump(true + false);
// 尝试进行字符串之间的乘法运算
var_dump('2' * '150');
// 字符串 100djdj 将被转换为 100
var_dump('100djdj' / 10);
// 字符串 djdj100 将被转换为零
var_dump('djdj100' / 10);
执行效果
int(0)
int(1)
int(300)
int(10)
int(0)
在需要使用布尔值的位置,PHP 将尝试将非布尔值的数据 转换为布尔类型的数据
。对此,请参考如下示例:
// 尝试将空字符串转换为布尔值
if(''){
print('Hello World' . "\n");
}
// 尝试将字符串 Hello World 转换为布尔值
if('Hello World'){
print('Hello China' . "\n");
}
// 尝试将数值 999 转换为布尔值
if(999){
print('久久久' . "\n");
}
执行效果
Hello China
久久久
在 PHP 中存在两种相等运算符,即弱类型相等运算符 ==
和强类型相等运算符 ===
,两者都可以用于判断两个操作数是否相等,但存在一些区别。
两者的 区别
在于,弱类型相等运算符
在对操作数进行比较之前,将 自动
进行类型转换以 使两者所属的数据类型相同
。而 强类型相等运算符
在进行比较时,要求两个值的 类型
和 值
都必须 完全相同
,不进行类型转换
。对此,请参考如下示例:
// 在通过弱类型比较运算符对数值与字符串进
// 行比较时,PHP 优先将字符串转换为数值。
// 由于两者转换为同一类型后,值相同,
// 故将返回 true。
var_dump('123' == 123);
// 由于两者的数据类型及值均不相同,故
// 将返沪 false。
var_dump('123' === 123);
执行效果
bool(true)
bool(false)
在 PHP 的 隐式类型转换过程
中,字符串转化为数值的具体规则如下:
首个字符不为数字且不为空格等空白字符
,则将该字符串转化为零。首个字符不为数字但为空格等空白字符
,则尝试读取其余字符,将遇到数字前的所有空白字符均转化为零,将遇到数字后的所有空白字符视为非数字字符;在遇到非数字字符时停止对字符串的读取并将已读取字符转化为数值
。首个字符为数字
,则尝试读取其余字符,在遇到非数字字符(除符合科学计数法格式的字符 e 或 E外)时停止对字符串的读取并将已读取字符转化为数值
。举个栗子
目标字符串 | 转化结果 |
---|---|
Hello123 | 0 |
1Hell2o3 | 1 |
0x8aHello123 | 0 |
9.384Hello | 9.384 |
0008743738Hello948 | 8743738 |
1.223e100 | 122.3 |
注:
PHP 在执行字符串到数值的隐式类型转换过程,均 以十进制表示法
为依据。同上述例子一般,0x8aHello123
中的 0x8a
并不会被识别为十六进制数,由于十进制中不存在 x
,故 PHP 在识别到字符 x
时就将立即停止读取并将读取到的字符串 0
转化为数值,故最终的转化结果为零。
在 PHP 中,若字符串与数值进行弱比较,则 PHP 将 优先把字符串转换为数值
后再进行比较。对此,请观察如下示例:
var_dump('Hello123' == 0); # bool(true)
var_dump('1Hell2o3' == 1); # bool(true)
var_dump('0x8aHello123' == 0); # bool(true)
var_dump('9.384Hello' == 9.384); # bool(true)
var_dump('0363534Hello' == 0); # bool(false)
var_dump('0008743738Hello948' == 8743738); # bool(true)
var_dump(' 8996Hello ' == 8996); # bool(true)
var_dump(' 89 9 ' == 89090); # bool(false)
var_dump(' 89 9 ' == 89); # bool(true)
var_dump('' == 0); # bool(true)
注:
空字符串
在字符串到数值的隐式类型转换过程中将被转化为 零
。
在 PHP 中,e
与 E
均表示 科学计数法(Scientific Notation)
。科学计数法由 基数
和 指数
两部分组成,常用于
表示非常大或非常小的数值。
在科学计数法中,基数 通常
是一个浮点数,介于 1
到 10
之间,而指数是一个整数,表示要将基数乘以 10
的多少次方。基数与指数之间以字符 e
或 E
进行分隔。
举个栗子
// 3.78 * 10 ^ 3
var_dump(3.78e3);
var_dump('3.78e3' == 3780);
// 3 * 10 ^ -1
var_dump(3E-1);
var_dump('3E-1' == 0.3);
执行效果
float(3780)
bool(true)
float(0.3)
bool(true)
零的任何指数次幂都为零
,因此以 0E
或 0e
为前缀的科学计数法表示的数值的结果都将为数值零。对此,请参考如下示例:
var_dump(0e3280);
var_dump('0e30284083' == 0);
var_dump('0esjlfjsld' == 0);
执行效果
float(0)
bool(true)
bool(true)
数值字符串
是指一个包含数字字符的字符串,数值字符串可以用于直接表示一个数值。
举个栗子
"123"
"-42"
"+384"
"3.14"
"-0.5"
"0.0000"
"00000000.0000"
"2.5e3"
"1.2e-2"
"+42.0E0"
"0004746"
"0305940"
" 484748 "
" 4847 "
" 3847"
注:
代表其他进制(非十进制)数值的符号
的字符串不能被称为数值字符串(数学中通常不使用这些符号来标识其他进制的数值
),如 0x1F
、0b10101
等字符串。在 PHP 中,八进制数值
通过 前导零
来表示,但在 数值字符串中
,前导零将 被视为普通数字,不具备标识八进制数值的功能
。intval()
等函数进行显式类型转化。包含空格等空白字符的字符串
为什么也可以是数值字符串?你可以理解为 一眼望去是数值的就是数值字符串
。PHP8 仍旧保留了隐式类型转换
这一特性,但在字符串与数值的弱比较方面做出了优化。在 字符串与数值的弱比较
过程中,PHP 将 依据字符串的不同
选择 将字符串转换为数值
或 将数值转换为字符串
后再进行比较。具体规则如下:
符合数值字符串的定义
,则 PHP 尝试 将字符串转化为数值
后再进行比较。不符合数值字符串的定义
,则 PHP 尝试 将数值转化为字符串
后再进行比较。举个栗子
# 数值 0x10 将首先转化为十进制数 16
# 后再转化为字符串与 '0x10' 进行比较。
var_dump('0x10' == 0x10); # bool(false)
var_dump('16' == 0x10); # bool(true)
var_dump('' == 0); # bool(false)
var_dump('0000.00000' == 0); # bool(true)
var_dump('989 ' == 989); # bool(true)
var_dump(' 989 ' == 989); # bool(true)
var_dump('0000989' == 989); # bool(true)
var_dump('9847Hello' == 9847); # bool(false)
var_dump('Hello' == 0); # bool(false)
PHP8 的这一改动 提高了代码的可预测性
,降低了许多安全隐患发生的可能
。在 PHP8 中,像这类的改动还有许多,也正是如此,PHP8 的开发一度被认为 混入了卓越的安全专家
。