可迭代对象和数组types提示?

我有很多函数要么为数组提供types提示,要么使用is_array()来检查variables的数组。

现在我开始使用可迭代的对象。 他们实现了Iterator或者IteratorAggregate 。 如果它们通过types提示,或者经过is_array() ,它们会被接受为数组吗?

如果我不得不修改我的代码,是否有一个通用的is_iterable() ,或者我必须做类似于:

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

还有什么其他的可迭代接口?

我认为你的意思是instanceof Iterator ,PHP没有Iterable接口。 它确实有一个Traversable接口。 IteratorIteratorAggregate都可以扩展Traversable (而且AFAIK是唯一这样做的)。

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

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

清楚的是, 所有的 php对象都可以用foreach迭代,但是其中只有一些实现了Traversable 。 所介绍的is_iterable函数因此不会检测到foreach可以处理的所有事情。

PHP 7.1.0 引入了iterable伪types和is_iterable()函数 ,它是专门为此目的而devise的:

这提出了一种新的iterable伪types。 这种types与callable类似,接受多种types而不是单一types。

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

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

这也增加了一个函数is_iterable() ,返回一个布尔值:如果一个值是可迭代的,将被iterable伪types接受,为其他值为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) 

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

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

我使用一个简单的(也许是一个小黑客)的方式来testing“迭代”。

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

当您尝试循环一个非可迭代variables时,PHP会引发警告。 通过在尝试迭代之前设置自定义error handling程序,可以将错误转换为exception,从而使您可以使用try / catch块。 之后,您还原以前的error handling程序不会中断程序stream。

这是一个小的testing用例(在PHP 5.3.15中testing):

 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 

不幸的是,你将不能使用types提示,将不得不做的is_array($var) or $var instanceof ArrayAccess东西。 这是一个已知的问题,但afaik仍然没有解决。 至less它不适用于我刚刚testing的PHP 5.3.2。

如果切换到使用可迭代对象,则可以使用types提示。

 protected function doSomethingWithIterableObject(Iterator $iterableObject) {} 

要么

 protected function doSomethingWithIterableObject(Traversable $iterableObject) {} 

但是,这不能用来同时接受可迭代对象和数组。 如果你真的想这样做,可以尝试构build一个包装函数,如下所示:

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