Указан неверный аргумент для foreach ()

304

Мне часто приходится обрабатывать данные, которые могут быть массивом или нулевой переменной, и снабжать их foreachэтими данными.

$values = get_values();

foreach ($values as $value){
  ...
}

Когда вы передаете foreach данными, которые не являются массивом, вы получаете предупреждение:

Предупреждение: неверный аргумент указан для foreach () в [...]

Предполагая, что невозможно реорганизовать get_values()функцию так, чтобы она всегда возвращала массив (обратная совместимость, недоступный исходный код по любой другой причине), мне интересно, какой самый чистый и эффективный способ избежать этих предупреждений:

  • Кастинг $valuesв массив
  • Инициализация $valuesв массив
  • Заворачивать foreachсif
  • Другое (пожалуйста, предложите)
Роберто Алои
источник
Вполне возможно, что $valuesэто не массив.
Бхаргав Нанекалва

Ответы:

509

Лично я считаю, что это самый чистый - не уверен, что это самый эффективный, ум!

if (is_array($values) || is_object($values))
{
    foreach ($values as $value)
    {
        ...
    }
}

Причиной моего предпочтения является то, что он не выделяет пустой массив, когда у вас все равно ничего не начинается.

Энди Шеллам
источник
4
Или используйте count (), чтобы выяснить, не является ли массив пустым
Kemo
76
@Kemo: count()не надежно. Если вы передаете count()нуль, он возвращает 0. Если вы передаете ему ненулевой аргумент, не являющийся массивом, он возвращает 1. Поэтому невозможно count()определить, является ли переменная массивом, когда переменная может быть пустым массивом, или массив, содержащий 1 элемент.
Энди Шеллам
12
Обратите внимание, что некоторые объекты являются повторяемыми, и этот ответ не учитывает их.
Брэд Кох
32
Должно быть if (is_array($values) || $values instanceof Traversable).
Боб Стейн
3
Обидно, что он не сказал, что это не было самым эффективным, хотя: D
Ги Пра
116

Как насчет этого? много чище и все в одну строчку.

foreach ((array) $items as $item) {
 // ...
 }
Аджит Р Наир
источник
7
Это единственное, что сработало для меня. По какой-то причине PHP не верил, что многомерный массив, который я построил, на самом деле был массивом массивов.
Джастин
1
То же самое здесь, это очень хорошее исправление для массива, содержащего либо массивы, либо нулевые значения. Просто добавьте тест в цикл foreach, чтобы продолжить, если данные нулевые.
Lizardx
Решил мою проблему. Спасибо!
Hitesh
1
Блестящий код, пропустил if, иначе для массива и значения без массива, используя $ _POST с флажком!
Янн Шабо
2
ПРИМЕЧАНИЕ. Несмотря на красивый внешний вид и устранение недопустимого предупреждения foreach, этот метод будет возвращать предупреждение о неопределенной переменной, если переменная не установлена ​​каким-либо образом. Используйте isset()или is_array()или оба, полностью в зависимости от вашего сценария и т. Д.
Джеймс
42

Я обычно использую конструкцию, подобную этой:

/**
 * Determine if a variable is iterable. i.e. can be used to loop over.
 *
 * @return bool
 */
function is_iterable($var)
{
    return $var !== null 
        && (is_array($var) 
            || $var instanceof Traversable 
            || $var instanceof Iterator 
            || $var instanceof IteratorAggregate
            );
}

$values = get_values();

if (is_iterable($values))
{
    foreach ($values as $value)
    {
        // do stuff...
    }
}

Обратите внимание, что эта конкретная версия не тестировалась, она набирается непосредственно в SO из памяти.

Редактировать: добавлен обходной чек

