Неправильний аргумент, наданий для foreach ()


304

Часто мені трапляється обробляти дані, які можуть бути або масивом, або нульовою змінною, а також деякі foreachз цими даними.

$values = get_values();

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

Коли ви подаєте foreach даними, які не є масивом, ви отримуєте попередження:

Попередження: Неправильний аргумент, наданий для foreach () у [...]

Якщо припустити, що get_values()функція рефактора неможливо завжди повертати масив (зворотна сумісність, недоступний вихідний код, будь-яка інша причина), мені цікаво, який найчистіший та найефективніший спосіб уникнути цих попереджень:

  • Кастинг $valuesдля масиву
  • Ініціалізація $valuesдо масиву
  • Загортання foreachзif
  • Інше (будь ласка, запропонуйте)

Цілком можливо, що $valuesце не масив.
Bhargav Nanekalva

Відповіді:


509

Особисто я вважаю це найчистішим - не впевнений, чи це найефективніше, розум!

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

Причиною моїх переваг є те, що він не виділяє порожній масив, коли у вас все одно нічого не починається.


4
Або скористайтеся count (), щоб з’ясувати, чи масив не порожній
Kemo

76
@Kemo: count()не є надійним. Якщо ви передаєте count()null, він повертає 0. Якщо ви передаєте йому ненульовий аргумент без масиву, він повертається 1. Тому неможливо використовувати, count()щоб визначити, чи змінна є масивом, коли змінна може бути порожнім масивом, або масив, що містить 1 елемент.
Енді Шелам

12
Зауважте, що деякі об'єкти є ітерабельними, і ця відповідь не враховує їх.
Бред Кох

32
Повинно бути if (is_array($values) || $values instanceof Traversable).
Боб Штейн

3
Прикро, що він не продовжував говорити, що це точно не найефективніше: D
Gui Prá

116

Як щодо цього? багато чистішого і все в одну лінію.

foreach ((array) $items as $item) {
 // ...
 }

7
Це єдине, що працювало на мене. Чомусь PHP не вірив, що багатовимірний масив, який я створив, був насправді масивом масивів.
Джастін

1
Тут же, це дуже приємне виправлення масиву, що містить або масиви, або нульові значення. Просто додайте тест у циклі foreach, щоб продовжити, якщо дані є недійсними.
Лізардкс

Вирішив мою проблему. Дякую!
Хітеш

1
Блискучий код, пропустив значення if, else для масиву та значення без масиву, використовуючи прапорець $ _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 із пам'яті.

Редагувати: додано прохідний чек


3
Найкраща відповідь. За винятком, я думаю, ви дійсно повинні перевірити, чи немає $var instanceof Traversable. Дивіться тут . Тому що, наприклад, ви можете пророкувати SimpleXMLElement , але це не примірник ні Iterator, ні IteratorAggregate.
Боб Штейн

2
Можливо, ви зможете видалити інші два класи, @Kris. Вони обидва продовжують проїжджати зараз і, здається, народилися саме так у 5.0.0. Хоча я відчуваю крихітні сумніви щодо того, чи завжди екземпляр застосовується до розширень.
Боб Штейн

1
@ BobStein-VisiBone: так (за винятком інтерфейсів, а не класів) Однак; Я ставлю Traverable перед цим, ні 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. Загортання передпліччя в межах, якщо
  4. Використання try{}catch(){}блоків
  5. Розробка правильного коду / тестування перед випуском продукції
  6. Для тестування масиву на належну форму ви можете використовувати array_key_existsпевний ключ або протестувати глибину масиву (коли він один!) .
  7. Завжди витягуйте свої допоміжні методи в глобальну область імен, щоб зменшити повторюваний код

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){
  ...
}

Проблема завжди є нульовою, а лиття - насправді очищенням.


3

Перш за все, кожна змінна повинна бути ініціалізована. Завжди.
Кастинг - це не варіант.
якщо 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; ?>

2
Ви не маєте на увазі return is_iterable($var) ? $var : array($var);?
SQB

3

Якщо ви використовуєте php7 і хочете обробляти лише невизначені помилки, це найчистіший IMHO

$array = [1,2,3,4];
foreach ( $array ?? [] as $item ) {
  echo $item;
}

2
foreach ($arr ? $arr : [] as $elem) {
    // Does something 
}

Це не перевіряє, чи це масив, але пропускає цикл, якщо змінна - нуль або порожній масив.


1

Я не впевнений, що це так, але, здається, ця проблема виникає неодноразово при переміщенні сайтів Wordpress або міграції динамічних сайтів загалом. У такому випадку переконайтеся, що хостинг, на який ви переходите, використовує ту саму версію PHP, яку використовує ваш старий сайт.

Якщо ви не мігруєте свій сайт, і це лише проблема, яка виникла, спробуйте оновити його до PHP 5. Це допоможе вирішити деякі з цих проблем. Це може здатися нерозумним рішенням, але трюк для мене.


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)вирішити цю проблему.

Вибачте, що не можу пояснити причину, але, як знадобилося певний час, щоб вирішити рішення, я подумав про краще поділитися цим спостереженням.


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; 
    }
}

-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
  } 
}

3
-1, Якщо $yourArray = 1;він спробує повторити, і ви отримаєте помилку. empty()не є підходящим тестом.
Бред Кох

@BradKoch абсолютно прав. is_array () - єдиний надійний спосіб перевірити, чи $ yourArray є масивом. Дивіться інші відповіді для детальної інформації про те, чому is_array () недостатньо - foreach також може обробляти ітератори.
cgeisel
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.