为什么在空字符串上没有匹配时,PHP的 sscanf() 会返回 -1,而不是0?

编程语言 2026-07-09

今天在我的代码里遇到一个bug,当我用空合并运算符把提交的值变成空字符串时,这个值正在被 [sscanf()] 验证和清洗。我的编程意图是在提交了一个有效整数时才调用某个方法,否则就不应该调用该方法,并返回 null

例如:

$details = sscanf($userInput ?? '', '%d', $id)) ? $this->fetchDetails($id) : null;

在单元测试时,我发现当提交的是非数字字符串时,行为很完美。但当 sscanf() 的第一个参数是空字符串时,三元条件表达式被评估为真,导致 fetchDetails() 方法失败,因为在本应接收整型参数的位置传入的是 null

这是怎么回事?这种行为有什么区别?

解决方案

PHPAPI in php_scanf_internal() 的最底部,位于 php-src/ext/standard/scanf.c 有一个条件,映射了C 的 scanf() 的继承行为。实际情况是,如果在匹配尚未开始前输入字符串就已经用尽,就会返回 SCAN_ERROR_EOF——这是一个等于 -1 的常量。因此,用户端编码尝试中的问题是一个“下溢(underflow)”问题。

...
done:
    result = SCAN_SUCCESS;

    if (underflow && (0==nconversions)) {
        scan_set_error_return( numVars, return_value );
        result = SCAN_ERROR_EOF;
    } else if (numVars) {
        zval_ptr_dtor(return_value );
        ZVAL_LONG(return_value, nconversions);
    } else if (nconversions < totalVars) {
        /* TODO: not all elements converted. we need to prune the list - cc */
    }
    return result;
}

简单解释这种行为:带更多测试的演示

code return $num
echo sscanf('', '%d', $num); -1 null
echo sscanf('foo', '%d', $num); 0 null
echo sscanf('411', '%d', $num); 1 411

在给定的示例中,简单地检查返回值是否大于0,而不是做布尔判断,三元表达式就会按预期工作。

$details = sscanf($userInput ?? '', '%d', $id)) > 0 ? $this->fetchDetails($id) : null;

请注意 fscanf() 也会表现出这种行为,因为源码中的一条注释指出:

此文件包含实现sscanf的基础代码,进而实现fscanf。

并且在上方的文档块中 PHPAPI int php_sscanf_internal()

/* {{{ php_sscanf_internal
 * This is the internal function which does processing on behalf of
 * both sscanf() and fscanf()

演示:

$handle = tmpfile();
fwrite($handle, "\n");
rewind($handle);
var_dump(
    fscanf($handle, '%d', $integer),
    $integer
);
/* outputs:
int(-1)
NULL
*/
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章