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
Iterable objects and array type 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()?
现在我开始使用可迭代的对象。他们实施Iterator或IteratorAggregate。如果它们通过类型提示或经历,它们会被接受为数组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 tocallable, accepting multiple types instead of one single type.
iterableaccepts anyarrayor object implementingTraversable. Both of these types are iterable usingforeachand can be used withyieldfrom 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 theiterablepseudo-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;
}

