php 可迭代对象和数组类型提示?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3584700/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 10:18:41  来源:igfitidea点击:

Iterable objects and array type hinting?

phpiteratortype-hinting

提问by user151841

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.

我有很多函数,它们要么具有数组的类型提示,要么用于is_array()检查变量的数组性。

Now I'm starting to use objects that are iterable. They implement Iteratoror IteratorAggregate. Will these be accepted as arrays if they pass through type hinting, or undergo is_array()?

现在我开始使用可迭代的对象。他们实施IteratorIteratorAggregate。如果它们通过类型提示或经历,它们会被接受为数组is_array()吗?

If I have to modify my code, is there a generic sort of is_iterable(), or must I do something like:

如果我必须修改我的代码,是否有一种通用的is_iterable(),或者我必须执行以下操作:

if ( is_array($var) OR $var instance_of Iterable OR $var instanceof IteratorAggregate ) { ... }

What other iterable interfaces are out there?

还有哪些其他可迭代的接口?

回答by NullUserException

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

我想你的意思是 instanceof Iterator,PHP 没有Iterable接口。不过它确实有一个Traversable界面。Iterator并且IteratorAggregate都扩展了Traversable(而且 AFAIK 他们是唯一这样做的)。

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

但是不,实现的对象Traversable不会通过is_array()检查,也没有内置is_iterable()函数。您可以使用的支票是

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

To be clear, allphp objects can be iterated with foreach, but only someof them implement Traversable. The presented is_iterablefunction will therefore not detect all things that foreach can handle.

需要明确的是,所有php 对象都可以使用 foreach 进行迭代,但只有其中一些实现了Traversable. 因此,所提供的is_iterable函数不会检测 foreach 可以处理的所有事情。

回答by Blackhole

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

PHP 7.1.0引入iterable伪类型和is_iterable()函数,专门为此目的而设计:

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

iterableaccepts any arrayor object implementing Traversable. Both of these types are iterable using foreachand can be used with yieldfrom within a generator.

这 [...] 提出了一种新的iterable伪类型。这种类型类似于callable,接受多种类型而不是单一类型。

iterable接受任何array或对象实现Traversable。这两种类型都可以迭代使用,foreach并且可以yield在生成器中使用。

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

This […] also adds a function is_iterable()that returns a boolean: trueif a value is iterable and will be accepted by the iterablepseudo-type, falsefor other values.

这个 […] 还添加了一个is_iterable()返回布尔值的函数:true如果一个值是可迭代的并且将被iterable伪类型接受,false对于其他值。

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)

回答by Isaac

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

我实际上不得不为 stdClass 添加一个检查,因为 stdClass 的实例确实在 foreach 循环中工作,但 stdClass 没有实现 Traversable:

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

回答by Tivie

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

我使用一种简单的(也许有点hackish)方法来测试“可迭代性”。

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.

当您尝试循环不可迭代变量时,PHP 会抛出警告。通过在尝试迭代之前设置自定义错误处理程序,您可以将错误转换为异常,从而使您能够使用 try/catch 块。之后,您恢复先前的错误处理程序以不中断程序流程。

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

这是一个小测试用例(在 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

回答by Raoul Duke

Unfortunately you won't be able to use type hints for this and will have to do the is_array($var) or $var instanceof ArrayAccessstuff. 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.

不幸的是,您将无法为此使用类型提示,并且必须这样做is_array($var) or $var instanceof ArrayAccess。这是一个已知问题,但它仍然没有解决。至少它不适用于我刚刚测试的 PHP 5.3.2。

回答by Jon Gilbert

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