count() emitting an E_WARNING

后端 未结 3 1213
-上瘾入骨i
-上瘾入骨i 2020-12-09 20:13

Prior to PHP 7.2 using count() on a scalar value or non-countable object would return 1 or 0.

For example: https://3v4l.org/tGRDE



        
相关标签:
3条回答
  • 2020-12-09 20:17

    The problem is that calling count() on a scalar or object that doesn't implement the Countable interface returns 1, which can easily hide bugs.

    Given the following:

    function handle_records(iterable $iterable)
    {
        if (count($iterable) === 0) {
            return handle_empty();
        }
    
        foreach ($iterable as $value) {
            handle_value($value);
        }
    }
    

    Passing a Generator that yields nothing would not call handle_empty() nor handle_value().
    Also, no indication would be given that neither were called.

    By default, this will still return 1, though will additionally log a warning. If anything, this warning will bring attention to potential bugs in the code.

    See Counting Non-Countables for further information.

    0 讨论(0)
  • 2020-12-09 20:22

    You can solve it by using "??"-operator. If the left side is null, the right side will be used. So as we have a empty array, our result will be zero.

    count(null ?? [])
    

    Another way would be to typecast it as an array.

    count((array) null)
    
    0 讨论(0)
  • 2020-12-09 20:28

    As we discussed, there are multiple ways to achieve the original functionality of count() and not emit an E_WARNING.

    In PHP 7.3 a new function was added is_countable, specifically to address the E_WARNING issue and the prevalence of applications adopting is_array($var) || $var instanceof \Countable in their code.

    In PHP 7.2, a Warning was added while trying to count uncountable things. After that, everyone was forced to search and change their code, to avoid it. Usually, the following piece of code became standard:

    if (is_array($foo) || $foo instanceof Countable) { // $foo is countable }

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


    Custom Function Replacement

    For that reason it seems the best method of resolving the issue, is to perform the same functionality that PHP is doing with is_countable and creating a custom function to ensure compliance with the original functionality of count.

    Example 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;
    }
    

    Result

    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() function

    Using the above replacement function, it is also possible to shim is_countable in PHP <= 7.2, so it is only used when needed, with minimal overhead.

    Example 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;
    }
    

    Ignore count() Warnings

    As the functionality of count() has not changed and not did not typically emit warnings in the past. An alternative to using a custom function, is to ignore the warning outright by using the @ Error Control Operator

    Warning: This approach has the impact of treating undefined variables as NULL and not displaying Notice: Undefined variable: message.

    Example https://3v4l.org/nmWmE

    @count($var);
    

    Result

    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
    

    Replace count() using APD extension

    As for replacing the internal PHP function count(). There is a PECL extension APD (Advanced PHP Debugger), that allows for override_function that works on core PHP functions. As the extension name suggests, it is technically meant for debugging, but is a viable alternative to replacing all instances of count for a custom function.

    Example

    \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;
    }
    
    0 讨论(0)
提交回复
热议问题