提问者:小点点

count()发出E_警告


在PHP7.2之前,对标量值或不可计数对象使用count()将返回10

例如:https://3v4l.org/tGRDE

var_dump(count(123)); //int(1)
var_dump(count(new stdclass)); //int(1)
var_dump(count('hello world'));  //int(1)
var_dump(count(null));  //int(0)

在对PHP7.2的更新中,使用上面演示的count()将发出警告消息。

尝试计数()不可计数类型(包括sizeof()别名函数)时,将发出E_警告。

警告:count():参数必须是实现可数[sic]的数组或对象

因此,许多流行的框架将提升E_警告,并抛出异常。

参数必须是实现可数的数组或对象

PHP开发人员也对错误提升行为进行了评论。

显示警告或将其转换为更严重错误/异常的环境将受到影响,但这应该引起对代码中错误的注意。

在PHP7.2中,如果不修改错误报告设置且不使用@count(),如何实现不发出E_警告的count()的先前行为?


共3个答案

匿名用户

正如我们所讨论的,有多种方法可以实现count()的原始功能,并且不发出E_WARNING

在PHP7.3中,添加了一个新函数is_countable,专门用于解决E_警告问题,以及在代码中采用is_数组($var)|$var instanceof\countable的应用程序的普遍性。

在PHP7.2中,在尝试计数无法计数的事物时添加了警告。在那之后,每个人都被迫搜索和更改他们的代码,以避免它。通常,以下代码成为标准:

if(is_array($foo)||$foo instanceof可数) { // $foo是可数的}

https://wiki.php.net/rfc/is-countable

出于这个原因,解决这个问题的最佳方法似乎是执行与PHP使用的is\u countable相同的功能,并创建一个自定义函数以确保符合count的原始功能。

实例https://3v4l.org/8M0Wd

function countValid($array_or_countable, $mode = \COUNT_NORMAL)
{
    if (
        (\PHP_VERSION_ID >= 70300 && \is_countable($array_or_countable)) ||
        \is_array($array_or_countable) ||
        $array_or_countable instanceof \Countable
    ) {
        return \count($array_or_countable, $mode);
    }

    return null === $array_or_countable ? 0 : 1;
}

结果

array: 3
string: 1
number: 1
iterator: 3
countable: 3
zero: 1
string_zero: 1
object: 1
stdClass: 1
null: 0
empty: 1
boolt: 1
boolf: 1

Notice: Undefined variable: undefined in /in/8M0Wd on line 53
undefined: 0

使用上述替换函数,还可以在PHP中填充is_countable

实例https://3v4l.org/i5KWH

if (!\function_exists('is_countable')) {
    function is_countable($value)
    {
        return \is_array($value) || $value instanceof \Countable;
    }
}

function countValid($array_or_countable, $mode = \COUNT_NORMAL)
{
    if (\is_countable($array_or_countable)) {
        return \count($array_or_countable, $mode);
    }

    return null === $array_or_countable ? 0 : 1;
}

因为count()的功能没有改变,过去通常不会发出警告。使用自定义函数的另一种方法是使用@错误控制运算符直接忽略警告

警告:此方法会将未定义变量视为NULL,并且不会显示注意:未定义变量:消息。

实例https://3v4l.org/nmWmE

@count($var);

结果

array: 3
string: 1
number: 1
iterator: 3
countable: 3
zero: 1
string_zero: 1
object: 1
stdClass: 1
null: 0
empty: 1
boolt: 1
boolf: 1
---
Undefined: 0

至于替换内部PHP函数count()。有一个PECL扩展APD(高级PHP调试器),它允许在核心PHP函数上使用覆盖函数。正如扩展名所示,它在技术上是用于调试的,但对于替换自定义函数的count的所有实例,它是一种可行的替代方法。

实例

\rename_function('count', 'old_count');
\override_function('count', '$array_or_countable,$mode', 'return countValid($array_or_countable,$mode);');

if (!\function_exists('is_countable')) {
    function is_countable($value)
    {
        return \is_array($value) || $value instanceof \Countable;
    }
}

function countValid($array_or_countable, $mode = \COUNT_NORMAL)
{
    if (\is_countable($array_or_countable)) {
        return \old_count($array_or_countable, $mode);
    }

    return null === $array_or_countable ? 0 : 1;
}

匿名用户

问题在于,对未实现可数接口的标量或对象调用count()会返回1,这很容易隐藏bug。

鉴于以下情况:

function handle_records(iterable $iterable)
{
    if (count($iterable) === 0) {
        return handle_empty();
    }

    foreach ($iterable as $value) {
        handle_value($value);
    }
}

传递一个不产生任何结果的生成器不会调用handle_empty()也不会调用handle_value()

默认情况下,这仍然会返回1,尽管还会记录一个警告。如果有的话,这个警告会引起人们对代码中潜在错误的注意。

有关更多信息,请参见计数不可数项。

匿名用户

你可以用"??"-运算符来解决。如果左侧为空,则使用右侧。所以当我们有一个空数组时,我们的结果将是零。

count(null ?? [])

另一种方法是将其类型化为数组。

count((array) null)

相关问题