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é

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é

Revision pruning with window functions and logarithms, when DQL wasn't enough

Every content update on the platform creates a revision. That’s by design: editors need a history they can roll back to, and the platform needs an audit trail. What nobody anticipated was the rate. Some articles go through forty saves in a single afternoon. A high-traffic piece accumulates hundreds of revisions over its lifetime. After a few months, the revision table had several million rows. Deleting them naively wasn’t an option. “Keep the last 50” loses all historical context for articles that haven’t been touched in a year. “Keep one per day” loses all the detail for content that’s actively being edited. What we needed was a distribution that matched how revisions are actually used: dense coverage for recent history, sparse coverage for old history. ...

September 27, 2020 · 8 min · Guillaume Delré

PHP 7.4: typed properties and the arrow function you actually want

PHP 7.4 landed November 28th. It’s the last 7.x release before PHP 8.0, and it feels like it. The features are substantial enough to stand on their own, but they also read as groundwork for what’s coming. Typed properties This is the one. Since PHP 7.0, you could type function parameters and return values. But class properties? Still untyped: class User { public int $id; public string $name; public ?DateTimeInterface $deletedAt; } 7.4 changes that. Typed properties enforce types at assignment, not just at call sites. Classes become self-documenting in a way that docblocks never quite managed, and the engine catches type errors before they propagate through half your stack. ...

January 12, 2020 · 6 min · Guillaume Delré

PHP 7.3: small wins that add up

PHP 7.3 shipped December 6th. No single killer feature. It’s a collection of quality-of-life improvements that individually feel minor but together make daily work noticeably less annoying. Flexible heredoc and nowdoc Until 7.3, the closing marker of a heredoc had to be at column zero. That forced awkward de-indentation in otherwise well-formatted code: // before $html = <<<HTML <div> <p>Hello</p> </div> HTML; // had to be at column 0, ugly // after $html = <<<HTML <div> <p>Hello</p> </div> HTML; The closing marker can now be indented to match the surrounding code, and that indentation is stripped from the content. This looks cosmetic. It’s not. Heredocs in nested contexts (class methods, conditionals) were visually jarring before. Now they fit. ...

January 20, 2019 · 6 min · Guillaume Delré

PHP 7.2: goodbye mcrypt, hello sodium

PHP 7.2 released November 30th. The headline isn’t a language feature, it’s a removal. mcrypt is gone. This is good news, even if it doesn’t feel that way when you’re the one migrating. The mcrypt problem mcrypt has been unmaintained since 2007. More than a decade of stagnation in a cryptography library. It was deprecated in 7.1, and 7.2 removes it entirely. The replacement is sodium, now bundled as a core extension. ...

January 14, 2018 · 6 min · Guillaume Delré

Enforcing UTC in Doctrine without touching your entities

A timestamp coming back from the database one hour off. Not every time. Only when the dev server runs in Europe/Paris and CI runs in UTC. The kind of bug that disappears when you look for it and comes back in production on a Friday evening. The problem isn’t in the business logic. It’s in what Doctrine quietly does with dates. What Doctrine does by default When you declare a datetime field in a Doctrine entity, the conversion between PHP and the database goes through DateTimeType. That class calls format() on your DateTime object to write to the database, and DateTime::createFromFormat() to read it back. No mention of timezone anywhere. ...

February 19, 2017 · 4 min · Guillaume Delré