if(0 == '%') echo "WTF, Php, why can't you compare things sanely?" - php

I just reduced a crazy bug to what's in the title of this question.
I'm sure there's a Perfectly Reasonable Explanation for why Php thinks that 0 == '%' (or any other special character, I think).
I thought it would be good to get that explanation on StackOverflow!

It will do the same to any string, converting a non-number string to integer always gives 0.

Using '===' (for exact, literal comparison -- the values must be the same type and also equal) instead of '==' solves this problem. With '==' it's trying to cast '%' to a number and 0 is the best it can do.

In the event that a strict comparison operator is not directly suitable, if you want both values to be compared as strings, you can use strcmp(). An example could be where both values are variables, and the types might be either string, or int. Instead of type casting to string and then using strict equality check, strcmp() can be less verbose.
php's type coercion is very convenient. But if you don't understand its many rules(some can bite), you should try to avoid using it. See
http://www.php.net/manual/en/types.comparisons.php

since '%' isn't true, it ought to be false (0), so of course 0=='%'
;-) funny though

This is because it converts '%' to number and it is 0.
The way, however, how PHP converts strings to numbers is pure evil IMHO, because non number string does not have to result in zero.
If it starts with a digit then php will result the first number it can find in it and ignore the rest of the string. So:
"2 times 5 equals 10"
..will result in 2 when casted to a number.
Article in the documentation

Related

Comparions fails with big number PHP

I was reading an article and I found this example:
// Prints bool(true)
var_dump('9223372036854775807' == '9223372036854775808');
// Prints bool(false)
var_dump('9223372036854775807' ==='9223372036854775808');
Anyone can explain me why?
From reddit and a php bug report.
A few things are happeing here.
== doesn't return false if the values are of different type. In this case PHP is converting the strings to floats to compare the numbers and due to thier size and the way PHP handles floats they are rounded to the same number, hence comparing the 2 strings as numbers they are the same.
=== enforces type must be same. So comparing those two as strings they are not the same.
As mentioned by #Classified in another comment this was fixed in php 5.4.4 so isn't a current bug.
The both cases should return FALSE because the numbers are different, but I think your doubt is about The difference between '==' and '===' AND why first case returns true right ?
When you use the '==' you're comparing if the values are equals BEFORE type-juggling, when you use the '===' you're comparing if the values equals AFTER type-juggling.
In another words, when using '==' you're comparing if values are equals, but when you're using '===' you're comparing if values and types are equals.
In these cases, both comparisons should return FALSE because the values are different, but about the first comparison returning TRUE, it is a bug in PHP, as you can see in: https://bugs.php.net/bug.php?id=54547
PS: 9223372036854775807 is the bigger int number, so if you check types with gettype(), you will see:
9223372036854775807 is an integer.
9223372036854775808 is a double.
As of PHP 5.4.4 this is no longer an issue.
The reason why this is happening, is because of type-juggling. In the first example you're checking if the two values are equal AFTER type-juggling so effectively it's comparing two string types.
And in the other example you are checking whether the two values are identical, as in the same type and the exact same value.
Check this.
<?php
var_dump('9223372036854775807' == '9223372036854775808');
var_dump('9223372036854775807' ==='9223372036854775808');
Output:
bool(false)
bool(false)
It doesn't. See your exact code above running here for proof https://3v4l.org/4oHvo
UPDATE:
Based on the below comments, it looks like the person asking is using a version of PHP that is 4 years old and no longer supported, and potentially vulnerable to security flaws. Update your PHP!
Please see the End of Life chart on php.net https://secure.php.net/eol.php

Why are strings "0e368798" and "00000000" equal, per PHP's equality operator?

Can someone explain why the following two statements return true?
if ('0e368798' == '00000000')
or
if ((string)'0e368798' == (string)'00000000')
Why do I have to use the strict operator to check the equality of these two strings?
Because XeY is X * 10^(Y), and 0 times anything is 0. 0000000 is also 0. And == in PHP very intuitively thinks that if it can be converted into a number, it should be.
EDIT: It was in a helpful comment that is now deleted, so with apologies to the commenter whose name I did not catch, I will repeat it here - from PHP docs on comparison:
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.

