I'm trying to simplify a piece of PHP code looking like this :
public function gather($parameters, $infos)
{
$gathered = [];
$number = false;
foreach ($infos as $info) {
foreach ($parameters as $parameter) {
if (strlen($parameter['number'])) {
$this->mark($parameter['number'], $info, $parameter, $gathered); // $gathered is passed by reference
$number = true;
continue;
}
if (strlen($parameter['default'])) {
$this->mark($parameter['default'], $info, $parameter, $gathered); // $gathered is passed by reference
continue;
}
if ($config['smth']) {
doSomething(...);
}
}
}
if (number > 0) {
doSomething(...);
}
return $gathered;
}
Here are the problems I have :
These nested foreaches are really ugly and I'd love to the second one in the other function. But it uses variables that are created and used outside. I already used references with $gathered even if it's not really good.
The conditions of all ifs are different and are using different variables, do you know any way to generalize a case like this?
I am ok to rewrite big parts of the functions, so I'd be happy to hear other design ideas too.
[EDIT] The mark functions add elements to $gathered with some conditions and transformations.
[EDIT] Added more details.
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>
I'm having the toughest time figuring out this problem and I can't seem to find the answer.
Here's what I'm trying to do: I have different nav menus on my website depending on the section. I've already pre-built the different variations and on each page declare $linkbox_array, which is the array of links for my nav menu on that page. On certain pages I display horizontally and on others vertically. When vertical, I need a disclaimer to be added to the bottom of the stacked link boxes.
So my function is trying to say this: if the $linkbox_array is $general_linkboxes and the $bodyClass is "withSidebar", then echo out a disclaimer after array item 2. Otherwise, just echo out the array items.
So this is what I've written (forgive me if it sucks, I'm new to this):
function display_linkboxes($array) {
if ($linkbox_array == $general_linkboxes && $bodyClass = "withSidebar") {
foreach ($array as $linkbox) {
if ($linkbox == $array[2]) {
echo $linkbox;
global $general_disclaimer;
echo $general_disclaimer;
} else {
echo $linkbox;
}
}
} else {
foreach ($array as $linkbox) {
echo $linkbox;
}
}
}
The problem is that it keeps spitting out the $general_disclaimer even when the two conditions aren't true. I tried to deconstruct the function and figure out where I was going wrong and I realized my if statement always evaluates to true even if I put in jibberish. For example:
function display_linkboxes($array) {
if ($linkbox_array == $askjfdalfjk) {
foreach ($array as $linkbox) {
echo $linkbox;
}
}
}
This evaluates to true and displays the links even though $askjfdalfjk doesn't exist. Then I have the opposite problem below. This won't display the links even though the if statement should evaluate to true:
function display_linkboxes($array) {
if ($bodyClass == "withSidebar") {
foreach ($array as $linkbox) {
echo $linkbox;
}
}
}
What in the world am I doing wrong?!?! Any help is greatly appreciated!
In your code:
function display_linkboxes($array) {
if ($linkbox_array == $general_linkboxes && $bodyClass = "withSidebar") {
...
$linkbox_array, $general_linkboxes, and $bodyClass are not in scope for this function, which means they are both equal to each other in that neither of them exists. You need to either pass them as variables to the function (recommended), or change your code to the following:
function display_linkboxes($array) {
global $linkbox_array, $general_linkboxes, $bodyClass;
if ($linkbox_array == $general_linkboxes && $bodyClass == "withSidebar") {
...
I've edited this answer to include the = -> == fix on $bodyClass
Probably you set $linkbox_array outside of your function, then your function doesn't knoew it, unless you ...
function display_linkboxes($array) {
global $linkbox_array; // <------ now the variable exists within the function.
if ($linkbox_array == $askjfdalfjk) {
foreach ($array as $linkbox) {
echo $linkbox;
}
}
}
Same with $bodyClass
Your if conditional just needs a bit of refactoring:
if(($linkbox_array == $general_linkboxes) && ($bodyClass == "withSidebar")) {}
A single '=' is an assignment operation and will always evaluate to true.
A == is the conditional equals operation you were looking for.
Furthermore if you are checking for array equality and order matters to you then you should use the '===' operator which checks not just for the same elements but also the same order
Lastly you have a scoping issue - if your linkbox array is evaluating as equal to a null variable then it hasn't been defined. You can use the print_r() operation to check this. If the array is defined outside the function then you should pass it to the function as a parameter.
Hi I have this code here:
public function length($args) {
if (isset($this->length)) {
foreach ($this->length as $k => $v) {
if (strlen($args[$k])>=$v[0] && strlen($args[$k])<$v[1]) {
return true;
} else {
array_push($this->form_errors, $v[2]);
return false;
}
}
} else {
return true;
}
}
I'm not sure why but its not working as expected. The foreach loop only loops through one of the $args[$k] even though there are 2 of them. Anyone have any idea whats going on? I'm writing this question in a hurry so if I need to explain anything else, please let me know.
Thanks!
Your two return statements in both sides of the if() clause terminate the function on the first iteration of the loop. Either the strlen stuff is true and you return true, or they're false and you return false. regardless, the loop ends.
If you simply want to jump to the next iteration, then use continue instead, which'd allow the foreach to proceed.
I have a piece of code where a variable can either be an array or just a string.
if(!is_array($relation['display_name']))
{
// do something with $relation['display_name']
}
else
{
foreach($relation['display_name'] as $display_name)
{
// do the same with $display_name
}
}
This of course works - but it's not very nice. And I would have to do it a lot of times. Is there a better way of doing this?
You can do it like this:
foreach((array)$relation['display_name'] as $display_name) {
// do something with $display_name
}
You could do something like this:
if(!is_array($relation['display_name'])) {
$relation['display_name'] = array($relation['display_name']);
}
# do your foreach here
I would advice you look into fixing the source of the problem.
Why is $relation['display_name'] sometimes an array and sometimes not?
Problem fixing is better then patching the outcome.
That being said, I would create the following:
function transformToArray($mValue) {
return (is_array($mValue)) ? $mValue : array($mValue);
}
You could write it much shorter with the ternary operator:
foreach ((is_array($a) ? $a : array($a)) as $val) {
...
}
I think this is not a bad way of handling this issue. Most of the PHP code I have seen handles stuff like this similar.
If using >= PHP 5.3 you could try something like this. It will run the code on the element if it is singular or implicitly over all array members if an array.
function call($element, $func) {
if (is_array($element)) {
foreach($element as $value) {
$func($value);
}
} else {
$func($element);
}
}
call($relation['display_name'], function($display_name) {
// Anything you wanna.
});
CodePad.