Question
Why does the following PHP code produce the error “Can't use method return value in write context” in older PHP versions?
if (!empty($r->getError())) {
// Handle the error
}
The getError() method simply returns a property:
public function getError()
{
return $this->error;
}
Is this not only reading the value returned by the method?
Short Answer
You will learn why older PHP versions reject empty() when it receives a method result, why PHP describes this as a “write context,” and how to write compatible, readable checks. You will also see how PHP 5.5 and later changed this behavior.
Concept
empty() is a PHP language construct used to test whether a value is considered empty. A value is empty when it is, for example, "", 0, "0", null, false, or [].
In PHP versions before 5.5, empty() could only be used directly with a variable-like value, such as:
empty($error)
empty($user->error)
empty($items[0])
A method call such as $r->getError() produces a temporary result, not a variable that PHP can directly inspect using the older empty() implementation. Therefore, older PHP reports:
Can't use method return value in write context
The message is confusing because your intention is to read. Internally, the older language rule required empty() to receive something PHP treated as a modifiable variable location. PHP calls that kind of location a write context.
Since PHP 5.5, empty() accepts expressions, including method calls. This is valid in modern PHP:
if (!empty($r->getError())) {
// Handle the error
}
For compatibility with PHP 5.4 or earlier, first store the returned value in a variable.
Mental Model
Think of a variable as a labelled box on a shelf:
$error
PHP can locate that box directly. In older PHP, empty() expected a box it could point to.
A method call is more like asking someone to hand you a note:
$r->getError()
You can read the note, but in older PHP, empty() was only designed to work with labelled boxes, not a note handed back by a method. Assigning the result to a variable gives it a labelled box first:
$error = $r->getError();
if (!empty($error)) {
// ...
}
Syntax and Examples
Use empty() when you intentionally want PHP's broad definition of “empty.”
$value = $r->getError();
if (!empty($value)) {
echo "An error was reported: " . $value;
}
In PHP 5.5 and later, you can pass the method result directly:
if (!empty($r->getError())) {
echo "An error was reported.";
}
empty() returns true for these values:
empty(""); // true
empty("0"); // true
empty(0); // true
empty(null); // true
empty(false); // true
empty([]); // true
It returns false for values such as:
empty("Error"); // false
empty(1); // false
empty(["x"]); // false
If an error message should specifically be a non-empty string, a more precise condition can be clearer:
$error = $r->getError();
if ($error !== '') {
// This treats null, 0, and false differently from empty().
}
Step by Step Execution
Consider this PHP 5.4-compatible code:
$error = $r->getError();
if (!empty($error)) {
echo "Error: " . $error;
}
Suppose getError() returns "Invalid password".
- PHP calls
$r->getError(). - The method returns the value stored in
$this->error. - That returned value is assigned to the local variable
$error. empty($error)evaluates tofalsebecause"Invalid password"is not empty.!falsebecomestrue.- PHP enters the
ifblock and prints the message.
If getError() returns an empty string instead:
$errorbecomes"".empty($error)istrue.!trueisfalse.- PHP skips the
ifblock.
Real World Use Cases
Common places to check method results for empty values include:
- Form validation: Check whether a validator produced an error message.
- API clients: Check whether a response object contains an API error.
- Import scripts: Check whether parsing an input row produced warnings or failures.
- Session or authentication code: Check whether a user-facing failure message was recorded.
- Configuration loading: Check whether an optional configuration value was supplied.
Example with a validator object:
$error = $validator->getError();
if (!empty($error)) {
http_response_code(422);
echo $error;
return;
}
// Continue because validation passed.
Real Codebase Usage
In production code, developers often retrieve a method result once and then validate it. This improves compatibility with older PHP and avoids calling a method more than necessary.
$error = $service->getError();
if ($error === null || $error === '') {
return;
}
logger()->error($error);
This is a guard clause: it exits early when there is no error, keeping the main path less nested.
For values with a clear expected type, prefer explicit checks over empty(). For example, if getError() is documented to return either null or a non-empty error string:
$error = $service->getError();
if ($error !== null) {
showError($error);
}
Explicit comparisons communicate the contract more clearly than empty(), especially because empty("0") is true.
Common Mistakes
Using direct method calls with empty() on PHP 5.4 or earlier
This fails on older PHP:
if (!empty($r->getError())) {
// ...
}
Use an intermediate variable instead:
$error = $r->getError();
if (!empty($error)) {
// ...
}
Assuming empty() only checks empty strings
empty() treats several values as empty, including 0, false, null, and the string "0".
$errorCode = "0";
if (empty($errorCode)) {
// This runs.
}
If "0" is valid data, use a stricter condition:
if ($errorCode === '') {
// Only an empty string matches.
}
Calling a method repeatedly
Avoid this when the method could be expensive or return a changing value:
if (!empty($r->getError())) {
logError($r->getError());
}
Prefer:
$error = $r->getError();
if (!empty($error)) {
logError($error);
}
Confusing isset() and empty()
isset($value) is false only when a variable is undefined or null. It is true for "", 0, and false.
$value = 0;
isset($value); // true
empty($value); // true
Comparisons
| Check | Main purpose | Result for null | Result for "" | Result for 0 / "0" |
|---|---|---|---|---|
empty($value) | Test PHP's broad “empty” category | true | true | true |
isset($value) | Test that a variable exists and is not null | false | true | true |
$value === '' | Test specifically for an empty string | false | true | false |
$value === null | Test specifically for null | true | false | false |
empty() is convenient when all false-like values mean “no usable value.” Explicit comparisons are better when the exact type or value matters.
Also note the version difference:
| PHP version | empty($object->method()) |
|---|---|
| PHP 5.4 and earlier | Invalid |
| PHP 5.5 and later | Valid |
Cheat Sheet
// PHP 5.5+: valid
if (!empty($object->getValue())) {
// Value is not empty.
}
// Compatible with PHP 5.4 and earlier
$value = $object->getValue();
if (!empty($value)) {
// Value is not empty.
}
empty()is a language construct, not a normal function.- Before PHP 5.5,
empty()required a variable-like operand. - PHP 5.5+ allows expressions such as method calls in
empty(). empty()istruefornull,false,0,"0","", and[].- Use
=== ''or=== nullwhen only one specific value should count as missing. - Store a method result once when it may be expensive or stateful.
FAQ
Why does PHP say “write context” when I am reading a value?
The wording comes from an older PHP implementation rule. empty() required something PHP treated like a variable location, even though your code was logically performing a read.
Can I use empty($object->method()) in modern PHP?
Yes. PHP 5.5 and later allow empty() to receive expressions, including method calls.
What is the compatible solution for PHP 5.4 and earlier?
Assign the method result to a variable first, then pass that variable to empty().
$error = $object->getError();
if (!empty($error)) {
// ...
}
Is empty() the same as checking for an empty string?
No. It also considers null, false, 0, "0", and empty arrays to be empty.
Should I use empty() for error messages?
It can be appropriate if every false-like value means “no error.” If the method contract says it returns null when there is no error, checking !== null is usually clearer.
Why save the return value in a variable even on modern PHP?
It can make code easier to read, prevents duplicate method calls, and supports older PHP versions if needed.
Mini Project
Description
Build a small validation result object that stores either no error (null) or a descriptive error message. Use a getter and an explicit check to decide whether to display the error. This models a common form-validation workflow.
Goal
Validate a username and display a message only when validation produces an error.
Requirements
Create a UsernameValidator class with a private error property.
Add a validate(string $username): bool method.
Add a getError(): ?string method that returns the current error.
Reject usernames shorter than 3 characters.
Display the error only when one exists.
Keep learning
Related questions
Are PDO Prepared Statements Enough to Prevent SQL Injection in PHP?
Learn how PDO prepared statements prevent SQL injection in PHP, what they protect, and the mistakes that still leave MySQL apps vulnerable.
Can You Bind an Array to an IN Clause in PHP PDO?
Learn how PDO handles placeholders in IN() clauses, why arrays cannot be bound directly, and the safe PHP pattern to build dynamic queries.
Choosing the Right MySQL Collation for PHP and UTF-8
Learn how MySQL character sets and collations work with PHP, and how to choose a practical UTF-8 setup for web applications.