Argomento non valido fornito per foreach ()


304

Mi capita spesso di gestire dati che possono essere una matrice o una variabile nulla e di alimentare alcuni foreachdi questi dati.

$values = get_values();

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

Quando si alimenta un foreach con dati che non sono un array, viene visualizzato un avviso:

Avviso: argomento non valido fornito per foreach () in [...]

Supponendo che non sia possibile riformattare la get_values()funzione per restituire sempre un array (compatibilità con le versioni precedenti, codice sorgente non disponibile, qualunque altra ragione), mi chiedo quale sia il modo più pulito ed efficiente per evitare questi avvisi:

  • Casting $valuesin array
  • Inizializzazione $valuesin array
  • Confezione foreachcon unif
  • Altro (si prega di suggerire)

È altamente possibile che $valuesnon sia un array.
Bhargav Nanekalva,

Risposte:


509

Personalmente trovo che questo sia il più pulito, non sono sicuro che sia il più efficiente, mente!

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

Il motivo della mia preferenza è che non alloca un array vuoto quando non hai nulla con cui iniziare.


4
Oppure usa count () per capire se l'array non è vuoto
Kemo

76
@Kemo: count()non è affidabile. Se passi count()null, restituisce 0. Se gli passi un argomento non null, non array, restituisce 1. Pertanto è impossibile utilizzare count()per determinare se la variabile è un array quando la variabile può essere un array vuoto, oppure un array contenente 1 oggetto.
Andy Shellam,

12
Nota che alcuni oggetti sono iterabili e questa risposta non tiene conto di quelli.
Brad Koch,

32
Dovrebbe essere if (is_array($values) || $values instanceof Traversable).
Bob Stein

3
È un peccato che non abbia continuato a dire che non era sicuramente il più efficiente, però: D
Gui Prá

116

Che ne dici di questo? molto più pulito e tutto in linea singola.

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

7
Questa è l'unica cosa che ha funzionato per me. Per qualche ragione, PHP non credeva che l'array multidimensionale che avevo creato fosse in realtà un array di array.
Giustino,

1
Lo stesso qui, questa è una correzione molto bella per un array che contiene array o valori null. Aggiungi semplicemente un test nel ciclo foreach per continuare se i dati sono nulli.
Lizardx,

Risolto il mio problema Grazie!
Hitesh,

1
Codice brillante, ignorato il valore if, else per array e none-array usando $ _POST con la casella di controllo!
Yann Chabot,

2
NOTA: sebbene sia bello guardare e risolva l'avvertimento foreach non valido, questo metodo restituirà un avviso variabile non definito se la variabile non è impostata in alcun modo. Usa isset()o is_array()entrambi, interamente a seconda del tuo scenario, ecc.
James,

42

Di solito uso un costrutto simile a questo:

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

Si noti che questa particolare versione non è stata testata, è stata digitata direttamente in memoria dalla memoria.

Edit: aggiunto Traversable controllo


3
Migliore risposta. Tranne che penso che dovresti davvero controllare se $var instanceof Traversable. Vedi qui . Perché ad esempio è possibile prevedere un SimpleXMLElement , ma non è un'istanza di Iterator o IteratorAggregate.
Bob Stein

2
Potresti riuscire a rimuovere le altre due classi, @Kris. Entrambi estendono Traversable ora e sembrano essere nati in questo modo in 5.0.0. Anche se ho un piccolo dubbio sul fatto che instanceof sia sempre applicato alle estensioni.
Bob Stein

1
@ BobStein-VisiBone: sì (tranne che sono interfacce, non classi) Tuttavia; Ho inserito Traversable prima di quelli, né Iterator né IteratorAggregate avrebbero mai avuto bisogno di essere verificati (in questo modo non rallenteranno l'esecuzione). Li ho lasciati per mantenere la risposta il più vicino possibile alla risposta originale che ho dato e per renderla ovvia / leggibile.
Kris

2
Penso che sarebbe giusto aggiungere is_object($var)re. php.net/manual/en/language.oop5.iterations.php
Mark Fox,

1
@MarkFox: sentiti libero, tuttavia l'ho lasciato intenzionalmente fuori; Non ne ho mai visto un uso che non fosse meglio servito dall'implementazione Iteratoro IteratorAggregate, ma ovviamente è solo la mia opinione e quindi soggettiva (non uso mai campi pubblici).
Kris,

15

Per favore, non dipendere dal cast come soluzione , anche se altri lo stanno suggerendo come un'opzione valida per prevenire un errore, potrebbe causarne un altro.

Attenzione: se si prevede di restituire una forma specifica di array, ciò potrebbe non riuscire. Per questo sono necessari ulteriori controlli.

Ad esempio, il cast di un valore booleano in un array (array)bool, NON comporta un array vuoto, ma un array con un elemento contenente il valore booleano come int: [0=>0]o [0=>1].

Ho scritto un test rapido per presentare questo problema . (Ecco un test di backup nel caso in cui il primo URL del test fallisca.)

Sono inclusi i test per: null, false, true, una class, una arraye undefined.


Testa sempre i tuoi input prima di usarli in foreach. suggerimenti:

  1. Controllo rapido del tipo :$array = is_array($var) or is_object($var) ? $var : [] ;
  2. Digitare array di suggerimenti nei metodi prima di utilizzare un foreach e specificare i tipi di ritorno
  3. Avvolgimento foreach all'interno di if
  4. Usando i try{}catch(){}blocchi
  5. Progettazione di codice / test adeguati prima delle versioni di produzione
  6. Per testare un array con la forma corretta, è possibile utilizzare array_key_existsun tasto specifico o testare la profondità di un array (quando è uno!) .
  7. Estrai sempre i tuoi metodi di supporto nello spazio dei nomi globale in modo da ridurre il codice duplicato

8

Prova questo:

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

;)