Why PHP casts two numerical strings to numbers before [loosely] comparing them?

I browsed through several similar questions, but they all only state the fact:
If ... comparison involves numerical strings, then each string is converted to a number and the comparison performed numerically.
Okay, I got it. It explains what is going on when '00001' == '1' returns TRUE.
The question is: Why PHP does so?
What is the reason for probing strings for being numeric, and then casting if so? Why can't we just compare two strings already?
I can fairly understand what casting is required if two operands has different types. But why it does "usual math" when both are strings?
You can compare two strings.
"00001" === "1" // false
Remember == means equivalent === means equal
My only guess as to why this is the case is because in the beginning PHP strived to be a type-less language before they went the route of becoming a loosely typed language.
PHP was originally string processor that allowed for a little scripting inside of it. Since all inputs on the web are textual in nature it had to work hard given textual input to behave sanely when it came to dealing with numbers.
I could be wrong about this but I don't believe the ability to explicitly cast data types entered the stage until PHP 4 with the introduction of the *val functions (like intval) etc. and I think the casting notation, like (int) came around after that.
For the non comparison operations it was pretty easy because they all have a type associated with them (+ - / * all deal with numbers whereas . deals with strings) so a clear path to how things should be cast in those cases is apparent.
But with equality or equivalence checks between variables then the only way to do that was to treat everything that looked like a number as a number because at the time the only way it could be gotten would be by externally would be as a string and there was no way to make it otherwise.
Because, PHP produce a product for End-Users, not for Application Developers.
So when you produce such product like below:
if (isset($_POST['submit'])){
if ($_POST['myinput'] == "1") echo 'Yes'; //or == 1
else echo 'NO';
}
?>
<form name="myform" method="POST" action="">
<input name="myinput">
<input name="submit" type="submit">
</form>
If the user enter 1 or 0001, what do you expect to print in both case? Yes or NO?
the answer is clear. we expect to see Yes. so this is why PHP does so
If for any rare reason, we need to definitely compare them, then we should change == to ===
Ah, finally I got it. It was quite stupid of me.
Comparison involves not only "is equal" but also "less than" and "greater than". And for the latter two it is obviously critical to cast numerical operands before comparison, because numbers often being represented in PHP as strings, and 11 have to be greater than 9 even if both stored in strings.
So, as compare_function() does all the comparisons at once, returns either 1, 0, -1 to tell if first operand is bigger, equal or less than second respectively - well, it's fairly explains, why operands being cast.

PHP - usage of is_numeric() necessary, or can use comparison signs work for all positive numeric cases?

