PHP header(Location: $url)失效的问题

先说一下背景,项目中有一个调度页任务是分配数据,然后跳转到指定的URL,然后在这个URL进行操作后,会再次跳转到调度页,然后再跳转到URL。前两天出现了一个很神奇的情况,第一次跳转到调度页是空白,第二次跳转是正常的,然后在指定的URL里操作后,跳转到调度页空白,重新打开调度页正常。

一、确认问题

问题复现没有问题,就是奇数次跳调度页会显示空白,也就是跳转失效。偶数次跳调度页正常。

二、查找原因

1、浏览器调试

首先确认正常的调度页会直接302到指定的URL,于是查看奇数次访问调度页的详情。结果发现调度页的Response里是空白,但是不为空,双击后发现是4个空格。

2、确认跳转的URL路径正常

调度页的跳转使用的是PHP header(Location: $url),那么需要确认跳转的URL路径正常,于是在location前输出路径,发现正常跳转的调度页Response是URL路径,跳转失败的调度页Response是4个空格+URL路径。

那么能够确认URL是正确的,直觉是URL前面的空格导致的问题。

3、确认原因

通过搜索引擎去查询header location失效的原因,发现在header location前不能有任何的输出,否则会导致跳转失效。那么原因就能确认了,是因为奇数次的跳转到调度页,会出现莫名的空格,导致跳转失败。

三、空格查找

1、是否是其他同事提交的测试输出

这个调度页是很久之前的逻辑,所以我第一反应是否是其他同事修改了这块的逻辑,然后忘记删除了测试输出。于是查看代码的提交记录,发现涉及到的文件没有更改记录,最近的时间都在19年了,那么排除这个原因。

2、空格代码定位

排除了其他同事提交的测试代码的问题,那么应该就是代码本身的逻辑问题。于是我对调度页的代码进行逐步定位,可以采用二分法。先注释一半,然后查看Response是否有空格,如果没有了就说明是这一部分的问题,如果还有说明是另一部分的问题,然后继续缩小范围。直到定位到某行代码。

我注释掉一行代码,然后空格消失了,那说明就是这行代码有问题。但是我第一反应是我找错了。

1
Tool_Log::writeLog($this->log_name, $log);

没错,就是输出了一行日志,然后就导致页面有空格。

继续往下跟,方法的逻辑很简单,判断日志文件是否存在,如果不存在则创建文件,然后把日志内容输出到文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 记录日志
* 日志格式:类型|时间|客户ip|日志内容
* @param string $type 日志文件标识
* @param string|array $content 日志内容
*/
public static function writeLog($type, $content) {

// 文件不存在则创建文件
$log_file = "" . date('Ymd').'_'.$type.'.log';

$log = array(
'time' => date("Y-m-d H:i:s"),
'content' => is_array($content)? json_encode($content,JSON_UNESCAPED_UNICODE) : $content,
);

// 写入日志
error_log(self::format_log($log) . "\n", 3, $log_file);
return true;
}

这个方法完全没有可能输出空格啊,所以我一度怀疑我是不是定位错了。但是确实注释掉这行代码,跳转就正常了,加上这行代码,跳转就失效了。

然后经过反复查看,我发现通用日志类最开头<?php前面有4个空格。。。

PHP是脚本语言,所以在<?php>之外的内容会认为是HTML标签,会直接输出。所以代码前的空格也会直接输出,导致了跳转失效。

四、总结

PHP不像Java需要编译,PHP是脚本语言,更灵活,开发更快,所以也就导致了代码执行没有进行严格的检查,很多时候会有一些坑,平常开发的时候还是要遵守代码规则,能够避免很多的问题。

五、问题反思

1、为什么之前没发现这个问题呢?

可能是一直没用到这个逻辑,是进行了多次逻辑判断才会到插入日志的这行代码。

2、为什么奇数次出问题,偶数次不出问题呢?

因为调度页的逻辑会判断是否有分配好的数据,如果有则直接跳转,如果没有则分配数据然后跳转。

奇数次进来会走分配数据然后跳转的的逻辑,分配数据会执行插入日志的代码,然后跳转失败了。偶数次进来判断有分配好的数据,则直接跳转了。

3、为什么一开始看不见文件开头的空格呢?

因为我是直接点方法名进入的这个类,刚好没有展示代码文件第一行。所以在这个类看了半天,才发现的问题。


# PHP

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×