2013-06-10 6 views
12

У меня есть относительно простую функцию, которая использует foreachPHP Traversable типа намек

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

Я хотел бы ввести подсказку, и Traversable, кажется, точный тип намек мне нужно

function foo(Traversable $t) { 

Однако это дает E_RECOVERABLE_ERROR при использовании массива (который, конечно же, может использоваться в foreach): example

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

Есть ли способ набрать подсказку или это невозможно?

+0

Из руководства (ссылка, которую вы опубликовали): 'Абстрактный базовый интерфейс, который не может быть реализован в одиночку. Вместо этого он должен быть реализован либо IteratorAggregate, либо Iterator.' – alfasin

+0

Я считаю, что вы можете использовать 'array' в качестве подсказки, я буду проверять свою теорию, да, вы можете – Dale

+0

@alfasin И как таковой, любой класс, реализующий« Итератор », также будет соответствовать 'Traversable', поскольку' Iterator' является подтипом 'Traversable'. Основная ООП-механика – dtech

ответ

9

PHP 7.1 представляет iterable type declaration для этой цели, которая принимает как массивы и экземпляры \Traversable.

В предыдущих версиях вы должны опустить объявление типа.

8

Существует ошибка об этом: #41942. Закрыто как «не ошибка». Поскольку массивы PHP не являются объектами, они не могут реализовать интерфейс и, таким образом, нет способа набрать подсказку как array, так и Traversable.

Вы можете использовать iterator_to_array, ArrayIterator или опустить тип подсказки. Обратите внимание, что 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 (новый ArrayIterator (array (1,2,3))' для преобразования массивов в объекты. – mAsT3RpEE

2

Проблема в том, что массивы не являются объектами, поэтому они не могут реализовать интерфейс. Поэтому вы не можете набирать подсказку как, array, так и Traversable.

+1

Существуют ли другие родственные объекты PHP, которые можно использовать в 'foreach', но не реализуют 'Traversable'? – dtech

+0

Нет, но вы можете написать свой собственный класс, который реализует' IteratorAggregate' или 'Iterator', и они расширяют' Traversable' – bpoiss

+1

. Но вот он: все объекты, которые можно использовать в 'foreach'. Если объект реализует' Traversable 'Итератор (или агрегированный итератор) будет пройден, иначе общедоступные свойства объекта http://php.net/manual/en/control-structures.foreach.php – pozs

3

Такая же проблема. Я отказался, я просто вручную закодировал все в функции.

Это должно дать вам функциональность вы хотите:

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

EDIT

Если вы хотите, чтобы отобразить тип $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) 
     )); 
    } 
}