Kris
источник
3
Лучший ответ. За исключением того, что я думаю, что вы действительно должны проверить, если $var instanceof Traversable. Смотрите здесь . Потому что, например, вы можете использовать элемент SimpleXMLElement , но он не является экземпляром Iterator или IteratorAggregate.
Боб Стейн
2
Вы можете удалить два других класса, @Kris. Они оба расширяют Traversable сейчас и, похоже, родились в 5.0.0. Хотя я чувствую крошечное сомнение относительно того, распространяется ли всегда экземпляр instanceof.
Боб Стейн
1
@ BobStein-VisiBone: да (за исключением того, что они являются интерфейсами, а не классами). Я добавил Traversable перед этим, ни Iterator, ни IteratorAggregate никогда не будут нуждаться в проверке (таким образом, они не будут замедлять выполнение). Я оставил их, чтобы ответ был как можно ближе к первоначальному ответу, который я дал, и чтобы он был очевидным / читабельным.
Крис
2
Я думаю, было бы справедливо добавить is_object($var)ре. php.net/manual/en/language.oop5.iterations.php
Марк Фокс
1
@MarkFox: не стесняйтесь, однако я намеренно пропустил это; Я никогда не видел такого использования, которое не было бы лучше реализовано реализацией Iteratorили IteratorAggregate, но это, конечно, только мое мнение и поэтому субъективно (я никогда не использую открытые поля).
Крис
15

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

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

Например , литье булева в массив (array)bool, будет НЕ привести к пустому массиву, а массив с одним элементом , содержащего логическое значение , как INT: [0=>0]или [0=>1].

Я написал быстрый тест, чтобы представить эту проблему . (Вот резервный тест на случай, если первый тестовый URL не пройден.)

Включены тесты для: null, false, true, A class, arrayи undefined.


Всегда проверяйте ввод перед использованием в foreach. Предложения:

  1. Быстрая проверка типов :$array = is_array($var) or is_object($var) ? $var : [] ;
  2. Напечатайте массивы хинтинга в методах перед использованием foreach и указанием возвращаемых типов
  3. Обтекание foreach внутри, если
  4. Использование try{}catch(){}блоков
  5. Разработка правильного кода / тестирование перед выпуском продукции
  6. Чтобы проверить массив на предмет правильной формы, вы можете использовать array_key_existsопределенный ключ или проверить глубину массива (если он один!) .
  7. Всегда извлекайте ваши вспомогательные методы в глобальное пространство имен, чтобы уменьшить количество дублирующегося кода.
AARTT
источник
8

Попробуй это:

//Force array
$dataArr = is_array($dataArr) ? $dataArr : array($dataArr);
foreach ($dataArr as $val) {
  echo $val;
}

;)

ГиголНет Гиголачвили
источник
1
Это не будет хорошо работать с ассоциативными массивами .. Метод is_array в целом лучше ... и проще ...
AO_
4
$values = get_values();

foreach ((array) $values as $value){
  ...
}

Проблема всегда нулевая, и на самом деле Casting является чистящим решением.

boctulus
источник
3

Прежде всего, каждая переменная должна быть инициализирована. Всегда.
Кастинг не вариант.
if get_values ​​(); может возвращать переменную другого типа, это значение, конечно, нужно проверить.

Ваш здравый смысл
источник
Приведение является опцией - если вы инициализируете массив с помощью, $array = (array)null;вы получите пустой массив. Конечно, это пустая трата памяти ;-)
Энди Шеллам,
2
+1: читай с сентиментальной точки зрения, мне все равно, может ли язык обойтись, переменные ДОЛЖНЫ быть объявлены и НЕ ДОЛЖНЫ проверяться результаты. Требуется, чтобы разработчики были в здравом уме, а журналы ошибок были короткими.
Крис
3

Более краткое расширение кода @ Kris

function secure_iterable($var)
{
    return is_iterable($var) ? $var : array();
}

foreach (secure_iterable($values) as $value)
{
     //do stuff...
}

особенно для использования внутри кода шаблона

<?php foreach (secure_iterable($values) as $value): ?>
    ...
