PHP 8.3: typed constants and the small wins that stick

PHP 8.3 landed November 23rd. Quiet release by PHP standards: no enum-sized shift, no JIT. What it does have is a focused set of improvements that close long-standing gaps in the type system and add functions that should have existed years ago. Typed class constants Class constants have been untyped since their introduction. PHP 8.3 fixes that: interface HasVersion { const string VERSION; } class App implements HasVersion { const string VERSION = '1.0.0'; } Without typed constants, an interface constant could be overridden with a completely different type in an implementing class and nothing would complain. Typed constants close that gap, and on interface-driven codebases the impact is immediate. ...

January 7, 2024 · 6 min · Guillaume Delré

API Platform 3.2: errors as resources and sub-resources come back

API Platform 3.2 arrived in October 2023 with three changes that pushed the state model further: errors became resources, sub-resources came back in a form that actually fits the architecture, and the last legacy extension point — event listeners — was formally replaced. Errors as resources Before 3.2, error handling was outside the resource model. Exceptions were caught by a Symfony event listener and converted to a response, with limited control over the shape of the output. ...

October 12, 2023 · 3 min · Guillaume Delré

API Platform 3.1: your resource doesn't have to be your entity

Four months after 3.0, API Platform 3.1 arrived with the first batch of features built on the new state model. Not every change is dramatic, but one of them solves a problem that drove a lot of convoluted workarounds in 2.x: your API resource no longer needs to be your Doctrine entity. The resource/entity split In 2.x, API Platform worked best when your API resource and your persistence model were the same class. Using a DTO as the API surface was possible through the Input/Output DTO system, but that system was removed in 3.0 — it complicated the state model without enough benefit. ...

January 23, 2023 · 4 min · Guillaume Delré

PHP 8.2: readonly classes and the deprecation that matters

PHP 8.2 dropped December 8th. Readonly classes are the headline. The deprecation of dynamic properties is the one that actually requires your attention. Dynamic properties deprecated PHP has always allowed adding properties to objects that weren’t declared in the class: class User {} $user = new User(); $user->name = 'Alice'; // no declaration, no error... until now In 8.2, this triggers a deprecation notice. In PHP 9.0 it becomes a fatal error. The grace period exists, but the migration clock is running. ...

January 22, 2023 · 5 min · Guillaume Delré

API Platform 3.0: a new state model and the end of DataProviders

API Platform 3.0 arrived in September 2022 with Symfony 6.1 as a hard minimum and a core architecture that looked nothing like 2.x. The migration guide is long. The reason it’s long is interesting. The old model had a conceptual leak. DataProviderInterface and DataPersisterInterface were called for every HTTP request, but the provider received the operation context as a hint — not as a contract. A collection provider and an item provider were separate interfaces, but both lived in the same mental bucket: “things that return data.” The HTTP layer knew what was being requested; the provider had to reconstruct that knowledge from context clues passed in the $context array. ...

November 18, 2022 · 4 min · Guillaume Delré

Swarrot vs Symfony Messenger: a real-world comparison

We migrated a media microservices platform to Symfony 6 at the start of 2022. Twelve services, most of them consuming messages from RabbitMQ via Swarrot. Symfony 6 made Messenger more central than ever, and during the migration planning a developer asked the obvious question: why not switch at the same time? It ships with the framework. It has retry logic, native AMQP support, first-party documentation. Our setup looked artisanal by comparison. ...

January 26, 2022 · 5 min · Guillaume Delré

Symfony 6.0: PHP 8.1 only, and the security system rebuilt

Symfony 6.0 released November 29, 2021. The defining characteristic: PHP 8.1 is the minimum. Not supported, required. The releases team waited for PHP 8.1 to ship, then cut Symfony 6.0 the next day. This isn’t just a version bump. It’s a commitment to build against the current language instead of the historical floor. The security system, finally rebuilt The Symfony security component has two systems. The old one (AnonymousToken, GuardAuthenticatorInterface, a tangle of interfaces that made you implement methods you didn’t need) had been deprecated. 6.0 removes it entirely. ...

January 12, 2022 · 5 min · Guillaume Delré

Symfony 5.4 LTS: enum support, route aliases, and the PHP 8.1 bridge

Symfony 5.4 landed November 29, 2021, same day as Symfony 6.0 and one day after PHP 8.1 was released. Not a coincidence. 5.4 is the LTS, and its job is to carry as much of 6.0’s feature set as possible while keeping 5.x compatibility intact. It’s also the first Symfony release that actually understands PHP 8.1 features. Enum support PHP 8.1 introduced native enums. Symfony 5.4 embraces them immediately: enum Status: string { case Active = 'active'; case Inactive = 'inactive'; } The EnumType form type renders enums as select fields, no custom transformers needed. The validator understands backed enums. The serializer maps enum values to their backing type and back. Three components updated in one shot, which meant migrating codebases from pseudo-enum constants to real PHP 8.1 enums was actually pretty smooth. ...

January 10, 2022 · 7 min · Guillaume Delré

PHP 8.1: enums, fibers, and the type system growing up

PHP 8.1 released November 25th. It follows 8.0’s sweeping overhaul with something different: fewer features, but each one thought through rather than bolted on. Enums This is the one that changes codebases the moment you upgrade. Before 8.1, PHP enumerations were either class constants, strings, or integers with nothing enforcing them: // before: nothing stops Status::INVALID from being passed const ACTIVE = 'active'; const INACTIVE = 'inactive'; // after enum Status: string { case Active = 'active'; case Inactive = 'inactive'; } function activate(Status $status): void { ... } PHP enums are objects, not scalars. They support methods, interfaces, and constants. Backed enums (with a string or int value) serialize cleanly and map to database columns naturally. Pure enums (no backing type) enforce domain concepts without worrying about serialization. ...

January 9, 2022 · 5 min · Guillaume Delré

PHP 8.0: match, named arguments, attributes, and JIT

PHP 8.0 shipped November 26th. I’ve been running it for six weeks on a side project and a greenfield service at work. It’s the most significant PHP release since 7.0, and in some ways more impactful, because the changes pile on top of each other in useful ways. JIT The Just-In-Time compiler was the headline announcement. The reality in production is more nuanced: for typical web apps (database queries, HTTP calls, template rendering) the gains are modest, because those workloads are I/O bound, not compute bound. Where JIT actually shines is CPU-intensive code: image manipulation, data transformation, mathematical computation. ...

January 10, 2021 · 8 min · Guillaume Delré