It seems that simple comparison signs >,>= and their reverse components can evaluate if a certain variable is a number or not. Example $whatami='beast'; ($whatami<0)?echo 'NaN':echo 'is numeric!';
Are there cases where is_numeric() usage is necessary for positive values (number >0)? It seems that using comparison signs above would determine if the variable is numeric..
As I have been finding out, a lot of these helper functions are really necessary because PHP isn't strongly typed. I posted a similar question (although not that similar) about isset earlier this week. One thing to note is that PHP will change your string to its integer value for comparisons during some instances (when there are mixed types). This can't be overlooked. I think this is a strong case for is_numeric
from PHP 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. 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.
Another thing to think about is that "what is 0" in PHP. It means a lot. It's not always numeric. It may be a numeric string, boolean false, integer, etc... This is why those helper functions exist.
To add to my answer:
change your example:
$whatami='beast';
($whatami<5) ? echo 'less than 5' : echo 'more than 5';
PHP would change 'beast' to its integer equivalent and then do the comparison. This would give unintended results. If you really wanted something similar, you'd have to wrap it in another conditional:
$whatami='beauty';
if(is_numeric($whatami){
($whatami<5) ? echo 'less than 5' : echo 'more than 5';
} else {
exit('what, am I not pretty enough for a beast?');
}
Then you would get your intended result (as weird as it may be).
There is a big difference between "can evaluate if a certain variable is a number or not" and "evaluate if a certain variable is a positive number". Using the comparison signs require you to test it twice (Both > & <= or >= & <) and may not be immediately obvious. is_numeric means you only need a single test and makes it quite obvious what you are doing.
Also, a string will evaluate as 0, meaning it throws your idea out. Stick with the proper commands :)
As per comment: Well, in this case, you are asking for comparing is_numeric against a test for positive numbers, excluding 0. This is not the intent for is_numeric, so naturally it may not be necessary. If you do a mathematical check that involves 0 as the answer or as part of the range, you will need is_numeric, otherwise you won't need it. The first part of your question asks a different question, so:
It seems that simple comparison signs >,>= and their reverse components can evaluate if a certain variable is a number or not - Incorrect
Are there cases where is_numeric() usage is necessary for positive values (number >0)? - No
It seems that using comparison signs above would determine if the variable is numeric - No. They can determine if a variable is either a non-zero number or unknown, not numeric.
Comparison will depend on the type of data on the left side of the operator.
The important thing to remember is that PHP is not a strongly typed language. If you want to compare a number and ensure it is a number, then yes, is_numeric() would be a good check. For example,
echo (is_numeric($whatami) && $whatami < 0) ? 'number greater than zero' : 'NaN or negative';
However, this shouldn't be generalized. If you can comment more on what you are wanting to do, you may find a more detailed answer.
Yes, there are cases.
For instance:
var_dump("5aa" > 4); //bool(true)
var_dump("5aa" > 6); //bool(false)
As you can see, the conversion of "5aa" to int(5). Let's see what is_numeric gives:
var_dump(is_numeric("5aa")); //bool(false)
So, is_numeric is more strict. Whether it's necessary depends on your application.
Notice that are cases where a numeric string and a number are not exactly the same thing:
var_dump("255" & "2"); //string(1) "2"
var_dump(255 & 2); //int(2)
See bitwise operations:
Be aware of data type conversions. If both the left-hand and right-hand parameters are strings, the bitwise operator will operate on the characters' ASCII values.

Very illogical php value comparisons

