可变对象和数组类型提示?

时间:2010-08-27 13:40:03

标签: php iterator type-hinting

我有很多函数要么对数组进行类型提示,要么使用is_array()来检查变量的数组。

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

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

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

还有哪些可迭代的接口?

6 个答案:

答案 0 :(得分:75)

我认为你的意思是Iterator的实例,PHP没有Iterable接口。它确实有一个Traversable接口。 IteratorIteratorAggregate都延伸Traversable(而AFAIK他们是唯一这样做的人)。

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

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

要明确的是,所有 php对象都可以使用foreach进行迭代,但只有某些实现Traversable。因此,所呈现的is_iterable函数不会检测到foreach可以处理的所有内容。

答案 1 :(得分:26)

PHP 7.1.0 has introduced iterable pseudo-type and the is_iterable() function,专为此目的而设计:

  

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

     

iterable接受任何array或实施Traversable的对象。这两种类型都可以使用foreach进行迭代,并且可以与生成器中的yield一起使用。

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

这个[...]还添加了一个函数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)

答案 2 :(得分:12)

我实际上必须添加对stdClass的检查,因为stdClass的实例在foreach循环中起作用,但是stdClass没有实现Traversable:

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

答案 3 :(得分:4)

我使用一种简单的(也许是一种小的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;
}

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

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

答案 4 :(得分:1)

很遗憾,您无法为此使用类型提示,并且必须执行is_array($var) or $var instanceof ArrayAccess 东西。这是一个众所周知的问题,但尚未解决。至少它不适用于我刚刚测试的PHP 5.3.2。

答案 5 :(得分:0)

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

protected function doSomethingWithIterableObject(Iterator $iterableObject) {}

protected function doSomethingWithIterableObject(Traversable $iterableObject) {}

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

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