Итерационные объекты и тип массива намекают?
у меня есть много функций, которые либо имеют тип намека на массивы, либо используют is_array() для проверки массивности переменной.
теперь я начинаю использовать объекты, которые являются итерационными. Они реализуют Iterator или IteratorAggregate. Будут ли они приниматься как массивы, если они проходят через намек типа или проходят is_array()?
если мне нужно изменить мой код, есть общий вид is_iterable(), или я должен сделать что-то вроде:
if ( is_array($var) OR $var instance_of Iterable OR $var instanceof IteratorAggregate ) { ... }
какие еще итерационные интерфейсы там есть?
6 ответов:
я думаю, вы имеете в виду instanceof
IteratorPHP, а неIterableинтерфейс. У него естьTraversableинтерфейс, хотя.IteratorиIteratorAggregateкак продлитьTraversable(и AFAIK они единственные, кто это делает).но нет, объекты реализации
Traversableне пройдетis_array()проверьте, нет ли встроенного
PHP 7.1.0 ввел the
iterableпсевдо-тип иis_iterable()функции, который специально разработан для такой цели:это [ ... ] предлагает новый
iterableпсевдо-тип. Этот тип аналогиченcallable, принимая несколько типов вместо одного типа.
iterableпринимаетarrayили объект, реализующийTraversable. Оба этих типа являются итерационными с помощьюforeachи может использоваться сyieldвнутри генератора.function foo(iterable $iterable) { foreach ($iterable as $value) { // ... } }это [ ... ] также добавляет функцию
is_iterable()Это возвращает логическое значение:trueесли значение iterable и будет принято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)
Мне действительно пришлось добавить проверку для stdClass, так как экземпляры stdClass работают в циклах foreach, но stdClass не реализует проходимость:
function is_iterable($var) { return (is_array($var) || $var instanceof Traversable || $var instanceof stdClass); }
Я использую простой (и, возможно, немного хакерский) способ проверить "итерабельность".
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
к сожалению, вы не сможете использовать подсказки типа для этого и должны будете сделать
is_array($var) or $var instanceof ArrayAccessматериал. Это известная проблема, но afaik она до сих пор не решена. По крайней мере, он не работает с PHP 5.3.2, который я только что протестировал.
вы можете использовать подсказку типа, если вы переключаетесь на использование итерационных объектов.
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; }
Comments