if($val) vs. if($val != "") vs. if(!empty($val)) -- which one? - php

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.

Related

PHP and proper way of using the strcmp function

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.

Check if variable has value and isn't empty

I want to check if a numeric variable has a value (including '0') and is not empty. Empty meaning EMPTY (''), not '0'.
Is this really the best I can do with PHP?
if (isset($variable) && $variable !== '') { ... }
I'd like to do this with one check without writing a function for it...
What you are trying to check is string length, not "empty". This can easily be done using strlen().
if (isset($variable) && strlen($variable) > 0) {
// Do something
}
If you want to exclude whitespace as invalid, you can add a trim() in there as well (generally recommended).
if (isset($variable) && strlen(trim($variable)) > 0 } {
// ...
}
The best thing you could do, is making your own custom function. The point is to pass the variables by reference to not trigger a warning, when you pass an undefined variable. As posted as comment, I'd use something along the line isset($variable) AND !empty($variable) AND !is_numeric($variable) AND $variable !== false to cover all cases.
Your custom function could look like this (improved version):
function is_blank(&$variable) {
return (bool) !(isset($variable) AND (!empty($variable) OR is_numeric($variable) OR $variable === false));
}
https://3v4l.org/ZcCDu
Yes, your way is the best (most efficient) way to:
insure the variable has been set (so you don't get an warning checking a variable that's not been set)
it's not the empty string ''
But, could be '0', 0,false, null, or [] which all count as empty in php, but you wish to consider as non-empty as indicated by your OP
your !== will ensure only exactly the string '' is compared (no casting/conversion)
The use of strlen works as well, but if you look at the opcode generated you'll see direct comparison is more 3 times computationally more efficient (assuming all operations are equally weighted, even more efficient if operations like DO_FCALL take significantly more cycles to execute than a basic IS_NOT_IDENTICAL check)
The !== ''version bytecode:
IS_NOT_IDENTICAL ~1 !0, ''
The strlen() > 0 version bytecode:
SEND_VAR !0
DO_FCALL 1 $1 'strlen'
IS_SMALLER ~2 $1, 0
(The answer has been edited. Consult the additionals further down under "ternary operations").
Why go through the trouble of using all that?
Just use an "not empty" if(!empty($var)){...}
However, if you're using this with a GET array, then yes; it would be best to use an isset() and empty() on a conditional statement.
I want to check if a variable has a value (including '0') and is not empty
That to me interprets as:
Check if a value has a value and is not empty (as you wrote) and stands to contain a 0 (zero).
Therefore:
if(!empty($var) && $var='0'){...}
I'd like to do this with one check without writing a function for it...
Use a ternary operator then.
However "without a function"... right well you can't. You still need "some type of function".
About that "ternary operator" I mentioned above. You can reference what are called "nested ternary operations" in both these Q&A's on Stack:
How to concatenate multiple ternary operator in PHP?
nested php ternary trouble: ternary output != if - else
That way you won't need a custom function.
Sidenote: I am by far not taking away or trying to take away from (Charlotte's) accepted answer (which should remain as accepted). This is just an additional method of achieving your (ultimate) goal.

Several PHP type-juggling comparisons, such as empty string and an empty array, return unexpected results

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.

Underlying philosophy behind php type comparisons

So there's this page on the php site which shows the result of comparing different values:
http://php.net/manual/en/types.comparisons.php
This is a helpful reference, but I would rather not have to visit this page every time I want to make sure that I'm doing type comparison right. So my question is
Is there some kind of underlying philosophy/reasoning behind the logic of type comparisons on PHP?
For example, I can see that for loose comparisons:
1, -1, "1" and "-1" can be treated as TRUE and 0 and "0" can be treated as FALSE;
Comparing the string value of a number against the number itself with yield TRUE;
but it becomes a bit hairy from then on trying to establish a pattern.
For casting directly to a boolean this is how it works.
All string with a length > 0 are true
All non 0 numbers are true
All non-empty arrays are true
All objects are true
Then these rules for comparing variables of the same type:
Objects are equivalent if their properties are equal
Arrays are equivalent if their keys and elements are equal
Strings are equivalent if they would produce the same output
Numbers are equivalent if they are mathematically equivalent
Booleans are equivalent if they have the same value.
For variable of different types the type that is higher on the above list is cast to the one that is lower then the comparison is made.
=== and !== operators don't cast prior to comparing but you should note objects are only === if they are the same instance.
The really odd one is arrays, they are === if they have the same keys and values defined in the same order.
$a = array("a"=>1, "b"=>2);
$b = array("b"=>2, "a"=>1);
$a == $b; // true
$a === $b; // false
and empty() is equivalent to !(bool)$var
EXCEPTIONS
Casting an array to a string will trigger a notice and unhelpfully cast as the text Array
Casting an object without a __toString method to a string will get you a fatal error.
Objects will not implicitly cast to an array, so any time you compare an object to an array it will yield a false (UPDATE confirmed that this is true even if object implemtents the ArrayAccess interface)
For strict === comparision, the logic is easy: each value entity is equal only to itself, so TRUE === TRUE, "1" === "1", but "1" !== 1 etc.
When it comes to == comparision, unfortunately there is no rule of thumb nor a clear logic. This is probably because the various forms of the operator were implemented by different programmers, without a central design decision. The best I can do is providing you with this graph to print and stick over the monitor:
The key of the grap is: A == B will be TRUE if and only if A and B are of two types directly connected by a line in the graph above. For instance, array() == NULL is TRUE because array() and NULL are directly connected, while array() == 0 is FALSE because there is no line connecting the two.
Lines marked in red are the tricky (non obvious) equalities.
I've omitted that each entity will be equal to itself (e.g. "1" == "1" etc.) but that should be easy to remember.
As a final note, I'd like to explain why "php" == 0 is TRUE (non empty, non number string is equal to 0): because PHP casts "php" to number before comparision and, since it's not a number, it defaults to 0 and makes the test TRUE.
Fun fact: there is no partition in this relation! If ever a transitive closure was allowed, you could easily say that True is False and False is True, destroying millennia of philosphy in four easy PHP statements :D
If the value contains something then it can be said to be true. For example, 1, 1.123, array("value"), etc. are all treated as true.
If the value can be said to be empty or void (i.e. lacking something) then it is seen as false. For example, 0, 0.0, array(), and so on.
This way of thinking about variables is not special to PHP. Many other languages do it in the same or similar way. E.g. Perl, C and Javascript, just to name a few.
There is imo a very straightforward guideline and a bug in the specification, which might be confusing.
Strict comparison checks equality in datatype and value.
Loose comparison checks equality in value only.
For an object (not part of the comparison table) is php quite straightforward:
if the object is the same instance as the other one, then is it strictly equal, otherwise might it be loosely equal.
Therefor is a 0 and a "0" loosely equal to each other and to false (and to any string). The latter can be understood as all strings are not numeric, hence false and the number that is equal to false is 0, hence all strings are equal to 0.
The comparison between null and array() is more complicated. If you check an array created with array() and compare that loosely and strictly, then will it return true. If you however check it with is_null, then will it return false. I think the latter is more logical, because an array() created with array() is not equal to '', where null is. I would think that this functional inconsistency between the function is_null() and the checks
== null or === null a bug, because it should not happen that using two different valid methods to check for a value return different results.
Null is also not an array according to the function is_array(), which is true. An empty array is an array according to the function is_array(), which should be true too. Hence should it never be true that null is equal to array().
There is no particular logic, but you can figure out some patterns.
"empty" values (null, false, 0, empty string and string '0') evaluate to false
comparison of numeric values is done implicitly converting them to integers until some version (there was a bug when two actually different long numeric strings counted as equal, now it's fixed)
when working with arrays, there is no difference between integer and numeric indexes, except when you call array_key_exists with explicit strict parameter
comparing number with string implicitly converts right argument to the type of the left one
return ($something); implicitly converts $something to string if it is not scalar
The base pattern is the same to the one used in C: anything non-zero is true for the sake of boolean comparisons.
In this sense, an empty string or array is also false.
The hairy scalar to look out for is '0', which is (very inconveniently) treated as empty too because it gets converted to an integer. array(0) is just as thorny on the array front.
When using strict comparisons (=== and !==), things are a lot more sane. In practice, it's often a good idea to cast input coming from superglobals and the database as appropriate, and to use these operators from that point forward.
I look at it the following way:
PHP is designed as a web programming language and all the input of the pages is based on strings [human-like perception] [This is by the way is also true for JavaScript]
Hence, all the strings which look like numbers (is_numeric() function), preliminary behave like numbers [comparison, casting].
That explains why extreme cases, like "0" are first implicitly thought to be cast to (int)0 and only then to false.

Should one use strict comparison in Strings?

I know that for instance, using:
if (in_array('...'), array('.', '..', '...') === true)
Over:
if (in_array('...'), array('.', '..', '...') == true)
Can increase performance and avoid some common mistakes (such as 1 == true), however I'm wondering if there is a reason to use strict comparisons on strings, such as:
if ('...' === '...')
Seems to do the exactly same thing as:
if ('...' == '...')
If someone can bring some light to this subject I appreciate it.
If you know both of the values are guaranteed to be strings, then == and === are identical since the only difference between the two is that === checks to see if the types are the same, not just the effective values.
However, in some cases you don't know for sure that a value is going to be a string - for example, with things like the $_GET and $_POST variables. Consider the following:
$_GET['foo'] == ""
The above expression will evaluate to true if foo was passed in as a blank string, but it will also evaluate to true if no value was passed in for foo at all. In contrast,
$_GET['foo'] === ""
will only evaluate to true if a blank string was explicitly passed in - otherwise the value of $_GET['foo'] might be equivalent to a blank string, but the type would not be since it would actually be an empty value for that index, not a string.
When you can use one or the other choose the strict comparison because:
It has better performance
It prevents unexpected results
When comparing strings you can still have unexpected results because a string could be empty or a variable you think is a string actually is not.
You would never use the comparison of two string literals because it can always be reduced to TRUE or FALSE. For example:
if ('...' === '...')
is the same as
if (TRUE)
So since you will always be comparing at least one variable you must assume that you can have unexpected results.
You can see benchmark results of various strict vs. loose comparisons at http://net-beta.net/ubench/. I have also ran my own tests and found the same results.
This is a micro optimization, which means you shouldn't go changing existing code because it isn't going to make a noticeable difference, but if you are writing new code you might as well practice using the most efficient techniques.

Categories