Invalid argument supplied for foreach()

304

A menudo se me ocurre manejar datos que pueden ser una matriz o una variable nula y alimentar algunos foreachcon estos datos.

$values = get_values();

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

Cuando alimenta a un foreach con datos que no son una matriz, recibe una advertencia:

Advertencia: argumento no válido proporcionado para foreach () en [...]

Suponiendo que no es posible refactorizar la get_values()función para que siempre devuelva una matriz (compatibilidad con versiones anteriores, código fuente no disponible, cualquier otra razón), me pregunto cuál es la forma más limpia y eficiente de evitar estas advertencias:

  • Fundición $valuesa matriz
  • Inicializando $valuesa la matriz
  • Envolviendo el foreachcon unif
  • Otro (por favor sugiera)
Roberto Aloi
fuente
Es muy posible $valuesque no sea una matriz.
Bhargav Nanekalva

Respuestas:

509

Personalmente, creo que es la más limpia, ¡no estoy seguro de si es la más eficiente!

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

La razón de mi preferencia es que no asigna una matriz vacía cuando no tienes nada para empezar de todos modos.

Andy Shellam
fuente
44
O el recuento de uso () para averiguar si el array no está vacío
Kemo
76
@ Kemo: count()no es confiable. Si pasa count()nulo, devuelve 0. Si pasa un argumento no nulo y sin matriz, devuelve 1. Por lo tanto, es imposible de usar count()para determinar si la variable es una matriz cuando la variable podría ser una matriz vacía, o Una matriz que contiene 1 elemento.
Andy Shellam
12
Tenga en cuenta que algunos objetos son iterables, y esta respuesta no los tiene en cuenta.
Brad Koch
32
Debe ser if (is_array($values) || $values instanceof Traversable).
Bob Stein
3
Sin embargo, es una pena que no haya dicho que no era seguro el más eficiente: D
Gui Prá
116

¿Que tal este? Lote más limpio y todo en una sola línea.

foreach ((array) $items as $item) {
 // ...
 }
Ajith R Nair
fuente
77
Esto es lo único que funcionó para mí. Por alguna razón, PHP no creía que la matriz multidimensional que construí fuera en realidad una matriz de matrices.
Justin
1
Lo mismo aquí, esta es una solución muy buena para una matriz que contiene matrices o valores nulos. Simplemente agregue una prueba en el bucle foreach para continuar si los datos son nulos.
Lizardx
Resuelto mi problema ¡Gracias!
Hitesh
1
¡Código brillante, se omitió el if, else para el valor de matriz y ninguno de matriz usando $ _POST con casilla de verificación!
Yann Chabot
2
NOTA: Si bien se ve hermosa y resuelve la advertencia foreach no válida, este método devolverá una advertencia de variable indefinida si la variable no está configurada de ninguna manera. Uso isset()o is_array()ambos, completamente según su escenario, etc.
James
42

Usualmente uso una construcción similar a esta:

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

Tenga en cuenta que esta versión en particular no se prueba, se escribe directamente en SO desde la memoria.

Editar: agregada verificación Trazable

Kris
fuente
3
La mejor respuesta. Excepto que creo que realmente deberías comprobar si $var instanceof Traversable. Ver aquí . Porque, por ejemplo, puede predecir un elemento SimpleXMLElement , pero no es una instancia de Iterator o IteratorAggregate.
Bob Stein
2
Es posible que pueda eliminar las otras dos clases, @Kris. Ambos extienden Traversable ahora y parecen haber nacido de esa manera en 5.0.0. Aunque estoy sintiendo una pequeña duda sobre si la instancia de siempre se aplica a las extensiones.
Bob Stein
1
@ BobStein-VisiBone: sí (excepto que son interfaces, no clases) Sin embargo; Puse Traversable antes que esos, ni Iterator ni IteratorAggregate necesitarían verificar (de esta manera no retrasarán la ejecución). Los dejé para mantener la respuesta lo más cerca posible de la respuesta original que di y para que fuera obvia / legible.
Kris
2
Creo que sería justo agregar is_object($var)re. php.net/manual/en/language.oop5.iterations.php
Mark Fox
1
@ MarkFox: Siéntete libre, sin embargo, lo omití intencionalmente; Nunca he visto un uso que no sirviera mejor implementando Iteratoro IteratorAggregate, pero eso es, por supuesto, solo mi opinión y, por lo tanto, subjetiva (nunca uso campos públicos).
Kris
15

No dependa de la conversión como una solución , aunque otros sugieran esto como una opción válida para evitar un error, podría causar otra.

Tenga en cuenta: si espera que se devuelva una forma específica de matriz, esto podría fallarle. Se requieren más controles para eso.

Por ejemplo, lanzar un booleano a una matriz (array)bool, NO dará como resultado una matriz vacía, sino una matriz con un elemento que contenga el valor booleano como int: [0=>0]o [0=>1].

Escribí una prueba rápida para presentar este problema . (Aquí hay una prueba de respaldo en caso de que falle la primera URL de prueba).

