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

Symfony 3.0 is not a rewrite

Symfony 2.8 and 3.0, released today, both at once, on purpose. The pairing is the whole message. 2.8 is the last of the 2.x line and an LTS. 3.0 is roughly 2.8 with the deprecated code deleted. Same features, cleaner body. This turns a scary major upgrade into two steps. Step one, move to 2.8. Minor upgrade, low risk. Then make the deprecation log empty. The phpunit bridge prints every deprecated call your code and tests touch. Burn the list down during normal sprints, item by item, each fix small and releasable on its own. ...

November 30, 2015 · 2 min · Murat Useinov

JSON column is not a schema

ALTER TABLE users ADD profile JSON; MySQL 5.7 went GA this week, and this line is now legal. Native JSON type: validation on insert, binary storage, functions to read paths. The question is not whether it works. It is what belongs in it. Classic case: user profile with flexible metadata. Marketing wants a new field every other week. The old options were bad in familiar ways. Forty nullable columns and an ALTER for every idea. Or EAV, key-value rows, where every read is a self-join festival and nothing has a type. ...

October 23, 2015 · 2 min · Murat Useinov

Kohana on PHP 7: whose problem is it

The test suite went green on the PHP 7 release candidate on the second try. I expected worse. Then I opened modules/ and the mood changed. The core mostly works. The community is patching compatibility in the 3.3 branch on GitHub right now, and the fixes are small: deprecated PHP 4 style constructors, changed engine behavior in dark corners. Whoever says the framework is dead is not reading the commit log. It is not dead. It is volunteers. ...

September 8, 2015 · 2 min · Murat Useinov

Redis cache and Redis queue are different databases

Two lines from redis.conf on one project: maxmemory 2gb maxmemory-policy allkeys-lru Correct for a cache. Then sessions moved into the same instance, because Redis was already there. Then the job queue, same reason. One process, three tenants. This works until the day it does not. Cache data is disposable by definition, and those two lines embrace that. Memory fills up, Redis evicts the coldest keys, the application rebuilds them on demand. Persistence is optional. After a restart a cold cache is an inconvenience, not an incident. ...

August 21, 2015 · 2 min · Murat Useinov

Run your tests on PHP 7 now

Three lines in .travis.yml. That is the whole cost of knowing in July what will break on PHP 7 in December. php: - 5.6 - nightly matrix: allow_failures: - php: nightly Alpha builds are out, release is planned for this autumn, and production is on 5.6 and will stay there for a while. The build stays green on 5.6. The nightly job fails quietly in the corner and produces a list of future problems while nobody is under pressure to read it. ...

July 8, 2015 · 2 min · Murat Useinov

Laravel 5.1 and the dependency ladder

PHP 5.5.9. That is the minimum for Laravel 5.1, which came out this week, the first Laravel with LTS: two years of bug fixes, three of security fixes. Two weeks ago I wrote the same about Symfony 2.7. Two frameworks arriving at the same idea in one summer is no coincidence. PHP applications got old enough to have a lifecycle. Draw the ladder for your project once. OS at the bottom. Then the PHP version the OS ships, or the one you build. Then the framework. Then the packages on top. Every rung has its own end-of-life, and the rungs are coupled. If production sits on some old distro with 5.4, the 5.1 upgrade is suddenly an ops project. And above you, half the packages will require 5.1 in their next major, so staying behind slowly cuts you off from fixes. ...

June 14, 2015 · 2 min · Murat Useinov

Symfony 2.7: LTS is the feature

Three years of bug fixes. Four years of security fixes. Symfony 2.7 came out this weekend, and that is the line from the announcement I care about. The changelog has new things in it, but the important word is on the label: LTS. For a pet project this is boring. For a product with paying users and a team, this is the actual feature. A big application does not upgrade for fun. Every framework upgrade is testing time, regression risk, and a sprint that produces nothing visible. Business asks a fair question: what do we get. “Newer version” is not an answer. “We keep receiving security fixes until 2018 without touching anything” is. ...

May 31, 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

Three ways to get a dependency in Laravel

Same repository, three ways to get it in Laravel 5. // 1. Constructor injection public function __construct(OrderRepository $orders) { $this->orders = $orders; } // 2. Facade $order = Orders::find($id); // 3. Service locator $orders = App::make('App\Repositories\OrderRepository'); All three work. The container resolves everything either way. The difference is in what the class tells you about itself. With constructor injection the dependencies are in the signature. You open the class, you read the constructor, you know what it needs. A test passes a mock and never touches the container. ...

March 9, 2015 · 2 min · Murat Useinov