← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Nullsafe Operator (?->)

PHP PHP 8.0+ Beginner
debt(d5/e1/b1/t5)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints list rector, phpstan, and php-cs-fixer — all specialist static analysis or refactoring tools. The pattern (nested ternary null checks in PHP 8.0+ code) is not caught by the compiler or default linter, but phpstan/rector can flag old-style null checks that should be ?-> and also detect misuse (e.g., using ?-> on non-nullable types).

e1 Effort Remediation debt — work required to fix once spotted

Closest to 'one-line patch or single-call swap' (e1). The quick_fix is a direct one-line substitution: replace $user !== null ? $user->getAddress() : null with $user?->getAddress(). Rector can automate this mechanically. There is no refactor needed beyond the substitution itself.

b1 Burden Structural debt — long-term weight of choosing wrong

Closest to 'minimal commitment' (b1). The nullsafe operator is a syntax-level choice affecting individual expressions. It applies to web, cli, and queue-worker contexts but imposes no structural weight on future maintainers — it is a localised, self-contained syntactic decision with no gravitational pull on the broader codebase.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap' (t5). The misconception field identifies that developers confuse ?-> with isset(): isset() checks for null and undefined, while ?-> short-circuits the chain on the first null but does not check for undefined variables. Additionally, a common_mistake notes that method arguments are evaluated before the null check, so side effects in arguments are not short-circuited — a documented gotcha most developers encounter when they first use chaining.

About DEBT scoring →

Also Known As

nullsafe operator ?-> operator PHP 8 nullsafe chain

TL;DR

Chains method calls and property accesses on potentially-null objects without nested null checks; returns null on the first null encounter.

Explanation

Introduced in PHP 8.0, the nullsafe operator ?-> short-circuits a method/property chain when it encounters null, returning null rather than throwing an Error. This eliminates deeply nested isset() / !== null guards for fluent interfaces. For example, $user?->getProfile()?->getAvatar()?->getUrl() returns null if any step is null. It is not a substitute for proper null handling in all cases — side-effect-producing methods in a chain may not execute if a preceding step returns null.

Common Misconception

The nullsafe operator ?-> is the same as checking with isset() first. isset() checks for null and undefined; ?-> short-circuits the chain and returns null on the first null encountered. They are not equivalent for complex chains and ?-> is significantly more readable.

Why It Matters

?-> returns null instead of throwing an error when called on null — it makes optional relationship traversal safe without wrapping every step in an if-null check.

Common Mistakes

  • Using ?-> on objects that should never be null — hides bugs that should throw.
  • Expecting ?-> to short-circuit side effects in method arguments — arguments are evaluated before the null check.
  • Not using it consistently — mixing isset() checks and ?-> in the same codebase for the same pattern.
  • Using it with property access but forgetting it works for method calls too — both are supported.

Code Examples

✗ Vulnerable
// Without nullsafe — verbose:
$country = null;
if ($user !== null) {
    $profile = $user->getProfile();
    if ($profile !== null) $country = $profile->getCountry();
}

// With nullsafe operator:
$country = $user?->getProfile()?->getCountry();
✓ Fixed
// Before PHP 8.0 — tedious null guards
$city = null;
if ($user !== null) {
    $address = $user->getAddress();
    if ($address !== null) {
        $city = $address->getCity();
    }
}

// PHP 8.0+ nullsafe operator — short-circuits on null
$city = $user?->getAddress()?->getCity();

// Works with method chains and property access
$countryCode = $order?->getCustomer()?->getAddress()?->getCountry()?->getIsoCode();

Added 15 Mar 2026
Edited 22 Mar 2026
Views 53
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 2 pings T 4 pings F 1 ping S 4 pings S 1 ping M 1 ping T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 2 pings T 1 ping W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Scrapy 13 Amazonbot 9 Perplexity 5 Ahrefs 4 Google 3 Unknown AI 2 Claude 2 ChatGPT 2 Meta AI 1 SEMrush 1
crawler 38 crawler_json 4
DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: Low
⚡ Quick Fix
Use $user?->getAddress()?->getCity() instead of $user !== null ? $user->getAddress() : null — the nullsafe operator short-circuits the entire chain on the first null
📦 Applies To
PHP 8.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
$obj !== null ? $obj->method() : null; nested ternary null checks before method calls in PHP 8.0+ code
Auto-detectable: ✓ Yes rector phpstan php-cs-fixer
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✓ Auto-fixable Fix: Low Context: Line


✓ schema.org compliant