可迭代对象和数组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
接口。 Iterator
和IteratorAggregate
都可以扩展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; }