in PHP (and even other languages but in the present case this is more for PHP), i often end up sometimes having to code long conditions such as the example below:
i have a code with many conditions and i want to display a different result based on certain conditions.
if something is RED and the other thing is RED, then print X,
if something is RED but the other thing is BLACK, then print Y
if something RED and the other thing is RED but a third thing is blue, then print X
& so on
is there a way to properly handle this by using some kind of data structure/configuration array/matrix/whatever ? that is, storing these "conditions" and "results" properly in some kind of configuration array or other ?
instead of having to code nested conditions that can be tricky to support afterwards
like this very small example but in a much bigger scale
if (preg_match(/something/, $string) {
$result = 'GREEN';
} elseif (preg_match(/something/, $string) {
$result = 'RED';
} else {
if (something else) {
$result = 'GREEN';
} else {
if (something OR something) {
$result = 'AMBER';
} else {
$result = 'GREEN';
}
}
}
or is it the only way of handling this ?
maybe with a single
if (something and something or something) {
} elseif (something and something and something) {
} elseif (something and something or something and something) {
} etc
thank you for your help
i'm coding an app that should display a different "status" for a certain data depending on many different data (other attributes of this data), and i'd like to avoid having unreadable code
You can use nested arrays:
$conditions = [
'/something1/' => [
'/something2/' => "X"
],
'thing3' => [
'/thing3/' => 'Y',
'/thing4/' => 'Z'
]
];
$matched = false;
foreach ($conditions as $key1 => $val1) {
if (preg_match($key1, $string)) {
if (is_array($val1)) {
foreach ($val1 as $key2 => $val2) {
if (preg_match($key2, $string)) {
$result = $val2;
$matched = true;
break;
}
}
} else {
$result = $val1;
$matched = true;
}
}
if ($matched) {
break;
}
}
if (!$matched) {
$result = 'default';
}
To allow arbitrary levels of nesting you could turn this into a recursive function.
For the purposes of the answer below I'm assuming you're coding OO PHP.
One variable
When I have one variable that determines the outcome, if the variable is boolean or close to being boolean (meaning it can only either be one or two different values), is to use an if() statement like you have. If the variable can be a variety of (known) values, like your colours example, I use a switch() function.
Two or more variables
If I have two variables that determine the outcome, for instance colour and size, I first use a switch(), then for each switch, I use a method that contains another switch().
It may be more verbose, but it is so much easier to keep track of in the long run. Below is a simplified example of the logic.
switch ($colour) {
case 'red':
red_handler($size);
break;
case 'green':
green_handler($size);
break;
case 'blue':
blue_handler($size);
break;
}
/** We already know the first variable value is red */
function red_handler($size)
{
switch ($size) {
case 'small':
echo "my fruit is a cherry";
break;
case 'medium':
echo "my fruit is an apple";
break;
case 'large':
echo "my fruit is a watermelon";
break;
}
}
In a similar vein to #dearsina i'd tend to separate it out into functions for this kind of thing, for example if you know there are lots of cases where it could return the same value, e.g.:
if(isGreen($string)) return 'GREEN';
else if (isRed($string)) return 'RED';
function isGreen($string) {
if(cond1)return true;
if(cond2 && cond3)return true;
if(cond4 || cond 5)return true;
return false;
}
function isRed($string) {
if(cond6)return true;
if(cond1 && cond7)return true;
if(cond2 || cond 8)return true;
return false;
}
we do sometimes use the style you've suggested...
if (something and something or something) {
} else if (something and something and something) {
but you can quickly end up back in the same problems of readability and maintenance issues
Related
I am in the making of some code that needs to check if a users login details are correct, and I therefore need a lot of if-statements inside each other. If any of the conditions in the if-statements are not true, they should alle return the same value. Is there an easy way of doing this, instead of writing the same multiple times? I have made an example below to visualize my problem. As you can see here I write " else { return false; }" multiple time, and this is what I am wondering if you are able to do more efficiently. Maybe so I only have to write "or else return false" once.
//some code
if (/*some condition*/) {
//some code
if (/*some new condition*/) {
//some code
if (/*some new condition*/) {
//some code
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
I am having a hard time finding a good way to explain my problem, so if you have a more elegant way of explaining it, do not hesitate to edit my post. I am also not quite sure that the title is as good as it could be, so if you have any ideas to an alternativ please say so :)
Lets say you have something like that (I added No):
if ( condition1 ) {
//some code 1
if ( condition2 ) {
//some code 2
if ( condition3 ) {
//some code 3
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
Since each time a condition is false, you exit the function returning false, you can directly test if the condition is false using a negation (if the negated condition is true):
if ( !condition1 ) {
return false;
}
//some code 1
if ( !condition2 ) {
return false;
}
//some code 2
if ( !condition3 ) {
return false;
}
//some code 3
This doesn't reduce the number of if statements, but you avoid many nesting levels and the else statements.
You can also try the switch statement. For many situations it will produce cleaner code.
<?php
if ($i == 0) {
echo "i equals 0";
} elseif ($i == 1) {
echo "i equals 1";
} elseif ($i == 2) {
echo "i equals 2";
}
switch ($i) {
case 0:
echo "i equals 0";
break;
case 1:
echo "i equals 1";
break;
case 2:
echo "i equals 2";
break;
}
?>
The switch statement is also compatible with using strings:
<?php
switch ($i) {
case "apple":
echo "i is apple";
break;
case "bar":
echo "i is bar";
break;
case "cake":
echo "i is cake";
break;
}
?>
Good luck! :)
I have a large switch statement in my code, and I want it to do something like this:
// assign $foo
switch ($foo) {
case 1:
case 2:
// do X, then break if 1, do Y and break if 2
case 3:
case 4:
case 5:
// do A & B, break if 3, do C if 4 or 5, do D if 5 and then break
}
Such "groups" are prevalent throughout the switch statement and currently I just repeat the logic, keeping each case separate.
Am I wrong in assuming that this could be restructured into something that's objectively "better"?
Edit: I excluded my original code snippet from the question, since it had deeply flawed logic and didn't conform to the basic concepts of using a switch, replacing it with pseudocode that resembles the desired result.
As commented, that switch doesn't actually work as you think.
You're simply looking for:
if (in_array($foo, [1, 2])) {
...
if ($foo == 2) {
...
}
}
Alternatively:
switch ($foo) {
case 1:
case 2:
...
if ($foo == 2) {
...
}
break;
}
TLDR;
For simple "is it this", use a switch, if you need logical checks, use an if
The answer;
What you are asking is quite subjective, while using a switch for something simple is good, i.e;
<?php
$case = getCaseFrom("X"); // Let's say this = "a"
switch ($case)
{
case "a" : {
$thing = "a";
break;
}
case "b" : {
$thing = "b";
break;
}
case "c" : {
$thing = "c";
break;
}
default : {
$thing = "d";
}
}
?>
The same could be achieved by using;
<?php
$case = getCaseFrom("x");
$thing = $case;
// And even shorter;
$thing = getCaseFrom("x");
?>
Whereas, if you needed some logic to this...
<?php
$operator = getOperatorFrom("X"); // In this case, add
$num1 = 10;
$num2 = 2;
switch ($operator)
{
case "add" : {
$res = num1 + num2;
break;
}
case "subtract" : {
$res = num1 - num2;
break;
}
case "multiply" : {
$res = num1 * num2;
break;
}
case "divide" : {
$res = num1 / num2;
break;
}
}
?>
The alternative;
And of course, all of the above switches case be done using if else clauses, but a switch (IMO) is a neater, and more readable approach depending on what your certain criteria are (see the drawbacks).
The easiest way to look at it is;
If you need to check if something matches a value, use an if, if it is an enumerated set of values (let's say 1 to 4), a switch is subjectively better
The drawbacks;
A switch also doesn't let you have multiple "and" checks in the same statement, for example;
<?php
$case = getCaseFrom("X"); // In this case, a (does not have "b")
switch ($case)
{
case "a" :
case "b" : {
// This checks for a or b, not both, despite this not containing "b"
// this will still be accessed
break;
}
case "c" : {
// This will not be used as the break stops
// it from falling through to this clause
break;
}
}
if (stristr("a", $case) && stristr("b", $case))
{
// This checks to see if case contains a AND b
// therefore, this will not be used, as this
// does not have both a and b in $case
}
else if (stristr("a", $case) || stristr("b", $case))
{
// This checks to see if case contains a OR b
// therefore, this will be used, as this
// is checking that $case has "a" or "b" in it
// This is the same as the switch in this instance as it uses "or"
}
The noteworthy;
To be aware of the following also would be useful;
Inside a switches case, you cannot use login, for example;
case getThing("a") :
would cause an error
NB: Of course, when using the case statements, you don't need the curly braces added, they are mainly for code folding and ease of reading
PHP novice here. I searched for this, but i'm sure i'm not using the right syntax regarding my issue. Apologies then if this is a duplicate:
I have these 3 variables:
$param = get_sub_field('custom_parameter');
$compare = get_sub_field('parameter_compare');
$param_val = get_sub_field('parameter_value');
each one would return this:
$param is 'my_parameter'
$compare is either '==', '<=', or '=>'
$param_val is something like '5' or any value that the user sets
What i have is an editing interface where the user can set their parameter name, set the compare and then add the value. To that they can also add an action that occurs if the parameter matches. I'm using this in conjunction with $_GET.
What i'd like to do is insert each variable from above into my if statement so the comparison is created by the user. However, it keeps giving me an error when i try to do this:
if($_GET[$param] $compare $param_val) {
// do something
}
The error i get is:
Parse error: syntax error, unexpected T_VARIABLE
This of course works just fine:
if($_GET[$param] == $param_val) {
// do something
}
Hopefully i've explained this well enough and any help is greatly appreciated.
Update: Thank you for answering this for me and jumping on it so quickly. Learned a ton here!!
function comparer($param, $compare, $param_val)
{
switch ($compare){
case '==': return $param == $param_val;
case '!=': return $param != $param_val;
case '<=': return $param <= $param_val;
case '>=': return $param >= $param_val;
case '<': return $param < $param_val;
case '>': return $param > $param_val;
default: return FALSE;
}
}
/* ... */
if (comparer($param, $compare, $param_val)){
// true
}
Very simple method to get you going. I would, at all costs, resist the temptation to use eval, unless you want to invest a lot of time in sanitizing those three parameters.
Oh, and an example
I think I would use a switch statement to avoid any scary eval code.
Such as:
switch($compare) {
case '==':
if($_GET[$param] == $param_val) {
// do something
}
break;
case '<=':
if($_GET[$param] <= $param_val) {
// do something
}
break;
case '>=':
if($_GET[$param] <= $param_val) {
// do something
}
break;
}
Look at eval()
http://php.net/manual/en/function.eval.php
With this you can parse a code you format in a string.
The best way would be to make a function for this. Have that function uses a switch to determine the operator, then return the comparison.
function compare($a, $b, $operator){
$ret = NULL;
switch($operator){
case '==':
$ret = $a == $b;
break;
case '>=':
$ret = $a >= $b;
break;
case '<=':
$ret = $a <= $b;
break;
}
return $ret
}
Then just simply call it:
if(compare($_GET[$param], $param_val, $compare)){
// do something
}
Hmm interesting, I think I'd approach it like this (untested):
function comparison($param, $compare, $param_val) {
if ($compare == '==') {
if ($param == $param_val) {
return true
}
}
if ($compare == '<=') {
if ($param <= $param_val) {
return true
}
}
if ($compare == '>=') {
if ($param >= $param_val) {
return true
}
}
}
Not very efficient or DRY, could probably use a switch as that would probably be better but this was the first thing to pop into my head.
Usage
if (comparison($param, $compare, $param_val)) {
echo 'it's true';
} else {
echo 'it's false';
}
Edit
As per usual, I have been beaten to the punch by better code :)
P.S. I'm not sure why you have $param and then use $_GET[$param] so I've just used $param in my answer.
I've got 3 variables:
$left_side = "'Username'";
$equation = "==";
$right_side = "'Username'";
I want to test these variables as it was an if statement like so:
if($left_side $equation $right_side) {
// do something
} else {
// do something else
}
I know this works:
if(eval("return ".$left_side." ".$equation." ".$right_side.";")) {
// do something
} else {
// do something else
}
I always tought it's 'not good' to use eval. Especially when you try to run user input.
Is there an other way to do this? I tried to google it, but it's not my friend to day ;)
You may do something like this:
function testValues($val1, $equation, $val2) {
$res = false;
switch($equation) {
case "==":
$res = $val1 == $val2;
break;
case ">":
$res = $val1 > $val2;
break;
//....
default:
throw new Exception("Unknown operator");
}
return $res;
}
and than use it like:
if(testValues($left_side,$equation,$right_side)) {
//do something
} else {
//do something
}
eval is evil. And no, there's no other (easy) solution, but maybe this one helps:
if ($equation == "==") {
if ($left_side == $right_side) {
// ... your code goes here.
} else {
// Do some other stuff.
}
}
You could use switch:
$left_side = "'Username'";
$equation = "doublequal";
$right_side = "'Username'";
switch($equation){
case 'doublequal':
if ($left_side == $right_side) {
// code
}
break;
//......
}
You should never use eval() especially with user input.
eval() is evil, but call_user_func() is evil too and every framework uses this function in one place or another.
Tools aren't evil. They are just tools. Evil is the way that we use them.
As Uncle Ben said: Great power involves great responsibility :)
I find this trick
http://gonzalo123.com/2012/03/12/how-to-use-eval-without-using-eval-in-php/
the idea is create a temporary file with the PHP source code, include this file with the standard PHP’s include functions and destroy the temporary file.
Let's take a look at the following code:
if ($a == 1) {
echo "this is stage 1";
}
else if ($a == 2) {
echo "this is stage 2";
}
else if ($a == 3) {
$a = 1;
// at this point I want something that restarts the if-else construct so
// that the output will be "this is stage 1"
}
I'm working on an if else construct at the moment and let's say that I have three stages and the if-else construct checks which stage I'm in.
Now it happens that some activities in stage 3 lead to a jump back to stage 1. Now I've already passed the code for stage one, which is why I want to somehow restart the if-else construct. Is there a way to do that? And even more important: Is there a better way to do what I want? Because my idea doesn't seem to be good practice.
You're right, it's bad practice.
You're asking for goto.
Example:
<?php
goto a;
echo 'Foo';
a:
echo 'Bar';
The above would never output 'Foo'
It's difficult to suggest the better method without seeing exactly what you're trying to do, but consider a switch.
switch ($a) {
case 3:
// Execute 3 stuff
// No break so it'll continue to 1
case 1:
// Execute 1 stuff
break // Don't go any further
case 2:
// 2 stuff
break;
}
That's probably not what you want either.
You may just want to abstract the code into functions and call them multiple times if necessary.
You can put an endless loop around your if and break out if you're done
while (1) {
if ($a == 1) {
echo "this is stage 1";
break;
}
else if ($a == 2) {
echo "this is stage 2";
break;
}
else if ($a == 3) {
$a = 1;
}
else {
break;
}
}
Maybe you want to look at Wikipedia - Finite-state machine and this question PHP state machine framework
The short answer is yes, there is a way, but the better answer is yes to your second question as well.
Put, at very least, the code that can get called from multiple locations in a function. For example,
function stageOneCode() {
//do stuff;
}
etc.. I would recommend a function for each stage, but it's hard to make recommendations without actually seeing what's being executed in the stages.
In any event, at the end of your stage three function, simply call your stage one function.
A recursive function is helpful for this (but maybe overkill if it will always revert back to 1)
function echo_stage($stage) {
if ($a == 1) {
return "this is stage 1";
}
else if ($a == 2) {
return "this is stage 2";
}
return echo_stage(1);
}
echo echo_stage(5);
Or:
switch ($number)
{
case 2 :
echo "this is stage 2";
break;
case 1:
default:
echo "this is stage 1"
}
use switch(). you can have a "default" case as well as specific cases.
A loop is what you are searching for:
// initialize $a
$a = 1;
// the while loop will return endless
while (true);
// if you want to break for any reason use the
// break statement:
// if ($whatever) {
// break;
// }
if ($a == 1) {
echo "this is stage 1";
}
else if ($a == 2) {
echo "this is stage 2";
}
else if ($a == 3) {
$a = 1;
// continue will go back to the head
// of the loop (step 1) early:
continue;
}
// don't forget to increment $a in every loop
$a++;
}