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 an if
Other (please suggest)
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.
How about this one? lot cleaner and all in single line.
foreach ((array) $items as $item) {
// ...
}
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
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
Try this:
//Force array
$dataArr = is_array($dataArr) ? $dataArr : array($dataArr);
foreach ($dataArr as $val) {
echo $val;
}
;)
$values = get_values();
foreach ((array) $values as $value){
...
}
Problem is always null and Casting is in fact the cleaning solution.
foreach ($arr ?: [] as $elem) {
// Do something
}
This doesen't check if it is an array, but skips the loop if the variable is null or an empty array.
Update from PHP 7.0 you should use the null coalescing operator:
foreach ($arr ?? [] as $elem) {
// Do something
}
This would solve the warning mentioned in the comment (here a handy table that compares ?: and ?? outputs).
First of all, every variable must be initialized. Always.
Casting is not an option.
if get_values(); can return different type variable, this value must be checked, of course.
If you're using php7 and you want to handle only undefined errors this is the cleanest IMHO
$array = [1,2,3,4];
foreach ( $array ?? [] as $item ) {
echo $item;
}
As of PHP >= 7.1.0 use is_iterable
https://www.php.net/manual/en/function.is-iterable.php
if (is_iterable($value)) {
foreach ($value as $v) {
...
}
}
More concise extension of #Kris's code
function secure_iterable($var)
{
return is_iterable($var) ? $var : array();
}
foreach (secure_iterable($values) as $value)
{
//do stuff...
}
especially for using inside template code
<?php foreach (secure_iterable($values) as $value): ?>
...
<?php endforeach; ?>
Warning invalid argument supplied for foreach() display tweets.
go to /wp-content/plugins/display-tweets-php.
Then insert this code on line number 591, It will run perfectly.
if (is_array($tweets)) {
foreach ($tweets as $tweet)
{
...
}
}
This warning is happening because the array that you want use is empty, you can use of below condition:
if ($your_array != false){
foreach ($your_array as $value){
echo $value['your_value_name'];
}
}
There seems also to be a relation to the environment:
I had that "invalid argument supplied foreach()" error only in the dev environment, but not in prod (I am working on the server, not localhost).
Despite the error a var_dump indicated that the array was well there (in both cases app and dev).
The if (is_array($array)) around the foreach ($array as $subarray) solved the problem.
Sorry, that I cannot explain the cause, but as it took me a while to figure a solution I thought of better sharing this as an observation.
Exceptional case for this notice occurs if you set array to null inside foreach loop
if (is_array($values))
{
foreach ($values as $value)
{
$values = null;//WARNING!!!
}
}
I'll use a combination of empty, isset and is_array as
$array = ['dog', 'cat', 'lion'];
if (!empty($array) && isset($array) && is_array($array) {
//loop
foreach ($array as $values) {
echo $values;
}
}
Use is_array function, when you will pass array to foreach loop.
if (is_array($your_variable)) {
foreach ($your_variable as $item) {
//your code
}
}
How about this solution:
$type = gettype($your_iteratable);
$types = array(
'array',
'object'
);
if (in_array($type, $types)) {
// foreach code comes here
}
What about defining an empty array as fallback if get_value() is empty?
I can't think of a shortest way.
$values = get_values() ?: [];
foreach ($values as $value){
...
}
<?php
if (isset($_POST['checkbox'])) {
$checkbox = $_POST['checkbox'];
foreach ($checkbox as $key) {
echo $key . '<br>';
}
}
?>
<input type="checkbox" name="checkbox[]" value="red">red <br>
<input type="checkbox" name="checkbox[]" value="green">green <br>
Related
Note: obviously, this can "easily" be done with multiple nested foreach() loops, however I am looking for a way that is less expensive (and, if possible, also less spagetti).
Given a data array of objects like this:
$data (array)
[] (obj)
->id
->name
->a_one
->a_two
->b_three
->b_four
->c_five
[] (obj)
...
and this array of matching prefixes:
$prefixes (array)
[] (str) "a_"
[] (str) "b_"
[] (str) "c_"
how can I find (and unset) all properties of all objects in $data that begin with one of the defined $prefixes, without using multiple foreach() loops?
Solution using foreach():
foreach($data as $datakey => $obj) {
foreach($prefixes as $prefix) {
foreach($obj as $property => $value) {
if(strpos($property, $prefix) === 0) {
unset($data[$datakey]->{$property});
}
}
}
}
Is there a better way?
Construct a regexp pattern that matches all of the prefixes, and check each key against the regexp:
$pattern = "^(" . join("|", $prefixes) . ")";
foreach($data as $datakey => $obj) {
foreach($data as $datakey => $obj) {
foreach($obj as $property => $value) {
if (preg_match($pattern, $property))
...
}
}
This still requires two loops, but there is no redundancy; each object in your data is examined only once. If you were to find a way to loop over all the objects' contents without two PHP loops, it will still take just as many steps. The code in your question looped over each object multiple times (once for each prefix), which is indeed redundant.
Not sure whether you'll prefer this or not but;
$prefixes = ['a_', 'b_'];
$filtered = array_map(function($object) use ($prefixes) {
return unsetPrefixedProperties($object, $prefixes);
}, $data);
function unsetPrefixedProperties($object, $prefixes) {
$regex = '/('. implode('|', $prefixes) .').+/';
foreach ($object as $key => $value) {
if (preg_match($regex, $key)) {
unset($object->{$key});
}
}
return $object;
}
Similar to alexis basically using regex to match, removing the need to loop through your prefixes. Utilising array map to form a new array of filtered objects just because in my personal opinion it seems cleaner and finally extracted the other loop into its own function just to improve readability.
Still not perfect, but it's a start.
Edit: Sandbox Example
$matchedKeys = [];
foreach ($data as $key => $value) {
foreach ($prefixes as $val) {
$length = strlen($val);
if (substr($key, 0, $length) === $val) {
$matchedKeys[] = $key;
}
}
}
The $matchedKeys array will hold the matching keys from object $data.
I'm struggling in finding a way to correctly do this logic.
If (this thing is null)
Skip it
Else
Don't skip it
I tried with if/else and while loops but each one will crash the program. I test something like this:
(inside a foreach)
if($value->getThing() == NULL) {
//HOW TO SKIP???
//I try to 'set' this thing
$value->setThing(0); //BUT IT Doesn't work because it's an associated object...
} else {
$value->getThing();
}
And tried this:
(inside foreach)
while ($value->getThing() != NULL) {
$value->getThing();
//Do Calculation...
}
Both just crash when it gets to the thing thats null. I know why but I can't figure out how to skip the null thing.
and if you can't tell, I'm a newbie. But I'm learning.
EDIT: The thing is null in the db.
Try this code :
foreach($values as $value){
if(!is_null($value->getThing())){
#do calculation
}
}
For "skipping" an entry you can use "continue".
foreach($array as $key => $value){
if($value['foo'] == null){
continue;
}
//Do the calculation
}
..or perhaps:
foreach($array as $key => $value){
if(is_null($value['foo'])){
//Null value treatment
continue;
}
//Do the calculation
}
What you are actually looking for is the NOT IS Operator as I like to call it.
foreach ($things as $thing) {
if (!is_null($thing)) {
// Do the stuff that you wanna do
}
}
The above dummy code teaches that you do not have to use an else. It also shows the is_null() function which checks if something is actually NULL. Furthermore it shows the ! operator that can also be translated to NOT IS.
What !is_null() actually says is: "If the return value of this function, variable and so on is not NULL..."
Good luck.
Try this:
$names = file('name.txt');
// To check the number of lines
echo count($names).'<br>';
foreach($names as $name) {
echo $name.'<br>';
}
I have this function:
function extractAvailable($assoc1, $assoc2){
if($assoc1) extract($assoc1);
else extract($assoc2);
}
What I expect is to call this function later in the global scope, and have my variables available, like so:
$arr1 = [];
$arr2 = ['one'=>1, 'two'=>'SecondItem'];
extractAvailable($arr1, $arr2);
With the call on extractAvailable(), I need to have the varialbes $one and $two available in the current scope. Obviously, I've got something wrongly figured concerning variable scope use here, 'cause it isn't working. When I try to use the variable, what I get instead is Notice: Undefined variable: one.
How do I get this to work?
You could add the new data to the $GLOBALS array which would have the effect of making them available in other scopes.
function extractAvailable($assoc1, $assoc2){
if($assoc1) {
foreach ($assoc1 as $key => $value) {
$GLOBALS[$key] = $value;
}
} else {
foreach ($assoc2 as $key => $value) {
$GLOBALS[$key] = $value;
}
}
}
But I have to wonder why you need to extract anything from a perfectly good array and place the exact same data into scalar variables.
All this does is double your memory requirement for no benefit whatsoever
If you want them to be available in the global scope, you can use variable variables instead of extract, and specify them as global.
function extractAvailable($assoc1, $assoc2){
if($assoc1) {
foreach ($assoc1 as $key => $value) {
global $$key;
$$key = $value;
}
} else {
foreach ($assoc2 as $key => $value) {
global $$key;
$$key = $value;
}
}
}
I've got a PHP object. One of the values within the object is in array. I'm trying to get a foreach to just read the array within the object but it doesn't seem to be working.
PHP:
foreach ($object->ArrayName as $value) {
echo $value;
}
is there any way of doing this?
well, if you have an object but you don't know which property is an array, you need to catch them all and verify if they are an array:
// get each value of the object and call it as property
foreach(get_object_vars($object) as $property) {
// if this property is an array...
if (is_array($property) {
// ... access to every value of that array
foreach($property as $value) {
// $property is your array and $value is every value
// here you can execute what you need
}
}
}
Use
is_array($obj)
to validate whether it's an array or not.
you said your php object contains arrays, hence you need to use 2 foreach loops like this.
<?php
foreach($object->ArrayName as $value) {
foreach($value as $item) {
echo $item;
}
}
?>
if the loop is withing the object wich mean inside the class use $this instead :
foreach ($this->ArrayName as $value) {
echo $value;
}
I need a solution for array_replace_recursive, because my php-version isn't high enough. I want to use this code:
$_GET = array_replace_recursive($_GET, array("__amp__"=>"&"));
easy, isn't it?
On the PHP docs page for array_replace_recursive, someone posted the following source code to use in place of it:
<?php
if (!function_exists('array_replace_recursive'))
{
function array_replace_recursive($array, $array1)
{
function recurse($array, $array1)
{
foreach ($array1 as $key => $value)
{
// create new key in $array, if it is empty or not an array
if (!isset($array[$key]) || (isset($array[$key]) && !is_array($array[$key])))
{
$array[$key] = array();
}
// overwrite the value in the base array
if (is_array($value))
{
$value = recurse($array[$key], $value);
}
$array[$key] = $value;
}
return $array;
}
// handle the arguments, merge one by one
$args = func_get_args();
$array = $args[0];
if (!is_array($array))
{
return $array;
}
for ($i = 1; $i < count($args); $i++)
{
if (is_array($args[$i]))
{
$array = recurse($array, $args[$i]);
}
}
return $array;
}
}
?>
The code above by #Justin is ok, save for 2 things:
Function is not readily available at start of php execution be cause it is wrapped in if(). PHP docu says
When a function is defined in a conditional manner such as the two examples shown. Its definition must be processed prior to being called.
Most importantly; calling the function twice results in fatal error.
PHP docu says
All functions and classes in PHP have the global scope - they can be called outside a function even if they were defined inside and vice versa.
So I just moved the recurse function outside array_replace_recursive function and it worked well. I also removed the if() condition and renamed it to array_replace_recursive_b4php53 for fear of future upgradings