Argument non valide pour la fonction foreach()

304

Il m'arrive souvent de gérer des données qui peuvent être un tableau ou une variable nulle et d'en nourrir certaines foreachavec ces données.

$values = get_values();

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

Lorsque vous alimentez un foreach avec des données qui ne sont pas un tableau, vous obtenez un avertissement:

Avertissement: Argument non valide fourni pour foreach () dans [...]

En supposant qu'il n'est pas possible de refactoriser la get_values()fonction pour toujours renvoyer un tableau (compatibilité descendante, code source non disponible, quelle que soit la raison), je me demande quelle est la manière la plus propre et la plus efficace d'éviter ces avertissements:

  • Casting $valuesdans un tableau
  • Initialisation $valuesau tableau
  • Envelopper le foreachavec unif
  • Autre (veuillez suggérer)
Roberto Aloi
la source
Il est fort possible que ce $valuesne soit pas un tableau.
Bhargav Nanekalva

Réponses:

509

Personnellement, je trouve que c'est le plus propre - je ne sais pas si c'est le plus efficace, l'esprit!

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

La raison de ma préférence est qu'il n'alloue pas de tableau vide quand vous n'avez rien pour commencer de toute façon.

Andy Shellam
la source
4
Ou utilisez count () pour déterminer si le tableau n'est pas vide
Kemo
76
@Kemo: count()n'est pas fiable. Si vous passez count()null, il renvoie 0. Si vous lui passez un argument non null et non tableau, il renvoie 1. Par conséquent, il est impossible d'utiliser count()pour déterminer si la variable est un tableau alors que la variable peut être un tableau vide, ou un tableau contenant 1 élément.
Andy Shellam
12
Notez que certains objets sont itérables, et cette réponse n'en tient pas compte.
Brad Koch
32
Devrait l'être if (is_array($values) || $values instanceof Traversable).
Bob Stein
3
C'est dommage qu'il n'ait pas
continué
116

Celui-ci, ça va? beaucoup plus propre et tout en une seule ligne.

foreach ((array) $items as $item) {
 // ...
 }
Ajith R Nair
la source
7
C'est la seule chose qui a fonctionné pour moi. Pour une raison quelconque, PHP ne croyait pas que le tableau multidimensionnel que j'avais construit était en fait un tableau de tableaux.
Justin
1
Idem ici, c'est une très bonne solution pour un tableau contenant soit des tableaux soit des valeurs nulles. Ajoutez simplement un test dans la boucle foreach pour continuer si les données sont nulles.
Lizardx
Résolu mon problème. Merci!
Hitesh
1
Code brillant, ignoré le if, else pour la valeur de tableau et aucun tableau en utilisant $ _POST avec la case à cocher!
Yann Chabot
2
REMARQUE: bien qu'elle soit belle et résout l'avertissement foreach non valide, cette méthode renvoie un avertissement de variable non définie si la variable n'est définie d'aucune façon. Utilisez isset()ou les is_array()deux, entièrement en fonction de votre scénario, etc.
James
42

J'utilise généralement une construction similaire à celle-ci:

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

Notez que cette version particulière n'est pas testée, elle est tapée directement dans SO depuis la mémoire.

Edit: ajout de la vérification Traversable

Kris
la source
3
Meilleure réponse. Sauf que je pense que vous devriez vraiment vérifier si $var instanceof Traversable. Voyez ici . Parce que, par exemple, vous pouvez foreach un SimpleXMLElement , mais ce n'est pas une instance de Iterator ou IteratorAggregate.
Bob Stein
2
Vous pourrez peut-être supprimer les deux autres classes, @Kris. Ils étendent tous les deux Traversable maintenant et semblent être nés de cette façon dans 5.0.0. Bien que je ressens un petit doute quant à savoir si instanceof s'applique toujours aux extensions.
Bob Stein
1
@ BobStein-VisiBone: oui (sauf que ce sont des interfaces, pas des classes) Cependant; J'ai mis Traversable avant ceux-ci, ni Iterator ni IteratorAggregate n'auraient jamais besoin d'être vérifiés (de cette façon, ils ne ralentiront pas l'exécution). Je les ai laissés pour garder la réponse aussi proche que possible de la réponse originale que j'ai donnée et pour la garder évidente / lisible.
Kris
2
Je pense qu'il serait juste d'ajouter is_object($var)re. php.net/manual/en/language.oop5.iterations.php
Mark Fox
1
@MarkFox: N'hésitez pas, mais je l'ai intentionnellement laissé de côté; Je n'en ai jamais vu une utilisation qui ne soit pas mieux servie par la mise en œuvre de Iteratorou IteratorAggregate, mais c'est bien sûr juste mon opinion et donc subjective (je n'utilise jamais les champs publics).
Kris
15

Veuillez ne pas dépendre de la diffusion comme solution , même si d'autres suggèrent cela comme une option valide pour éviter une erreur, cela pourrait en provoquer une autre.

Attention: Si vous vous attendez à ce qu'une forme spécifique de tableau soit renvoyée, cela peut vous échouer. Plus de contrôles sont nécessaires pour cela.

Par exemple, la conversion d'un booléen en un tableau (array)booln'entraînera PAS un tableau vide, mais un tableau avec un élément contenant la valeur booléenne sous la forme d'un int: [0=>0]ou [0=>1].

J'ai écrit un test rapide pour présenter ce problème . (Voici un test de sauvegarde au cas où la première URL de test échouerait.)

Inclus sont des tests pour: null, false, true, un class, un arrayet undefined.


Testez toujours votre entrée avant de l'utiliser dans foreach. Suggestions:

  1. Vérification rapide du type :$array = is_array($var) or is_object($var) ? $var : [] ;
  2. Tapez les tableaux d'indices dans les méthodes avant d'utiliser un foreach et de spécifier les types de retour
  3. Enveloppant foreach dedans si
  4. Utilisation de try{}catch(){}blocs
  5. Concevoir le bon code / tester avant la sortie de la production
  6. Pour tester un tableau par rapport à la forme appropriée, vous pouvez utiliser array_key_existsune clé spécifique ou tester la profondeur d'un tableau (quand il en est un!) .
  7. Extrayez toujours vos méthodes d'assistance dans l'espace de noms global de manière à réduire le code en double
AARTT
la source
8

Essaye ça:

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

;)

