With PHP suddenly back in the spotlight, I figured this was a good time to revisit one of the web's most successful, most criticized, and occasionally most bewildering programming languages.
PHP has been powering websites for decades. It has evolved a lot, particularly through PHP 7 and 8, but it still carries some fascinating quirks.
And I'm not talking about obscure syntax or features nobody uses. I'm talking about perfectly ordinary code that does something completely unexpected.
Here are three of my favorites.
1. The String "0" Is False. The String "00" Is True.
Let's start with something simple.
var_dump((bool) "0"); // false
var_dump((bool) "00"); // true
var_dump((bool) "false"); // true
var_dump((bool) ""); // false
Isn't that beautiful?
The string "0" evaluates to false, but "00" evaluates to true.
Even the literal string "false" evaluates to true.
PHP considers an empty string and the specific string "0" to be falsy. Other nonempty strings, including "00", are truthy.
Now consider:
$password = "0";
if (!$password) {
echo "No password provided!";
}
Except a password was provided. PHP just decided it didn't count.
And if you're thinking that empty() might save you:
empty("0"); // true
empty("00"); // false
Nope.
The lesson: Don't use truthiness to determine whether a string contains meaningful input. Be explicit about what you're checking.
2. PHP Arrays Can Silently Overwrite Your Data
PHP arrays aren't really conventional arrays. They're ordered maps that support both integer and string keys.
Which sounds perfectly reasonable until you do this:
$data = [
"8" => "eight",
8 => "EIGHT",
"08" => "zero eight"
];
print_r($data);
You might expect three entries.
You get two.
Array
(
[8] => EIGHT
[08] => zero eight
)
PHP automatically converts the string key "8" to the integer 8.
So the second entry overwrites the first.
But "08" stays a string because it isn't a valid decimal integer representation for this conversion.
And it gets better:
$data = [
1 => "integer",
"1" => "string",
true => "boolean"
];
print_r($data);
Result:
Array
(
[1] => boolean
)
Three apparently different keys. One surviving value.
The first two collide because "1" becomes integer 1. The third collides because true also becomes integer 1.
The lesson: PHP array keys undergo implicit type conversion. If your application depends on distinguishing keys by type, you're in for an unpleasant surprise.
3. A foreach Loop Can Quietly Corrupt Your Array
This is my favorite, because it's the kind of bug that can survive a code review.
Consider:
$numbers = [1, 2, 3];
foreach ($numbers as &$number) {
// Do some processing
}
foreach ($numbers as $number) {
// Do some more processing
}
print_r($numbers);
Nothing particularly exotic here.
The first loop iterates by reference. The second iterates normally.
What does the final array contain?
Array
(
[0] => 1
[1] => 2
[2] => 2
)
Wait. What happened to the 3?
The culprit is that innocent-looking ampersand in the first loop:
foreach ($numbers as &$number)
When that loop finishes, $number remains a reference to the last element of the array.
The reference doesn't magically disappear when the loop ends.
Then the second loop reuses $number.
Each iteration assigns another array value to a variable that's still referencing the last element.
By the time the second loop finishes, the last element has been overwritten with the second-to-last value.
The truly delightful part?
This behavior is explicitly documented in the PHP manual.
The fix is simple:
foreach ($numbers as &$number) {
// Do some processing
}
unset($number); // Break the reference
But the fact that you need to remember to do this is what makes the behavior so interesting.
The lesson: References in PHP can outlive the control structures that introduced them. Reusing a variable after a by-reference loop can have side effects that are surprisingly difficult to spot.
So, Is PHP Broken?
Not really.
All three behaviors follow documented rules. They're not random, and they're not bugs in the PHP interpreter.
But they illustrate something interesting about programming languages.
A language can be internally consistent while still producing results that surprise the people using it.
PHP has spent decades balancing developer convenience, backward compatibility, and language evolution. Sometimes those priorities have produced genuinely useful features. Sometimes they've produced behavior that makes you stare at your screen for a few minutes.
And that's part of what makes exploring programming languages so entertaining.
This was fun so I think I'll make it a series looking at the stranger corners of familiar languages.
Top comments (0)