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 Iterator
or 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 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).
我想你的意思是 instanceof Iterator
,PHP 没有Iterable
接口。不过它确实有一个Traversable
界面。Iterator
并且IteratorAggregate
都扩展了Traversable
(而且 AFAIK 他们是唯一这样做的)。
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
但是不,实现的对象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_iterable
function will therefore not detect all things that foreach can handle.
需要明确的是,所有php 对象都可以使用 foreach 进行迭代,但只有其中一些实现了Traversable
. 因此,所提供的is_iterable
函数不会检测 foreach 可以处理的所有事情。
回答by Blackhole
PHP 7.1.0 has introducedthe iterable
pseudo-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
iterable
pseudo-type. This type is analogous tocallable
, accepting multiple types instead of one single type.
iterable
accepts anyarray
or object implementingTraversable
. Both of these types are iterable usingforeach
and can be used withyield
from 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:true
if a value is iterable and will be accepted by theiterable
pseudo-type,false
for 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 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.
不幸的是,您将无法为此使用类型提示,并且必须这样做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;
}