Ungültiges Argument für foreach ()


304

Es passiert mir oft, Daten zu verarbeiten, die entweder ein Array oder eine Nullvariable sein können, und einige foreachmit diesen Daten zu versorgen.

$values = get_values();

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

Wenn Sie einen foreach mit Daten füttern, die kein Array sind, erhalten Sie eine Warnung:

Warnung: Ungültiges Argument für foreach () in [...]

Angenommen, es ist nicht möglich, die get_values()Funktion so umzugestalten , dass immer ein Array zurückgegeben wird (Abwärtskompatibilität, nicht verfügbarer Quellcode, aus welchem ​​anderen Grund auch immer), frage ich mich, welcher Weg am saubersten und effizientesten ist, um diese Warnungen zu vermeiden:

  • Casting $valueszum Array
  • Initialisierung $valuesauf Array
  • Wickeln Sie die foreachmit einemif
  • Andere (bitte vorschlagen)

Es ist sehr wahrscheinlich, dass $valueses sich nicht um ein Array handelt.
Bhargav Nanekalva

Antworten:


509

Persönlich finde ich das am saubersten - ich bin mir nicht sicher, ob es am effizientesten ist!

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

Der Grund für meine Präferenz ist, dass kein leeres Array zugewiesen wird, wenn Sie sowieso nichts zu Beginn haben.


4
Oder verwenden Sie count (), um herauszufinden, ob das Array nicht leer ist
Kemo

76
@Kemo: count()ist nicht zuverlässig. Wenn Sie count()null übergeben, wird 0 zurückgegeben. Wenn Sie ein Nicht-Null-Argument ohne Array übergeben, wird 1 zurückgegeben. Daher kann nicht count()bestimmt werden, ob die Variable ein Array ist, wenn die Variable ein leeres Array sein könnte, oder ein Array mit 1 Element.
Andy Shellam

12
Beachten Sie, dass einige Objekte iterierbar sind und diese Antwort diese nicht berücksichtigt.
Brad Koch

32
Sollte sein if (is_array($values) || $values instanceof Traversable).
Bob Stein

3
Es ist eine Schande, dass er nicht weiter sagte, es sei nicht sicher das effizienteste: D
Gui Prá

116

Wie wäre es mit diesem? viel sauberer und alles in einer Zeile.

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

7
Dies ist das einzige, was für mich funktioniert hat. Aus irgendeinem Grund glaubte PHP nicht, dass das von mir erstellte mehrdimensionale Array tatsächlich ein Array von Arrays war.
Justin

1
Dies ist auch hier eine sehr schöne Lösung für ein Array, das entweder Arrays oder Nullwerte enthält. Fügen Sie einfach einen Test in die foreach-Schleife ein, um fortzufahren, wenn die Daten null sind.
Lizardx

Mein Problem gelöst. Vielen Dank!
Hitesh

1
Brillanter Code, übersprungen das if, else für Array- und Nicht-Array-Wert mit $ _POST mit Kontrollkästchen!
Yann Chabot

2
HINWEIS: Diese Methode sieht zwar gut aus und löst die ungültige foreach-Warnung auf, gibt jedoch eine undefinierte Variablenwarnung zurück, wenn die Variable in keiner Weise festgelegt ist. Verwenden Sie isset()oder is_array()oder beides, ganz abhängig von Ihrem Szenario usw.
James

42

Normalerweise verwende ich ein ähnliches Konstrukt:

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

Beachten Sie, dass diese bestimmte Version nicht getestet wird, sondern direkt aus dem Speicher in SO eingegeben wird.

Bearbeiten: Traversable Check hinzugefügt


3
Beste Antwort. Außer ich denke du solltest wirklich prüfen ob $var instanceof Traversable. Siehe hier . Zum Beispiel können Sie jedes SimpleXMLElement foreach , aber es ist keine Instanz von Iterator oder IteratorAggregate.
Bob Stein

2
Möglicherweise können Sie die beiden anderen Klassen, @Kris, entfernen. Beide erweitern jetzt Traversable und scheinen in 5.0.0 auf diese Weise geboren worden zu sein. Obwohl ich einen winzigen Zweifel daran habe, ob die Instanz immer auf Erweiterungen angewendet wurde.
Bob Stein

1
@ BobStein-VisiBone: ja (außer es handelt sich um Schnittstellen, keine Klassen). Ich habe Traversable vor diese gestellt, weder Iterator noch IteratorAggregate müssten jemals überprüft werden (auf diese Weise werden sie die Ausführung nicht verlangsamen). Ich habe sie belassen, um die Antwort so nah wie möglich an der ursprünglichen Antwort zu halten und sie offensichtlich / lesbar zu halten.
Kris

2
Ich denke, es wäre fair, is_object($var)re hinzuzufügen . php.net/manual/en/language.oop5.iterations.php
Mark Fox

1
@ MarkFox: Fühlen Sie sich frei, aber ich habe es absichtlich weggelassen; Ich habe noch nie eine Verwendung dafür gesehen, die durch Implementierung Iteratoroder nicht besser bedient werden konnte IteratorAggregate, aber das ist natürlich nur meine Meinung und daher subjektiv (ich benutze niemals öffentliche Felder).
Kris

15

Bitte verlassen Sie sich nicht auf das Casting als Lösung , auch wenn andere dies als gültige Option vorschlagen, um einen Fehler zu vermeiden, kann dies zu einem anderen führen.

Beachten Sie: Wenn Sie erwarten, dass eine bestimmte Form von Array zurückgegeben wird, schlägt dies möglicherweise fehl. Dafür sind weitere Prüfungen erforderlich.

Das Umwandeln eines Booleschen Werts in ein Array (array)boolführt beispielsweise NICHT zu einem leeren Array, sondern zu einem Array mit einem Element, das den Booleschen Wert als int: [0=>0]oder enthält [0=>1].

