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!')
};
Related
I'm not using php as my main back end language, but sometime I like to test some things on different languages and I saw that this if won't work. Why? $var is string so it exists.
$var = '0';
if($var) {
echo 'a';
}
PHP treats '0' as equal to 0 which is equal to false so your statement reduces to
if(false) {
echo 'a';
}
I recently discovered this interesting article by Deceze.
But I'm a bit confused by one of its advises:
never use empty or isset for variables that should exist
Using empty() is not good choice to test if $foo = ''; is empty?
What he means is if you want to check if the string is empty then empty won't do that. Empty can mean false, 0, null. Anything 'falsy'.
E.g. these are all true:
<?php
$string = null;
if (empty($string)) {
echo "This is true";
}
$string = '';
if (empty($string)) {
echo "This is true";
}
$string = 0;
if (empty($string)) {
echo "This is true";
}
If you want to check if the string is an empty string you should do this check for '':
<?php
$string = '';
if (isset($string) && $string === '') {
echo "This is true";
}
$string = null;
if (isset($string) && $string === '') {
echo "This is false";
}
You should not use empty (or isset) if you expect $foo to exist. That means, if according to your program logic, $foo should exist at this point:
if ($foo === '')
Then do not do any of these:
if (isset($foo))
if (empty($foo))
These two language constructs suppress error reporting for undefined variables. That's their only job. That means, if you use isset or empty gratuitously, PHP won't tell you about problems in your code. For example:
$foo = $bar;
if (empty($føø)) ...
Hmm, why is this always true, even when $bar contains the expected value? Because you mistyped the variable name and you're checking the value of an undefined variable. Write it like this instead to let PHP help you:
if (!$føø) ...
Notice: undefined variable føø on line ...
The condition itself is the same, == false (!) and empty produce the same outcome for the same values.
How exactly to check for an empty string depends on what values you do or don't accept. Perhaps $foo === '' or strlen($foo) == 0 is the check you're looking for to ensure you have a string with something in it.
PHP's empty() can be used in many cases.
It works for checking:
if a string is blank
if a variable is undefined or null
And of course empty() is best for your case too.
Try using this php if function:
$retVal = (condition) ? a : b ;
where condition: $value == null
a: is the value to display if $value is null
b: is the actual value to display or any other value to be displayed when $value is not null
In case of further guidance, kindly comment
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
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
Given the following code:
if (is_valid($string) && up_to_length($string) && file_exists($file))
{
......
}
If is_valid($string) returns false, does the php interpreter still check later conditions, like up_to_length($string)?
If so, then why does it do extra work when it doesn't have to?
Yes, the PHP interpreter is "lazy", meaning it will do the minimum number of comparisons possible to evaluate conditions.
If you want to verify that, try this:
function saySomething()
{
echo 'hi!';
return true;
}
if (false && saySomething())
{
echo 'statement evaluated to true';
}
Yes, it does. Here's a little trick that relies on short-circuit evaluation. Sometimes you might have a small if statement that you'd prefer to write as a ternary, e.g.:
if ($confirmed) {
$answer = 'Yes';
} else {
$answer = 'No';
}
Can be re-written as:
$answer = $confirmed ? 'Yes' : 'No';
But then what if the yes block also required some function to be run?
if ($confirmed) {
do_something();
$answer = 'Yes';
} else {
$answer = 'No';
}
Well, rewriting as ternary is still possible, because of short-circuit evaluation:
$answer = $confirmed && (do_something() || true) ? 'Yes' : 'No';
In this case the expression (do_something() || true) does nothing to alter the overall outcome of the ternary, but ensures that the ternary condition stays true, ignoring the return value of do_something().
Bitwise operators are & and |.
They always evaluate both operands.
Logical operators are AND, OR, &&, and ||.
All four operators only evaluate the right side if they need to.
AND and OR have lower precedence than && and ||. See example below.
From the PHP manual:
// The result of the expression (false || true) is assigned to $e
// Acts like: ($e = (false || true))
$e = false || true;
// The constant false is assigned to $f before the "or" operation occurs
// Acts like: (($f = false) or true)
$f = false or true;
In this example, e will be true and f will be false.
Based on my research now, PHP doesn't seem to have the same && short circuit operator as JavaScript.
I ran this test:
$one = true;
$two = 'Cabbage';
$test = $one && $two;
echo $test;
and PHP 7.0.8 returned 1, not Cabbage.
No, it doesn't anymore check the other conditions if the first condition isn't satisfied.
I've create my own short-circuit evaluation logic, unfortunately it's nothing like javascripts quick syntax, but perhaps this is a solution you might find useful:
$short_circuit_isset = function($var, $default_value = NULL) {
return (isset($var)) ? : $default_value;
};
$return_title = $short_circuit_isset( $_GET['returntitle'], 'God');
// Should return type 'String' value 'God', if get param is not set
I can not recall where I got the following logic from, but if you do the following;
(isset($var)) ? : $default_value;
You can skip having to write the true condition variable again, after the question mark, e.g:
(isset($super_long_var_name)) ? $super_long_var_name : $default_value;
As very important observation, when using the Ternary Operator this way, you'll notice that if a comparison is made it will just pass the value of that comparison, since there isn't just a single variable. E.g:
$num = 1;
$num2 = 2;
var_dump( ($num < $num2) ? : 'oh snap' );
// outputs bool 'true'
My choice: do not trust Short Circuit evaluation in PHP...
function saySomething()
{
print ('hi!');
return true;
}
if (1 || saySomething())
{
print('statement evaluated to true');
}
The second part in the condition 1 || saySomething() is irrelevant, because this will always return true. Unfortunately saySomething() is evaluated & executed.
Maybe I'm misunderstood the exact logic of short-circuiting expressions, but this doesn't look like "it will do the minimum number of comparisons possible" to me.
Moreover, it's not only a performance concern, if you do assignments inside comparisons or if you do something that makes a difference, other than just comparing stuff, you could end with different results.
Anyway... be careful.
Side note: If you want to avoid the lazy check and run every part of the condition, in that case you need to use the logical AND like this:
if (condition1 & condition2) {
echo "both true";
}
else {
echo "one or both false";
}
This is useful when you need for example call two functions even if the first one returned false.