PHP's null coalescing operator ?? supplies a fallback when a value is undefined or null. The nullsafe operator ?-> accesses an object member only when the object is not null, producing null otherwise. They often appear together, but they answer different questions: one chooses a default, and the other traverses an optional object relationship.
This guide shows how they preserve valid zeros, how a chain short-circuits, and why neither operator substitutes for validating data or handling every exception. It also covers ??=, which stores a default only when a target is missing or null. The examples require PHP 8.0 for nullsafe access.
Null coalescing preserves false-like values
$value ?? $fallback returns the value when it exists and is not null. It otherwise evaluates and returns the fallback. This closely matches an isset-based choice. Zero, false and an empty string are preserved, which is different from the short ternary ?: that replaces false-like values.
For optional array keys, $settings['retries'] ?? 3 avoids an undefined-key warning and supplies three for a missing or null entry. It does not distinguish absence from explicit null. Use array_key_exists when that difference is part of an update contract. A present value still needs type and range validation.
You can chain fallbacks, such as $local ?? $shared ?? 'default'. The first available non-null value wins. Keep the order meaningful and avoid creating a long chain that masks which configuration source should be authoritative. A required missing setting should sometimes raise an error instead of silently receiving a plausible default.
Null coalescing assignment stores the default
$settings['timeout'] ??= 10 assigns ten only when the target is missing or null. If it already holds zero or false, that value remains. The construct became available in PHP 7.4. It is convenient when initializing optional internal settings before later code reads them directly.
Unlike a normal coalescing expression, coalescing assignment changes the target. That can be helpful in a local configuration array but surprising when a function receives shared mutable state. Do not use it casually to mutate request data or objects just to make a later line shorter. Prefer a local normalized value when the original input should remain available for diagnostics.
The fallback is evaluated only when needed. If computing it is expensive, that avoids unnecessary work. If computing it has side effects, those effects occur only on the missing/null path. Keep initialization functions predictable so a subtle difference in null state does not accidentally trigger unrelated operations.
Nullsafe access handles null objects
$customer?->address reads address when customer is an object and produces null when customer is null. You can also call a method with ?->. PHP short-circuits the rest of that nullsafe chain when it reaches null, so later accesses and arguments in that skipped chain are not evaluated.
Use ?-> at each relationship that may independently be null. If customer exists but its address can be null, $customer?->address?->city makes both optional points visible. An ordinary arrow at a nullable step can still cause an error. The syntax should reflect the actual object model rather than adding question marks everywhere.
Nullsafe access does not handle a wrong non-null type, a missing member, or an exception thrown by a method. It is also read-only: you cannot assign through a nullsafe chain or use it as a writable reference target. A mutation needs a clear null check and a deliberate operation.
Decide whether absence is acceptable
Optional display data is a good fit for nullsafe access. An absent avatar or secondary contact can use a placeholder. Required data is different. If an order must have a delivery address before checkout, silently printing Unknown can conceal an invalid order. Validate the requirement at the correct stage and report a clear error.
Defaults also need domain meaning. A missing quantity should not automatically become one unless the endpoint contract says so. A missing authorization result must not become allowed. Coalescing is a language convenience; it does not decide whether absence is safe or valid.
Consult the PHP comparison operator reference for coalescing behavior and the object basics reference for nullsafe access. The version boundary is useful when maintaining a PHP 7 application: null coalescing is available from PHP 7.0, but nullsafe access requires PHP 8.0.
PHP Null Coalescing and Nullsafe Operators Examples
Each example shows the code, the output it produces and what each part does.
Compare defaults without losing zero
The existing retry count remains zero. Both an explicit null timeout and an absent page-size key receive defaults. These outcomes are a policy choice: for a required production timeout, you might reject null rather than treat it as permission to select ten.
<?php
$settings = ['retries' => 0, 'timeout' => null];
echo ($settings['retries'] ?? 3) . PHP_EOL;
echo ($settings['timeout'] ?? 10) . PHP_EOL;
echo ($settings['page_size'] ?? 12) . PHP_EOL;0
10
12Traverse an optional address
The first chain reaches a real city. The second stops at the null guest, then coalescing supplies the display fallback. A customer with a null address would also receive Unknown because that optional step uses nullsafe access. The combined expression is readable because it follows one simple relationship chain.
<?php
class Address {
public function __construct(public string $city) {}
}
class Customer {
public function __construct(public ?Address $address) {}
}
$customer = new Customer(new Address('Lahore'));
$guest = null;
echo ($customer?->address?->city ?? 'Unknown') . PHP_EOL;
echo ($guest?->address?->city ?? 'Unknown') . PHP_EOL;Lahore
UnknownCommon PHP Null Coalescing and Nullsafe Operators Mistakes
Hiding errors behind broad fallback chains
A default does not repair an invalid present value. If retries is the string banana, coalescing returns it because it is non-null. If a method throws while reading an address, nullsafe syntax does not catch the exception. Separate optionality from type correctness, domain validation and exception handling.
Avoid using nullsafe access to make a required object look optional throughout an application. That can spread placeholders instead of identifying where construction failed. A clear constructor contract or validation step often produces a more useful failure than a chain of defaults.
PHP Null Coalescing and Nullsafe Operators in Real Projects
Practical task: preserve a disabled retry policy
Normalize a settings array with a default retry count of three and a default timeout of ten. An existing retry value of zero means retries are disabled and must remain zero. A null timeout means use the default. Use coalescing assignment, then print the normalized values.
<?php
$settings = ['retries' => 0, 'timeout' => null];
$settings['retries'] ??= 3;
$settings['timeout'] ??= 10;
echo 'Retries: ' . $settings['retries'] . PHP_EOL;
echo 'Timeout: ' . $settings['timeout'] . PHP_EOL;Retries: 0
Timeout: 10The operation deliberately changes this local array. Try replacing the first assignment with a short ternary expression and see why zero becomes three. In real configuration loading, follow normalization with validation: negative retries or an array-shaped timeout remain invalid even though they are non-null.
PHP Null Coalescing and Nullsafe Operators FAQ
Does ?? treat false as missing?
No. False, zero and empty strings are preserved. Only missing or null triggers its fallback.
Does ?-> create an object?
No. It stops access when the object is null and produces null. It does not fill in a relationship.
Can I assign through ?->?
No. Use an explicit check before a deliberate mutation.
Does nullsafe catch exceptions?
No. A method that runs can still throw. Handle failures according to the operation's contract.
Should every missing value have a default?
No. Required configuration or domain data should fail clearly when absent. A default is appropriate only when its meaning is defined.
PHP Null Coalescing and Nullsafe Operators Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
A required delivery address is missing. What should checkout do?
PHP Null Coalescing and Nullsafe Operators Quiz
2 questions to check what you learned.
What does 0 ?? 3 produce?
- 3
- 0
- null
What does nullsafe access do when its object is null?
- Constructs the missing object
- Catches every exception
- Produces null and short-circuits that chain