Introduction
PHP has changed a great deal across its recent major versions. Features added in PHP 7.4, 8.0, and 8.1 make code shorter, safer, and easier to read. WordPress core stays conservative for backward compatibility, but in your own plugins and themes you can use these features freely, as long as you respect the minimum PHP version your code targets.
You will learn:
- Typed properties and constructor property promotion
- Arrow functions and the match expression
- The nullsafe operator and null coalescing assignment
- Named arguments and enums
- Readonly properties and the spread operator
A Word on PHP Versions First
Every feature below has a minimum PHP version. Always know the lowest version you support and check each feature against it.
- PHP 7.4: typed properties, arrow functions, null coalescing assignment, spread in arrays
- PHP 8.0: constructor promotion, match, nullsafe operator, named arguments
- PHP 8.1: enums, readonly properties, first-class callable syntax
You can declare a minimum in composer.json and check at runtime with the PHP_VERSION_ID constant.
Typed Properties
Before PHP 7.4, class properties had no declared type. Typed properties let you declare the expected type and PHP enforces it.
class Kubic_Product {
public string $name;
public float $price;
public int $stock = 0;
}
Assigning $product->price = ‘free’ now throws an error immediately instead of causing a confusing bug later. This is one of the easiest modern features to adopt with a big payoff.
Constructor Property Promotion
Classes often repeat each property three times: declaration, constructor parameter, and assignment. PHP 8.0’s constructor promotion collapses all three into one.
// Old way
class Kubic_Box {
private string $label;
public function __construct( string $label ) {
$this->label = $label;
}
}
// Promoted (PHP 8.0+)
class Kubic_Box {
public function __construct( private string $label ) {}
}
Arrow Functions
Arrow functions are a short form of closure for single-expression callbacks. They capture outside variables automatically — no use keyword needed.
$prices = array( 100, 200, 300 );
$rate = 0.18;
$with_tax = array_map( fn( $p ) => $p * ( 1 + $rate ), $prices );
Use it for small transformations passed to array_map, array_filter, and usort. For anything longer than one expression, use a normal closure.
The match Expression
match is a cleaner alternative to long switch statements. It returns a value, uses strict comparison, and needs no break statements.
$label = match ( $post->post_status ) {
'publish' => 'Live',
'draft' => 'Draft',
'pending' => 'Awaiting review',
default => 'Unknown',
};
match uses === so ‘1’ does not accidentally match 1, and forgetting a break can no longer cause fall-through bugs. If no arm matches and there is no default, match throws an error rather than silently doing nothing.
The Nullsafe Operator
The nullsafe operator (?->) short-circuits a chain of object accesses: if any part is null, the expression stops and returns null instead of erroring.
// Without nullsafe
$country = null;
if ( $order !== null && $order->get_address() !== null ) {
$country = $order->get_address()->country;
}
// With nullsafe (PHP 8.0+)
$country = $order?->get_address()?->country;
Null Coalescing and Its Assignment Form
The null coalescing operator (??) returns the first value that exists and is not null:
$page = $_GET['page'] ?? 'home';
PHP 7.4 added the assignment version (??=), which sets a variable only if it is currently null or unset:
$options ??= get_option( 'kubic_settings', array() );
Named Arguments
Named arguments let you pass arguments by parameter name rather than position, making calls with optional parameters far clearer:
function kubic_register(
string $name,
bool $public = false,
bool $searchable = true,
string $icon = 'dashicons-admin-post'
) {}
// Only set the icon, leave the rest as defaults:
kubic_register( name: 'book', icon: 'dashicons-book' );
Enums
Enums define a fixed set of possible values as a dedicated type, replacing loose strings or integers used as status flags.
enum Order_Status: string {
case Pending = 'pending';
case Completed = 'completed';
case Refunded = 'refunded';
}
function kubic_set_status( Order_Status $status ) {
// $status can only be one of the three cases
}
kubic_set_status( Order_Status::Completed );
With an enum, only defined cases are allowed. Typos like ‘complete’ instead of ‘completed’ are caught at the type level. Enums are a great fit for any field with a known, fixed list of values.
Readonly Properties
A readonly property can be set once (usually in the constructor) and never changed again:
class Kubic_Money {
public function __construct(
public readonly int $amount,
public readonly string $currency
) {}
}
$price = new Kubic_Money( 2500, 'USD' );
$price->amount = 0; // Error: cannot modify readonly property
The Spread Operator for Arrays
$defaults = array( 'public' => true, 'show_ui' => true );
$overrides = array( 'show_ui' => false );
$args = array( ...$defaults, ...$overrides );
// public => true, show_ui => false (later value wins)
Note that wp_parse_args() remains the WordPress-native way to merge arrays and is still preferred in code meant to match core style.
Best Practices
- Know your minimum PHP version and check each feature against it before using it.
- Adopt typed properties and constructor promotion first — biggest readability gain for least effort.
- Use match instead of switch for value-returning branches.
- Reach for enums whenever a value comes from a known, fixed list.
- State your minimum PHP version in your plugin header’s Requires PHP field.
Common Mistakes
Using a feature your hosting cannot run. Enums on PHP 7.4 cause an instant fatal error. Always confirm the minimum version.
Relying on loose comparison when switching to match. match uses ===. If your old switch depended on ‘1’ matching 1, convert the values explicitly.
Treating readonly as deep immutability. A readonly property holding an array or object stops you reassigning the property, but it does not freeze the nested data.
FAQ
Can I use these features if WordPress core does not?
Yes. Core stays conservative, but your plugin or theme runs on whatever PHP the server provides. As long as the server meets your declared minimum, you can use modern features freely.
How do I tell users which PHP version my plugin needs?
Add a Requires PHP header to your main plugin file. WordPress reads it and prevents activation on older versions.
Is match always better than switch?
Usually, when you want to return a value and rely on strict comparison. switch still has its place when you need to run several statements per case.
Conclusion
Modern PHP gives you shorter, safer, clearer code, and you can use it in WordPress today as long as you respect your minimum version. Typed properties and constructor promotion clean up your classes, match and enums remove whole categories of bugs, and small operators like ??, ??=, and ?-> trim boilerplate from everyday code.
Start with typed properties, promotion, and match, then layer in enums and readonly properties as your projects target newer PHP.