PHPBooleans & Truthiness

Booleans & Truthiness

The bool type has only two possible values, true and false, and on the surface that seems like the simplest type PHP has. The subtlety is not the type itself, but the rules PHP uses to decide whether some other type — a string, a number, an array — should count as true or false when it shows up in a boolean context such as an if statement. Those rules are called truthiness, and PHP's specific list of exceptions is a well-known source of bugs if you have not memorized it.

The literal bool values

true and false

PHP
<?php
$isLoggedIn = true;
$hasPermission = false;

var_dump($isLoggedIn);
var_dump($hasPermission);
var_dump($isLoggedIn && $hasPermission);
var_dump($isLoggedIn || $hasPermission);
bool(true)
bool(false)
bool(false)
bool(true)

true and false are keywords and are case-insensitive (TRUE and False both work), though writing them lowercase is the near-universal convention in modern PHP code.

The falsy values

When any non-boolean value is used where a boolean is expected, PHP converts it using a short, fixed list of "falsy" values. Everything that is not on this list converts to true. Memorizing this list is genuinely worth doing, because it is short and it is the source of nearly every truthiness surprise in PHP.

  • The integer 0 and the float 0.0 (and -0.0).

  • The empty string "".

  • The string "0" — but no other numeric string, e.g. "0.0" and "00" are both truthy.

  • An empty array [].

  • null.

Checking each falsy value

PHP
<?php
$falsyCandidates = [0, 0.0, "", "0", [], null];

foreach ($falsyCandidates as $candidate) {
    echo var_export($candidate, true) . ' is ' . ($candidate ? 'truthy' : 'falsy') . "\n";
}
0 is falsy
0.0 is falsy
'' is falsy
'0' is falsy
array (
) is falsy
NULL is falsy
The surprising truthy cases

Two values in particular catch people off guard because they look like they "should" be falsy by analogy with "0", but PHP treats them as truthy: the string "0.0" and a string containing just a space, " ".

'0.0' and ' ' are truthy

PHP
<?php
var_dump((bool) "0.0");  // bool(true) - only the exact string "0" is falsy
var_dump((bool) " ");    // bool(true) - a space is a non-empty string
var_dump((bool) "0");    // bool(false)
var_dump((bool) "false"); // bool(true) - the word "false" is just a non-empty string!
bool(true)
bool(true)
bool(false)
bool(true)
Only the exact string "0" is falsy
PHP's falsy-string rule is extremely literal: it checks whether the string is precisely `"0"`, not whether the string represents zero numerically. `"0.0"`, `"0.00"`, and `" 0"` (with a leading space) are all truthy, because none of them are byte-for-byte equal to the single-character string `"0"`.
The falsy-values table

Value

Truthy or falsy?

0

Falsy

0.0

Falsy

"0"

Falsy

""

Falsy

[]

Falsy

null

Falsy

"0.0"

Truthy

" " (single space)

Truthy

"false"

Truthy

[0]

Truthy (a non-empty array, even of falsy values)

The classic string-"0" comparison bug

A frequent real-world bug comes from strpos(), which returns the integer position where a substring was found, or false if it was not found at all. If the substring is found at position 0 — the very start of the string — a loose truthiness check treats that success as if it were a failure, because 0 is falsy.

The strpos() pitfall

PHP
<?php
$sentence = "PHP is fun";

if (strpos($sentence, "PHP")) {
    echo "Found it!";
} else {
    echo "Not found."; // this branch runs, even though "PHP" IS in the string!
}
Not found.

strpos($sentence, "PHP") actually returns 0, because "PHP" starts at index 0 — but if (0) is falsy, so the wrong branch executes. The fix is to compare against false explicitly with the strict !== operator, rather than relying on truthiness.

The correct, strict fix

PHP
<?php
$sentence = "PHP is fun";

if (strpos($sentence, "PHP") !== false) {
    echo "Found it!";
} else {
    echo "Not found.";
}
Found it!
Newer alternatives sidestep the problem entirely
PHP 8.0 added `str_contains()`, `str_starts_with()`, and `str_ends_with()`, which return a proper `bool` instead of a mixed int-or-false value. Reaching for `str_contains($sentence, "PHP")` avoids the truthiness trap altogether, since there is no ambiguous `0` result to worry about.
Converting explicitly with (bool)

Explicit boolean casting

PHP
<?php
var_dump((bool) "yes");   // bool(true) - any non-empty, non-"0" string
var_dump((bool) 3.14);    // bool(true) - any non-zero number
var_dump((bool) [0, 0]);  // bool(true) - non-empty array, contents don't matter
bool(true)
bool(true)
bool(true)
Tip
Whenever a function can return a meaningful `0`, an empty string, or `false` as distinct, valid results (like `strpos()`, `array_search()`, or `preg_match()`), always compare its result with `===` or `!==` rather than treating it as a plain boolean. It costs nothing and eliminates an entire category of off-by-truthiness bugs.