Ich habe einen kurzen Test geschrieben, um dieses Problem darzustellen . (Hier ist ein Backup-Test für den Fall, dass die erste Test-URL fehlschlägt.)

Enthalten sind Tests für: null, false, true, ein class, ein arrayund undefined.


Testen Sie Ihre Eingabe immer, bevor Sie sie in foreach verwenden. Vorschläge:

  1. Schnelle Typprüfung :$array = is_array($var) or is_object($var) ? $var : [] ;
  2. Geben Sie Hinweis-Arrays in Methoden ein, bevor Sie foreach verwenden und Rückgabetypen angeben
  3. Wrap foreach innerhalb von if
  4. Mit try{}catch(){}Blöcken
  5. Entwerfen des richtigen Codes / Testens vor Produktionsfreigaben
  6. Um ein Array gegen die richtige Form zu testen, können Sie es array_key_existsfür einen bestimmten Schlüssel verwenden oder die Tiefe eines Arrays testen (wenn es einer ist!) .
  7. Extrahieren Sie Ihre Hilfsmethoden immer in den globalen Namespace, um doppelten Code zu reduzieren

8

Versuche dies:

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

;)


1
Dies funktioniert nicht gut mit assoziativen Arrays. Die is_array-Methode ist insgesamt besser ... und einfacher ...
AO_

4
$values = get_values();

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

Das Problem ist immer null und Casting ist in der Tat die Reinigungslösung.


3

Zunächst muss jede Variable initialisiert werden. Immer.
Casting ist keine Option.
if get_values ​​(); kann eine andere Typvariable zurückgeben, dieser Wert muss natürlich überprüft werden.


Casting ist eine Option - wenn Sie ein Array mit initialisieren, erhalten $array = (array)null;Sie ein leeres Array. Natürlich ist es eine Verschwendung von Speicherzuweisung ;-)
Andy Shellam

2
+1: aus einem sentimentalen Sicht zu lesen, es ist mir egal , wenn die Sprache tun können , ohne, Variablen MÜSSEN und unzuverlässige Ergebnisse deklariert werden muss überprüft werden. Es ist erforderlich, dass die Entwickler gesund sind und die Fehlerprotokolle kurz sind.
Kris

3

Prägnantere Erweiterung des Codes von @ Kris

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

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

speziell für die Verwendung von Inside Template Code

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

2
Meinst du nicht return is_iterable($var) ? $var : array($var);?
SQB

3

Wenn Sie PHP7 verwenden und nur undefinierte Fehler behandeln möchten, ist dies meiner Meinung nach das sauberste

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

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

Dies prüft nicht, ob es sich um ein Array handelt, sondern überspringt die Schleife, wenn die Variable null oder ein leeres Array ist.


1

Ich bin nicht sicher, ob dies der Fall ist, aber dieses Problem scheint bei der Migration von WordPress-Sites oder der Migration dynamischer Sites im Allgemeinen mehrmals aufzutreten. Wenn dies der Fall ist, stellen Sie sicher, dass das Hosting, auf das Sie migrieren, dieselbe PHP-Version verwendet, die Ihre alte Site verwendet.

Wenn Sie Ihre Site nicht migrieren und dies nur ein aufgetretenes Problem ist, versuchen Sie, ein Update auf PHP 5 durchzuführen. Dadurch werden einige dieser Probleme behoben. Könnte wie eine dumme Lösung erscheinen, hat aber den Trick für mich getan.


1

Ein Ausnahmefall für diesen Hinweis tritt auf, wenn Sie das Array in jeder for-Schleife auf null setzen

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

1

Wie wäre es mit dieser Lösung:

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

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

1

Warnung ungültiges Argument für foreach()Anzeige-Tweets angegeben. geh zu /wp-content/plugins/display-tweets-php. Fügen Sie dann diesen Code in Zeile 591 ein. Er läuft einwandfrei.

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

Genial! Das sollte die akzeptierte Lösung sein. In meinem Fall habe ich if (is_array($_POST['auto'])){ // code }
Folgendes

0

Es scheint auch eine Beziehung zur Umwelt zu geben:

Ich hatte diesen Fehler "ungültiges Argument für foreach () angegeben" nur in der Entwicklungsumgebung, aber nicht in prod (ich arbeite auf dem Server, nicht auf localhost).

Trotz des Fehlers zeigte ein var_dump an, dass das Array gut vorhanden war (in beiden Fällen app und dev).

Das if (is_array($array))rund um foreach ($array as $subarray)das Problem gelöst.

Es tut mir leid, dass ich die Ursache nicht erklären kann, aber da ich eine Weile gebraucht habe, um eine Lösung zu finden, dachte ich daran, dies besser als Beobachtung zu teilen.


0

Verwenden Sie die Funktion is_array, wenn Sie das Array an jede Schleife übergeben.

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

0

Was ist mit der Definition eines leeren Arrays als Fallback, wenn get_value()es leer ist?
Ich kann mir keinen kürzesten Weg vorstellen.

$values = get_values() ?: [];

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

0

Ich werde eine Kombination verwenden empty, issetund is_arrayals

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

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

-3

Ich würde das gleiche tun wie Andy, aber ich würde die 'leere' Funktion verwenden.

wie so:

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, Wenn $yourArray = 1;es versucht zu iterieren, erhalten Sie den Fehler. empty()ist kein geeigneter Test.
Brad Koch

@BradKoch ist absolut richtig. is_array () ist die einzige zuverlässige Methode, um zu testen, ob $ yourArray ein Array ist. Weitere Antworten dazu, warum is_array () nicht ausreicht, finden Sie in anderen Antworten. Foreach kann auch Iteratoren verarbeiten.
Cgeisel
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.