GigolNet Guigolachvili
la source
1
Cela ne fonctionnera pas bien avec les tableaux associatifs .. La méthode is_array est globalement meilleure ... et plus facile ...
AO_
4
$values = get_values();

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

Le problème est toujours nul et Casting est en fait la solution de nettoyage.

boctulus
la source
3

Tout d'abord, chaque variable doit être initialisée. Toujours.
Le casting n'est pas une option.
if get_values ​​(); peut renvoyer une variable de type différent, cette valeur doit être vérifiée, bien sûr.

Votre bon sens
la source
La conversion est une option - si vous initialisez un tableau à l'aide de $array = (array)null;vous obtenez un tableau vide. Bien sûr, c'est un gaspillage d'allocation de mémoire ;-)
Andy Shellam
2
+1: lu d'un point de vue sentimental, peu m'importe si le langage peut s'en passer, les variables DOIVENT être déclarées et les résultats peu fiables DOIVENT être vérifiés. Il est nécessaire de garder les développeurs sains d'esprit et les journaux d'erreurs courts.
Kris
3

Extension plus concise du code de @ Kris

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

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

en particulier pour utiliser le code du modèle à l'intérieur

<?php foreach (secure_iterable($values) as $value): ?>
    ...
<?php endforeach; ?>
HongKilDong
la source
2
Tu ne veux pas dire return is_iterable($var) ? $var : array($var);?
SQB
3

Si vous utilisez php7 et que vous souhaitez gérer uniquement les erreurs non définies, c'est le IMHO le plus propre

$array = [1,2,3,4];
foreach ( $array ?? [] as $item ) {
  echo $item;
}
Edwin Rodríguez
la source
2
foreach ($arr ? $arr : [] as $elem) {
    // Does something 
}

Cela ne vérifie pas s'il s'agit d'un tableau, mais ignore la boucle si la variable est nulle ou un tableau vide.

T30
la source
1

Je ne sais pas si c'est le cas, mais ce problème semble se produire plusieurs fois lors de la migration de sites wordpress ou de la migration de sites dynamiques en général. Si tel est le cas, assurez-vous que l'hébergement vers lequel vous migrez utilise la même version PHP que votre ancien site utilise.

Si vous ne migrez pas votre site et que c'est juste un problème qui s'est posé, essayez de mettre à jour vers PHP 5. Cela résout certains de ces problèmes. Cela peut sembler une solution idiote mais a fait l'affaire pour moi.

Erik
la source
1

Un cas exceptionnel pour cet avis se produit si vous définissez le tableau sur null dans la boucle foreach

if (is_array($values))
{
    foreach ($values as $value)
    {
        $values = null;//WARNING!!!
    }
}
Farid Movsumov
la source
1

Que diriez-vous de cette solution:

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

if (in_array($type, $types)) {
    // foreach code comes here
}
julien
la source
1

Avertissement argument non valide fourni pour l' foreach()affichage des tweets. aller à /wp-content/plugins/display-tweets-php. Insérez ensuite ce code sur la ligne numéro 591, il fonctionnera parfaitement.

if (is_array($tweets)) {
    foreach ($tweets as $tweet) 
    {
        ...
    }
}
Saad Khanani
la source
Impressionnant! Cela devrait être la solution acceptée. dans mon cas, j'ai ajouté ceci:if (is_array($_POST['auto'])){ // code }
Jodyshop
0

Il semble également y avoir une relation avec l'environnement:

J'ai eu cette erreur "argument non valide fourni poureach ()" uniquement dans l'environnement de développement, mais pas dans prod (je travaille sur le serveur, pas sur localhost).

Malgré l'erreur, un var_dump a indiqué que le tableau était bien là (dans les deux cas, app et dev).

Le if (is_array($array))tour du a foreach ($array as $subarray)résolu le problème.

Désolé, je ne peux pas expliquer la cause, mais comme il m'a fallu un certain temps pour trouver une solution, j'ai pensé à mieux partager cela comme une observation.

araldh
la source
0

Utilisez la fonction is_array, lorsque vous passerez un tableau à une boucle foreach.

if (is_array($your_variable)) {
  foreach ($your_variable as $item) {
   //your code
}
}
Super modèle
la source
0

Qu'en est-il de la définition d'un tableau vide comme repli s'il get_value()est vide?
Je ne peux pas penser à un moyen le plus court.

$values = get_values() ?: [];

foreach ($values as $value){
  ...
}
Quentin Veron
la source
0

Je vais utiliser une combinaison de empty, issetet is_arraycomme

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

if (!empty($array) && isset($array) && is_array($array) {
    //loop
    foreach ($array as $values) {
        echo $values; 
    }
}
Rotimi
la source
-3

je ferais la même chose qu'Andy mais j'utiliserais la fonction 'vide'.

ainsi:

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
la source
3
-1, s'il $yourArray = 1;essaie d'itérer, et vous obtiendrez l'erreur. empty()n'est pas un test approprié.
Brad Koch
@BradKoch a absolument raison. is_array () est le seul moyen fiable de tester si $ yourArray est un tableau. Voir les autres réponses pour savoir pourquoi is_array () n'est pas suffisant - foreach peut également gérer les itérateurs.
cgeisel