The last time I've used PHP was probably around 2017, although it was just in the context of supporting some WordPress sites. By that time 7.2 had already been released, but I had no idea. I wanted to avoid working with PHP at all costs.
This month I took some time to check what good things have been added to the language that I was unaware of. To be honest, things are looking great.
The list below is not exhaustive, I only reference what I’ll be using, or find notable.
- Array destructuring (v7.1)
- Spread operator within arrays (v7.4) & (v8.1)
- Match expressions (v8.0)
- Enumerations (enums) (v8.1)
- Arrow functions (v7.4)
- Named parameters (v8.0)
- Null coalescing operator (v7.0)
- Null coalescing assignment operator (v7.4)
- Null-safe operator (v8.0)
- Spaceship operator (v7.0)
- Multi catch exception handling (v7.1)
- str_starts_with, str_ends_with, str_contains (v8.0)
- Return Types (v7.0)
- Union types (v8.0)
- Null and Void return types (v7.1)
- Never return type (v8.1)
- Grouped imports (v7.0)
- Constructor property promotion (v8.0)
- WeakMaps (v8.0)
Array destructuring
Added in v7.1
// arrays
$posts = [[1, 2, 3, 4], [5, 6, 7]];
[$publishedPosts, $draftPosts] = $posts;
// or skip
[, $draftPosts] = $posts;
// associative arrays
$post = [
'title' => 'Modern PHP',
'description' => '...',
'status' => 'Published'
];
['title' => $title, 'description' => $description] = $post;
Spread operator within arrays
- Initial support for arrays in v7.4
- Support for string-keyed (associative) arrays in v8.1
// arrays
$userPosts = [1, 2, 3, 4, 5];
$userPosts = [...$userPosts, 6];
// associative arrays
$userPosts = [
'id-a' => 'Published',
'id-b' => 'Draft',
];
$userPosts = [...$userPosts, 'id-c' => 'Draft'];
Match expressions
Added in v8.1
The one thing I’m most jealous it’s missing in JavaScript
// using a switch statement
switch ($status) {
case 'Published':
$message = 'The post has been published';
break;
case 'Draft':
$message = 'The post is in draft state';
break;
}
// as a match expression
$message = match($status) {
'Published' => 'The post has been published',
'Draft' => 'The post is in draft state',
};
Enumerations (enums)
Added in v8.1
// previously using a class
class Status {
const DRAFT = 'Draft';
const PUBLISHED = 'Published';
const ARCHIVED = 'Archived';
}
// using an enum
enum PostStatus {
case Draft;
case Published;
case Archived;
}
$status = PostStatus::Draft;
Arrow functions
Added in v7.4
fn (args) => expression
(!) Arrow functions can’t be multi-line
$publishedPosts = array_filter($posts,
fn($post) => $post->status === 'Published'
)
Named parameters
Added in v8.0
function enableThisConfig($optionA, $optionB, $somethingElse, $anotherOne) {
//
}
// 6 months later reading this.. ??
enableThisConfig(true, true, false, 'DEV');
// using named params
enableThisConfig(
optionA: true,
optionB: true,
somethingElse: false,
anotherOne: 'DEV',
);
Null coalescing operator
Added in v7.0
// this annoying thing
$data['name'] = isset($data['name']) ? $data['name'] : 'Guest';
// to
$data['name'] = $data['name'] ?? 'Guest';
Null coalescing assignment operator
Added in v7.4
// our previous example
$data['name'] = $data['name'] ?? 'Guest';
// to
$data['name'] ??= 'Guest';
Null-safe operator
Added in v8.0
class User {
public function getPreferences() {}
}
class Preferences {
public function getColorScheme() {}
}
$user = new User;
// ❌ preferences could be null
$colorScheme = $user->getPreferences()->getColorScheme();
// alternative
$preferences = $user->getPreferences();
$colorScheme = $preferences ? $preferences->getColorScheme() : null;
// using null-safe operator
$colorScheme = $user->getPreferences()?->getColorScheme();
Spaceship operator
Added in v7.0
$result = 1 <=> 1 // 0
$result = 1 <=> 2 // -1
$result = 2 <=> 1 // 1
$array = [1, 4, 5, 6, 7, 8 ,9 ,2];
usort($array, fn($a, $b) => $a <=> $b);
Multi-catch exception handling
Added in v7.1
try {
// ...
} catch(ErrorA | ErrorB $e) {
//
} catch(Exception $e) {
// general case
}
New string utils
Added in v8.0
Previously we would use strpos
or some other creative solution.
$string = 'Modern PHP';
if (str_starts_with($string, 'Modern')) {}
if (str_ends_with($string, 'PHP')) {}
if (str_contains($string, 'Modern')) {}
Return types
Added in v7.0
function getPost(int $postId): Post {
//
}
Union types
Added in v8.0
function updateTotal(int|float $cost) {
//
}
Null and Void return types
Added in v7.1
// Notice the `?` before the type
function getPost(int $postId): ?Post {
// can return null
}
function setPostTitle(int $postId, string $title): void {
persistInDB($postId, $postTitle);
// won't return anything, or optionally can do:
// `return;`
}
Never return type
Added in v8.1
function notImplemented(): never {
throw new Exception('Not implemented');
}
Import grouping
Added in v7.0
A small but welcome addition
// all in separate lines
use App\Entity\Task;
use App\Entity\Reminder;
use App\Entity\Todo;
// now
use App\Entity\{Task, Reminder, Todo};
Constructor property promotion
Added in v8.0
// from this
class User {
public string $name;
public Address $address;
public function __construct(string $name, Address $address) {
$this->name = $name;
$this->address = $address;
}
}
// to this
class User {
public function __construct(protected string $name, protected Address $address) {
// nothing else needed
}
}
Weakmaps
Not really using Weakmaps, not even in JavaScript, but I find it a great addition
// code taken from https://php.watch/versions/8.0/weakmap
class CommentRepository {
private WeakMap $comments_cache;
public function getCommentsByPost(Post $post): ?array {
if (!isset($this->comments_cache[$post])) {
$this->comments_cache[$post] = $this->loadComments($post);
}
return $this->comments_cache[$post]
}
}
I have to say I’m very happy to see these features, and I look forward to the next PHP releases with great excitement. Although I’ll be mostly working with Laravel and its extensive library (collections & helpers) watching the language grow is lovely.
🐘