Nullable Types
What Is a Nullable Type? In simple terms: Sometimes a value may legitimately be absent : a customer without a middle name, an order without a coupon, a search that finds no user. A nullable type says
What Is a Nullable Type?
In simple terms: Sometimes a value may legitimately be absent: a customer without a middle name, an order without a coupon, a search that finds no user. A nullable type says "this is a string — or nothing (null)". It makes the possibility of "no value" explicit in the code, so both PHP and other developers know they must handle it.
Technically: A nullable type is written with a leading question mark: ?string. It is equivalent to the union type string|null (PHP 8.0+). Nullable types can be used for parameters, return values, and properties. A non-nullable type such as string rejects null with a TypeError.
| Declaration | Accepts | |
|---|---|---|
string $name | Strings only | |
?string $name | Strings or null | |
| `string | null $name` | Same as ?string |
?string $name = null | Strings or null, and the argument may be omitted |
Nullable Parameters
<?php
function greet(?string $name): string
{
return $name === null ? 'Hello, guest!' : "Hello, $name!";
}
echo greet('Asha') . "\n";
echo greet(null) . "\n";
try {
greet();
} catch (ArgumentCountError $e) {
echo strstr($e->getMessage(), ' in ', true) . "\n";
}
Output
Hello, Asha!
Hello, guest!
Too few arguments to function greet(), 0 passed
Nullable Is Not the Same as Optional
?string $namemeans the argument may benull, but it must still be passed.- To make it optional, add a default value:
?string $name = null.
function greet(?string $name = null): string { ... }
greet(); // allowed: $name is null
greet(null); // allowed
greet('Asha'); // allowed
Implicitly Nullable Parameters Are Deprecated (PHP 8.4)
Older code often relied on a special rule: a parameter with a non-nullable type and a default of null was implicitly nullable:
<?php
function oldStyle(string $coupon = null): string
{
return $coupon ?? 'no coupon';
}
echo oldStyle() . "\n";
Output (PHP 8.4+)
PHP Deprecated: oldStyle(): Implicitly marking parameter $coupon as nullable is deprecated, the explicit nullable type must be used instead in /path/script.php on line 2
no coupon
The declaration string $coupon claims the parameter is always a string, while = null silently contradicts that. Since PHP 8.4, write the nullability explicitly:
function newStyle(?string $coupon = null): string
Tools such as PHP-CS-Fixer and Rector can fix this automatically in existing code bases.
Nullable Return Types
A nullable return type tells callers that a function may return "nothing" — typically for lookups:
<?php
final class Address
{
public function __construct(public string $city) {}
}
final class User
{
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly ?Address $address = null,
) {}
}
function findUser(int $id): ?User
{
$users = [
7 => new User(7, 'Asha', new Address('Chennai')),
8 => new User(8, 'Bala'),
];
return $users[$id] ?? null;
}
foreach ([7, 8, 9] as $id) {
$user = findUser($id);
$name = $user?->name ?? 'Unknown user';
$city = $user?->address?->city ?? 'city not set';
echo "#$id: $name, $city\n";
}
Output
#7: Asha, Chennai
#8: Bala, city not set
#9: Unknown user, city not set
How It Works
findUser()returns aUserornullwhen no user exists — the?Userreturn type documents this.- The nullsafe operator
?->(see Nullsafe Operator) stops the chain and returnsnullwhen the value on its left isnull, instead of throwing an error. - The null coalescing operator
??supplies a fallback fornullresults. Addressis also nullable onUser: Bala has no address, so$user?->address?->cityisnull.
Nullable Properties
<?php
final class Customer
{
public ?string $nickname = null; // initialized with null
public ?string $referralCode; // uninitialized
}
$customer = new Customer();
var_dump($customer->nickname);
try {
var_dump($customer->referralCode);
} catch (Error $e) {
echo $e->getMessage() . "\n";
}
Output
NULL
Typed property Customer::$referralCode must not be accessed before initialization
A nullable type allows null, but it does not set null automatically. Without = null, the typed property starts uninitialized (see PHP Properties).
Avoiding Unnecessary Nulls
Every nullable value forces every caller to check for null. Nulls that spread through an application lead to repetitive checks — or to forgotten checks and errors. Use nullable types where "absent" is a genuine, expected state, and consider alternatives elsewhere:
| Situation | Instead of null |
|---|---|
| A list with no items | Return an empty array [] |
| An operation that must succeed | Throw an exception when it fails |
| "No behavior" (no discount, no logger) | A Null Object that does nothing |
| A value with a sensible default | Use the default ('INR', 0, '') |
Example: The Null Object Pattern
<?php
interface Discount
{
public function apply(float $amount): float;
}
final class PercentageDiscount implements Discount
{
public function __construct(private float $percent) {}
public function apply(float $amount): float
{
return round($amount * (1 - $this->percent / 100), 2);
}
}
final class NoDiscount implements Discount
{
public function apply(float $amount): float
{
return $amount;
}
}
final class Order
{
public function __construct(
private float $subtotal,
private Discount $discount = new NoDiscount(),
) {}
public function total(): float
{
return $this->discount->apply($this->subtotal); // no null check needed
}
}
echo (new Order(1000))->total() . "\n";
echo (new Order(1000, new PercentageDiscount(10)))->total() . "\n";
Output
1000
900
Instead of a ?Discount property and if ($this->discount !== null) checks, an order always has a discount — sometimes one that changes nothing. (new in a parameter default requires PHP 8.1+.)
Common Mistakes
Confusing Nullable With Optional
Effect: ArgumentCountError when the argument is omitted.
Fix: Add = null if the argument may be left out.
Relying on Implicit Nullability
Effect: Deprecation notices in PHP 8.4.
Fix: Write ?Type $param = null.
Forgetting to Handle null Return Values
Effect: "Attempt to read property on null" warnings or errors.
Fix: Check for null, or use ?-> and ??.
Using null for Empty Collections
Effect: Every caller must check for null before looping.
Fix: Return an empty array.
Best Practices
- Use nullable types only where "no value" is a legitimate, expected state.
- Write nullable types explicitly (
?Type), never implicitly through= nullalone. - Handle
nullimmediately with??,?->, or explicit checks. - Return empty collections instead of
nullfor lists. - Consider exceptions or Null Objects instead of nullable returns and properties.
Official reference: https\://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.nullable