前言
在 PHP 中,&&
和 ||
是逻辑运算符,分别用于逻辑与(AND)和逻辑或(OR)操作。而 and 和 or 也是逻辑运算符,执行相同的基本逻辑操作,但不同之处在于它们的优先级。
简单来说,&&
和 ||
的优先级较高,而 and 和 or 的优先级较低。
这意味着,在表达式中,&&
和 ||
会先于其他低优先级的运算符(例如赋值运算符 =)进行计算,而 and 和 or 会后于其他高优先级的运算符进行计算。
举例
$result = true || false;
// 结果是 true,因为 || 的优先级高于 =,所以先执行 true || false,然后将结果(true)赋值给 $result
$result = true or false;
// 结果也是 true,但与上面的计算不同。因为 or 的优先级低于 =,所以这里实际上是先将 true 赋值给 $result,然后执行 false,但 false 的值没有被用到。
实际应用中有如下一段文件上传代码的判断检查(假设文件名为 a.php):
$file = $_FILES["file"];
$allowedExts = ["gif", "jpeg", "jpg", "png", "webp"];
$temp = explode(".", $_FILES["file"]["name"]);
$extension = end($temp);
if ($extension == "") {
exit("<script language=\"JavaScript\">;alert(\"未选择图片!\");location.href=\"../setup.php\";</script>;");
}
if ($file["type"] == "image/jpeg" || $file["type"] == "image/png" || $file["type"] == "image/jpg" || $file["type"] == "image/gif" || $file["type"] == "image/webp" && in_array($extension, $allowedExts)) {
} else {
exit("<script language=\"JavaScript\">;alert(\"文件类型错误,请上传图片!\");location.href=\"../setup.php\";</script>;");
}
if ($file["size"] > 5242880) {
exit("<script language=\"JavaScript\">;alert(\"请上传5MB以内的图片!\");location.href=\"../setup.php\";</script>;");
}
代码的本意是判断上传的文件类型是否为 jpeg、png、jpg、gif 或 webp当中的一种.且同时需要文件名后缀也是其中的一中.
但问题就出在 if 条件最后的 &&
判断上, 因为 &&
的优先级高于 ||
,这意味这个条件语句中,&&
操作会首先被计算,这就导致逻辑错误。
实际上的执行顺序如下:
- 检查
$file["type"]
是否等于"image/jpeg"
,或者"image/png"
,或者"image/jpg"
,或者"image/gif"
。 - 然后检查
$file["type"]
是否等于"image/webp"
并且$extension
是否在$allowedExts
数组中。
由于没有圆括号将所有的文件类型检查组合在一起,这个条件实际上只在 $file["type"]
是 "image/webp"
并且 $extension
是允许的扩展名时,才要求 $extension
必须在 $allowedExts
数组中。对于其他文件类型,即使扩展名不在允许的列表中,代码也会判断为有效。
考虑到上传的文件名为 a.php
,根据这段代码的逻辑,$extension
将会是 php
,这不在 $allowedExts
数组中。然而,由于 $file["type"]
可能被客户端错误地设置为任何上述的有效图片MIME类型,这样的文件会被错误地接受,因为扩展名检查只对 "image/webp"
类型的文件生效。
实验如下
<?
$name = "a.php";
$type = "image/png";
$allowedExts = ["gif", "jpeg", "jpg", "png", "webp"];
$temp = explode(".", $name);
$extension = end($temp);
echo $extension;
echo "\n";
if($type == "image/png" || $type == "image/jpg" && in_array($extension, $allowedExts)){
// if($type == "image/png" || $type == "image/jpg" and in_array($extension, $allowedExts)){
// if(($type == "image/png" || $type == "image/jpg") && (in_array($extension, $allowedExts))){
echo "OK\n";
}else{
echo "Bad\n";
}
这里利用 https://onlinephp.io/ 来进行快速的测试验证
可以看到结果是 OK,即验证通过,但是文件名后缀却是 php ,违背了本意.
我给出的测试demo有两个注释的修正示例,结果是符合初衷的:
适当的修改为类似如下即可避免此种逻辑错误.
$file = $_FILES["file"];
$allowedExts = ["gif", "jpeg", "jpg", "png", "webp"];
$temp = explode(".", $file["name"]);
$extension = strtolower(end($temp)); // 将扩展名转换为小写
// 确保选择了文件
if ($extension == "") {
die("未选择图片!");
}
// 检查文件扩展名和MIME类型
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file["tmp_name"]);
$allowedMimeTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
if (in_array($extension, $allowedExts) && in_array($mime, $allowedMimeTypes)) {
// 文件类型和扩展名都有效
// 继续处理文件上传...
} else {
die("文件类型错误,请上传图片!");
}
总结
&&
运算符和||
运算符都是用于将多个条件连接起来,判断整个条件表达式的真假。&&
运算符要求所有连接的条件同时为真,只要有一个条件为假,整个表达式就为假。||
运算符只要有一个条件为真,整个表达式就为真。- 当使用
&&
运算符时,如果第一个条件为假,PHP就会停止执行后面的条件判断,因为已经可以确定整个表达式的结果为假。 - 当使用
||
运算符时,如果第一个条件为真,PHP也会停止执行后面的条件判断,因为已经可以确定整个表达式的结果为真。
总结:&&
运算符要求所有条件都为真,||
运算符只要有一个条件为真。根据不同的条件逻辑,选择合适的运算符来构建条件表达式,以实现逻辑判断。