Sometimes I bang my head into the wall, because of this.
var_dump(ord(true), ord(false));
gives:
int(49)
int(0)
So TRUE is converted into ASCII code 49 - number 1, and FALSE to ASCII code 0 (zero byte). Why such inconsistency in TRUE/FALSE conversions into a string? Why FALSE can't be converted into ASCII code 48 - number 0 where integer context is expected (because TRUE is '1')?
The biggest problem from such definitions is that if you store Boolean values in some variables which you later save in the database - then TRUE is stored as '1' and FALSE as '' - an empty string. So before storing in database you need to cast to integer (int)($bool_variable). Given that PHP supports full automatic type-casting, the need to perform a manual casting in some scenario(s) is very frustrating and a bit stupid (either ALL types should be interchangeable or user must perform casting itself between all types).
Any ideas?
It inherently doesn't make sense to ask for the ord of a bool. ord expects a string, so casts any input to a string. true casts to '1', and false casts to ''. The ord of '1' is 49, and the ord of an empty string is 0.
That doesn't mean that true and false are defined as such. true is defined as true and false is defined as false. It's merely the type casting rules that you're stumbling over (and yes, they're arguably arcane). Most databases support native boolean types, or their PHP database API will convert PHP booleans to the database's equivalent, as long as you use the API correctly.
As for why those casting rules exist:
A boolean TRUE value is converted to the string "1". Boolean FALSE is converted to "" (the empty string). This allows conversion back and forth between boolean and string values.
https://www.php.net/manual/en/language.types.string.php#language.types.string.casting
No, it doesn't make any more sense than this.
Part of your confusion is that ord is simply not the right function to use here - it is not a casting function, and not a debugging function that accepts any type. It is a function which expects a string, and returns an int, so PHP has to first cast the boolean to a string, and then run the function.
Note that PHP does not convert false to an ASCII NUL byte (0). That's just an artefact of you using ord() outside of its purpose: you passed an empty string, and asked "what's the first byte of this string?" Arguably, it should have given you an error, but in this case, it decided to give you 0.
A better test would be using var_dump, which is intended for inspecting PHP values:
var_dump(true); // bool(true)
var_dump(false); // bool(false)
var_dump((string)true); // string(1) "1"
var_dump((string)false); // string(0) ""
If you want to cast a boolean to a chosen representation, the best approach is just to use the ternary operator:
$bitAsInt = $booleanValue ? 1 : 0;
$bitAsString = $booleanValue ? '1' : '0';
$booleanKeyword = $booleanValue ? 'true' : 'false';
$boolString = $booleanValue ? "'t'" : "'f'";
All of the above would be appropriate for some database systems in some contexts, and serve as a good explanation why (string)$booleanValue can't just give you the right thing every time.
There are some other scenarios where (string)false giving an empty string is the most useful, and that's what PHP has decided on. That is:
(string)$booleanValue === ($booleanValue ? '1' : '')
There are always many answers to "why". For historical context, this was likely inspired by Perl (which was popular for web programming at the time of PHP's creation) - although it doesn't have a boolean type as such, it does exhibit similar casting behaviour:
print ( 1==1 ); # true as string, gives '1'
print ( 1==2 ); # false as string, gives ''
print 0 + ( 1==1 ); # true as int, gives 1
print 0 + ( 1==2 ); # false as int, gives 0
An empty string is considered by php as false. Whether it's read from a database or not.
Are you trying to solve a database query problem?
Related
Can somebody explain to me why this codes returns "TRUE".
I know that i should use the "===" rather "==" but I run to this code and wondering why it returns to true. Thanks in advance.
<?php
$s = "final";
$i = 0;
if($s == $i){
echo "TRUE";
}else{
echo "FALSE";
}
When you are trying to compare string and number, interpretator converts your string to int, so you got 0 == 0 at final. Thats why string == 0 is true.
Take a look at the PHP comparison tables.
You can see in the "Loose comparisons with ==" table that comparing the number 0 with a string containing text ("php" in the example) evaluates to TRUE.
This is just a property of the loose comparisons implemented in PHP. I wouldn't search for any more logic behind this than that this is a given.
As mentionned above, it is an issue with php's loose comparison. The accepted answer on php string comparasion to 0 integer returns true? Explains it well enough IMHO. in short "==" attempts to cast your string into an int, and since it fails, the resulting int has a value of 0
From PHP 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.
And from PHP string conversion to numbers:
The value is given by the initial portion of the string. If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero).
So when you compare integer and a string, PHP tries to convert string to integer first and as "final" doesn't contain any valid numeric data, it is converted to 0.
You can try:
var_dump( intval('12final') ); //int(12)
var_dump( floatval('1.2final') ); //float(1.2)
This is because of both 12final and 1.2final start with valid numeric data (12 and 1.2 respecrively), their converted value is not 0.
I encountered a strange and unexpected behavior in PHP while comparing some string values. The first two statements below return true when I would expect them to return false. The last statement returns false as expected. I'm aware of PHP's Type Juggling, but what I understand from the docs is that type juggling happens when you are comparing two different data types like a string and an integer. In the examples below though both literals are strings. Does this mean that when you are doing string comparison in PHP it inspects both strings to see if they look like integers and if so type casts the both of them to integers and then compares those integer values. So my question is under what conditions does this behavior happen, how exactly does string comparison work in PHP?
var_dump("10" == "10.0000");
var_dump("10" == "+10.");
var_dump("10" == "10 ");
#output
bool(true)
bool(true)
bool(false)
Updates
So baba's answer below comparison involves numerical strings really helped in getting me to understand what's going on. The function is_numeric will return to you whether or not a string is considered to be a numeric string. interestingly "10 " is not considered a numeric string but " 10" is. I dug around the PHP source code and I believe the implementation of is_numeric is in the is_numeric_string_ex function. From that one can tell exactly when PHP will treat a string as a numeric string.
You are getting error because of the position of the space this would return true
var_dump("10" == " 10"); // true
So if you RUN
var_dump("10" == "10 "); //false
What you are actually Running is because it would be treated as a string
var_dump("10" == 0); //false
This is because Type juggling would convert "10 " to 0 this is in the PHP Documentation
FROM PHP DOC
TRUE if $a is equal to $b after type juggling.
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. The type conversion does not take place when the comparison is === or !== as this involves comparing the type as well as the value.
If you want to trick type juggling
var_dump("10" == 0 + "10 "); // true
This is Because
An example of PHP's automatic type conversion is the addition operator '+'. If either operand is a float, then both operands are evaluated as floats, and the result will be a float. Otherwise, the operands will be interpreted as integers, and the result will also be an integer. Note that this does not change the types of the operands themselves; the only change is in how the operands are evaluated and what the type of the expression itself is.
I suppose the definite answer lies buried somewhere in the vastness of
compare_function
in php-src/Zend/zend_operators.c and the macros used in there.
In this case, all the 'strings' except the last one, are treated like floats or integers, and then compared. Thats why line 1 and 2 give a true.
In line 3 there is a space in the string, and that means for php that is is a 'real' string, and that line gets strings compared.
I do not like loose typed languages either, but for php this is the way it works.
if ("false" == 0) echo "true\n";
// => true
While PHP documentation says:
var_dump((bool) "false"); // bool(true)
If it is a bug where can I file it?
Nope, not a bug. Don't bother filing it.
This seemingly inconsistent behavior has nothing to do with the word "false". It has to do with your string consisting of only letters and no numeric digits, so when it gets cast to an integer for comparison (== 0), it results in 0, making 0 == 0 which evaluates to true.
If you compare any other alphabetic string to 0, you'll get the same result:
"abcd" == 0
"a" == 0
"true" == 0
It is not a bug, it's a well documented feature when comparing values of different datatypes - in this case, string to integer. PHP converts the string to an integer to do the comparison, and "false" converts to 0.
See the manual:
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.
and this page on string to number conversion
If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero).
To avoid this behaviour, use comparison with strict typing:
if ("false" === 0) echo "true\n";
The problem is that PHP casts variables unless you use a strict comparison. As others mentioned, the string "false" is being cast to an integer.
If you want to do a comparison without typecasting use:
"false" === 0
This might help: These are the only things that PHP considers false:
Actual boolean false
NULL values (empty)
The Integer 0
Try to avoid string comparisons of text and stick with booleans!
Anyone know why in php
var_dump( "cat" == 0 );
Evaluates to true? Also I realize:
var_dump( "cat" === 0 );
has the intended result, but curious as to why the first case would be true. Also this is php 5.3.
The string is being implicitly converted to an integer. See the documentation:
If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero).
The PHP Manual has a type comparison table in it, which gives you an idea of what happens when comparing variables of two different data types.
Your first example (a 'loose' comparison since it does not also check the data types of the two operands) implicitly converts the string on the left to an integer. Since it does not start with a number, the string is converted to the integer 0, which is equal to the integer 0.
Your second example compares not only the values but the types as well. Since the type is different, the comparison is false.
Because you are implicitly casting the string to an integer, and as the string does not contain numbers it is cast to a 0
"cat" is juggling to 0 as an integer, that is why it is true
but if you typed
var_dump( "01" == 0 );
it would have been false because 1 is not equals 0
My understanding is...
if is_numeric($input) === true
then either
is_float($input) === true OR
is_int($input) === true OR
$input === 0 OR
$input is a numeric string (meaning it'd satisfy one of the first 3 if it weren't wrapped in quotes).
Is that accurate? Are there other differences?
See PHP's documentation on is_numeric. It talks about everything that is allowed, and it's more than is_float and is_int.
It's also important to note that is_int only works on things that are type integer, meaning string representations are not allowed. This is a common problem when verifying that form input is an integer. You should use filter_var or something from the filter family with the filter FILTER_VALIDATE_INT. For floats, use FILTER_VALIDATE_FLOAT.
Also, if the reason you are trying to check for an integer is to validate a parameter as being an int, then in PHP 7 you can do this:
function foo(int $i) {
// $i is guaranteed to be an int (is_int) will be true
}
PHP 7 has two different modes for converting to int; this answer explains it a bit more.
Note that this is probably not what you want if you are validating the contents of a form element. Use the filter_var solution for that.
See the docs. A numeric value can be:
An integer
A float
Exponential
A positive Hexadecimal
A string containing most of these