count()函数发出E_WARNING警告

13
在PHP 7.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)

更新到PHP 7.2+中,像上面演示的使用count()将会发出警告信息。

现在尝试计算(包括sizeof()别名函数)不可计数类型时,将会发出一个E_WARNING

Warning: count(): Parameter must be an array or an object that implements Countable [sic]

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

[ErrorException] count(): Parameter must be an array or an object that implements Countable

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

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

如何在PHP 7.2+中实现count()的先前行为,而不会发出E_WARNING,而且不需要修改错误报告设置和不使用@count()呢?

2
你传递了一个字符串/数字,但是在第一次使用时使用了错误的函数。 - Lawrence Cherone
4
我会尝试翻译你的内容:我希望回滚到7.1。PHP开发人员应该将7.2命名为8.0;通常接受的版本号指南规定,当更改导致向后不兼容时,应增加主要(第一个)数字。期望次要修补程序(第二个数字增量)不具有此类破坏是合理的。 - citrusy
1
您实际上正在询问“模拟count()的最佳方法是什么”,这不是一个SO有效的问题。因为它会得到多个产生有争议结果的答案。您认为@是不好的,尽管它能够产生期望的结果,但这是基于您非常合理的担忧。长期可维护性也是主观的。这一切归结为您希望继续使用自2013年以来已经记录的不受预期方式中的count。在我看来,最好的方法是重构您的代码以兼容7.2+,原因就是像您指出的那样,将来它可能会抛出异常。 - Will B.
1
我只是在想知道stackoverflow发生了什么事。无论如何,感谢提供答案的链接。很遗憾不能在这里发布,我很想得到那些解决方案的反馈或者提供反馈。真的非常遗憾。 - Toskan
1
这就是为什么人们使用stackoverflow的原因 - 找到解决问题的方法 - 而不仅仅是批评一个对许多开发人员非常重要的问题。我感激这个问题和所有的答案。 - Sol
显示剩余19条评论
3个回答

15

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

在PHP 7.3中添加了一个新函数is_countable,专门解决E_WARNING问题和应用程序在其代码中采用is_array($var) || $var instanceof \Countable的普遍性。

在PHP 7.2中,尝试计算不可计数的事物时会出现警告。之后,每个人都被迫搜索并更改他们的代码以避免这种情况。通常,下面的代码成为标准:

 

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

 

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


自定义函数替代

因此,似乎解决该问题的最佳方法是执行与is_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

Shim is_countable() 函数

使用上述替代函数,可以在 PHP <= 7.2 中为 is_countable 进行 shimming,以便在需要时仅使用一次,并带有最小的开销。

示例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() 的警告

由于 count() 的功能没有改变,且通常在过去不会发出警告。一个替代使用自定义函数的方法是通过使用 @ 错误控制运算符 来直接忽略警告。

注意:这种方法的影响是将未定义的变量视为 NULL,并且不显示 Notice: Undefined variable: 消息。

示例 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

使用APD扩展替换count()

要替换内部的PHP函数count(),可以使用PECL扩展APD(Advanced PHP Debugger),该扩展允许使用override_function来覆盖核心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;
}

6
问题在于对一个标量或未实现可计数接口的对象调用count()会返回1,这可能会掩盖错误。

考虑以下情况:

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

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

传递一个生成器,它不产生任何值将不会调用handle_empty()handle_value()。此外,也不会提示两者都没有被调用。
默认情况下,这仍然会返回1,但会额外记录一个警告。如果有的话,此警告将引起对代码中潜在错误的关注。
有关更多信息,请参见计算不可计数物品

好的,你说得没错。问题是,这就是这个函数的工作方式。如果你不理解这个函数的工作原理,它可能会导致错误,同意吗?现在他们所做的是改变了一个核心函数,因为有人不喜欢旧函数。这样做破坏了很多库。 - Toskan
我只是在计数之前将变量设置为数组,像这样。如果(!is_array($arrayVariable)){ $arrayVariable = array(); } - Apit John Ismail

1
您可以使用“??”运算符来解决这个问题。如果左侧为空,则使用右侧。因此,由于我们有一个空数组,结果将为零。
count(null ?? [])

另一种方法是将它强制转换为数组。
count((array) null)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接