<?php endforeach; ?>
HongKilDong
источник
2
Ты имеешь в виду return is_iterable($var) ? $var : array($var);?
SQB
3

Если вы используете php7 и хотите обрабатывать только неопределенные ошибки, это самое чистое ИМХО

$array = [1,2,3,4];
foreach ( $array ?? [] as $item ) {
  echo $item;
}
Эдвин Родригес
источник
2
foreach ($arr ? $arr : [] as $elem) {
    // Does something 
}

Это не проверяет, является ли это массивом, но пропускает цикл, если переменная равна нулю или пустой массив.

T30
источник
1

Я не уверен, так ли это, но эта проблема, по-видимому, возникала несколько раз при миграции WordPress сайтов или вообще динамических сайтов. В этом случае убедитесь, что хостинг, на который вы переходите, использует ту же версию PHP, что и ваш старый сайт.

Если вы не мигрируете свой сайт, и это просто проблема, попробуйте обновить до PHP 5. Это решит некоторые из этих проблем. Может показаться глупым решением, но сделал свое дело для меня.

Erik
источник
1

Исключительный случай для этого уведомления происходит, если вы устанавливаете массив в нуль внутри цикла foreach

if (is_array($values))
{
    foreach ($values as $value)
    {
        $values = null;//WARNING!!!
    }
}
Фарид Мовсумов
источник
1

Как насчет этого решения:

$type = gettype($your_iteratable);
$types = array(
    'array',
    'object'
);

if (in_array($type, $types)) {
    // foreach code comes here
}
юлианский
источник
1

Предупреждение неверный аргумент предоставлен для foreach()отображения твитов. перейти к /wp-content/plugins/display-tweets-php. Затем вставьте этот код в строку номер 591, он будет работать отлично.

if (is_array($tweets)) {
    foreach ($tweets as $tweet) 
    {
        ...
    }
}
Саад Ханани
источник
Потрясающие! Это должно быть принятым решением. в моем случае я добавил это:if (is_array($_POST['auto'])){ // code }
Jodyshop
0

Кажется также, что есть отношение к окружающей среде:

У меня была ошибка «неверный аргумент предоставлен foreach ()» только в среде разработчика, но не в prod (я работаю на сервере, а не на localhost).

Несмотря на ошибку, var_dump показал, что с массивом все в порядке (в обоих случаях app и dev).

Во if (is_array($array))всем foreach ($array as $subarray)решена проблема.

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

araldh
источник
0

Используйте функцию is_array, когда вы передадите массив в цикл foreach.

if (is_array($your_variable)) {
  foreach ($your_variable as $item) {
   //your code
}
}
Супер модель
источник
0

Как насчет определения пустого массива как запасного, если get_value()он пуст?
Я не могу придумать кратчайшего пути.

$values = get_values() ?: [];

foreach ($values as $value){
  ...
}
Квентин Верон
источник
0

Я буду использовать комбинацию empty, issetи is_arrayкак

$array = ['dog', 'cat', 'lion'];

if (!empty($array) && isset($array) && is_array($array) {
    //loop
    foreach ($array as $values) {
        echo $values; 
    }
}
Rotimi
источник
-3

я бы сделал то же самое, что и Энди, но я бы использовал «пустую» функцию.

вот так:

if(empty($yourArray))
{echo"<p>There's nothing in the array.....</p>";}
else
{
foreach ($yourArray as $current_array_item)
  {
    //do something with the current array item here
  } 
}
as_bold_as_love
источник
3
-1, если $yourArray = 1;попытаться повторить, и вы получите ошибку. empty()не подходит тест
Брэд Кох
@BradKoch абсолютно прав. is_array () - единственный надежный способ проверить, является ли $ yourArray массивом. Посмотрите другие ответы, чтобы узнать, почему is_array () недостаточно - foreach также может обрабатывать итераторы.
cgeisel