I am working on the following snippet. What is the point of using !== 0 inside the first if condition while even the second condition is returning same result without using !== 0?
I was following some online tutorial and noticed that most developer are using the !== 0 but I accidentally noticed that I am also getting same result back , at least in this specific example without using !== 0
<?php
$name1 = "Geeks";
$name2 = "geeks";
if (strcmp($name1, $name2) !== 0) {
echo 'Strings are not equal';
}
else {
echo 'Strings are equal';
}
if (strcmp($name1, $name2)) {
echo 'Strings are not equal';
}
else {
echo 'Strings are equal';
}
?>
The "===" and "!==" comparison operators assert two things:
The values are equal, and
The values are of the same type
The short answer to your question "what is the point of using !== with the strcmp function" is simply "it's good practice". That's really the only reason regarding strcmp specifically, and != would give you the exact same result when it comes to that function.
The long answer is as follows:
PHP is traditionally a loosely typed language. That is, datatypes were not all that important and PHP implicitly cast types for you automatically. It still does this by default (although lots of stuff has been added to improve the situation over recent years). For example, if you add the string "1" to the integer 1, PHP will cast the string to an integer automatically and return the integer value 2. Strongly typed languages would return an error if you tried to do that. Another example is that PHP will cast 0 to boolean false and any other non-zero value to boolean true.
It's that second example that causes problems with some of PHP's built-in functions. For example, the strpos() function. If you check the documentation page for strpos you'll see a big "Warning" in the "Return Values" section stating "This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE". For that reason it advises you to use the "===" operator to make sure you are getting the exact value and type that you expect. So "=== 0" means the string was found at the beginning of the input, and "=== false" means the string was not found. If you just used "== false" you won't be distinguishing between the string being found at the beginning of the input and the string not being found at all (because 0 will be implicitly cast to boolean false for the "if" statement).
So, developers got into the habit of testing for equality and type by using "===" and "!==". It's a good habit.
Since strcmp always return an integer, and you are always comparing it to an integer, there is no implicit casting and so comparing types as well as equality is not neccessary.
Related
I have a problem baffling me terribly. I noticed this before but didn't give it any heed until today.
I was trying to write my own check for integer strings. I know of is_numeric() but it does not suffice since it counts float as numeric not only integers and is_int() which does not work on string numbers.
I did something similar to this
$var1 = 'string';
$var2 = '123';
var_dump( (int)$var1 == $var1);// boolean true
var_dump((int)$var2 == $var2);// boolean true
var_dump((int)$var1);//int 0
var_dump($var1);//string 'string' (length=6)
As expected the second var dump outputs true since I expect with php's loose comparison that the string and integer versions be equal.
However with the first, I don't get why this is so. I have tried casting to bool and it still gives me the same result.
I have tried assigning the cast var to a new variablr and comparing the two, still the same result
Is this something I am doing wrong or it is a php bug?
***Note
I am not comparing types here. I'm actually trying to take advantage of the fact that int 0 is not equal to string 'string'.
I wrote my integer check differently so I don't really need alternatives for that.
***Edit
I did some extra checking and it turns out that 0 == 'string' is true as well. How is that possible?
***Edit 2
There are multiple correct answers below to the question. Thanks to everyone who answered.
It's not a bug, it's a feature. Any string can be casted to an integer, but the cast will return 0 if the string doesn't start with an integer value. Also, when comparing an integer and a string, the string is casted to an integer and then the check is done against the two integers. Because of that rule, about just any random string is "equal" to zero. (To bypass this behavior, you should use strcmp, as it performs an explicit string comparison by casting anything passed to a string.)
To make sure I'm dealing with an integer, I would use is_numeric first, then convert the string to an int, and verify that the stringified int corresponds to the input value.
if (is_numeric($value) && strcmp((int)$value, $value) == 0)
{
// $value is an integer value represented as a string
}
According to php.net http://php.net/manual/en/language.operators.comparison.php:
var_dump(0 == "a"); // 0 == 0 -> true
So, I think it is juggling the types, and actually casting both sides to int. Then comparing either the sum of the ascii values or the ascii values of each respective index in the string.
First of all in mathematices '=' is called transitive b/c (A=B and B=C => A=C) is valid.
This is not the case with PHPs "=="!
(int)$var1 == $var1
In that case PHP will cast 'string' to 0 - that's a convention.
Then ==-operator will implicitely have the second operand 'string' also be casted to integer -> as well 0.
That leads to true.
You made an error with your post, the correct output is this:
bool(true)
bool(true)
int(0)
string(6) "string"
What happens is this:
Because you cast the variable to an integer, and you compare it to an integer with a loose comparison ==, PHP will first implicitely cast the string to an integer, a more explicit but 100% equivalent form would be: if((int)$var1 == (int) $var1)
See 1), the same thing applies here
It prints int(0), as it should, because it fails to parse the number, it will return 0 instead.
Prints string(6) "string" - as expected
Note: This answer is in response to a related question about the Twig template engine, that was marked as a duplicate, and redirects here.
Because the context is different, this answer is provided to those members of the SO community who may benefit from additional details specifically related to twig exclusively.
TL;DR: see this post How do the PHP equality (== double equals) and identity (=== triple equals) comparison operators differ?
Problem
Context
Twig template engine (latest version as of Fri 2017-01-27T05:12:25)
Scenario
DeveloperYlohEnrohK uses comparison operator in twig expression
DeveloperYlohEnrohK notices unexpected results when using equality comparison operator
Questions
Why does the equality comparison operator (==) produce unexpected results in Twig?
Why do the following produce different results?
{{ dump(0 == 'somekey') }} ==> true
{{ dump(0|lower == 'somekey') }} ==> false
Solution
Since Twig is based on PHP, the casting, implicit type-conversion and comparison semantics of PHP apply to Twig templates as well.
Unless DeveloperYlohEnrohK is intentionally and specifically leveraging the behavior of implicit type-conversion in PHP, the comparison expression will almost certainly produce counterintuitive and unexpected results.
This is a well-known circumstance that is described in detail in this SO post on PHP equality.
Solution: Just as is the case with standard PHP, unless the well-known circumstance is accounted for, using === in Twig is much less likely to produce unexpected results.
Pitfalls
As of this writing, the Twig template engine does not support === in the same way as standard PHP
Twig does have a substitute for === using same as
Because of this, the treatment of this well-known circumstance differs slightly between PHP and Twig.
See also
How do the PHP equality (== double equals) and identity (=== triple equals) comparison operators differ?
http://twig.sensiolabs.org/doc/2.x/tests/sameas.html
If you want to compare types of variables too you should use ===.
Here's a function that more rigorously tests for either an int or an integer string.
function isIntegerNumeric($val) {
return (
is_int($val)
|| (
!is_float($val)
&& is_numeric($val)
&& strpos($val, ".") === false
)
);
}
It's relatively quick and avoids doing any string checking if it doesn't have to.
The triple equal I think everyone understands; my doubts are about the double equal. Please read the code below.
<?php
//function to improve readability
function compare($a,$b,$rep)
{
if($a == $b)
echo "$rep is true<br>";
else
echo "$rep is false<br>";
}
echo "this makes sense to me<br>";
compare(NULL,0,'NULL==0');
compare(NULL,"",'NULL==""');
compare(NULL,[],'NULL==[]');
compare(0,"",'0==""');
echo "now this is what I don't understand<br>";
compare("",[],'""==[]');
compare(0,[],'0==[]');
compare(0,"foo",'0=="foo"');
echo "if I cast to boolean then it makes sense again<br>";
compare("",(bool)[],'""==(bool)[]');
compare(0,(bool)[],'0==(bool)[]');
?>
Output:
this makes sense to me
NULL==0 is true
NULL=="" is true
NULL==[] is true
0=="" is true
now this is what I don't understand
""==[] is false
0==[] is false
0=="foo" is true
if I cast to boolean then it makes sense again
""==(bool)[] is true
0==(bool)[] is true
I would expect an empty array to be "equal" to an empty string or to the integer 0. And I wouldn't expect that the integer 0 would be "equal" to the string "foo". To be honest, I am not really understanding what PHP is doing behind the scenes. Can someone please explain to me what is going on here?
The simple answer is that this is the way php has been designed to work.
The outcomes are well defined in the docs comparison operators and comparison tables.
A == comparison between an array (your first two queries) and a string always results in false.
In a == comparison between a number and a string (your third query) the string is converted to a number and then a numeric comparison made. In the case of 0=='foo' the string 'foo' evaluates numerically to 0 and the test becomes 0==0 and returns true. If the string had been 'numeric' e.g. "3" then the result in your case would be false (0 not equal to 3).
Whether the design is "correct" (whatever that may mean) is arguable. It is certainly not always immediately obvious. An illustrative example of the potential fury of the debate can be found in Bug#54547 where the devs argue strongly that the design is rooted in php's history as a web language where everything is a string and should be left alone, and others argue php "violates the principle of least surprise".
To avoid uncertainty use === wherever possible, with the added benefit of potentially showing up assumptions in your code that may not be valid.
As someone has already said, the PHP automatic casting rules can be quite tricky, and it is worth using === unless you know both sides will be of the same type. However I believe I can explain this one:
""==[] (returns false)
The initial string "" indicates the comparison will be a string one, and thus [] is cast to a string. When that happens, the right hand side of the comparison will be set to the word Array. You are therefore doing this comparison:
"" == "Array" (returns false)
and thus false is the correct result.
Edit: a helpful comment below casts doubt on my answer via this live code example. I should be interested to see what other answers are supplied.
I have seen programming practices where a string or a boolean can be returned from a function. In such cases, is it recommended to check for empty() and isset() in the if loops, or will just doing a check like if($returnvar) will work or not.
Here's a piece of code I am playing around with. What do you think should be the output and are the checks correct in the if condition?
Thanks,
<?php
function testMe()
{
try
{
$returnText = 'John King Rocks';
return $returnText;
}
catch (Exception $e)
{
return false;
}
}
$str = testMe();
if ($str)
{
echo $str;
}
?>
This code should work (for this specific example of $str), but checking for Boolean in PHP is risky, as you suggested, and should be done with caution, I would suggest (in general) to check it as follows:
if ($str !== false)
{
echo $str;
}
What will happen is $str will be type casted to a boolean. So, the string will evaluate to false when it is:
The empty string
The string "0"
Every other value is considered true, which may not be the behavior you desire. So, to answer your questions:
What do you think should be the output?
Well, the return string doesn't match either of the two conditions, so the if statement will evaluate to true and the $str will be echo()'d. (Not to mention that the above code can never produce an Exception).
Are the checks correct in the if?
That depends on the functionality you're looking for. If you want to consider all strings (including "" and "0") to be valid, and to skip the echo() only when the function returns false, then you should check if the return value's equality with !== false. Otherwise, if those two conditions above are acceptable as false, you should be fine.
if is not a loop. It's a conditional. echo false will be coerced to echo '0', so the if check is only necessary if you don't want to print a zero. However, echo null will not print anything at all. Even better would be to return empty string. You should avoid mixing return types. Many other languages don't even allow it.
By the way your exception handling does nothing .. the contents of the try cannot throw an exception. Swallowing exceptions is also bad, and you need a $ before the e.
In answer to your question, there is a phrase "paranoid programming is professional programming." That is, you should do as many checks as possible if you want your application to work consistently (and the if check is good in this case), but it's also important to know what methods you are working with and the expected result. Do you want some other handling if testMe is false? Or do you want to just not print anything?
I see a lot of people using a variety of different methods to check whether of a variable is empty, there really seems to be no consensus. I've heard that if($foo) is exactly the same as if(!empty($foo)) or if($foo != ""). Is this true?
I realize it's a really simple question, but I'd really like to know. Are there any differences? Which method should I use?
Difference between bare test and comparison to empty string
if($foo != "") is equivalent to if($foo) most of the time, but not always.
To see where the differences are, consider the comparison operator behavior along with the conversion to string rules for the first case, and the conversion to boolean rules for the second case.
What I found out is that:
if $foo === array(), the if($foo != "") test will succeed (arrays are "greater than" strings), but the if($foo) test will fail (empty arrays convert to boolean false)
if $foo === "0" (a string), the if($foo != "") test will again succeed (obviously), but the if($foo) test will fail (the string "0" converts to boolean false)
if $foo is a SimpleXML object created from an empty tag, the if($foo != "") test will again succeed (objects are "greater than" strings), but the if($foo) test will fail (such objects convert to boolean false)
See the differences in action.
The better way to test
The preferred method to test is if(!empty($foo)), which is not exactly equal to the above in that:
It does not suffer from the inconsistencies of if($foo != "") (which IMHO is simply horrible).
It will not generate an E_NOTICE if $foo is not present in the current scope, which is its main advantage over if($foo).
There's a caveat here though: if $foo === '0' (a string of length 1) then empty($foo) will return true, which usually is (but may not always be) what you want. This is also the case with if($foo) though.
Sometimes you need to test with the identical operator
Finally, an exception to the above must be made when there is a specific type of value you want to test for. As an example, strpos might return 0 and also might return false. Both of these values will fail the if(strpos(...)) test, but they have totally different meanings. In these cases, a test with the identical operator is in order: if(strpos() === false).
No it's not always true. When you do if($foo) PHP casts the variable to Boolean. An empty string, a Zero integer or an empty array will then be false. Sometimes this can be an issue.
You should always try to use the most specific comparison as possible, if you're expecting a string which could be empty use if($foo==='') (note the three equal signs). If you're expecting either (boolean) false or a resource (from a DB query for instance) use if($foo===false){...} else {...}
You may read the documentation about casting to boolean to find the answer to this question. There's a list in there with which values are converted to true and false, respectively.
Note that empty also checks if the variable is set, which regular comparison does not. An unset variable will trigger an error of type E_NOTICE during comparison, but not when using empty. You can work around this using the isset call before your comparison, like this:
if(isset($foo) && $foo != '')
if() "converts" the statement given to a bool, so taking a look at the documentation for boolean seems to be what you're looking for. in general:
empty strings (""), empty arrays (array()), zero (0) and boolean false (false) are treated as false
everything else ("foo", 1, array('foo'), true, ...) is treated as true
EDIT :
for more information, you could also check the type comparison tables.
empty($foo) should return true in all of these cases: 0,"", NULL.
For a more complete list check this page: http://php.net/manual/en/function.empty.php
No, it's not equal. When variable is not defined, expression without empty will generate notice about non-defined variable.
PHP:
$a = "0";
$b = "00";
var_dump(empty($a)); # True (wtf?)
var_dump($a == $b); # True... WTF???
var_dump(empty($b)); # False WWWTTTFFFF!!??
I've read the docs. But the docs don't give explanation as to why they designed it this way. I'm not looking for workarounds (I already know them), I'm looking for an explanation.
Why is it like this? Does this make certain things easier somehow?
As for "0" == "00" resolving to true, the answer lies in Comparison Operators:
If you compare an integer with a
string, the string is converted to a
number. If you compare two numerical
strings, they are compared as
integers. These rules also apply to
the switch statement.
(emphasis added)
Both "0" and "00" are numerical strings so a numerical comparison is performed and obviously 0 == 0.
I'd suggest using === instead if you don't want any implicit type conversion.
As for empty():
The following things are considered to
be empty:
"" (an empty string)
0 (0 as an integer)
"0" (0 as a string)
NULL
FALSE
array() (an empty array)
var $var; (a variable declared, but without a value in a class)
http://au2.php.net/empty
The following things are considered to be empty:
"0" (0 as a string)
but "00" will not be considered empty.
It all stems from the language designers goal of "doing the right thing".
That is a given piece of code should do what the niave programmer or casual viewer of a piece of code would expect it too. This was not an easy goal to acheive.
Php has avoided most of worst pitfalls of other languages (like C's if (a = b) { ... or perl' s if ( "xxxx" == 0) { print "True!"; }).
The 0 == 0000 and if ("000") { echo "True!"; } are two of the few cases where code might not do exactly what you expect, but in pracice it is seldom a problem. In my experience the "cure" using the exact comparison operator === is the one thing guarenteed to have novice php programmers scratching there heads and searching the manual.
It has do do with what PHP considers empty, and, as #Shadow imagined, it's a dynamic typing issue. 0 and 00 are equal in PHP's eyes. Consider using the strict equality instead:
($a === $b) // is a equal to b AND the same type (strings)
Check the docs for empty http://us.php.net/empty. That should take care of the first and third lines.
for the second, it's because PHP is dynamically typed. the interpreter is inferring the type of the variables for use in the context in which you have used them. In this case the interpreter is probably thinking that you are trying to compare numbers and converting the string to ints before comparing.
From the documentation one can assume that 0 can be either an int or a 1 char string to signify empty. 00 would be more of a formatting assumption since there's no such thing as 00, but there is 0. 00 would be implying a 2 integer format, but the empty() function is only written for 0.
FWIW IANA php developer.