先说一下背景,项目中有一个调度页任务是分配数据,然后跳转到指定的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 | /** |
这个方法完全没有可能输出空格啊,所以我一度怀疑我是不是定位错了。但是确实注释掉这行代码,跳转就正常了,加上这行代码,跳转就失效了。
然后经过反复查看,我发现通用日志类最开头<?php前面有4个空格。。。
PHP是脚本语言,所以在<?php>之外的内容会认为是HTML标签,会直接输出。所以代码前的空格也会直接输出,导致了跳转失效。
四、总结
PHP不像Java需要编译,PHP是脚本语言,更灵活,开发更快,所以也就导致了代码执行没有进行严格的检查,很多时候会有一些坑,平常开发的时候还是要遵守代码规则,能够避免很多的问题。
五、问题反思
1、为什么之前没发现这个问题呢?
可能是一直没用到这个逻辑,是进行了多次逻辑判断才会到插入日志的这行代码。
2、为什么奇数次出问题,偶数次不出问题呢?
因为调度页的逻辑会判断是否有分配好的数据,如果有则直接跳转,如果没有则分配数据然后跳转。
奇数次进来会走分配数据然后跳转的的逻辑,分配数据会执行插入日志的代码,然后跳转失败了。偶数次进来判断有分配好的数据,则直接跳转了。
3、为什么一开始看不见文件开头的空格呢?
因为我是直接点方法名进入的这个类,刚好没有展示代码文件第一行。所以在这个类看了半天,才发现的问题。