@IT·互联网虞大胆的PHP之旅

fgetss() PHP函数的Bug

2018-11-07  本文已影响24人  虞大胆的叽叽喳喳

昨天在工作上遇到一个比较怪异的问题,觉得很有意思,特此记录下,也许你也曾经遇到过该问题。

原系统有一个程序,接收用户上传的 CSV 文件,这个文件共有10万行,每一行就一列,保存的数据是用户的邮箱地址,程序在接收到这个文件后,经过一系列其他逻辑处理,最后将这10万个邮件地址保存到数据库表中,每一个邮件地址一天表记录。

可程序运行结果只能录入5200条数据,为了排查问题,自己脑洞了很多可能遇到的情况,比如是不是文件太大了,是不是邮件地址不合法被丢弃了,经过 N 长时间的断点排查,大概定位到是以下代码产生的问题:

$handle = fopen("./file.csv", "r");
if ($handle) {
    while (!feof($handle)) {
        $buffer = fgetss($handle, 4096);
           
        if ($buffer=="") {
            continue ;
        }
        //存入到数据库表中
        saveData($buffer);
    }
    fclose($handle);
}

程序看上去很简单,之所以录入的数据少了95%,可能是因为有很多条数据($buffer变量)为空,可我将 file.csv 打开一看,觉得数据挺正常的,怎么会有那么多数据为空呢?

仔细观察了下,突然发现 fgetss() 函数,平时从文件句柄读取一行数据用的都是 fgets() 函数,我下意识的将 fgetss() 改为 fgets(),程序居然运行正确了,成功录入了10万条数据。

很明显了,可能就是 fgetss() 函数的锅,打开手册看看这函数是干啥的:

(PHP 4, PHP 5, PHP 7)

fgetss — 从文件指针中读取一行并过滤掉 HTML 标记

和 fgets() 相同,只除了 fgetss() 尝试从读取的文本中去掉任何 HTML 和 PHP 标记。

也就是说该函数在 fgets()函数的基础之上多做了一步工作,就是去除 HTML 和 PHP 标签,肯定是在运行过程中遇到了什么特殊字符,导致读出来的数据为空,可一个大大的问号出现了,难道 95% 的数据有特殊字符?不太可能吧。

我想了个方法,先找出这个字符是啥,改了下代码:

$handle = fopen("./file.csv", "r");
$i = 0  ;
if ($handle) {
    while (!feof($handle)) {
        $buffer = fgetss($handle, 4096);
        $i++ ;
        if ($buffer=="") {
            echo $i . "-" . $debugstr . "\r\n" ;
            continue ;
        }

        $debugstr = $buffer ;
    }
    fclose($handle);
}

程序逻辑就是如果遇到为空的字符,打印上一个字符,得到的结果如下:

5200-yhyy?yj'??
5201-yhyy?yj'??
5202-yhyy?yj'??
...
100000-yhyy?yj'??

也就是说代码遇到了一行特殊字符,导致它后面的数据全部为空了,这个影响就比较大了,如果遇到一行数据处理不成功抛弃即可,但它导致后面的数据读取全为空了,这就是问题的关键所在。我在 file.csv 中找到了这行特殊数据 yhyy?yj'??<@sina.com,至于为什么会出现该问题就不得而知了,然后我在 PHP7 中也测试了下,还是同样的问题,算 PHP 的一个 Bug 吗?

最后也吐槽下 PHP,虽然函数非常多,方便开发,但非常的不结构化,这一点比 Python 差了很多,fgets() 作为一个标准的 C 函数,干它该干的,读取一行数据即可,非要基于 fgets() 再包装一个不伦不类的 fgetss() 函数,显得画蛇添足。


【这篇文章于2018-10-28号发表于公众号,地址https://mp.weixin.qq.com/s/nFgiK-gOva6OxW4s66HosQ,也可以关注我的公众号(ID:yudadanwx,虞大胆的叽叽喳喳)】

上一篇 下一篇

猜你喜欢

热点阅读