Se incluyen las pruebas de: null, false, true, un class, una arrayy undefined.


Siempre pruebe su entrada antes de usarla en foreach. Sugerencias:

  1. Comprobación rápida de tipos :$array = is_array($var) or is_object($var) ? $var : [] ;
  2. Escriba matrices de sugerencias en métodos antes de usar un foreach y especificar tipos de retorno
  3. Envolviendo foreach dentro si
  4. Usando try{}catch(){}bloques
  5. Diseño de código / prueba adecuados antes de los lanzamientos de producción
  6. Para probar una matriz con la forma adecuada, puede usar array_key_existsuna clave específica o probar la profundidad de una matriz (¡cuando es una!) .
  7. Siempre extraiga sus métodos auxiliares en el espacio de nombres global para reducir el código duplicado
AARTT
fuente
8

Prueba esto:

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

;)

GigolNet Guigolachvili
fuente
1
Esto no funcionará bien con matrices asociativas. El método is_array es en general mejor ... y más fácil ...
AO_
4
$values = get_values();

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

El problema siempre es nulo y, de hecho, la fundición es la solución de limpieza.

boctulus
fuente
3

En primer lugar, cada variable debe ser inicializada. Siempre.
El casting no es una opción.
si get_values ​​(); puede devolver una variable de tipo diferente, este valor debe ser verificado, por supuesto.

Su sentido común
fuente
La conversión es una opción: si inicializa una matriz usando $array = (array)null;, obtendrá una matriz vacía. Por supuesto, es un desperdicio de asignación de memoria ;-)
Andy Shellam
2
+1: leer desde un punto de vista sentimental, no me importa si el lenguaje puede prescindir, las variables DEBEN declararse y los resultados poco confiables DEBEN verificarse. Se requiere para mantener a los desarrolladores cuerdos y el registro de errores es corto.
Kris
3

Extensión más concisa del código de @ Kris

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

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

especialmente para usar código interno de plantilla

<?php foreach (secure_iterable($values) as $value): ?>
    ...
<?php endforeach; ?>
HongKilDong
fuente
2
No quiere decir return is_iterable($var) ? $var : array($var);?
SQB
3

Si está usando php7 y desea manejar solo errores indefinidos, esta es la IMHO más limpia

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

Esto no comprueba si se trata de una matriz, pero omite el ciclo si la variable es nula o una matriz vacía.

T30
fuente
1

No estoy seguro de si este es el caso, pero este problema parece ocurrir varias veces al migrar sitios de WordPress o migrar sitios dinámicos en general. Si este es el caso, asegúrese de que el alojamiento al que está migrando use la misma versión de PHP que usa su sitio anterior.

Si no está migrando su sitio y esto es solo un problema que ha surgido, intente actualizar a PHP 5. Esto soluciona algunos de estos problemas. Puede parecer una solución tonta, pero me funcionó.

Erik
fuente
1

Se produce un caso excepcional para este aviso si configura la matriz como nula dentro del bucle foreach

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

¿Qué tal esta solución?

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

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

Argumento de advertencia no válido proporcionado para foreach()mostrar tweets. ir a /wp-content/plugins/display-tweets-php. Luego inserte este código en la línea número 591, se ejecutará perfectamente.

if (is_array($tweets)) {
    foreach ($tweets as $tweet) 
    {
        ...
    }
}
Saad Khanani
fuente
¡Increíble! Esa debería ser la solución aceptada. en mi caso he agregado esto:if (is_array($_POST['auto'])){ // code }
Jodyshop
0

También parece haber una relación con el medio ambiente:

Tuve ese error de "argumento no válido proporcionado foreach ()" solo en el entorno de desarrollo, pero no en prod (estoy trabajando en el servidor, no localhost).

A pesar del error, var_dump indicó que la matriz estaba bien allí (en ambos casos, app y dev).

El if (is_array($array))alrededor foreach ($array as $subarray)resolvió el problema.

Lo siento, no puedo explicar la causa, pero como me llevó un tiempo encontrar una solución, pensé en compartir esto mejor como una observación.

araldh
fuente
0

Use la función is_array, cuando pasará la matriz a foreach loop.

if (is_array($your_variable)) {
  foreach ($your_variable as $item) {
   //your code
}
}
Súper modelo
fuente
0

¿Qué pasa con la definición de una matriz vacía como alternativa si get_value()está vacía?
No puedo pensar en un camino más corto.

$values = get_values() ?: [];

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

Usaré una combinación de empty, issety is_arraycomo

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

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

Haría lo mismo que Andy, pero usaría la función 'vacía'.

al igual que:

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
fuente
3
-1, si $yourArray = 1;intenta iterar, y obtendrás el error. empty()No es una prueba adecuada.
Brad Koch
@BradKoch tiene toda la razón. is_array () es la única forma confiable de probar si $ yourArray es una matriz. Consulte otras respuestas para obtener detalles sobre por qué is_array () no es suficiente: foreach también puede manejar iteradores.
cgeisel