Table of Contents
I just spent 2 hours debugging a “works fine without this condition” bug, only to discover the most face-palm PHP mistake I’ve made in years: checking is_string('field_name') instead of is_string($data['field_name']).
The Bug
The code was supposed to merge currency codes from multiple sources, but only if a specific field contained a valid string:
$currencyCodes = [];
foreach ($batches as $batch) {
// ... collect from batches ...
}
// Merge in historical data if it exists
if (is_string('historical_currencies')) { // π BUG HERE
$currencyCodes = array_unique(array_merge(
$currencyCodes,
explode(',', $batch['historical_currencies'])
));
}
See the problem? I’m checking if the literal string 'historical_currencies' is a string (it always is), not whether $batch['historical_currencies'] contains string data.
The Result
When $batch['historical_currencies'] was NULL:
- The condition
is_string('historical_currencies')evaluated toTRUE(literal strings are always strings) - The code tried to
explode(',', NULL) - PHP 8.1+ throws a deprecation warning, but older versions silently return an empty array
- Result: missing data, no error logs, mystery bug
The Fix
// Correct version
if (is_string($batch['historical_currencies'])) {
$currencyCodes = array_unique(array_merge(
$currencyCodes,
explode(',', $batch['historical_currencies'])
));
}
Or better yet, use optional chaining with type safety:
if (!empty($batch['historical_currencies']) && is_string($batch['historical_currencies'])) {
$currencyCodes = array_unique(array_merge(
$currencyCodes,
explode(',', $batch['historical_currencies'])
));
}
How This Slipped Through
- No static analysis – PHPStan/Psalm would catch this immediately
- Conditional always TRUE – so tests with valid data passed
- Silent failure – no exception thrown, just missing results
The Lesson
Type-checking functions operate on values, not field names.
This applies to all type checks:
is_array('items')β vsis_array($data['items'])βis_numeric('total')β vsis_numeric($data['total'])βis_null('deleted_at')β vsis_null($data['deleted_at'])β
When in doubt, echo the value you’re checking. If you see a field name instead of actual data, you’re checking the wrong thing.
And seriously, run PHPStan. It would have caught this in 0.2 seconds.
Leave a Reply