Expect you have following situation:
$myVar = null;
switch($myVar) {
case is_int($myVar):
echo "i am an int";
break;
case null:
echo "i am null";
break;
default:
echo "failure";
}
The result is "i am an int". But I expected "i am null" as a result.
Is this a bug in php (my version: 5.5.11) or natural behaviour?
switch($myVar) means test the value of $myvar against each case expression until a match is found.
is_int($myVar) returns a Boolean false when $myvar is null.
Loose comparison of null against Boolean false (null will be converted to a Boolean false for the comparison) returns a true, so the case is accepted
You can get round this by changing the order of your case statements to test the null case first, but
Moral: It's never a good idea to use expressions in case statements
Why dont you use gettype() to identify the data type of the variable.
$myvar = null;
echo gettype($myvar)."\n";
$myvar = 1;
echo gettype($myvar)."\n";
$myvar = "a";
echo gettype($myvar);
DEMO
Related
I have this code:
$r = do_something($data);
if ($r == 1)
{
echo "it is 1";
}
switch ($r)
{
case "a":
print "a";
break;
case "b":
print "b";
break;
default:
print "default";
}
With this code the output should be:
it is 1
default
but the surprise is that the output is:
it is 1
a
How is this possible?
edit: after some test i see that:
$r === true.
so the new question is: when the var is true how work the switch?
PHP's "type juggling" rules are rather tricky, and occasionally unintuitive and even controversial. There's a great big table in the manual showing what happens when you make comparisons of various sorts, but to explain your particular case:
You are working with three different types: $r is currently true, which is a boolean; the if statement tests against 1, which is an integer; and the switch statement tests against "a" and "b", which are strings.
When you compare a boolean to an integer, PHP first converts the integer to a boolean, using the rule that 0 converts to false, and anything else converts to true. This has the effect in your case of $r == 1 evaluating to true, but $r == 42 would also evaluate to true.
When you compare a boolean to a string, a similar thing happens, but here the string gets converted according to this rule: an empty string is false, anything else is true. So $r == "a" also evaluates to true in your example, which is why that branch of the switch statement is executed.
To get the result you were hoping for, you need to force the type conversion to happen in a different way. There are a few ways to do this, but the simplest in your case is to cast $r to the same type as what you're comparing it against:
(int)$r will give you the integer 1 for a value of true, and 0 for false, so if ( (int)$r == 1 ) will give the same result, but be clearer that $r wasn't actually an integer at that point in the code.
more importantly, (string)$r will give you the string "1", so switch( (string)$r ) won't have to do any "type juggling" to compare against strings like "a" and "b", and you won't get any surprises there.
I think your do_something($data) function returns 1 if it does what it suppose to do. I tried this code and it works. Here it is.
function do_something($data){
return 1;
}
$r= do_something('abc');
if ($r == 1)
{
echo "it is 1<br />";
}
switch ($r)
{
case "a" : print "a"; break;
case "b" : print "b"; break;
default : print "default";
}
OUTPUT:
it is 1
default
having the integer 0 as switch parameter will take the first result "foo":
$data=0; // $data is usually coming from somewhere else, set to 0 here to show the problem
switch ($data) :
case "anything":
echo "foo";
break;
case 0:
echo "zero";
break;
default:
echo "bar";
endswitch;
How do I change this, so the switch will write "zero" as expected?
The switch/case statement uses loose comparison, and, like it or not, 0 == "anything" is true:
Comparison Operators
[...] If you compare a number with a string or the comparison involves
numerical strings, then each string is converted to a number and the
comparison performed numerically. These rules also apply to the switch
statement. [...]
var_dump(0 == "a"); // 0 == 0 -> true
One solution is to change all case statements to string, and do a string comparison:
$data = 0;
switch ((string) $data): ## <- changed this
case "anything":
echo "foo";
break;
case "0": ## <- and this
echo "zero";
break;
default:
echo "bar";
endswitch;
Switch/case statement uses "loose-comparison" (i.e. ==. in this case, 0 also means false and 1 also means true. (http://www.php.net/manual/en/types.comparisons.php#types.comparisions-loose)
To avoid this problem, two solutions:
1) As suggested by #zzlalani, add quotes.
case '0': ...
2) Explicitly cast the switch statement to force a strict comparison (===)
switch((string)($data)) { ... }
Do like this
$data=0;
switch ($data)
{
case 0:
echo "bar";
break;
default:
echo "foo";
break;
}
EDIT :
How do I change this, so the switch will write "zero" as expected?
You need to move your case statement above.
$data=0;
switch ($data) :
case 0: // Moved this case to the begining
echo "zero";
break;
case "anything":
echo "foo";
break;
default:
echo "bar";
endswitch;
This is because switch does not do a "strict-type" checking.
I have this code where $difference=0
switch ($difference)
{
case 0<=$difference && $difference<300:
return "A";
break;
case 300<=$difference && $difference<600:
return "B";
break;
}
I think that switch must return A but it returs B. Whats wrong ???
I'm posting this merely as informative.
Switches can be used with expressions, however this is tricky to do:
<?php
$value = 300;
$other = 1;
switch (true) {
case $value <= 300 && $other:
$var = 'A';
break;
case $value >= 300 && !$other:
$var = 'B';
break;
case $value >= 300 && $other:
$var = 'C';
break;
case $value > 300 && $other:
$var = 'D';
break;
default:
$var = 'FALSE';
break;
}
echo $var;
The above code will display 'C' correctly. A combination of if/else statements is equivalent but I tend to find the switch more readable. Be sure to always include a default case whose value you can rely on (or maybe return from the function, throw an exception, etc).
You can also compare other variables, types, call functions (although not recommended), etc to match the value declared in the switch statement (in this case, true but can be an integer, string or any other base type).
Switches with expressions like you are using will not be evaluated as you might think. Use an if statement instead:
if(0<=$difference && $difference<300)
{
return "A";
}
else if(300<=$difference && $difference<600)
{
return "B";
}
In this case what is happening is $difference being equal to 0 is being interpreted as false. Thus you have switch(false). Each case is evaluated as a whole and there for case 2 actually returns false thus matching the switch statements evaluation and returning B. It also has to do with loose type checking. PHP.net
I have the following php code that gives me an unexpected result:
$foo = NULL;
switch($foo)
{
case 0:
print "What?!";
}
I'd expect the result to be nothing, but it matches case 0. The php manual says that NULL is a non-value, so how can it equal 0?
The switch statement applies loose comparison which means that the following things are treated as equivalent to 0:
false
0
"0"
NULL
"any string"
""
beacuse php is not type strict language
$foo = NULL;
if( isset( $foo ) ) {
switch( $foo ) {
case 0:
print "WTF!!!";
}
}
This can be also written like
$foo = NULL;
switch( true )
{
case ( 0 === $foo ):
print "What?!";
default:
print "Default?!";
}
PHP is doing a type-coerced, weak comparison. You will need to do this instead:
$foo = NULL;
if ($foo === 0)
print "WTF!!!";
You can do what I did - it's lazy but it works.
Before running the switch, I checked if the value is null and, if so, changed it to something known:
IF ($foo==null) {
$foo == 99;
}
switch($foo)
{
case 99:
print "This is NULL"; break;
case 0:
print "What?!";
}
I'm assuming here, but it could be that the switch statement coerces the value of $foo when comparing to 0. To test this hypothesis, why don't you try adding this above the switch statement:
echo $foo == NULL;
This should echo 1 before the curse, if I'm correct...
EDIT: The inaccuracy is with my testing, and it was pointed out to me. Check the comments, if you are interested.
From what I've tested, the top answer is inaccurate.
It seems as though a PHP switch statement sees NULL as a "joker", and applies any case to it.
I tried with different numbers and with a string, and they all fired. Nothing there suggests it should be NULL, not even on PHP loose comparison.
So my suggestion is adding a case NULL: at the start, just as you add default at the end.
$foo = NULL;
switch($foo)
{
case NULL:
print "This is NULL"; break;
case 0:
print "What?!";
}
As of PHP 8, you can use the match expression:
The match expression branches evaluation based on an identity check of a value.
Similarly to a switch statement, a match expression has a subject expression that is compared against multiple alternatives.
Unlike switch, it will evaluate to a value much like ternary expressions. Unlike switch, the comparison is an identity check (===) rather than a weak equality check (==).
Match expressions are available as of PHP 8.0.0.
Per your example:
$foo = null;
$value = match($foo) {
0 => print('What?')
};
Will output:
Fatal error: Uncaught UnhandledMatchError: Unhandled match value of type null
So you can add a try/catch and handle it accordingly or add a default "catch-all":
$compare = null;
$value = match($compare) {
0 => print('What?'),
default => print('Default!')
};
<?php
$var = 0;
switch($var) {
case "a":
echo "I think var is a";
break;
case "b":
echo "I think var is b";
break;
case "c":
echo "I think var is c";
break;
default:
echo "I know var is $var";
break;
}
?>
Maybe someone else will find this fascinating and have an answer. If you run this, it outputs I think the var is a when clearly it's 0. Now, I'm most certain this has something to do with the fact that we're using strings in our switch statement but the variable we're checking is an integer. Does anyone know why PHP behaves this way? It's nothing too major, but it did give me a bit of a headache today.
Thanks folks!
If you compare an integer with a string, the string is converted to a number. So effectively your snippet is equivalent to:
$var = 0;
switch($var) {
case 0: // "a" gets converted to 0.
echo "I think var is a";
break;
case 0: // "b" gets converted to 0.
echo "I think var is b";
break;
case 0: // "c" gets converted to 0.
echo "I think var is c";
break;
default:
echo "I know var is $var";
break;
}
Which will produce I think var is a as output as the first case body gets executed. Even though there are 3 candidates, the first one is selected because it appears at the top.
In PHP, when you compare a string and an integer, whether it be in a switch statement or using the regular comparison operators, the string is converted to an integer (unless you're using the === operator).
And when converting a string to an integer, a string that doesn't start with either a digit or a sign is always converted to 0.
See the documentation here and here.