PHP functions that look simple but hide traps

PHP provides hundreds of built-in functions that are really useful in everyday development. Many of them seem very simple to use at first, but some hide behaviors that can easily cause subtle bugs. After several years of development, I still sometimes get unpleasant surprises.
Let's look at some examples of functions that are worth knowing well before using them, to avoid small bugs that are not immediately obvious.
empty()
`empty()` is often used to check whether a variable contains a value.
if (empty($value)) {
// ...
}The problem is that `empty()` considers several valid values as empty:
empty(0); // true
empty("0"); // true
empty(false); // true
empty([]); // true
empty(null); // trueThis can lead to bugs that are difficult to spot.
For example:
$quantity = 0;
if (empty($quantity)) {
echo "Quantity missing";
}The message will be displayed even though the quantity is actually `0`. I still run into a bug caused by this at least once every 2 or 3 years!
When you only want to check whether a variable is `null`, it is often preferable to use an explicit comparison:
if ($quantity === null) {
// ...
}in_array()
This function looks harmless:
in_array($value, $array);However, by default, it performs a non-strict comparison.
in_array("1", [1]); // true
in_array(false, [0]); // trueIn most cases, it is preferable to enable strict mode:
in_array($value, $array, true);This is probably the third parameter I forget most often.
array_search()
Same problem.
$key = array_search($value, $array);The function returns the found index or `false` if no result exists. The trap:
$key = array_search("apple", $array);
if (!$key) {
echo "Not found";
}If the element is located at index `0`, this condition will also be true.
You should always use:
if ($key === false) {
echo "Not found";
}strpos()
This is probably one of the most famous PHP traps.
$pos = strpos($text, "php");Like `array_search()`, this function returns either the found position or `false`. The wrong code:
if (!strpos($text, "php")) {
echo "Not found";
}If the string starts with `"php"`, the position is `0`, and the condition fails. The correct approach:
if (strpos($text, "php") === false) {
echo "Not found";
}Since PHP 8, `str_contains()` is often more readable when you simply want to know whether a substring exists.
if (str_contains($text, "php")) {
// ...
}count()
We sometimes see:
if (count($items) > 0) {
// ...
}Why not. But if you simply want to know whether an array is empty, you can write:
if ($items !== []) {
// ...
}
// or more simply:
if (!empty($items)) {
// ...
}On the other hand, be careful never to call `count()` on a variable whose type is unknown.
count(null);On older versions of PHP, this produced a warning. Since PHP 8, it throws a `TypeError`.
trim()
Many developers think that `trim()` only removes spaces. In reality, it also removes:
- tabs;
- line breaks;
- NULL characters;
- several other control characters.
This is usually what we want, but it is better to know it.
json_decode()
This function is widely used.
$data = json_decode($json, true);Two traps are common.
First trap:
$data = json_decode("null", true);The result is:
nullExactly like an invalid JSON string. You should therefore check:
json_last_error();Or, since PHP 7.3, use:
$data = json_decode(
$json,
true,
512,
JSON_THROW_ON_ERROR
);floatval()
Many people think this function validates a number. In reality:
floatval("123abc"); // 123
floatval("abc"); // 0It converts the value as much as possible. I have already been caught by this behavior once. I could not understand why an input was not rejected.
If the goal is to validate user input, it is better to use:
filter_var(
$value,
FILTER_VALIDATE_FLOAT
);date()
Another common mistake:
echo date("Y-m-d");This function uses PHP's current timezone. On a poorly configured server, you can get an incorrect date.
When timezone handling is important, it is preferable to use `DateTimeImmutable` objects with an explicit timezone.
substr()
`substr()` works perfectly... as long as you are working with ASCII strings. The problem appears as soon as the string contains accented characters or Unicode characters:
$text = "école";
echo substr($text, 0, 1);The result will not be `"é"` but an invalid character, because `substr()` works on bytes rather than characters. When I started developing, I made some mistakes because of this behavior (or rather because of my lack of knowledge).
To manipulate UTF-8 strings, it is preferable to use multibyte functions:
echo mb_substr($text, 0, 1, 'UTF-8');The same problem exists with several string manipulation functions:
- `strlen()` → `mb_strlen()`
- `strpos()` → `mb_strpos()`
- `strtolower()` → `mb_strtolower()`
- `strtoupper()` → `mb_strtoupper()`
If your application handles text containing accents, emojis, or non-Latin characters, `mb_*` functions are generally preferable.
Conclusion
The problem with these functions is that they sometimes hide unintuitive behaviors that can easily cause bugs if you are not aware of them.
However, they do exactly what their documentation says. The problem is often that we interpret their behavior incorrectly, and with the use of AI, I read documentation less and less often...