DI attributes: config moves into the class

Three lines of YAML in a file two directories away, for one integer that one service reads. That was the first thing I converted when I started moving a project’s service config from YAML into attributes, to see where the line is. public function __construct( #[Autowire('%env(int:IMPORT_BATCH_SIZE)%')] private int $batchSize, ) {} Now the class tells you everything about itself. Same with #[TaggedIterator] for collecting all implementations of an interface, and #[AsDecorator] for wrapping a service. These are local facts. A local fact belongs next to the code it describes. When I open the class, I want to stop searching. ...

March 27, 2023 · 2 min · Murat Useinov

Laravel 10 is a quiet major

Process::run('gzip -k dump.sql'). Out of the whole Laravel 10 release this week, that is the line I will actually use. The rest is quiet. Skeleton and framework code got native type declarations instead of docblocks, PHP 8.1 is the floor, and there is the Process facade. That is roughly the whole story. Some people are disappointed. I am not. A major release that is mostly maintenance means the framework is an adult. ...

February 16, 2023 · 2 min · Murat Useinov

Before Laravel 10: raise the baseline first

composer why-not php 8.1. One command, and it tells you who holds you back. Run it before making any plans. Laravel 10 comes next month and requires PHP 8.1. One project I help with is on Laravel 9 and PHP 8.0. The temptation is one heroic branch: new PHP, new framework, new package versions. When that branch breaks in production, you will not know which of the three changes broke it. ...

January 7, 2023 · 2 min · Murat Useinov

PHP 8.2: stricter by default

PHP 8.2 was released on December 8. My checklist for it looks like every other December, which is the whole point of a yearly cadence. On the surface a quiet release. Readonly classes, DNF types, standalone true, false and null types, constants in traits. Underneath, the more important half: another round of cleaning old dynamic behavior. Dynamic properties deprecated, ${var} string interpolation deprecated, utf8_encode deprecated. The language keeps trading looseness for predictability, and I keep voting for the trade. ...

December 16, 2022 · 2 min · Murat Useinov

Readonly classes for value objects

Two readonly keywords for two properties in a two-property class. That is what an immutable Money looks like in PHP 8.1: final class Money { public function __construct( public readonly int $amount, public readonly string $currency, ) {} } PHP 8.2 is at release candidate stage, and the feature I am waiting for is this one. The keyword moves up and says it once: final readonly class Money { public function __construct( public int $amount, public string $currency, ) {} } Every property is readonly, and the class refuses dynamic properties on top. Cosmetics, yes. But cosmetics that make the right thing the short thing, and that changes what people actually write. ...

November 8, 2022 · 2 min · Murat Useinov

Dynamic properties are leaving

$invoice->totall = 250; One extra letter. No error, no warning. A second property appears, the real total stays null, and the bug surfaces three screens later as “total is empty sometimes”. I have hunted this exact bug. Twice. Both times it took hours, because the write looks correct and the read looks correct. They just disagree on one letter. PHP 8.2 lands in December, and the deprecation that will touch the most code is this one. Writing to an undeclared property becomes deprecated. In some future major it becomes an error. For fifteen years it was legal: ...

September 3, 2022 · 2 min · Murat Useinov

Fast query, slow endpoint

The profiler said the query takes 20 ms. The export endpoint took 900 ms and a quarter of a gigabyte of memory. The missing 880 ms was Doctrine turning five thousand rows into five thousand entities. Hydration is not free. For every row the ORM builds an object, fills properties through reflection, registers it in the unit of work, creates proxies for relations. Per row it is nothing. Times five thousand, it is the endpoint. The profiler shows SQL because SQL is easy to show. ...

August 23, 2022 · 2 min · Murat Useinov

Laravel 9 and the boring major

One evening. That was the Laravel 9 upgrade on a mid-size project last week. Most of the diff was composer.json and a few config files. Laravel 9 is out this month. Symfony 6 components under the hood, PHP 8.0 minimum, Flysystem 3. And from now on, one major per year. The upgrade guide is short, and the “high impact” section is shorter. This is a policy: small majors, often. I want to defend this policy, because I remember the other kind. A project that sat on an old major for three years, because “we will upgrade when there is time”. There is never time. Then the version goes EOL, a security fix forces the jump, and you pay three years of drift in one horrible month. Packages you depend on drop support one by one, and suddenly it is not a framework upgrade anymore. It is archaeology. ...

February 25, 2022 · 2 min · Murat Useinov

Order status is an enum now

One row in orders had status 'canceled', with one l. Nobody knows how long it sat there. The enum found it on the first day. PHP 8.1 enums are six weeks old, enough time in one project to say something practical. The first candidate was obvious: order status. For years it was a class with string constants, Order::STATUS_PAID, Order::STATUS_SHIPPED. Constants look safe, but nothing stops a function from receiving 'payed'. The parameter type was string, and string accepts everything. ...

January 8, 2022 · 2 min · Murat Useinov

PHP 8.1: enums, readonly and a word about Fibers

'payed'. One letter, half a day of debugging, a few years ago. The string walked straight through OrderStatus::isValid(), because someone had added it to the constants list “for compatibility”. Every project I touched has that class: a bag of string constants, an isValid() helper, and a prayer. The type system never knew these strings were special. PHP 8.1 came out yesterday. Point release on paper, and it brings the feature I wanted since forever. Enums. ...

November 26, 2021 · 2 min · Murat Useinov