Podano niepoprawny argument dla foreach ()


304

Często zdarza mi się obsługiwać dane, które mogą być tablicą lub zmienną zerową, i karmić niektóre foreachz tych danych.

$values = get_values();

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

Gdy karmisz foreach danymi, które nie są tablicą, pojawia się ostrzeżenie:

Ostrzeżenie: podano nieprawidłowy argument dla foreach () w [...]

Zakładając, że nie można refaktoryzować get_values()funkcji, aby zawsze zwracała tablicę (kompatybilność wsteczna, niedostępny kod źródłowy, bez względu na inny powód), zastanawiam się, który jest najczystszym i najbardziej wydajnym sposobem uniknięcia tych ostrzeżeń:

  • Przesyłanie $valuesdo tablicy
  • Inicjowanie $valuesdo tablicy
  • Zawijanie foreachza pomocąif
  • Inne (proszę zasugerować)

Jest bardzo możliwe, że $valuesto nie jest tablica.
Bhargav Nanekalva,

Odpowiedzi:


509

Osobiście uważam, że jest to najczystsze - nie jestem pewien, czy jest najbardziej wydajne, pamiętaj!

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

Powodem dla mnie jest to, że nie przydziela pustej tablicy, kiedy i tak nie masz nic na początek.


4
Lub użyj funkcji count (), aby dowiedzieć się, czy tablica nie jest pusta
Kemo,

76
@Kemo: count()nie jest wiarygodny. Jeśli przekażesz count()null, zwróci 0. Jeśli przekażesz argument inny niż null, nie będący tablicą, zwróci 1. Dlatego niemożliwe count()jest określenie, czy zmienna jest tablicą, gdy zmienna może być pustą tablicą, lub tablica zawierająca 1 element.
Andy Shellam,

12
Zauważ, że niektóre obiekty są iterowalne i ta odpowiedź ich nie uwzględnia.
Brad Koch,

32
Powinno być if (is_array($values) || $values instanceof Traversable).
Bob Stein

3
Szkoda, że ​​nie powiedział, że nie był to na pewno najbardziej wydajny: D
Gui Prá

116

Co powiesz na ten? dużo czystsze i wszystko w jednej linii.

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

7
To jedyna rzecz, która działała dla mnie. Z jakiegoś powodu PHP nie wierzyło, że wielowymiarowa tablica, którą zbudowałem, była tablicą tablic.
Justin

1
To samo tutaj, jest to bardzo fajna poprawka dla tablicy zawierającej tablice lub wartości null. Wystarczy dodać test w pętli foreach, aby kontynuować, jeśli dane są puste.
Lizardx

Rozwiązałem mój problem. Dzięki!
Hitesh,

1
Genialny kod, pominąłem if, w przeciwnym razie dla tablicy i wartości bez tablicy, używając $ _POST z polem wyboru!
Yann Chabot,

2
UWAGA: Mimo że pięknie wygląda i rozwiązuje niepoprawne ostrzeżenie foreach, ta metoda zwróci niezdefiniowane ostrzeżenie o zmiennej, jeśli zmienna nie zostanie w żaden sposób ustawiona. Użyj isset()lub is_array()obu, całkowicie w zależności od twojego scenariusza itp.
James

42

Zwykle używam konstrukcji podobnej do tej:

