How to differentiate between null or "" and 0 in PHP? - php

I have a table of codes in a database. One of the codes has the id of 0.
I'm making a search function and I want one of the filters to be the codes, but it's optional. So, I need to differentiate between "0" and null in an if statement.
Here is one idea that looks to me like it would work...
if ( 0 < (int)$input['code'] or false !== strpos($input['code'], "0") ){
// filter the search with the code
}
From this answer to a general question there are not that many differences between 0 and null. isset() might be one but empty() will not work. I'm not absolutely sure if the input is null or "" after it has been processed, I think it may be "". If this is the case, isset() will not work either.
Is there a better way to differentiate between 0 and null/""?

Use the === and !== operators for these sort of comparisons. These "strict" or "true" comparison. These operators were invented specifically to help deal with the sort of comparison problems you've described.
You also might be interested in the PHP comparison tables. This docs page runs through a lot of the various edge cases in PHP equality.

Related

for loop with isset() != null - why?

for($i=0; isset($_POST['key_str'][$i]) != null; $i++)
{
// some php here
}
I've inherited some legacy code at work and I've found above for() loop in several places. I've been writing PHP, Javascript and Python for years now and have never seen anything like this. My gut tells me this is the person who wrote this legacy code came from a different language. And may have not been very experienced.
Questions:
1) Does isset($_POST['key_str'][$i]) perform better than count($_POST['key_str'])?
2) Does this resemble syntax that you'd typically find in another language? If so, which language?
Inside isset, $i (which is incremented in the loop) is used in this expression. $_POST['key_str'][$i], which is part of the check.
So basically, $_POST['key_str'] is expected to be an array, and this loop will loop over all items in that array.
If you like, you could use count(), or replace the whole thing with a foreach loop, although that may result in a warning if $_POST['key_str'] is not set at all or is not an array. isset is a very easy way to get around that, because it handles all those situations and will return false, so the loop will simply not be entered in that scenario.
isset() is a language construct that returns a boolean, thus it can never be null. However, the comparison uses the equal operator (==) rather than the identical operator (===) thus type juggling applies:
Type of Operand 1 Type of Operand 2 Result
bool or null anything Convert both sides to bool
… and null casts to boolean false so:
true != null → true != false → true
false != null → false != false → false
Thus the loop is equivalent to:
for($i=0; isset($_POST['key_str'][$i]); $i++){}
In other words, != null is redundant and harms readability.
Now, square brackets are used to read an array element by key or a string byte by offset. Since $_POST is an external variable the loop can actually do both—I presume the former was intended. Without further context it's impossible to say what it's meant to accomplish or how to rewrite it but I suspect it's a convoluted alternative to foreach().
So to answer your questions:
It's irrelevant. They do entirely different things.
This is opinion-based (to me, it's the syntax of someone who's not familiar with programming in general.)

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.

Is (isset(..) || array_key_exists(...)) a faster way of detecting null values than just array_key_exists(...))?

While researching how to detect null values in an array, I came across some user's comment under the http://www.php.net/manual/en/function.array-key-exists.php manual page.
It said that
if (isset(..) || array_key_exists(...))
{
...
}
is faster than doing
if array_key_exists(...))
{
...
}
The bench marks posted for 100000 runs were
array_key_exists() : 205 ms
is_set() : 35ms
isset() || array_key_exists() : 48ms
My question:
Is (isset(..) || array_key_exists(...)) faster than array_key_exists() ?
If so, why?
EDIT: In writing out this question I think I found my answer. I've decided to post the question anyway to see if my thinking is correct.
Which is faster depends on the array you are checking. If the array contains a value other than null, "", or 0
if (isset(..) || array_key_exists(...)){
}
the above code will be faster, because isset will be checked then the code executed. Array_key_exists will not be run.
If the array contains a value of null, "", or 0 then isset will be tested and then array_key_exists.
This will take longer than simply testing for array_key_exists by itself.
So the question which is faster depends a lot on the array you are checking.
A lot of people have said that it doesn't really matter. They didn't explain why it doesn't matter though. I guess they mean that the speed improvements are so minimal that it isn't worth bothering with. They may also mean that which is faster is dependent on the values assigned in your array (and thus different each time.)
Ultimately though, if you know that most of the keys will be assigned values other than null, "" or 0 and you really need to determine when null values are assigned, then use
if (isset(..) || array_key_exists(...)){
}
Sorry, but to detect null values in an array, you can first CHECK the array using in_array which will tell you if the array even contains a "null". I'm not quite sure what you want to actually achieve, do you want to find a null value or what? Will edit when you tell us more.
The most reliable method would be isset() || array_ky_exists() as it checks the most, and short-circuit evaluation will speed up the process.
However, as pst said in the comments: It just doesn't matter. Really, it makes almost no difference in the end.
Basically, though, no, it is not. In the end, basic math would tell you that two checks is not faster than one check.

identity conditional "===" , performance, and conversion

I've always came away from stackoverflow answers and any reading I've done that "===" is superior to "==" because uses a more strict comparison, and you do not waste resources converting value types in order to check for a match.
I may be coming at this with the wrong assumption, so I assume part of this question is, "is my assumption true?"
Secondly,
I'm dealing specifically with a situation where I'm getting data from a database in the form of a string "100".
The code I am comparing is this...
if ($this->the_user->group == 100) //admin
{
Response::redirect('admin/home');
}
else // other
{
Response::redirect('user/home');
}
vs.
if ( (int) $this->the_user->group === 100) //admin
{
Response::redirect('admin/home');
}
else // other
{
Response::redirect('user/home');
}
or even
if (intval($this->the_user->group) === 100) //admin
{
Response::redirect('admin/home');
}
else // other
{
Response::redirect('user/home');
}
is any integrity (or performance) gained by manually casting or converting simply so you can use the identity ('===') comparison?
In your particular case == is the better option. As you (as can be seen in your code) have probably already found out many database functions will always return strings, even if you fetch an integer. So type strict comparison really only bloats your code.
Furthermore you are adding a potential (let's call it theoretic) security risk. E.g. (int) '100AB2' would yield 100. In your case this probably can't happen, but in others it may.
So: Don't overuse strict comparison, it's not always good. You mainly need it only in ambiguous cases, like the return value of strpos.
There is a performance difference between == and === - latter will be even twice as fast, see Equal vs identical comparison operator.
The difference, however is too small to be bothered with - unless the code is executed millions of times.
That's a really tiny optimization you're doing there. Personally, I don't think it's really worth it.
Any boost you gain from not casting the value when using === is lost when you explicitly cast the value. In your case, since the type is not important to you, you should just do == and be done with it.
My recommendation would be to keep === for when you need to check type as well - e.g. 0 evaluating to false and so on.
Any performance gains will be microscopically small, unless you're performing literally billions and trillions of these comparisons for days/months/years on-end. The strict comparison does have its uses, but it also is somewhat of anomally in PHP. PHP's a weakly typed language, and (usually) does the right thing for auto-converting/casting values to be the right thing. Most times, it's not necessary to do a strict comparison, as PHP will do the right thing.
But there are cases, such as when using strpos, where the auto-conversion will fail. strpos will return '0' if the needle you're searching is right at the start of the haystack, which would get treated as FALSE, which is wrong. The only way to handle this is via the strict comparison.
PHP has some WTF loose comparisons that return TRUE like:
array() == NULL
0 == 'Non-numeric string'
Always use strict comparison between a variable and a string
$var === 'string'

Why does comparison and empty() behave like this in PHP?

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.

Categories