I have the following PHP (the server is running version 5.3.x) in a script which is giving me a result that I am having trouble understanding. The general idea of this code is that I have a "normal mode", and two maintenance modes. In the first maintenance mode, data is only evaluated and can be viewed by an admin but is not stored to the database. If I set $maintenance_mode_enabled = 2;, then the same "preview" output should be displayed but only SOME specific updates to the database should be processed. The reason I added the ==2 comparison is because I found the need for a third option after I had setup the true/false for the basic default maintenance mode. At any rate, I noticed 18 records on my last maintenance_mode_enabled = true; run that were partially updated during the process, just as though I had set maintenance_mode_enabled = 2;.
$maintenance_mode_enabled = true;
if ($maintenance_mode_enabled){
echo "Case 0\n";
}
if (!$maintenance_mode_enabled){
echo "Case 1\n";
}
if ($maintenance_mode_enabled == 2){
echo "Case 2\n";
}
The output I get is:
Case 0
Case 2
From what I understood, true (being boolean) is definitely not equal to 3. I am familiar with some oddities when comparing false, NULL and 0, but this problem with integers and TRUE is entirely new to me.
Any ideas as to why this isn't working? I realize that I can just as easily change $maintenance_mode_enabled to an integer instead of a bolean by default, and set it as either 0, 1 or 2 to get the desired results, but I really want to understand WHY this seems to defy logic.
The reason this happens is because you're comparing a boolean to an integer. As with many languages, at the core of the comparison function it's casting the second part of your comparison to a boolean. Any non-NULL, non-zero, non-empty or non-false value, in this case 2 is "true."
As the previous answer mentions I would change the code to use strict comparison. I would also change from three separate if-statements to one if-elseif statement:
if ($maintenance_mode_enabled === true) {
// catches only true not > 0
echo "Case 0\n";
} elseif ($maintenance_mode_enabled === false) {
// catches only true not = 0
echo "Case 1\n";
} elseif ((int)$maintenance_mode_enabled === 2) {
echo "Case 2\n";
}
I recommend this change because maintenance mode can only have one value.
EDIT
I didn't realize true and 2 could coexist. You could do:
if ($maintenance_mode_enabled) {
echo "Case 0\n";
if (2 === (int)$maintenance_mode_enabled) {
echo "Case 2\n";
}
} else {
echo "Case 1\n";
}
Use the === operator for true otherwise all non 0 / null / false will be true.
Use the === operator for false otherwise all equal to 0 / false / null will show as "false"
The following will output Case 0
<?php
$maintenance_mode_enabled = true;
if ($maintenance_mode_enabled === true){
// catches only true not just > 0
echo "Case 0\n";
}
elseif (!$maintenance_mode_enabled === false){
// catches only false not including = 0
echo "Case 1\n";
}
elseif ($maintenance_mode_enabled == 2){
echo "Case 2\n";
}
?>
Oh, NOW I get it. It seems the problem here is that when I do the loose (==) comparison of a boolean with an integer, the type casting is converting the integer into a boolean, thus resulting in 2 being equal to true - since both are being tested as booleans. The solution is to use strict (===) comparison, so that both must be of the same type... i.e.: 2 (integer), is not exactly the same as true, since true is is of a different type - boolean.
Related
From my knowledge, bitwise operators perform a check on all corresponding bits, as in:
echo 64 | 32; //prints 96
echo 'a' & 'b'; //prints `
While conditional && and || operators perform operations on boolean values:
echo (int)(true && false); //prints: 0
echo (int)(true || false); //prints: 1
When I (in my head) want to predict the result of a bitwise operation, I first convert the values into their binary representation (which depends on the datatype). After this, I compare it bit-for-bit, and convert the result into the suitable output type (which I suppose is determined by the operands). Although at one point, I tried doing the same with boolean values, which (from my knowledge) only consists of one bit in memory, making true corresponding to 1₂, and making false corresponding to 0₂ (in binary). Performing bitwise operations on these values should therefore produce a similar result as with && and ||, right? To show you what I mean:
true & false => 1₂ & 0₂ => 0₂ => false
true | false => 1₂ | 0₂ => 1₂ => true
~true => ~1₂ => 0₂ => false
(Not including xor, as there's no corresponding conditional boolean operator.)
To me, it looks like the behaviour should be really equivalent to conditional operators:
true && false => false
true || false => true
!true => false
So therefore, I set this code up to test it:
echo "true AND false: " . ((true && false) ? "1" : "0") . "<br />\n";
echo "true OR false: " . ((true || false) ? "1" : "0") . "<br />\n";
echo "NOT true: " . ((!true) ? "1" : "0") . "<br />\n";
echo "<br />\n";
echo "true BITAND false: " . ((true & false) ? "1" : "0") . "<br />\n";
echo "true BITOR false: " . ((true | false) ? "1" : "0") . "<br />\n";
echo "BITNOT true: " . ((~true) ? "1" : "0") . "<br />\n";
It gave me the following output:
true AND false: 0
true OR false: 1
NOT true: 0
true BITAND false: 0
true BITOR false: 1
Fatal error: Unsupported operand types in C:\Abyss Web Server\htdocs\handler.php on line 21
So from this, I have two questions:
What's the point with && and ||, if we (as it seems) can use & and | on boolean values instead?
Why can't I do ~true (or with other words, why aren't boolean values supported)? To me, it sounds logical that ~true returns false.
I did come up with one thing, being that && and || will (sometimes) cast the values into bool and thereafter return the correct results, if we (by mistake) would happen to pass a value that's not of type bool. But to solve that, can't we just do a cast first? Such as:
if ((bool)$foo & (bool)$bar) { ...
It feels like I'm missing a major piece here that changes everything... But just in case I didn't, I included as much info as I could. Could someone make me understand this a bit better, by answering my two questions? I'm pretty confused at this point, and I've been thinking about this for quite some time.
Answer 1
Parts of a boolean expressions (||, &&, !, ...) are only evaluated if needed (from left to right):
if ($a | func()) { } // func is always called
if ($a || func()) { } // func is not called if $a is true,
// because expression is true whatever func will return
if ($a && func()) { } // func is not called if $a is false,
// because expression is false whatever func will return
func() || exit(); // exit() will be called if func() returns false
Take a look at the documentation: http://php.net/manual/en/language.operators.logical.php
Answer 2
~true seems not to be meaningful: true is 0x00...01 and ~true will be 0xff...fe and not false 0x000...0:
var_dump(~((int)true)); // prints: int(-2)
echo dechex(~((int)true)); // prints: fffffffffffffffe
Use !-operator instead:
var_dump(!true); // prints: bool(false)
Résumé
Use bitwise operators only if you need to change bits.
true and false boolean flags, albeit being stored in memory as 32-bit or 64-bit values, should be considered just as two-states boolean values. You'll end up using it only on brach guards so you should not make arithmetic over them. && and || evaluates as boolean flags, bitwise operators & and | evaluates as the same of the operands (e.g. int & int evaluates as int, int && int evaluates as boolean).
the story is:
use && and || when you have to take decisions over some conditions. Use & and | to perform boolean arithmetic over values.
that's C++
C doesn't have any built-in boolean values so any non zero value is a true, and zero is false.
Why does PHP return 0 when a logical AND returns FALSE, but does not return the 0 when a conditional AND returns FALSE? Witness:
php > function a(){
php { echo "a";
php { return FALSE;
php { }
php > function b(){
php { echo "b";
php { return TRUE;
php { }
php > echo (a() && b())."\n";
a
php >
php > echo (a() & b())."\n";
ab0
php >
Notice that the second echo statement ends with 0, yet the first does not. Why?
&& returns a boolean. The boolean false when cast to a string is '', an empty string.
& is a bitwise and (not a "conditional and", whatever that is), which produces a number (or binary blob, depending on the arguments) as a result. In this case, the operation results in the number 0.
In second case
php > echo (aa() & b())."\n";
is a bitwise AND. You need to use && for comparison
Actually, neither of the answers provided are fully correct. The right answer is threefold.
The "problem" here is that && is a short-circuit operator. So it basically moves from the left-most argument to the right most one-by-one. For each one it checks if it returns true, if not, it stops and WILL NOT execute the next statement. PHP Documentation on logical operators.
Ergo: in the first case, you see only a (and not b) due to shortcircuiting. Then you also don't see the 0 because it's a boolean (not an int).
Then in the 2nd case you see an integer 0 because he's using a bitwise AND instead of a logical.
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
This question already has answers here:
PHP Type-Cast Confusion
(3 answers)
Closed 9 years ago.
$isClient = 0;
if($isClient == 0) echo "is client 0\n";
if($isClient == "n") echo "is client n\n";
if($isClient == "y") echo "is client y\n";
Considering the code above, it outputs the following
is client 0
is client n
is client y
I must be missing something here! How come 0 is equated with "n" and "y"???
A link to the above code for testing
Here you are performing loose comparison (NB: a strict comparison is done with help of three equal signs: ===) and in this case a string evaluates to zero because == is left associative, which means, that if the first operand is an integer, the second operand is typecast to an integer as well (thus "n" is interpreted as 0). That is just the way PHP works.
Since $isClient and "n" are of different data types, they have to be converted to a common data type in order to be compared.
(int)"y" and (int)"n" both evaluate to 0, so your equality holds. If you want to do a strict comparison, use the triple equals sign. It checks data types as well:
<?php
$isClient = 0;
if($isClient === 0) echo "is client 0\n";
if($isClient === "n") echo "is client n\n";
if($isClient === "y") echo "is client y\n";
?>
This generates the correct output:
is client 0
In PHP, when you want to compare values, you need to use three equality signs (===), because:
As described in the section about expressions [see below], expression is evaluated to its Boolean value. If expression evaluates to TRUE, PHP will execute statement, and if it evaluates to FALSE - it'll ignore it. More information about what values evaluate to FALSE can be found in the 'Converting to boolean' section. (Source)
and
One last thing worth mentioning is the truth value of expressions. In many events, mainly in conditional execution and loops, you're not interested in the specific value of the expression, but only care about whether it means TRUE or FALSE. The constants TRUE and FALSE (case-insensitive) are the two possible boolean values. When necessary, an expression is automatically converted to boolean. See the section about type-casting for details about how. (Source)
So your code has to be modified as follows:
$isClient = 0;
if($isClient === 0) echo "is client 0\n";
if($isClient === "n") echo "is client n\n";
if($isClient === "y") echo "is client y\n";
Outputs
is client 0
I am wondering why following statement in PHP is returning true?
true>=4
for example such line will echo 1
echo true>=4;
Can anyone explain me the logic behind this?
4 is also true (because it's non-zero), and true is equal to true, so it's also greater than or equal to true.
If a bool or null is compared to anything other than a string, that thing is cast to a bool. See the docs.
In addition to Davids answer, I thought to add something to give a little more depth.
PHP unlike other programming languages, if your not careful with your operators/syntax you can fall into tricky pot holes like the one you experience.
As David said,
4 is also true (because it's non-zero), and true is equal to true, so
it's also greater than or equal to true.
Take this into account
True is greater than false.
true = 1
false = 0
So take this for example:
$test = 1;
if ($test == true){
echo "This is true";
}else{
echo "This is false";
}
The above will output
This is true
But if you take this:
$test = 1;
if ($test === true){
echo "This is true";
}else{
echo "This is false";
}
The above will output:
This is false
The added equals sign, looks for an exact match, thus looking for the integer 1 instead of PHP reading 1 as true.
I know this is a little off topic, but just wanted to explain some pot holes which PHP contains.
I hope this is some help
Edit:
In response to your question:
echo true>=4;
Reason you are seeing 1 as output, is because true/false is interpreted as numbers (see above)
Regardless if your doing echo true>=4 or just echo true; php puts true as 1 and false as 0