PHP 7.4: typed properties first, preloading later

Two lines of a class, and half of my docblocks can be deleted: class Money { private int $amount; private string $currency; } PHP 7.4 came out yesterday, and this is the part I start using on Monday. Those docblocks existed only to say @var int. Now the language says it, and unlike the docblock, it checks. Assign a string, get a TypeError at the assignment, not a strange bug three layers later. There is one new state to learn. A typed property without a default is uninitialized, and reading it before the first write throws. This is the feature. “Object exists but is not filled yet” finally fails loudly instead of pretending to be null. ...

November 29, 2019 · 2 min · Murat Useinov

PostgreSQL 12 inlines your CTE

grep -rn "WITH " src/ was the first thing I ran after upgrading to PostgreSQL 12 this month. I was looking for a trick that had stopped working. For years WITH was an optimization fence. The planner materialized the CTE first and only then ran the outer query. Everyone used this both ways. As a bug: you wrap a subquery into a CTE for readability, the planner stops pushing conditions inside, a fast query becomes a scan of half a table. As a feature: you write a CTE on purpose, to pin evaluation order and stop the planner from being creative. Half of the CTE advice on the internet is really advice about the fence. ...

October 18, 2019 · 2 min · Murat Useinov

N+1 hides on your laptop

Ten rows in the dev database, on the same machine, 0.1 ms per query. Ten extra queries is one millisecond. The page feels instant, the code ships. Production has a thousand rows and the database one network hop away. Round trip is about a millisecond even in a good datacenter. A thousand queries is a second of pure network waiting. Not slow SQL. Each query is fast. The plural is slow. ...

July 5, 2019 · 2 min · Murat Useinov

PHP 7 measured on real code

One endpoint. The heaviest catalog page of one project, same data, same opcache settings, PHP 5.6 against 7.0. That is the only benchmark I trust. 7.0 has been out for three weeks, everyone has seen the hello-world numbers, and I do not believe hello-world numbers on principle. Response time dropped about 40 percent. Memory per request, more than half. No code changes. I ran it again because I did not believe the first numbers either. The engine rewrite is real: smaller internal structures, cheaper function calls, and typical framework code is exactly that, thousands of small calls and arrays. ...

December 26, 2015 · 2 min · Murat Useinov

Where Doctrine spends your milliseconds

$order->getCustomer()->getName() inside a foreach. Every time I open a slow Doctrine endpoint, I find some version of this line within ten minutes. The PHP is rarely slow. The shape of the data access is. Same few places every time. Lazy loading. The classic N+1. Doctrine proxies make it invisible: you touch the relation in a loop and every touch is a query. The profiler query count tells the truth. The code does not. ...

April 23, 2015 · 2 min · Murat Useinov