Iterable objects and array type hinting?

前端 未结 6 1565
遇见更好的自我
遇见更好的自我 2020-11-30 03:28

I have a lot of functions that either have type hinting for arrays or use is_array() to check the array-ness of a variable.

Now I\'m starting to use ob

相关标签:
6条回答
  • 2020-11-30 03:59

    Unfortunately you won't be able to use type hints for this and will have to do the is_array($var) or $var instanceof ArrayAccess stuff. This is a known issue but afaik it is still not resolved. At least it doesn't work with PHP 5.3.2 which I just tested.

    0 讨论(0)
  • 2020-11-30 04:13

    I use a simple (and maybe a little hackish) way to test for "iterability".

    function is_iterable($var) {
        set_error_handler(function ($errno, $errstr, $errfile, $errline, array $errcontext)
        {
            throw new \ErrorException($errstr, null, $errno, $errfile, $errline);
        });
    
        try {
            foreach ($var as $v) {
                break;
            }
        } catch (\ErrorException $e) {
            restore_error_handler();
            return false;
        }
        restore_error_handler();
        return true;
    }
    

    When you try to loop a non iterable variable, PHP throws a warning. By setting a custom error handler prior the attempt to iterate, you can transform an error into an exception thus enabling you to use a try/catch block. Afterwards you restore the previous error handler to not disrupt the program flow.

    Here's a small test case (tested in PHP 5.3.15):

    class Foo {
        public $a = 'one';
        public $b = 'two';
    }
    
    $foo = new Foo();
    $bar = array('d','e','f');
    $baz = 'string';
    $bazinga = 1;
    $boo = new StdClass();    
    
    var_dump(is_iterable($foo)); //boolean true
    var_dump(is_iterable($bar)); //boolean true
    var_dump(is_iterable($baz)); //boolean false
    var_dump(is_iterable($bazinga)); //bolean false
    var_dump(is_iterable($boo)); //bolean true
    
    0 讨论(0)
  • 2020-11-30 04:14

    I actually had to add a check for stdClass, as instances of stdClass do work in foreach loops, but stdClass does not implement Traversable:

    function is_iterable($var) {
        return (is_array($var) || $var instanceof Traversable || $var instanceof stdClass);
    }
    
    0 讨论(0)
  • 2020-11-30 04:15

    You CAN use type hinting if you switch to using iterable objects.

    protected function doSomethingWithIterableObject(Iterator $iterableObject) {}
    

    or

    protected function doSomethingWithIterableObject(Traversable $iterableObject) {}
    

    However, this can not be used to accept iterable objects and arrays at the same time. If you really want to do that could try building a wrapper function something like this:

    // generic function (use name of original function) for old code
    // (new code may call the appropriate function directly)
    public function doSomethingIterable($iterable)
    {
        if (is_array($iterable)) {
            return $this->doSomethingIterableWithArray($iterable);
        }
        if ($iterable instanceof Traversable) {
            return $this->doSomethingIterableWithObject($iterable);
        }
        return null;
    }
    public function doSomethingIterableWithArray(array $iterable)
    {
        return $this->myIterableFunction($iterable);
    }
    public function doSomethingIterableWithObject(Iterator $iterable)
    {
        return $this->myIterableFunction($iterable);
    }
    protected function myIterableFunction($iterable)
    {
        // no type checking here
        $result = null;
        foreach ($iterable as $item)
        {
            // do stuff
        }
        return $result;
    }
    
    0 讨论(0)
  • 2020-11-30 04:16

    PHP 7.1.0 has introduced the iterable pseudo-type and the is_iterable() function, which is specially designed for such a purpose:

    This […] proposes a new iterable pseudo-type. This type is analogous to callable, accepting multiple types instead of one single type.

    iterable accepts any array or object implementing Traversable. Both of these types are iterable using foreach and can be used with yield from within a generator.

    function foo(iterable $iterable) {
        foreach ($iterable as $value) {
            // ...
        }
    }
    

    This […] also adds a function is_iterable() that returns a boolean: true if a value is iterable and will be accepted by the iterable pseudo-type, false for other values.

    var_dump(is_iterable([1, 2, 3])); // bool(true)
    var_dump(is_iterable(new ArrayIterator([1, 2, 3]))); // bool(true)
    var_dump(is_iterable((function () { yield 1; })())); // bool(true)
    var_dump(is_iterable(1)); // bool(false)
    var_dump(is_iterable(new stdClass())); // bool(false)
    
    0 讨论(0)
  • 2020-11-30 04:21

    I think you mean instanceof Iterator, PHP doesn't have an Iterable interface. It does have a Traversable interface though. Iterator and IteratorAggregate both extend Traversable (and AFAIK they are the only ones to do so).

    But no, objects implementing Traversable won't pass the is_array() check, nor there is a built-in is_iterable() function. A check you could use is

    function is_iterable($var) {
        return (is_array($var) || $var instanceof Traversable);
    }
    

    To be clear, all php objects can be iterated with foreach, but only some of them implement Traversable. The presented is_iterable function will therefore not detect all things that foreach can handle.

    0 讨论(0)
提交回复
热议问题