Php web making error message when posting a comment
It often happens to me to handle data that can be either an array or a null variable and to feed some foreach
with these data.
$values = get_values();
foreach ($values as $value){
...
}
When you feed a foreach with data that are not an array, you get a warning:
Warning: Invalid argument supplied for foreach() in [...]
Assuming it's not possible to refactor the get_values()
function to always return an array (backward compatibility, not available source code, whatever other reason), I'm wondering which is the cleanest and most efficient way to avoid these warnings:
- Casting
$values
to array - Initializing
$values
to array - Wrapping the
foreach
with anif
- Other (please suggest)
Solution 1:
Personally I find this to be the most clean - not sure if it's the most efficient, mind!
if (is_array($values) || is_object($values))
{
foreach ($values as $value)
{
...
}
}
The reason for my preference is it doesn't allocate an empty array when you've got nothing to begin with anyway.
Solution 2:
How about this one? lot cleaner and all in single line.
foreach ((array) $items as $item) {
// ...
}
Solution 3:
I usually use a construct similar to this:
/**
* 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...
}
}
Note that this particular version is not tested, its typed directly into SO from memory.
Edit: added Traversable check
Solution 4:
Please do not depend on casting as a solution, even though others are suggesting this as a valid option to prevent an error, it might cause another one.
Be aware: If you expect a specific form of array to be returned, this might fail you. More checks are required for that.
E.g. casting a boolean to an array
(array)bool
, will NOT result in an empty array, but an array with one element containing the boolean value as an int:[0=>0]
or[0=>1]
.
I wrote a quick test to present this problem. (Here is a backup Test in case the first test url fails.)
Included are tests for: null
, false
, true
, a class
, an array
and undefined
.
Always test your input before using it in foreach. Suggestions:
-
Quick type checking:
$array = is_array($var) or is_object($var) ? $var : [] ;
- Type hinting arrays in methods before using a foreach and specifying return types
- Wrapping foreach within if
- Using
try{}catch(){}
blocks - Designing proper code / testing before production releases
- To test an array against proper form you could use
array_key_exists
on a specific key, or test the depth of an array (when it is one !). - Always extract your helper methods into the global namespace in a way to reduce duplicate code