"0", as a string containing one character, is not something empty intuitively. Why does PHP treat it as FALSE when converted to a boolean, unlike other programming languages?
2 Answers
PHP was designed (or, rather, evolved) for use with web requests, where you're frequently dealing with string input (URL parameters, or POST requests from a form in a browser). As such, it will automatically cast strings to other types.
A simple example of this is that '1' + '2'
gives 3
, not an error, or '12
', or some other interpretation. By the same logic, the string '0'
can be used as a numeric 0
.
Meanwhile, like many languages, PHP treats certain values as "falsy" when cast to boolean - things that are intuitively "empty", as you say. That includes numeric 0
, as well as the empty string ''
and the empty array []
. In an if
statement, the expression is explicitly cast to boolean, so if ( 0 )
is the same as if ( false )
.
Putting these two things together, you get a conundrum: on the one hand, as you say '0'
is a non-empty string; on the other hand, we have said that it can be used as a numeric 0
, which is "empty". PHP opts to treat the "zero-ness" as more important than the "stringiness", so that '0'
is considered "falsy".
In short: '0' == 0 == false
; or (bool)'0' === (bool)(int)'0'
-
1I think PHP took this from Perl, which has the same “
"0"
is false” behaviour. In Perl, there literally isn't any user-visible difference between the string"0"
and number0
– annoying when handling JSON, but very intuitive when handling textual data. It is then impossible to have the number0
be falsey without also the string"0"
being falsey. PHP and JavaScript borrow this design decision, but add some confusion by distinguishing strings/bools/numeric types while still allowing implicit conversions (PHP:0 == "0 foo"
,0 == false
, but"0 foo" == true
:==
isn't transitive)– amonCommented Dec 19, 2016 at 12:49 -
@amon Yes, Perl takes the interesting approach of (nearly) all operators and built-in functions forcing operands to a particular type, with extra operators like
eq
for string equality. However, this doesn't save it from the non-transitivity of lossy casts:"0 foo"
is truthy in a boolean context, but is equal to0
under==
. Soif ( ! $foo ) ..
and$false = (1 == 2); if ( $foo == $false ) ...
don't give the same result. I guess the language is missing a "boolean equality" operator which could treat this consistently...– IMSoPCommented Dec 19, 2016 at 13:20 -
Although "0" is 0 when evaluated as numeric, when we use a string in a boolean context (for example in an if statement), we are not interested in its numeric value. If we want to interpret it as numeric, we would compare it with some number, for example,
$_POST['id'] == 0
, which makes the intent clear that we want to treat the user input as a number. Commented Dec 20, 2016 at 1:20 -
@MichaelTsang Sure, if you want to make the intent clear, never use anything in an implicit boolean context; indeed, the most consistent language design would be to error if you tried to, rather than trying to give you a "reasonable" result. But that "we are not interested in..." is a matter of opinion; why are you not interested in its numeric value? How does PHP know that? You may not agree with the way PHP's authors resolved the question, but there is a sound logic to how it works.– IMSoPCommented Dec 20, 2016 at 10:16
-
1@MestreLion That example was of Perl rather than PHP. The crucial difference being that there is no separate boolean type in Perl, so
$false
ends up set to0
; so when you write$foo == $false
, Perl doesn't know you want a boolean comparison, and casts to int instead. In PHP, that doesn't happen, because$false
is boolean false, so==
casts to boolean, giving the same result as!
– IMSoPCommented Sep 29, 2018 at 14:09
According to the PHP documentation on booleans, it says that:
When converting to boolean, the following values are considered
FALSE
...
the empty string, and the string "0"
...
Otherwise:
Every other value is considered TRUE (including any resource).
If you run:
var_dump((bool) "0");
It will print:
bool(false)
So it's working as expected.
To explicitly answer your question:
However, in most cases the cast is unnecessary, since a value will be automatically converted if an operator, function or control structure requires a boolean argument.
This means that PHP's "autocast" will cast "0" to integer 0, which is FALSE
as well in a control structure like say an if()
statement.
-
I would like to know the rationale of the design decision treating "0" as FALSE when converted to boolean, rather than the need of explicit casting. Commented Dec 20, 2016 at 1:17
'0'
is treated as0
in$x + 1
, why shouldn't it also be treated as0
, and thereforefalse
, inif ( $x )
?