PHP Traversable 类型提示

20

我有一个相对简单的函数,其中使用了foreach

function foo($t) {
     $result;
     foreach($t as $val) {
         $result = dosomething($result, $val);
     }
     return $result;
}

我想使用类型提示,Traversable 似乎是我需要的精确类型提示。
 function foo(Traversable $t) {

然而,当使用数组(当然可以在foreach中使用)时,会出现E_RECOVERABLE_ERROR示例

 Argument 1 passed to foo() must implement interface Traversable, array given

有没有一种方法可以进行类型提示,或者这不可能吗?

@alfasin 因此,实现 Iterator 接口的任何类也将匹配 Traversable,因为 IteratorTraversable 的子类型。这是基本的面向对象编程机制。 - dtech
1
@Dale,提前道歉,我可能漏掉了什么:数组不实现迭代器(据我所知),而你正在尝试使用数组,是吗? - Nir Alfasi
@alfasin,我只是在提醒你可以使用array作为类型提示,例如function foo(array $array){} - Dale
@ Dale,但是数组没有实现IteratorTraversable - Nir Alfasi
@alfasin 我并没有说它会这样做。我建议他使用数组作为类型提示,再见。 - Dale
显示剩余5条评论
4个回答

25

PHP 7.1引入了iterable类型声明,可以接受数组和\Traversable实例。

在之前的版本中,你必须省略类型声明。


8
这个问题有一个错误报告:#41942。被标记为“不是错误”。由于PHP数组不是对象,它们无法实现接口,因此没有办法同时对arrayTraversable进行类型提示。
您可以使用iterator_to_arrayArrayIterator或省略类型提示。请注意,iterator_to_array将整个迭代器复制到数组中,可能效率较低。
// These functions are functionally equivalent but do not all accept the same arguments
function foo(array $a) { foobar($a); }
function bar(Traversable $a) { foobar($a); }
function foobar($a) {
    foreach($a as $key => $value) {
    }
}

$array = array(1,2,3)
$traversable = new MyTraversableObject();

foo($array);
foo(iterator_to_array($traversable));

bar(new ArrayIterator($array));
bar($traversable);

foobar($array);
foobar($traversable);

2
你也可以使用 foo(new ArrayIterator(array(1,2,3)) 将数组转换为对象。 - mAsT3RpEE

4

有同样的问题。我已经放弃了,我只是手动编写函数中的所有内容。

这应该会给您想要的功能:

function MyFunction($traversable)
{
    if(!$traversable instanceof Traversable && !is_array($traversable))
    {
        throw new InvalidArgumentException(sprintf(
            'Myfunction($traversable = %s): Invalid argument $traversable.'
            ,var_export($traversable, true)
       ));
    }
}

编辑

如果您只想显示$traversable的类型,并且希望该功能可以在子类中继承。

public function MyMethod($traversable)
{
    if(!$traversable instanceof Traversable && !is_array($traversable))
    {
        throw new InvalidArgumentException(sprintf(
            '%s::MyMethod($traversable): Invalid argument $traversable of type `%s`.'
            ,get_class($this)
            ,gettype($traversable)
       ));
    }
}

2
问题在于,数组不是对象,因此它们无法实现接口。因此,你不能同时对`array`和`Traversable`进行类型提示。

1
是否有其他 PHP 原生实体可用于 foreach 但不实现 Traversable - dtech
不可以,但是你可以编写自己的类来实现IteratorAggregateIterator接口,并且它们都继承了Traversable接口。 - bpoiss
1
但是事实上,所有的对象都可以在foreach中迭代。如果对象实现了Traversable接口,则会遍历迭代器(或聚合迭代器),否则将遍历对象的公共属性。http://php.net/manual/en/control-structures.foreach.php - pozs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接