The Empty String In PHPThe Empty String in PHP: One Value, Too Many Jobs

The humble empty string “” is everywhere. It’s the quiet default, the silent failure signal or the silent success signal, the lazy cast, the natural accumulator. Behind the obvious simplicity of the empty string in PHP lies a surprising amount of overloaded meanings, which leads to misunderstandings. Let’s review some situations.

Default Value for Properties and Arguments

The most common use of “” is perfectly reasonable: signalling “nothing provided yet.”

<?php
class User {
    public string $nickname = '';
}

function greet(string $name = ''): string {
    return $name !== '' ? "Hello, $name!" : "Hello, stranger!";
}
?>

As a default, the empty string is idiomatic, type safe, fatal error free and generally fine: it provides a “no value” meaning, and “empty string” is genuinely the same thing in so many domains.

Yet, a missing name and a name that was explicitly cleared are two different states. When that distinction matters, many application rely on ?string with a null default instead:

<?php
class User {
    public ?string $nickname = null; // null = never set; '' = deliberately blank
}
?>

Now, there is no more confusion between no value and empty string. This is at odds with PHP trying to enforce using string with native functions, rather than accept null as the empty string. Such tradition is probably at the root of many modern errors.

Invalid Value: Where Even Empty String Can’t Go

ord() has no problem with default values: its only argument is actually compulsory. It is a string. But what happens when an string is passed to ord()? it returns the ASCII number of that character. String, character? The slip in the vocabulary is important.

ord() returns the ASCII number of the first character in the passed string. PHP has no way to specify a single character, and the best type is to use string. Since ord() only works on the first element of the string, it emits a warning since PHP 8.5 to indicate that the string must be of length 1.

And it emits the same warning for a string of length 0, such as the empty string. At that point, the warning is not about the unused information, but rather the absence of character to process: an empty string has no character.

<?php
ord(''); // ord of what?
?>

There are some situations where an empty string should not be allowed, because, while it is a string, it is also holding no character. So, sorting an array of strings, or looking for the minimum or maximum in that same array might be subject to adaptation to a specific domain. By default, with PHP, empty string has the lowest precedence over any other string.

Error Sentinel: Nothing Could Be Processed

Several built-in PHP functions, and quite some custom ones, return “” to signal failure. substr() is a classic example: in pre-PHP 8.0, it returned false in case of failure. It would also return “” when asked for it, like when the $length parameter is passed as 0.

<?php
$result = substr('hello', 10); // false in PHP 7, '' in PHP 8
?>

This pattern also appears in userland code:

<?php
function extractToken(string $header): string {
    if (!str_contains($header, 'Bearer ')) {
        return ''; // ← empty string as "I failed"
    }
    return substr($header, 7);
}
?>

The problem is that “” is a valid, successful result in many contexts. Returning it as an error conflates two meanings. The caller now has to guess:

<?php
$token = extractToken($header);
if ($token === '') {
    // ...was the token legitimately empty, or did extraction fail?
}
?>

The better approach is explicit: throw an exception, return null, or use a result type. Reserve “” for “a string that happens to be empty,” not “something went wrong”.

All considered, it is the same as the default value we met in the first phase, but now, at the end of the process, not the beginning.

Success Sentinel: Non-Empty Means Error

And now, this: the inverse pattern, and arguably the most puzzling one for the non-initiated. A function returns “” on success and a non-empty message string on failure:

<?php
function validateEmail(string $email): string {
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        return 'Invalid email address.';
    }
    return ''; // success is silence
}

$error = validateEmail($input);
if ($error !== '') {
    echo $error;
}
?>

This shows up in older codebases, some validation libraries and system calls. When it works, there is nothing to report, so the returned value is “”. When something fails, there is a concrete error message, and the returned string will be not-empty.

This approach works, but it inverts the normal semantics of a return value. Reading if ($error !== ”) is a small cognitive stumble every time. A bool return with a separate error retrieval mechanism, a thrown exception, or a proper validation result object communicates intent far more clearly.

The Empty-String Cast

PHP’s loose typing makes it easy to use “” as an implicit cast to string:

<?php
$value = (int) $_GET['count'];
$display = '' . $value; // cast int to string via concatenation
?>

Or worse, initialising a variable as “” so a subsequent concatenation typecasts whatever ends up in it:

<?php
$output = '';
$output .= $someObject; // relies on __toString being defined
?>

This is fragile, prone to Fatal Error, when used with object, or to Warnings, when used with arrays. It depends on implicit coercion and __toString magic, hides the intent, and produces confusing bugs when objects don’t implement __toString. Use an explicit cast:

<?php
$display = (string) $value;
$output  = (string) $someObject;
?>

This is all the more strange that there are quite some situations where it works without warning. It is the case with integers, null, booleans…

<?php
$x = null;
$output  = $x . '';
?>

The short cut is tempting, until it becomes important to check what happened to the concatenated value. At that point, explicit is better than implicit.

String Accumulator Initialisation

This final situation is perhaps the most widespread use of “”, and the one most worth replacing:

<?php
$result = '';
foreach ($items as $item) {
    $result .= formatItem($item) . ', ';
}
echo rtrim($result, ', ');
?>

Starting with “” and appending in a loop, or over many method calls, is intuitive but subtly inefficient: PHP extends the string everytime one extra piece is added to the accumulator variable. Most of the time, it is quick, as PHP pre-allocate extra memory for such an event, but at some point, it has to stop and, extends the memory or copy the new string somewhere with more space.

The idiomatic PHP replacement is to collect the bits and pieces into an array and implode() it all onace, at the end:

<?php
$parts = [];
foreach ($items as $item) {
    $parts[] = formatItem($item);
}
echo implode(', ', $items);
?>

This is cleaner, memory efficient and faster. It makes the separator handling obvious, even if the separator is the empty string “” itself.

So many usage of the empty string in PHP

The empty string is a nice feature, although it is not even considered as such: it comes with the language, and anyone would look for it if it was missing. But it is so common that tt’s just been handed far too many jobs.

The fix, in almost every case, is the same: disambiguate the empty string and be explicit about what it means. PHP has the tools, may it be null, typed returns, exceptions, arrays, to code precisely the intend, without hiding it behind a pair of empty quotes.