1
Questo non funzionerà bene con le matrici associative. Il metodo is_array è complessivamente migliore ... e più facile ...
AO_

4
$values = get_values();

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

Il problema è sempre nullo e la fusione è in realtà la soluzione di pulizia.


3

Prima di tutto, ogni variabile deve essere inizializzata. Sempre.
Casting non è un'opzione.
if get_values ​​(); può restituire variabili di tipo diverso, questo valore deve essere verificato, ovviamente.


Il cast è un'opzione: se si inizializza un array usando $array = (array)null;si ottiene un array vuoto. Ovviamente è uno spreco di allocazione di memoria ;-)
Andy Shellam

2
+1: letto da un punto di vista sentimentale, non mi interessa se la lingua può farne a meno, le variabili DEVONO essere dichiarate e i risultati inaffidabili DEVONO essere controllati. È necessario per mantenere gli sviluppatori sani di mente e i registri degli errori brevi.
Kris

3

Estensione più concisa del codice di @ Kris

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

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

soprattutto per l'utilizzo del codice template interno

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

2
Non intendi return is_iterable($var) ? $var : array($var);?
SQB,

3

Se stai usando php7 e vuoi gestire solo errori non definiti, questo è l'IMHO più pulito

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

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

Questo non controlla se si tratta di un array, ma salta il ciclo se la variabile è nulla o un array vuoto.


1

Non sono sicuro che sia così, ma questo problema sembra verificarsi più volte durante la migrazione di siti wordpress o la migrazione di siti dinamici in generale. In tal caso, assicurati che l'hosting a cui stai eseguendo la migrazione utilizzi la stessa versione di PHP utilizzata dal tuo vecchio sito.

Se non stai migrando il tuo sito e questo è solo un problema che si è verificato, prova ad aggiornare a PHP 5. Questo risolve alcuni di questi problemi. Potrebbe sembrare una soluzione sciocca, ma ha fatto il trucco per me.


1

Caso eccezionale per questo avviso si verifica se si imposta l'array su null all'interno del ciclo foreach

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

1

Che ne dici di questa soluzione:

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

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

1

Argomento di avviso non valido fornito per i foreach()tweet di visualizzazione. vai a /wp-content/plugins/display-tweets-php. Quindi inserire questo codice sulla riga numero 591, funzionerà perfettamente.

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

Eccezionale! Questa dovrebbe essere la soluzione accettata. nel mio caso ho aggiunto questo:if (is_array($_POST['auto'])){ // code }
Jodyshop il

0

Sembra esserci anche una relazione con l'ambiente:

Ho avuto l'errore "argomento non valido fornito foreach ()" solo nell'ambiente di sviluppo, ma non in prod (sto lavorando sul server, non localhost).

Nonostante l'errore, var_dump ha indicato che l'array era ben presente (in entrambi i casi app e dev).

L' if (is_array($array))intorno alla foreach ($array as $subarray)risolto il problema.

Mi dispiace, non posso spiegare la causa, ma dato che mi ci è voluto un po 'per trovare una soluzione, ho pensato di condividerla meglio come osservazione.


0

Usa la funzione is_array, quando passerai l'array per ogni ciclo.

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

0

Che ne dite di definire un array vuoto come fallback se get_value()è vuoto?
Non riesco a pensare a un modo più breve.

$values = get_values() ?: [];

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

0

Userò una combinazione di empty, issete is_arraycome

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

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

-3

farei la stessa cosa di Andy ma userò la funzione 'vuoto'.

così:

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, se $yourArray = 1;tenterà di iterare e verrà visualizzato l'errore. empty()non è un test adatto.
Brad Koch,

@BradKoch ha assolutamente ragione. is_array () è l'unico modo affidabile per verificare se $ yourArray è un array. Vedi altre risposte per i dettagli sul perché is_array () non è sufficiente: foreach può gestire anche gli iteratori.
cgeisel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.