Journal
Modern PHP in 2026: What Changed and Why It Matters
If your mental model of PHP is still mysql_query() and spaghetti templates, you’re about a decade behind. Modern PHP is typed, fast, and expressive. Here’s what changed.
Enums (PHP 8.1+)
PHP finally has first-class enums, both pure and backed:
enum Status: string
{
case Draft = 'draft';
case Published = 'published';
case Archived = 'archived';
public function label(): string
{
return match($this) {
self::Draft => 'Draft',
self::Published => 'Published',
self::Archived => 'Archived',
};
}
}
$post->status = Status::Published;
echo $post->status->label(); // "Published"
No more class constants masquerading as enums. They’re type-safe, support methods and interfaces, and work with match expressions.
Readonly Classes (PHP 8.2+)
Mark an entire class as readonly and every property becomes immutable after construction:
readonly class Money
{
public function __construct(
public int $amount,
public string $currency,
) {}
}
$price = new Money(1500, 'USD');
$price->amount = 2000; // Error: Cannot modify readonly property
Combined with constructor promotion, you get immutable value objects in three lines.
Fibers (PHP 8.1+)
Fibers provide cooperative multitasking — the foundation for async PHP without callbacks:
$fiber = new Fiber(function (): void {
$value = Fiber::suspend('paused');
echo "Resumed with: $value";
});
$result = $fiber->start(); // "paused"
$fiber->resume('hello'); // "Resumed with: hello"
You won’t use Fibers directly in most code. They power libraries like ReactPHP, Amp, and Laravel’s concurrency features. But understanding them explains how modern async PHP works under the hood.
Typed Properties and Union Types
Properties have supported type declarations since PHP 7.4, and union types arrived in 8.0:
class Article
{
public string $title;
public int|float $rating;
public ?DateTimeImmutable $publishedAt;
public string|Stringable $content;
}
Intersection types (8.1) and DNF types (8.2) complete the picture:
function process((Countable&Iterator)|null $items): void
{
// $items is either null or implements both Countable and Iterator
}
Match Expressions (PHP 8.0+)
A strict, expression-based alternative to switch:
$message = match($response->status()) {
200 => 'OK',
301, 302 => 'Redirect',
404 => 'Not Found',
500 => 'Server Error',
default => 'Unknown',
};
No fallthrough, strict comparison, and it returns a value. It replaced half the switch statements in my codebases overnight.
Named Arguments (PHP 8.0+)
Skip optional parameters and make calls self-documenting:
$user = User::create(
name: 'Aby',
email: 'aby@example.com',
role: Role::Admin,
);
Performance: OPcache and JIT
PHP 8.0 introduced a JIT compiler. Combined with OPcache, PHP’s performance is in a different league than it was five years ago:
- OPcache caches compiled bytecode, eliminating re-parsing on every request
- JIT compiles hot bytecode paths to machine code at runtime
- Real-world benchmarks show 10-30% improvements for CPU-bound workloads
For typical web apps (I/O bound), OPcache alone gives you most of the win. JIT shines in computation-heavy scenarios.
The Ecosystem
The language improvements enabled a modern ecosystem:
- Laravel continues to push boundaries with queues, events, and first-party packages
- Symfony provides rock-solid components used across the PHP world
- Composer is a mature dependency manager
- PHPStan/Psalm provide static analysis approaching TypeScript’s strictness
- PHP-CS-Fixer and Pint enforce consistent code style
PHP in 2026 isn’t the language people love to mock. It’s the language powering a significant chunk of the web, and for good reason.