/**
 * 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...
    }
}

Zauważ, że ta konkretna wersja nie jest testowana, jest wpisana bezpośrednio do SO z pamięci.

Edycja: dodano czek przejezdny


3
Najlepsza odpowiedź. Tyle że uważam, że naprawdę powinieneś sprawdzić, czy $var instanceof Traversable. Zobacz tutaj . Ponieważ na przykład możesz przewidzieć SimpleXMLElement , ale nie jest to ani instancja Iterator, ani IteratorAggregate.
Bob Stein

2
Możesz być w stanie usunąć pozostałe dwie klasy, @Kris. Oba rozszerzają teraz Traversable i wydaje się, że tak się urodziły w 5.0.0. Chociaż mam niewielkie wątpliwości, czy instancja zawsze odnosi się do rozszerzeń.
Bob Stein

1
@ BobStein-VisiBone: tak (tyle, że są to interfejsy, a nie klasy) Jednak; Przed nimi umieściłem Traversable, ani Iterator, ani IteratorAggregate nigdy nie będą wymagały weryfikacji (w ten sposób nie spowolnią wykonywania). Zostawiłem je, aby zachować odpowiedź możliwie najbliższą pierwotnej odpowiedzi, której udzieliłem, i aby była oczywista / czytelna.
Kris

2
Myślę, że byłoby sprawiedliwie dodać is_object($var)ponownie. php.net/manual/en/language.oop5.iterations.php
Mark Fox

1
@MarkFox: Nie krępuj się, jednak celowo go pominąłem; Nigdy nie widziałem takiego zastosowania, które nie byłoby lepiej obsługiwane przez implementację Iteratorlub IteratorAggregate, ale to oczywiście tylko moja opinia i dlatego subiektywna (nigdy nie używam pól publicznych).
Kris,

15

Nie polegaj na przesyłaniu jako rozwiązania , nawet jeśli inni sugerują to jako prawidłową opcję, aby zapobiec błędowi, może to spowodować inną.

Pamiętaj: jeśli oczekujesz zwrotu określonej formy tablicy, może to Cię nie powieść. Wymagane są w tym celu dodatkowe kontrole.

Np. Rzutowanie wartości logicznej na tablicę NIE(array)bool spowoduje, że tablica będzie pusta, ale tablica z jednym elementem zawierającym wartość logiczną jako int: lub .[0=>0][0=>1]

Napisałem szybki test, aby przedstawić ten problem . (Oto test zapasowy na wypadek, gdyby pierwszy testowy adres URL zawiódł).

Zawarte są testy dla: null, false, true, A class, arrayi undefined.


Zawsze sprawdzaj dane wejściowe przed użyciem ich w foreach. Propozycje:

  1. Szybkie sprawdzanie typu :$array = is_array($var) or is_object($var) ? $var : [] ;
  2. Wpisz tablice podpowiedzi w metodach przed użyciem foreach i określeniem typów zwracanych
  3. Zawijanie foreach wewnątrz if
  4. Używanie try{}catch(){}bloków
  5. Projektowanie właściwego kodu / testowania przed wydaniami produkcyjnymi
  6. Aby przetestować tablicę pod kątem prawidłowej formy, możesz użyć array_key_existsokreślonego klucza lub przetestować głębokość tablicy (gdy jest to jeden!) .
  7. Zawsze wyodrębniaj metody pomocnicze do globalnej przestrzeni nazw w taki sposób, aby zredukować zduplikowany kod

8

Spróbuj tego:

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

;)


1
To nie zadziała dobrze w przypadku tablic asocjacyjnych. Metoda is_array jest ogólnie lepsza ... i łatwiejsza ...
AO_

4
$values = get_values();

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

Problem jest zawsze zerowy, a Casting jest w rzeczywistości rozwiązaniem czyszczącym.


3

Przede wszystkim należy zainicjować każdą zmienną. Zawsze.
Przesyłanie nie jest opcją.
if get_values ​​(); może zwrócić zmienną innego typu, tę wartość należy oczywiście sprawdzić.


Przesyłanie jest opcją - jeśli zainicjujesz tablicę przy użyciu $array = (array)null;, otrzymasz pustą tablicę. Oczywiście marnotrawstwo przydziału pamięci ;-)
Andy Shellam

2
+1: odczytane z sentymentalnego punktu widzenia, nie obchodzi mnie, czy język się obejdzie, zmienne MUSZĄ zostać zadeklarowane, a wiarygodne wyniki MUSZĄ zostać sprawdzone. Konieczne jest utrzymanie rozsądnego poziomu programistów i dzienników błędów.
Kris,

3

Bardziej zwięzłe rozszerzenie kodu @ Kris

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

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

szczególnie do używania wewnątrz kodu szablonu

<?php foreach (secure_iterable($values) as $value): ?>
    ...
<?php endforeach; ?>

2
Nie masz na myśli return is_iterable($var) ? $var : array($var);?
SQB,

3

Jeśli używasz php7 i chcesz obsłużyć tylko niezdefiniowane błędy, jest to najczystszy IMHO

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

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

To nie sprawdza, czy jest to tablica, ale pomija pętlę, jeśli zmienna jest pusta lub pusta.


1

Nie jestem pewien, czy tak jest, ale ten problem wydaje się występować wiele razy podczas migracji witryn Wordpress lub ogólnie migracji witryn dynamicznych. W takim przypadku upewnij się, że hosting, do którego przeprowadzasz migrację, używa tej samej wersji PHP, której używa stara witryna.

Jeśli nie przeprowadzasz migracji swojej witryny, a to jest tylko problem, który się pojawił, spróbuj zaktualizować do wersji PHP 5. To rozwiązuje niektóre z tych problemów. Może to wydawać się głupim rozwiązaniem, ale załatwiło sprawę.


1

Wyjątkowy przypadek tego zawiadomienia występuje, jeśli ustawisz tablicę na zero w pętli foreach

if (is_array($values))
{
    foreach ($values as $value)
    {
        $values = null;//WARNING!!!
    }
}

1

A co z tym rozwiązaniem:

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

if (in_array($type, $types)) {
    // foreach code comes here
}

1

Podano niepoprawny argument ostrzeżenia dla foreach()wyświetlanych tweetów. idź do /wp-content/plugins/display-tweets-php. Następnie wstaw ten kod do linii numer 591, będzie działał idealnie.

if (is_array($tweets)) {
    foreach ($tweets as $tweet) 
    {
        ...
    }
}

Niesamowite! To powinno być przyjęte rozwiązanie. w moim przypadku dodałem to:if (is_array($_POST['auto'])){ // code }
Jodyshop

0

Wydaje się również, że istnieje związek ze środowiskiem:

Miałem ten błąd „dostarczono nieprawidłowy argument foreach ()” tylko w środowisku deweloperskim, ale nie w prod (pracuję na serwerze, a nie localhost).

Pomimo błędu zmienna_dump wskazała, że ​​tablica jest w porządku (w obu przypadkach app i dev).

if (is_array($array))Wokół foreach ($array as $subarray)rozwiązało problem.

Przepraszam, że nie potrafię wyjaśnić przyczyny, ale ponieważ znalezienie rozwiązania zajęło mi trochę czasu, pomyślałem o lepszym udostępnieniu tego jako spostrzeżenia.


0

Użyj funkcji is_array, gdy przekażesz tablicę do pętli foreach.

if (is_array($your_variable)) {
  foreach ($your_variable as $item) {
   //your code
}
}

0

Co powiesz na zdefiniowanie pustej tablicy jako zastępczej, jeśli get_value()jest pusta?
Nie mogę wymyślić najkrótszej drogi.

$values = get_values() ?: [];

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

0

Użyję kombinacji empty, isseta is_arrayjako

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

if (!empty($array) && isset($array) && is_array($array) {
    //loop
    foreach ($array as $values) {
        echo $values; 
    }
}

-3

zrobiłbym to samo co Andy, ale używałbym funkcji „pustej”.

tak:

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, jeśli $yourArray = 1;spróbuje iterować, a otrzymasz błąd. empty()nie jest odpowiednim testem.
Brad Koch,

@BradKoch ma absolutną rację. is_array () to jedyny niezawodny sposób na sprawdzenie, czy $ yourArray jest tablicą. Zobacz inne odpowiedzi, aby dowiedzieć się, dlaczego is_array () nie jest wystarczający - foreach może również obsługiwać iteratory.
cgeisel
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.