Use the nullsafe operator without hiding missing business data
A customer may not have a billing address yet. The nullsafe operator can express optional traversal, but it should not turn required billing data into a silent null just before creating an invoice.
Traverse genuinely optional values
<?php
$customer = (object) ['address' => null];
$city = $customer?->address?->city;
assert($city === null);
$customer->address = (object) ['city' => 'Patan'];
assert($customer?->address?->city === 'Patan');
Each nullable step must be considered. The operator short-circuits when its left-hand object is null. It does not validate arbitrary property names, turn every warning into null, or rescue a method that throws internally.
Display rules and action rules differ
A profile preview can show “Address not provided.” Issuing an invoice may require a valid address. Use an explicit validation or domain error at the action boundary instead of letting a null value travel until a database constraint rejects it.
Avoid long chains that conceal which relationship is missing. Assigning an intermediate value to a meaningful name can make both the rule and the error message clearer.
Test an absent customer, an absent address, and a complete address. Then separately test the operation that requires the address. The optional display should render safely, while the required operation should fail with an actionable explanation. A passing nullsafe expression is not proof that the surrounding business workflow has the information it needs.
Reference
Official documentation. The examples here illustrate the stated boundary; adapt them to your application and test its failure paths.