I stumbled upon a very strange bit of PHP code. Could someone explain why this is happening? *****BONUS POINTS***** if you can tell my why this is useful.
<?php
if(0=='a'){
print ord(0)." should NEVER equal ".ord('a')."<br>";
}
if(false==0){
print "false==0<br>";
}
if('a'==false){
print "a==false<br>";
}
?>
And the resulting output:
48 should NEVER equal 97
false==0
In PHP, 'a' is not the ASCII character a, but the string a. In a numeric context, it is equal to 0. For instance intval('a') results in a value of 0.
This is useful because PHP is primarily used for processing text, and one might want to try the test (123 == '123'), which is true. And given that a number in single (or double) quotation marks is treated as the number, it doesn't make sense for a string with no numeric value to be treated as anything other than 0.
Oh yeah, one more thing. 'a' in a boolean context is true, not false. I believe this makes some types of text processing more natural, but I honestly can't think of an example at this late hour.
Well, there's always the PHP type cheat sheet for that!
This is a basic principle of weakly/dynamically typed languages called type juggling. Types will be cast to other types in certain circumstances. When you compare a string to a number, the string will be cast into a number. When comparing anything to a boolean, that value will be cast to a boolean.
There are rules for every type as to how it will be cast into another type or how it compares to other types. 'a' happens to be converted to 0 when cast to a number (the only logical choice, really). To avoid this type casting, test not with the equality operator ==, but with the identity operator ===.
As James pointed out, this is useful since PHP deals a lot with strings that are really numbers. For example, HTML forms only submit strings, even if the value is a number. It also allows for some really terse code, like:
$result = someOperation();
if (!$result) {
// $result may be null, false, 0, '' or array(),
// all of which we're not interested in
error();
}
It also means you have to be really careful about what to check for in which circumstances though, since a value might unexpectedly cast into something else. And admittedly, 'a' == 0 in itself is really a pitfall of type juggling rather than helpful. It's one of the situations where you have to be careful and test like if (is_numeric($var) && $var == 0).
ord() takes characters, so PHP turns 0 into '0'. And 0 is equal to false, even though it is not identical (===).
Check out the PHP type comparison tables from the manual. It's a really handy thing to have close at hand until you've internalised it and has been invaluable to my understanding of exactly what will evaluate to true and when.
Others have already answered the core of the question, but I think it's important to state that in PHP, the only non-empty string that does not evaluate to "true" with the == operator is "0" as PHP treats any string containing only numbers as an integer or float.
The rationale for this is that PHP is fairly loosely typed and tries to allow integers, strings, floats and boolean values to be interchangeable. A real-world and extremely common example of this is if you're using the mysql or PDO functions, strings are returned for everything, even if the underlying column is an integer.
Consider the following sql:
CREATE TABLE `test`.`pants` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`some_other_int` INT NOT NULL
) ENGINE = InnoDB;
INSERT INTO `test`.`pants` (`id`, `some_other_int`)
VALUES ('1', '1'), ('2', '0');
And the following code:
<?php
$c = mysql_connect('127.0.0.1', 'user', 'password');
mysql_select_db('test', $c);
$r = mysql_query('SELECT * FROM pants', $c);
while ($row = mysql_fetch_assoc($r)) {
var_dump($row);
foreach($row as $k=>$v) {
if (!is_string($v))
echo "field {$v} was not a string!\n";
}
}
The "field x was not a string!" message is never printed, even though every column in the database is an integer. Suppose you want to actually use the second row in that table.
<?php
$id = 2;
$r = mysql_query(sprintf('SELECT * FROM pants WHERE id=%s', mysql_real_esacpe_string($id)), $c);
$row = mysql_fetch_assoc($r);
// this is the important bit
if (0 == $row['some_other_int']) {
echo "It was zero!";
}
If the string "0" was not treated as the integer 0 for the comparison, the above code would never print "It was zero!". The programmer would be required to take responsibility for juggling the type of the value which comes out of the database. This is not desirable for a loosely typed language.
Strict equality including type is tested using the "Is really, truly, honest to god equal to" operator, which is represented by the symbol "===".
I don't see how ('a'==0) is helpful
$var = '123abc';
if (123 == $var)
{
echo 'Whoda thunk it?';
}
It comes down to PHP's implicit conversion rules.
I'm failing at thinking of a practical example, but that's the basic reason why you're seeing that behavior.
Expansion:
In your example, 'a' is converted to 0 (zero), for the comparison. Imagine that for the purpose of the comparison, it's equivalent to '0a'. (That's the numeral zero, not the letter 'o.')
Further expansion:
I thought there was a good example use case for this in the manual, but I failed to find it. What I did come across should help shed some light on this "illogical" situation.
PHP is first and foremost a Web
language, not a general-purpose
scripting language. Since the Web is
not typed and everything is a string,
I had to do things slightly
differently early on to make PHP do
what people expected. Specifically,
"123"==123 needs to be true in order
to not have to type cast every single
numeric user input.
http://bugs.php.net/bug.php?id=48012
That doesn't exactly answer the question, but it points in the general direction.
PHP is a loosely typed language, and allows you to compare values of different types without throwing errors, which makes it very easy to use but as you have found can cause some weird but logical outputs.
Your first example:
if(0=='a'){
print ord(0)." should NEVER equal ".ord('a')."<br>";
}
When two different types of values are compared, one value is first turned into the same type as another via a cast and then compared. In the example of Int and String the string is converted into Int. When PHP turns a letter into a string it takes all the first numeric characters and then chops of the rest: i.e '123123afraa' becomes 123123, '9a9' becomes 9. If the string does not start with numerals it is given the value of 0.
Therefor your example is really: 0===(string)'a' which is really 0===0 since 'a' does not start with a numeric. I think you were expecting PHP to return the value of 'a' in ASCII which it does not! This is really useful to sanitise strings, php rarely needs to deal with ascii values it is too high level for that. (Its for making websites!)
When a string is compared to a boolean a value of '' or '0' are false, all other values are true. This is useful so you can check if a value is 'empty':
url http://domain.com/?foo=
if ($_GET['foo']))
{
// do something
}
When an integer is compared to a boolean the values of 0 is false other values are true, this is pretty standard.
So, all in all you need to understand what happens when different types of variables are compared with the == operator. Also it is probably wise to realise that == is almost never what you want and using === (which will not typecast your values) is ALOT safer.
The code seems to emanate from a unit test for the purpose of catching failures, hence the seemingly weird comparisons. In the same light, it may be preparatory to the main unit test to confirm that the == operator is working properly - as it should.

Categories