fgetss() PHP函数的Bug

作者: 虞大胆的叽叽喳喳 | 来源:发表于2018-11-07 17:18 被阅读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,虞大胆的叽叽喳喳)】

    相关文章

      网友评论

      • helloKimmy:这个代码真的能运行吗?测试用数据,背后可能有干扰源,因为好像是个查询代码。是每回运行到一个地方就截断了么?区别在于,两个指令,一个不查看数据内容,另一个检查数据内容。测试用数据是想用来侵入服务器专门制作的吧?哈哈!

      本文标题:fgetss() PHP函数的Bug

      本文链接:https://www.haomeiwen.com/subject/nvgvxqtx.html