Named Arguments, Match Expressions, Nullsafe Operator
PHP 8.0 introduced three syntax improvements that make code significantly more readable and safe: named arguments, the match expression, and the nullsafe operator.
Named Arguments
Named arguments let you pass function arguments by parameter name, skipping optional parameters and making calls self-documenting.
Before PHP 8.0
// What does true, false, true mean?
array_slice($array, 0, 10, true);
// Had to pass all intermediate args to reach the one you want
htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', false);
With Named Arguments
array_slice(array: $array, offset: 0, length: 10, preserve_keys: true);
htmlspecialchars(
string: $string,
flags: ENT_QUOTES | ENT_SUBSTITUTE,
encoding: 'UTF-8',
double_encode: false
);
Named Arguments in Your Own Functions
function createUser(
string $name,
string $email,
string $role = 'user',
bool $active = true,
?string $avatar = null
): User {
// ...
}
// Only override what you need — no positional gymnastics
$user = createUser(
name: 'Alice',
email: 'alice@example.com',
avatar: 'alice.jpg'
// role and active use defaults
);
Match Expression
match is a stricter, more powerful replacement for switch:
// switch — implicit type coercion, fall-through, verbose
switch ($status) {
case 1:
$label = 'Active';
break;
case 2:
case 3:
$label = 'Inactive';
break;
default:
$label = 'Unknown';
}
// match — strict comparison, expression (not statement), exhaustive
$label = match($status) {
1 => 'Active',
2, 3 => 'Inactive',
default => throw new InvalidArgumentException("Unknown status: $status"),
};
Key Differences: match vs switch
| Feature | switch | match |
|---|---|---|
| Comparison | Loose (==) | Strict (===) |
| Fall-through | Yes (explicit break needed) | No |
| Return value | No | Yes (it's an expression) |
| Exhaustiveness | No error on missing case | UnhandledMatchError if no arm matches |
| Multiple conditions | One per case | Comma-separated |
match with no-match Error
$value = 'unexpected';
$result = match($value) {
'a' => 1,
'b' => 2,
};
// Throws: UnhandledMatchError: Unhandled match case
This is often desirable — it forces you to handle all cases explicitly.
Nullsafe Operator (?->)
The nullsafe operator (?->) short-circuits a chain when it encounters null, returning null instead of throwing a fatal error.
The Problem It Solves
// Before PHP 8.0 — deeply nested null checks
$city = null;
if ($user !== null) {
if ($user->getAddress() !== null) {
if ($user->getAddress()->getCity() !== null) {
$city = $user->getAddress()->getCity()->getName();
}
}
}
// PHP 8.0 — clean chain
$city = $user?->getAddress()?->getCity()?->getName();
Nullsafe with Method Calls
// Returns null if any link in the chain is null
$country = $order?->getShippingAddress()?->getCountry()?->getCode();
// Works with array access too (not nullsafe, but combinable)
$name = $user?->getProfile()?->settings['display_name'] ?? 'Anonymous';
Nullsafe + Named Arguments Combination
// Expressive, null-safe, self-documenting
$formatted = $user?->getProfile()?->formatName(
style: 'full',
locale: $request->getLocale()
);
When NOT to Use Nullsafe
The nullsafe operator can hide programming errors. Use it when null is a valid expected state, not as a shortcut to avoid thinking about null safety:
// ✅ Good: User may not have a profile (optional relationship)
$bio = $user?->getProfile()?->getBio();
// ❌ Bad: Order MUST have a customer — null here is a bug, not a valid state
$email = $order?->getCustomer()?->getEmail(); // Hides missing customer