is_iterable()
Функция is_iterable() в PHP 7.1+ проверяет, является ли переменная итерируемой — массивом или объектом с интерфейсом Traversable.
Введение
is_iterable() — встроенная функция PHP (доступна начиная с PHP 7.1), которая определяет, можно ли перебрать значение с помощью цикла foreach. Она возвращает true ровно для двух видов значений:
- Массивы — любой массив является итерируемым.
- Объекты, реализующие интерфейс
Traversable— на практике это объекты, реализующиеIteratorилиIteratorAggregate, а также генераторы (функции, использующиеyield).
Всё остальное — строки, целые числа, логические значения, null и обычные объекты (например, stdClass) — не является итерируемым, даже если интуитивно кажется «списком». На этой странице рассматриваются синтаксис, правила определения итерируемости, распространённые подводные камни и случаи, когда is_iterable() действительно полезна.
Синтаксис
is_iterable(mixed $value): boolФункция принимает один аргумент, $value, и возвращает логическое значение:
| Аргумент | Результат |
|---|---|
| Массив | true |
Объект Traversable (Iterator, IteratorAggregate, генератор) | true |
Всё остальное (строка, int, обычный объект, null, …) | false |
Начиная с PHP 8.0 существует соответствующий псевдотип iterable, который можно использовать в объявлениях типов — см. раздел Когда использовать ниже.
Базовый пример
$var1 — массив, поэтому он итерируемый. $var2 — строка: хотя к символам строки можно обращаться по индексу, управлять ею через foreach нельзя, поэтому is_iterable() возвращает false.
Что считается итерируемым
Интересные случаи связаны с объектами. Объект является итерируемым только если он реализует Traversable (напрямую или через Iterator/IteratorAggregate), либо является генератором. Обычный объект итерируемым не является.
<?php
function genFn() {
yield 1;
yield 2;
}
class MyCollection implements IteratorAggregate {
private array $items = [1, 2, 3];
public function getIterator(): Iterator {
return new ArrayIterator($this->items);
}
}
var_dump(is_iterable([1, 2, 3])); // bool(true) array
var_dump(is_iterable(genFn())); // bool(true) generator
var_dump(is_iterable(new MyCollection())); // bool(true) Traversable
var_dump(is_iterable("hello")); // bool(false) string
var_dump(is_iterable(42)); // bool(false) int
var_dump(is_iterable(new stdClass())); // bool(false) plain object
var_dump(is_iterable(null)); // bool(false) null
?>Главный вывод: stdClass (и любой объект без Traversable) возвращает false, даже несмотря на то что foreach может перебирать его публичные свойства. is_iterable() намеренно сообщает только о значениях, которые являются итерируемыми по контракту, а не случайно.
Распространённые подводные камни
- Строки не являются итерируемыми. Строка — скалярное значение, а не коллекция, поэтому
is_iterable("abc")возвращаетfalse. Чтобы проверить строку, используйтеis_string(). - Обычные объекты не проходят проверку.
is_iterable(new stdClass())возвращаетfalse. Если вам нужно лишь убедиться, что значение является каким-либо объектом, используйтеis_object(); если же требуется именно перебираемый объект — вызывайтеis_iterable(). - Это не то же самое, что
is_array().is_array()возвращаетtrueтолько для массивов и отклоняет генераторы и объектыTraversable. Используйтеis_iterable(), когда нужно принимать и массивы, и объекты-итераторы. nullвозвращаетfalse. Передача неинициализированного илиnull-значения безопасна — функция просто вернётfalse, не вызвав ошибки.
Когда использовать
Используйте is_iterable() как защитное условие перед foreach, чтобы функция могла принимать как массив, так и ленивый итератор, не аварийно завершаясь на некорректных входных данных:
<?php
function sumAll(mixed $data): int {
if (!is_iterable($data)) {
throw new InvalidArgumentException('Expected an iterable.');
}
$total = 0;
foreach ($data as $value) {
$total += $value;
}
return $total;
}
echo sumAll([1, 2, 3, 4]), "\n"; // 10
function counter() {
yield 5;
yield 10;
}
echo sumAll(counter()), "\n"; // 15
?>Одна и та же функция работает и с обычным массивом, и с генератором, поскольку оба удовлетворяют условию is_iterable().
Нередко более чистым решением является объявление типа iterable (PHP 7.1+), которое позволяет PHP самостоятельно проверять ограничение и избавляет от ручной проверки:
<?php
function sumAll(iterable $data): int {
$total = 0;
foreach ($data as $value) {
$total += $value;
}
return $total;
}
?>Используйте функцию is_iterable(), когда значение имеет тип mixed и вы хотите ветвиться во время выполнения; используйте подсказку типа iterable, когда итерируемость является обязательным требованием к параметру.
Заключение
is_iterable() отвечает на один конкретный вопрос: можно ли перебрать это значение циклом foreach? Она возвращает true для массивов и объектов Traversable (включая генераторы) и false для всего остального. Используйте её как защитное условие для входных данных типа mixed, предпочитайте подсказку типа iterable, когда итерируемость обязательна, и помните, что функция строже, чем кажется — строки и обычные объекты не являются итерируемыми. Для связанных проверок смотрите is_array(), is_object() и gettype().