写在前面
连续碰了两次不同的PHP弱类型题,都卡了很久,还是总结一下吧
有几个点在网上不同师傅的文章意见不一样,本着实践出真知的想法,本次的所有内容都跑了代码,或者查阅了官方文档
本次的代码保留了老版本的测试结果,而我会基于新的版本进行测试,本文的注意点部分很多都是最新版本中的变动
至于考证每种绕过方式具体哪些版本可用,工作量实在太大,先写进Todo里吧
PHP版本:PHP Version 8.2.2
概念
- 强类型是两个不同类型的变量不能用同一块内存存储
- 弱类型是两个不同类型的变量可以用同一块内存存储
PHP 是弱类型语言
弱类型简单来说就是数据类型可以被忽视的语言,和强类型语言的强制数据类型定义不同,弱类型可以一个变量赋不同数据类型的值
PHP 类型比较表
弱比较/松散比较/==
注意PHP 8.0.0是一个分水岭,有些弱比较无法使用了
强比较/严格比较/===
类型转换问题
比较操作符
===
在进行比较的时候,会先判断两种字符串的类型是否相等,再比较==
在进行比较的时候,会先将字符串类型转化成相同,再比较
如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行
比较简单,不过多举例
Hash 比较缺陷
"0e132456789"=="0e7124511451155" //true
"0e123456abc"=="0e1dddada" //false
"0e1abc"=="0" //true
在进行比较运算时,如果遇到了 0exxxxx(xxxxx为纯数字) 这种字符串,就会将这种字符串解析为科学计数法
如果不满足 0exxxxx(xxxxx为纯数字) 这种模式,就会当作字符串进行比较,所以不会相等
注意点
在最新版本中"0e1abc"
与"0"
弱比较也不相等
var_dump("0e1abc"=="0"); // false
十六进制转换
"0x1e240"=="123456" //true
"0x1e240"==123456 //true
"0x1e240"=="1e240" //false
当其中的一个字符串是 0x 开头的时候,PHP 会将此字符串解析成为十进制然后再进行比较
注意点
在最新版本中,弱比较不再将字符串解析成为十进制然后再进行比较
"0x1e240"=="123456" //false
"0x1e240"==123456 //false
类型转换
<?php
$test=1 + "10.5"; // $test=11.5(float)
$test=1 + "-1.3e3"; //$test=-1299(float)
$test=1 + "bob-1.3e3";//$test=1(int)
$test=1 + "2admin";//$test=3(int)
$test=1 + "admin2";//$test=1(int)
?>
PHP 手册:
当一个字符串当作一个数值来取值,其结果和类型如下:
- 如果该字符串没有包含
.
,e
,E
,并且其数值在整型的范围之内,则该字符串被当作 int 来取值 - 其他所有情况下都被作为 float 来取值
该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为 0
注意点
在PHP 8.0.0引入了数字字符串,PHP 8.0.0之后之前说的部分规则不适用了
<?php
$foo = 1 + "10.5"; // $foo 是 float (11.5)
$foo = 1 + "-1.3e3"; // $foo 是 float (-1299)
$foo = 1 + "bob-1.3e3"; // PHP 8.0.0 起产生 TypeError;在此之前 $foo 是 integer (1)
$foo = 1 + "bob3"; // PHP 8.0.0 起产生 TypeError;在此之前 $foo 是 integer (1)
$foo = 1 + "10 Small Pigs"; // PHP 8.0.0 起,$foo 是 integer (11),并且产生 E_WARNING;在此之前产生 E_NOTICE
$foo = 4 + "10.2 Little Piggies"; // PHP 8.0.0 起,$foo 是 float (14.2),并且产生 E_WARNING;在此之前产生 E_NOTICE
$foo = "10.0