<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Php on Murat Useinov</title>
    <link>https://useinov.com/tags/php/</link>
    <description>Recent content in Php on Murat Useinov</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 17 Aug 2026 21:05:00 +0300</lastBuildDate>
    <atom:link href="https://useinov.com/tags/php/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Reading PHP 8.6 RFCs as constraints</title>
      <link>https://useinov.com/posts/2026/php-86-rfcs/</link>
      <pubDate>Mon, 17 Aug 2026 21:05:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2026/php-86-rfcs/</guid>
      <description>&lt;p&gt;Accepted, declined, discussion drama. That is how most posts about PHP 8.6 RFCs read, and the vote cycle is in full swing this month. I read RFCs for a different reason. Not &amp;ldquo;what do I get&amp;rdquo;, but &amp;ldquo;what will my current code look like next year&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;One RFC is a feature. Ten RFCs are a direction. When several push the same way, toward stricter types, explicit declarations, deprecating some old freedom, the direction tells you which of your habits are becoming legacy while you still type them. Syntax sugar is the least interesting part. Deprecations are the most interesting, because every deprecation is a migration with a countdown attached.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Dependency upgrades without a big bang</title>
      <link>https://useinov.com/posts/2026/small-dependency-upgrades/</link>
      <pubDate>Fri, 27 Feb 2026 19:45:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2026/small-dependency-upgrades/</guid>
      <description>&lt;p&gt;A &lt;code&gt;composer.lock&lt;/code&gt; that had not changed in three years. A colleague showed it to me last week, not as a confession, as a normal fact about the project. Nobody decided this. Every single upgrade looked small and risky, so everybody postponed it. Three years of postponed small risks equals one migration project with a budget and a name.&lt;/p&gt;
&lt;p&gt;The alternative is dull and it works. Upgrades as normal operations, like backups.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 8.5 one month later</title>
      <link>https://useinov.com/posts/2026/php-85-month-later/</link>
      <pubDate>Mon, 19 Jan 2026 21:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2026/php-85-month-later/</guid>
      <description>&lt;p&gt;Last week of December, low traffic, good test suite. That is when we moved one project to PHP 8.5. Not brave, convenient. A month later I can sort the release into two piles.&lt;/p&gt;
&lt;p&gt;Pile one is the demo features. Every release has them. They look great in a tweet-sized example, then you search the codebase and find two places to use them. Fine. Not why you upgrade.&lt;/p&gt;
&lt;p&gt;Pile two is the boring changes. Less boilerplate in places you touch every day. Types that say what a function really returns. Small things that make PHPStan complain earlier instead of production complaining later. This pile is why you upgrade, and you only see it after some weeks, never in the release notes.&lt;/p&gt;</description>
    </item>
    <item>
      <title>From Kohana to PHP 8.5</title>
      <link>https://useinov.com/posts/2025/eleven-years-later/</link>
      <pubDate>Thu, 11 Dec 2025 21:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2025/eleven-years-later/</guid>
      <description>&lt;p&gt;A Kohana ORM, an N+1 on a catalog page, and a blog post about it. That was November 2014. It is December 2025, the code around me runs PHP 8.5, and I went back and read that first post.&lt;/p&gt;
&lt;p&gt;The surface changed completely. A 2014 controller was a fat method: it read the request, validated by hand, called the ORM, formatted the response, sometimes sent an email on the way out. Types lived in docblocks and in hope. Deploy was files over FTP and a prayer. Today the same feature is a typed handler with constructor injection, validation at the boundary, a use case object, a message on a queue for the email, a trace id through the whole thing. The engine checks what a comment used to promise. I do not miss the old way.&lt;/p&gt;</description>
    </item>
    <item>
      <title>The November upgrade train</title>
      <link>https://useinov.com/posts/2025/november-upgrade-train/</link>
      <pubDate>Fri, 21 Nov 2025 22:30:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2025/november-upgrade-train/</guid>
      <description>&lt;p&gt;PHP 8.5 came out yesterday. Symfony 7.4 LTS and 8.0 land at the end of the month, same as every year. November is upgrade month now. First the cargo, then the timetable.&lt;/p&gt;
&lt;p&gt;From 8.5 I care about three things. The pipe operator makes transformation chains read left to right:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$slug&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$title&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;trim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtolower&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;preg_replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/\s+/&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The new URI extension puts standards-correct URL parsing in core. &lt;code&gt;parse_url&lt;/code&gt; had opinions instead of a specification, I will not miss it. And &lt;code&gt;#[\NoDiscard]&lt;/code&gt; lets a method declare that ignoring its return value is a bug. Made for result-style APIs, where a dropped return is a swallowed error.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Coding agents meet legacy PHP</title>
      <link>https://useinov.com/posts/2025/agents-and-legacy/</link>
      <pubDate>Fri, 10 Oct 2025 21:30:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2025/agents-and-legacy/</guid>
      <description>&lt;p&gt;A strange-looking condition in a 2015 module, &amp;ldquo;fixed&amp;rdquo; by an agent. All tests green, static analysis clean, and the condition was a business rule with history. My review caught it. Barely.&lt;/p&gt;
&lt;p&gt;That came out of an experiment I ran this month: a coding agent on one legacy PHP module, since agents can do multi-file changes now and every old codebase suddenly looks like a target.&lt;/p&gt;
&lt;p&gt;The setup matters more than the model. I did not ask &amp;ldquo;modernize this&amp;rdquo;. I gave a narrow migration goal on one bounded module: replace a deprecated API, add proper namespaces, keep behavior. The same shape of task I would give to Rector. The difference is that the agent also handles the irregular cases Rector cannot express, the places where people wrote creative code in 2015.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vendor/autoload.php is not free</title>
      <link>https://useinov.com/posts/2025/autoload-not-free/</link>
      <pubDate>Wed, 20 Aug 2025 20:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2025/autoload-not-free/</guid>
      <description>&lt;p&gt;46 milliseconds. That is how long the bootstrap of one large application took before the framework even started, in a trace I pulled this week. A good share of it was autoloading.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;require vendor/autoload.php&lt;/code&gt; looks like a constant of nature. With plain PSR-4 rules it is a loop: every class load walks the prefixes and asks the filesystem whether a file exists. A few thousand classes on a cold request, and the loop becomes a number you can see in a flame graph.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Symfony 7.3 and the code I get to delete</title>
      <link>https://useinov.com/posts/2025/delete-your-helpers/</link>
      <pubDate>Fri, 30 May 2025 15:25:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2025/delete-your-helpers/</guid>
      <description>&lt;p&gt;Symfony 7.3 was released this week. I opened the release notes with a text file next to them. The file is a list of things to delete.&lt;/p&gt;
&lt;p&gt;Every project older than three years carries a layer of custom helpers that were reasonable when written. The framework moves, the helpers stay. Nobody reviews them, because they work.&lt;/p&gt;
&lt;p&gt;Two examples from one project.&lt;/p&gt;
&lt;p&gt;A base console command class with sugar for arguments and options, written around 2021. Symfony 7.3 has invokable commands, arguments come as parameters with attributes:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Native lazy objects, the ORM view</title>
      <link>https://useinov.com/posts/2025/doctrine-lazy-objects/</link>
      <pubDate>Wed, 09 Apr 2025 22:35:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2025/doctrine-lazy-objects/</guid>
      <description>&lt;p&gt;Open the Doctrine proxy folder of any project older than a year. A generated subclass for every entity, getters overridden, one file per class in the cache dir. It worked for fifteen years, and the seams always showed: final classes, public typed properties, &lt;code&gt;instanceof&lt;/code&gt; surprises, a folder of code you pretend not to see.&lt;/p&gt;
&lt;p&gt;PHP 8.4 lazy objects were designed with exactly this in mind. The native way needs no generated class:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 12 is a boring release</title>
      <link>https://useinov.com/posts/2025/laravel-12-boring/</link>
      <pubDate>Wed, 26 Feb 2025 23:30:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2025/laravel-12-boring/</guid>
      <description>&lt;p&gt;Laravel 12 came out on Monday. Tuesday evening I upgraded one mid-size project. The whole diff is composer constraints and the lock file. Tests green, deployed before midnight.&lt;/p&gt;
&lt;p&gt;People in chats are disappointed. Where are the big features. A major that mostly refreshes dependencies and starter kits, is it even a major.&lt;/p&gt;
&lt;p&gt;I remember 4.2 to 5.0. New folder structure, new config system, half of the packages dead on arrival. We planned it as a separate project, because it was one. Skip a major, and the next one cost double.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 8.4 after the hype</title>
      <link>https://useinov.com/posts/2025/php84-in-production/</link>
      <pubDate>Wed, 15 Jan 2025 15:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2025/php84-in-production/</guid>
      <description>&lt;p&gt;A private field, a getter, a setter with one line of normalization. Three members for one idea, repeated through every entity folder I have ever opened. PHP 8.4 is two months old, the conference demos are done, and the honest question is which features earn a place in a working codebase.&lt;/p&gt;
&lt;p&gt;My filter is simple. A feature is good when it deletes code.&lt;/p&gt;
&lt;p&gt;Property hooks pass:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;User&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtolower&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;trim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The class is shorter and the normalization cannot be bypassed. But keep hooks boring. A hook that talks to the database is a magic getter from 2008 in new syntax.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Lazy objects move into the engine</title>
      <link>https://useinov.com/posts/2024/lazy-objects/</link>
      <pubDate>Sat, 23 Nov 2024 12:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2024/lazy-objects/</guid>
      <description>&lt;p&gt;Fifty ghosts initialized in a loop are still fifty one queries.&lt;/p&gt;
&lt;p&gt;PHP 8.4 is out since Thursday. Property hooks get the headlines, but the feature I keep coming back to is native lazy objects. It legalizes a trick ORMs have done with generated code for fifteen years.&lt;/p&gt;
&lt;p&gt;The trick: you load an order, &lt;code&gt;$order-&amp;gt;customer&lt;/code&gt; should be a Customer, and you do not want a query until someone actually touches it. Doctrine generates a proxy class at build time, a subclass that overrides every method with &amp;ldquo;initialize first, then call parent&amp;rdquo;. It works. It is also a pile of magic: generated files, edge cases with final classes and private properties, strange things in var_dump.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Property hooks, before the hype settles</title>
      <link>https://useinov.com/posts/2024/property-hooks/</link>
      <pubDate>Mon, 14 Oct 2024 20:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2024/property-hooks/</guid>
      <description>&lt;p&gt;Six lines of getter and setter ceremony around one &lt;code&gt;strtolower&lt;/code&gt;. PHP 8.4 is in release candidates, and property hooks delete exactly that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Customer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strtolower&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;trim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Assignment stays assignment, &lt;code&gt;$customer-&amp;gt;email = $input&lt;/code&gt;, and normalization happens on the way in. For this exact case, one value, no dependencies, I am convinced. The property remains a property.&lt;/p&gt;
&lt;p&gt;Where I get careful is everything past normalization. A setter method is ugly, but it announces itself. &lt;code&gt;setEmail()&lt;/code&gt; in a call stack tells you code ran. &lt;code&gt;$obj-&amp;gt;email = $x&lt;/code&gt; looks free, and with hooks it is not. Put validation that throws into a hook, and every plain assignment becomes a possible exception, invisible at the call site. Put a side effect in, and you have built magic the next person discovers through a debugger at midnight. We spent years removing &lt;code&gt;__get&lt;/code&gt; and &lt;code&gt;__set&lt;/code&gt; from codebases for this exact crime. Hooks are their respectable cousins, better typed, analyzable, and the temptation is identical.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Pick the library with a profiler, not a chart</title>
      <link>https://useinov.com/posts/2024/profiler-not-benchmark/</link>
      <pubDate>Wed, 07 Aug 2024 20:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2024/profiler-not-benchmark/</guid>
      <description>&lt;p&gt;Two hundred thousand ops per second against eighty. That was the bar chart, and the team was ready to pick a serializer from it. I asked for one day.&lt;/p&gt;
&lt;p&gt;The day went like this.&lt;/p&gt;
&lt;p&gt;Take a representative workload. Not a synthetic three-field object, but a hundred real payloads from production logs, with the nested structures, the nullable mess, the one field that is sometimes a list and sometimes a map. Anonymize them, save as fixtures. This step is the whole method. The bar chart was measured on someone else&amp;rsquo;s data with someone else&amp;rsquo;s configuration, and a serializer is exactly the kind of code whose cost depends on the shape of the input.&lt;/p&gt;</description>
    </item>
    <item>
      <title>A faster sprintf will not fix your p99</title>
      <link>https://useinov.com/posts/2024/microbenchmark-trap/</link>
      <pubDate>Tue, 18 Jun 2024 23:15:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2024/microbenchmark-trap/</guid>
      <description>&lt;p&gt;Three times faster. That is the number in the benchmark posts about PHP 8.4, where simple &lt;code&gt;sprintf()&lt;/code&gt; calls with basic placeholders get turned into plain string operations at compile time. The function call disappears.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%s-%d&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$prefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I like this work. I do not like how people read it.&lt;/p&gt;
&lt;p&gt;A microbenchmark measures one function in a loop, alone, with a warm cache and nothing else to do. Now profile a real request from the same application. In every backend I have profiled the picture has the same shape: most of the wall time is waiting. SQL queries, Redis round trips, an HTTP call to some partner API. The CPU spent inside PHP is a modest slice, and &lt;code&gt;sprintf&lt;/code&gt; inside that slice is a rounding error. Three times faster rounding error is still a rounding error.&lt;/p&gt;</description>
    </item>
    <item>
      <title>One trace through HTTP, queue and database</title>
      <link>https://useinov.com/posts/2024/one-trace/</link>
      <pubDate>Thu, 08 Feb 2024 19:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2024/one-trace/</guid>
      <description>&lt;p&gt;A request creates a job. The job calls an external API and writes to the database. Something in this chain is slow, and the logs tell four disconnected stories.&lt;/p&gt;
&lt;p&gt;Grepping a request id across services is our folklore. OpenTelemetry finally makes the boring standard version practical in PHP.&lt;/p&gt;
&lt;p&gt;The core is small. One trace id for the whole causal chain, a span id for every operation, and one rule: pass the context along. Over HTTP it is a single header:&lt;/p&gt;</description>
    </item>
    <item>
      <title>AI assistants do not know your production</title>
      <link>https://useinov.com/posts/2023/ai-assistants-limits/</link>
      <pubDate>Sat, 23 Dec 2023 10:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2023/ai-assistants-limits/</guid>
      <description>&lt;p&gt;A DTO with a dozen fields, a framework migration, a slow query incident. Three tasks from the last few months, all done with Copilot and ChatGPT open. Here is where the line runs for me.&lt;/p&gt;
&lt;p&gt;Task one, boilerplate. The DTO, its validation rules, a test with fixtures. The assistant writes this faster than I type, and the result needs only a quick read. Same for converting an array-shaped legacy structure into typed classes. This is honest time saved, maybe an hour a day. The code is the kind where being generic is correct.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 8.3 and Symfony 7 in one month</title>
      <link>https://useinov.com/posts/2023/november-baselines/</link>
      <pubDate>Thu, 30 Nov 2023 10:45:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2023/november-baselines/</guid>
      <description>&lt;p&gt;&lt;code&gt;public const int TIMEOUT_SECONDS = 30;&lt;/code&gt; One week old, and the one thing I waited for from PHP 8.3.&lt;/p&gt;
&lt;p&gt;Busy November. 8.3 released last week, and yesterday Symfony shipped 6.4 LTS and 7.0 on the same day. Time to plan, not to jump.&lt;/p&gt;
&lt;p&gt;Typed class constants are a small thing, but constants were the last untyped island in a class, and a child class could silently redefine one to a different type. Also &lt;code&gt;json_validate()&lt;/code&gt;, which checks JSON syntax without building the whole tree in memory. Everyone had a helper that did &lt;code&gt;json_decode&lt;/code&gt; and threw the result away. Now the helper can retire.&lt;/p&gt;</description>
    </item>
    <item>
      <title>FrankenPHP: the server absorbs PHP</title>
      <link>https://useinov.com/posts/2023/frankenphp-first-look/</link>
      <pubDate>Fri, 27 Oct 2023 18:25:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2023/frankenphp-first-look/</guid>
      <description>&lt;p&gt;One binary, my code, a Caddyfile. That is the whole deployment of a small internal service I moved to FrankenPHP in staging this week.&lt;/p&gt;
&lt;p&gt;The classic PHP deployment is a couple: Nginx in front, PHP-FPM behind, FastCGI between them. Two configs, two processes to supervise, one socket to misconfigure. It works, we all know it by heart, and knowing it by heart is the only reason we call it simple.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Persistent PHP workers need discipline</title>
      <link>https://useinov.com/posts/2023/persistent-worker-discipline/</link>
      <pubDate>Mon, 03 Apr 2023 15:45:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2023/persistent-worker-discipline/</guid>
      <description>&lt;p&gt;A static array with the comment &amp;ldquo;cache, cleared per request&amp;rdquo;. It went to production on RoadRunner last month, and the memory graph turned into a staircase.&lt;/p&gt;
&lt;p&gt;PHP had one superpower nobody advertised: the request died. Every leak, every forgotten static, every open transaction was erased when the process shut down. Shared-nothing was not architecture. It was amnesia, and amnesia forgave us everything.&lt;/p&gt;
&lt;p&gt;RoadRunner and Swoole take it away. The worker lives for thousands of requests, and the class of bugs changes on the first day. That &amp;ldquo;per request&amp;rdquo; cache became a cache per worker lifetime, and the supervisor killed the process when it ran out of memory. A logger kept the request id in a property, so entries from user B carried the id of user A. And the best one: an exception in the middle of a Doctrine transaction left the connection with the transaction open, and the next request on that worker silently joined it.&lt;/p&gt;</description>
    </item>
    <item>
      <title>DI attributes: config moves into the class</title>
      <link>https://useinov.com/posts/2023/symfony-di-attributes/</link>
      <pubDate>Mon, 27 Mar 2023 23:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2023/symfony-di-attributes/</guid>
      <description>&lt;p&gt;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&amp;rsquo;s service config from YAML into attributes, to see where the line is.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;#[&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;Autowire&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%env(int:IMPORT_BATCH_SIZE)%&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$batchSize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the class tells you everything about itself. Same with &lt;code&gt;#[TaggedIterator]&lt;/code&gt; for collecting all implementations of an interface, and &lt;code&gt;#[AsDecorator]&lt;/code&gt; 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.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 10 is a quiet major</title>
      <link>https://useinov.com/posts/2023/laravel-ten-quiet/</link>
      <pubDate>Thu, 16 Feb 2023 20:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2023/laravel-ten-quiet/</guid>
      <description>&lt;p&gt;&lt;code&gt;Process::run(&#39;gzip -k dump.sql&#39;)&lt;/code&gt;. Out of the whole Laravel 10 release this week, that is the line I will actually use.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Before Laravel 10: raise the baseline first</title>
      <link>https://useinov.com/posts/2023/laravel-upgrade-baseline/</link>
      <pubDate>Sat, 07 Jan 2023 22:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2023/laravel-upgrade-baseline/</guid>
      <description>&lt;p&gt;&lt;code&gt;composer why-not php 8.1&lt;/code&gt;. One command, and it tells you who holds you back. Run it before making any plans.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 8.2: stricter by default</title>
      <link>https://useinov.com/posts/2022/php-eight-two/</link>
      <pubDate>Fri, 16 Dec 2022 21:35:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2022/php-eight-two/</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;On the surface a quiet release. Readonly classes, DNF types, standalone &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt; and &lt;code&gt;null&lt;/code&gt; types, constants in traits. Underneath, the more important half: another round of cleaning old dynamic behavior. Dynamic properties deprecated, &lt;code&gt;${var}&lt;/code&gt; string interpolation deprecated, &lt;code&gt;utf8_encode&lt;/code&gt; deprecated. The language keeps trading looseness for predictability, and I keep voting for the trade.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Readonly classes for value objects</title>
      <link>https://useinov.com/posts/2022/readonly-classes/</link>
      <pubDate>Tue, 08 Nov 2022 22:45:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2022/readonly-classes/</guid>
      <description>&lt;p&gt;Two &lt;code&gt;readonly&lt;/code&gt; keywords for two properties in a two-property class. That is what an immutable &lt;code&gt;Money&lt;/code&gt; looks like in PHP 8.1:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Money&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$currency&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;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:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Money&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$currency&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;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.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Dynamic properties are leaving</title>
      <link>https://useinov.com/posts/2022/dynamic-properties-deprecated/</link>
      <pubDate>Sat, 03 Sep 2022 20:30:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2022/dynamic-properties-deprecated/</guid>
      <description>&lt;p&gt;&lt;code&gt;$invoice-&amp;gt;totall = 250;&lt;/code&gt; One extra letter. No error, no warning. A second property appears, the real &lt;code&gt;total&lt;/code&gt; stays null, and the bug surfaces three screens later as &amp;ldquo;total is empty sometimes&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Fast query, slow endpoint</title>
      <link>https://useinov.com/posts/2022/hydration-cost/</link>
      <pubDate>Tue, 23 Aug 2022 15:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2022/hydration-cost/</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 9 and the boring major</title>
      <link>https://useinov.com/posts/2022/laravel-nine-cadence/</link>
      <pubDate>Fri, 25 Feb 2022 12:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2022/laravel-nine-cadence/</guid>
      <description>&lt;p&gt;One evening. That was the Laravel 9 upgrade on a mid-size project last week. Most of the diff was &lt;code&gt;composer.json&lt;/code&gt; and a few config files.&lt;/p&gt;
&lt;p&gt;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 &amp;ldquo;high impact&amp;rdquo; section is shorter. This is a policy: small majors, often.&lt;/p&gt;
&lt;p&gt;I want to defend this policy, because I remember the other kind. A project that sat on an old major for three years, because &amp;ldquo;we will upgrade when there is time&amp;rdquo;. 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.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Order status is an enum now</title>
      <link>https://useinov.com/posts/2022/enums-for-status/</link>
      <pubDate>Sat, 08 Jan 2022 23:15:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2022/enums-for-status/</guid>
      <description>&lt;p&gt;One row in &lt;code&gt;orders&lt;/code&gt; had status &lt;code&gt;&#39;canceled&#39;&lt;/code&gt;, with one l. Nobody knows how long it sat there. The enum found it on the first day.&lt;/p&gt;
&lt;p&gt;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, &lt;code&gt;Order::STATUS_PAID&lt;/code&gt;, &lt;code&gt;Order::STATUS_SHIPPED&lt;/code&gt;. Constants look safe, but nothing stops a function from receiving &lt;code&gt;&#39;payed&#39;&lt;/code&gt;. The parameter type was &lt;code&gt;string&lt;/code&gt;, and &lt;code&gt;string&lt;/code&gt; accepts everything.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 8.1: enums, readonly and a word about Fibers</title>
      <link>https://useinov.com/posts/2021/php81-enums/</link>
      <pubDate>Fri, 26 Nov 2021 20:45:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2021/php81-enums/</guid>
      <description>&lt;p&gt;&lt;code&gt;&#39;payed&#39;&lt;/code&gt;. One letter, half a day of debugging, a few years ago. The string walked straight through &lt;code&gt;OrderStatus::isValid()&lt;/code&gt;, because someone had added it to the constants list &amp;ldquo;for compatibility&amp;rdquo;. Every project I touched has that class: a bag of string constants, an &lt;code&gt;isValid()&lt;/code&gt; helper, and a prayer. The type system never knew these strings were special.&lt;/p&gt;
&lt;p&gt;PHP 8.1 came out yesterday. Point release on paper, and it brings the feature I wanted since forever. Enums.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Generics we already have</title>
      <link>https://useinov.com/posts/2021/phpstan-generics/</link>
      <pubDate>Sat, 09 Oct 2021 20:05:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2021/phpstan-generics/</guid>
      <description>&lt;p&gt;A template calls &lt;code&gt;getEmail()&lt;/code&gt; on an Order at three in the morning. The method returned &lt;code&gt;array&lt;/code&gt;, the docblock said &amp;ldquo;array of User&amp;rdquo;, and eight months later someone put an Order in it. Everything was fine until it was not.&lt;/p&gt;
&lt;p&gt;PHP still has no generics and will not get them soon. Meanwhile PHPStan and Psalm shipped them anyway, in docblocks. Checked at analysis, not at runtime. For everyday backend work that turns out to be most of the value.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Attributes in Symfony 5.3</title>
      <link>https://useinov.com/posts/2021/symfony-attributes/</link>
      <pubDate>Fri, 04 Jun 2021 21:05:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2021/symfony-attributes/</guid>
      <description>&lt;p&gt;&lt;code&gt;#[Route(&#39;/orders/{id}&#39;)]&lt;/code&gt; right above the method, and nothing in &lt;code&gt;config/routes&lt;/code&gt;. Symfony 5.3 came out last week, and with PHP 8 attributes configuration finally found its place. Routes, autowiring hints, some validation, native syntax next to the code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;#[&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;Route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/orders/{id}&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;methods&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;show&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nd&#34;&gt;Response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I spent years defending YAML routing. The argument was separation: code is code, wiring is wiring, one file shows the whole URL map. The argument was never wrong. It lost to practice. In every real project the first thing you do with a route is jump to the controller, and the first thing you do with a controller is wonder which route hits it. Two files, one mental join, forever. Attributes remove the join. Rename a method, the metadata moves with it. Delete the class, no orphaned YAML block stays behind to confuse the next person.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Symfony Runtime: front controller as a callable</title>
      <link>https://useinov.com/posts/2021/symfony-runtime/</link>
      <pubDate>Fri, 16 Apr 2021 23:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2021/symfony-runtime/</guid>
      <description>&lt;p&gt;&lt;code&gt;public/index.php&lt;/code&gt;, unchanged since I learned Symfony. Create the request from globals, run the kernel, send the response, terminate. Symfony 5.3 arrives next month, and the Runtime component rewrites this file. It looks like a small refactoring. It is a statement about where PHP is going.&lt;/p&gt;
&lt;p&gt;Baked into those few lines is one big assumption: one process, one request, then we die. FPM made the assumption true for fifteen years, so nobody saw it as an assumption.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 8 in production: dependencies first</title>
      <link>https://useinov.com/posts/2021/php8-dependencies-first/</link>
      <pubDate>Fri, 08 Jan 2021 23:20:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2021/php8-dependencies-first/</guid>
      <description>&lt;p&gt;&lt;code&gt;composer why-not php 8.0&lt;/code&gt;. That is the first command of the migration, and it has nothing to do with syntax.&lt;/p&gt;
&lt;p&gt;PHP 8.0 is six weeks old. Twitter is full of match expressions and constructor promotion. Meanwhile the real upgrade of a working project is a dependency problem. Your code is maybe twenty percent of what runs in production. The rest is the framework, thirty packages and a dozen extensions, and any one of them can be the blocker.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 8.0: ignore the JIT, take the rest</title>
      <link>https://useinov.com/posts/2020/php8-new-baseline/</link>
      <pubDate>Fri, 27 Nov 2020 21:25:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2020/php8-new-baseline/</guid>
      <description>&lt;p&gt;JIT on, JIT off, the same Symfony endpoint. Difference within noise. Same for a Laravel endpoint. PHP 8.0 came out yesterday and this was the first thing I checked, because every headline is about the JIT.&lt;/p&gt;
&lt;p&gt;Not a scandal. A web request spends its time in I/O, in MySQL, in framework code full of method calls the JIT cannot do much with. JIT is for tight numeric loops, and your controller has none. If you compute fractals in PHP, congratulations. The rest of us can leave it off.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Average response time explains nothing</title>
      <link>https://useinov.com/posts/2020/average-explains-nothing/</link>
      <pubDate>Tue, 04 Aug 2020 20:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2020/average-explains-nothing/</guid>
      <description>&lt;p&gt;Average response time: 180 ms. Ticket from support: &amp;ldquo;the API is slow&amp;rdquo;. Both true. The average is a diplomat. It offends nobody and tells you nothing.&lt;/p&gt;
&lt;p&gt;Latency is a distribution. Our 180 ms hides a p50 of 90 ms and a p99 above four seconds. One request in a hundred is terrible, and with thirty requests per page load, most users hit that unlucky one regularly. The people complaining are not imagining things. They live in the tail, and the average never visits there.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 8 will retire some of our workarounds</title>
      <link>https://useinov.com/posts/2020/php8-old-patterns/</link>
      <pubDate>Fri, 03 Jul 2020 22:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2020/php8-old-patterns/</guid>
      <description>&lt;p&gt;&lt;code&gt;@param int|string $id&lt;/code&gt;. I grepped one service this morning: dozens of those, and every one is a wish. The docblock says what the signature could not. First alpha of PHP 8 came out last week, and native union types turn the wish into a contract, checked at runtime and by tooling. Half of my phpdoc can go.&lt;/p&gt;
&lt;p&gt;Everyone writes about the JIT. I keep thinking about the less shiny thing: how many of our daily patterns exist only to compensate for a missing language feature, and are about to become legacy.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Types are for reading, not for the compiler</title>
      <link>https://useinov.com/posts/2020/typed-code-navigation/</link>
      <pubDate>Sat, 27 Jun 2020 22:20:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2020/typed-code-navigation/</guid>
      <description>&lt;p&gt;&lt;code&gt;OrderRepository&lt;/code&gt; in a constructor. Click. I am there. That is how I explore a big Symfony project now, and I noticed my main reason for types has changed. Not bug catching anymore. Navigation.&lt;/p&gt;
&lt;p&gt;Symfony 5.1 is out, PHP 7.4 is everywhere I work, and the IDE knows every caller and every implementation. Compare with the array-passing style we all wrote for years:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// what is in $data? read three call sites to find out
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;versus&lt;/p&gt;</description>
    </item>
    <item>
      <title>Waiting for three APIs, one at a time</title>
      <link>https://useinov.com/posts/2020/parallel-http-requests/</link>
      <pubDate>Tue, 21 Apr 2020 21:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2020/parallel-http-requests/</guid>
      <description>&lt;p&gt;Three external calls on one page: prices, stock, delivery estimate. Each answers in about 300 ms. The page waits a full second, because we call them one after another. PHP is synchronous, what can you do.&lt;/p&gt;
&lt;p&gt;Turns out, something. Symfony HttpClient is lazy. &lt;code&gt;request()&lt;/code&gt; sends and returns immediately. The waiting happens when you read the response. So start all three, read later:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$prices&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$client&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$pricesUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$stock&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$client&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$stockUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$delivery&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$client&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$deliveryUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;prices&amp;#39;&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$prices&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toArray&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;stock&amp;#39;&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$stock&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toArray&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;delivery&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$delivery&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toArray&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Under the hood it is curl multi. Three requests fly at once, wall time is the slowest one instead of the sum. Our second became 350 ms. No swoole, no reactphp, no async rewrite. Same boring controller.&lt;/p&gt;</description>
    </item>
    <item>
      <title>A Redis lock is a promise you cannot fully keep</title>
      <link>https://useinov.com/posts/2020/redis-lock-honesty/</link>
      <pubDate>Wed, 26 Feb 2020 21:45:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2020/redis-lock-honesty/</guid>
      <description>&lt;p&gt;Two workers, one order, processed twice. Every project gets this day. Someone writes &lt;code&gt;SETNX&lt;/code&gt;, calls it a distributed lock, closes the ticket. I want to slow down here, because the ticket is not closed.&lt;/p&gt;
&lt;p&gt;The small bugs first. A lock needs a TTL, or a crashed worker holds it forever. A lock needs an owner token, or worker A releases the lock of worker B:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bin2hex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;random_bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$ok&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$redis&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;lock:order:&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$orderId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;nx&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;ex&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the release must be atomic: compare the token and delete in one Lua script. Check in PHP, delete in a second command, and there is a gap. Something will land in that gap.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Static state outlives the job</title>
      <link>https://useinov.com/posts/2019/worker-static-state/</link>
      <pubDate>Sun, 22 Dec 2019 23:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2019/worker-static-state/</guid>
      <description>&lt;p&gt;One static array, from one of our projects:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Settings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$cache&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tenantId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;isset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tenantId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tenantId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tenantId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tenantId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;??&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A reasonable per-request cache. Under PHP-FPM it died with the process. In a queue worker it lives forever.&lt;/p&gt;
&lt;p&gt;PHP had one great architectural feature nobody put in the manual: the process died after every request. Leak memory, cache nonsense in a static, forget to close things. Did not matter. The dying process forgave everything.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 7.4: typed properties first, preloading later</title>
      <link>https://useinov.com/posts/2019/php74-preloading/</link>
      <pubDate>Fri, 29 Nov 2019 15:25:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2019/php74-preloading/</guid>
      <description>&lt;p&gt;Two lines of a class, and half of my docblocks can be deleted:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Money&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$currency&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;PHP 7.4 came out yesterday, and this is the part I start using on Monday. Those docblocks existed only to say &lt;code&gt;@var int&lt;/code&gt;. Now the language says it, and unlike the docblock, it checks. Assign a string, get a &lt;code&gt;TypeError&lt;/code&gt; at the assignment, not a strange bug three layers later.&lt;/p&gt;
&lt;p&gt;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. &amp;ldquo;Object exists but is not filled yet&amp;rdquo; finally fails loudly instead of pretending to be null.&lt;/p&gt;</description>
    </item>
    <item>
      <title>200 OK is not health</title>
      <link>https://useinov.com/posts/2019/kubernetes-probes-php/</link>
      <pubDate>Wed, 07 Aug 2019 18:05:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2019/kubernetes-probes-php/</guid>
      <description>&lt;p&gt;Route &lt;code&gt;/health&lt;/code&gt;, returns &lt;code&gt;ok&lt;/code&gt;, everyone happy. That is the first version of every health endpoint on the project we are moving to Kubernetes. Then somebody pastes the same path into &lt;code&gt;livenessProbe&lt;/code&gt; and &lt;code&gt;readinessProbe&lt;/code&gt;, and a thirty second database hiccup becomes a long evening.&lt;/p&gt;
&lt;p&gt;The two probes ask different questions.&lt;/p&gt;
&lt;p&gt;Readiness asks: should this pod get traffic right now. Here it is correct to check dependencies. Database unreachable, cache cold, migrations still running: answer no. Kubernetes takes the pod out of the Service, traffic goes to the others, the pod returns when the world improves. Failing readiness is cheap and reversible. A polite &amp;ldquo;not now&amp;rdquo;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>One use case, one class</title>
      <link>https://useinov.com/posts/2019/application-services/</link>
      <pubDate>Wed, 26 Jun 2019 15:30:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2019/application-services/</guid>
      <description>&lt;p&gt;Four hundred lines in a checkout action, half of them inside one try. That is one way a project dies. The other is &lt;code&gt;OrderManager&lt;/code&gt;: thirty methods, six injected services, and nobody can say what the class is for, because it is for everything about orders.&lt;/p&gt;
&lt;p&gt;Both diseases have the same cure and it is embarrassingly simple. One use case, one class, one public method.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;PlaceOrder&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;OrderRepository&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$orders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;PaymentGateway&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$payments&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;EventDispatcher&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$events&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;PlaceOrderCommand&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OrderId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// the whole story, top to bottom
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The shape answers three questions by itself. What comes in: one command object, a thing you can log, validate, put in a queue. What it needs: the constructor lists dependencies of this use case, not of orders in general, so when &lt;code&gt;PlaceOrder&lt;/code&gt; suddenly needs the mailer you see it and can ask why. What comes out: one result. Reading &lt;code&gt;handle()&lt;/code&gt; from top to bottom tells the whole story of placing an order. No chapter hidden in a base class or a trait.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 7.3 and the value of small releases</title>
      <link>https://useinov.com/posts/2018/php-73-small-steps/</link>
      <pubDate>Thu, 27 Dec 2018 15:35:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2018/php-73-small-steps/</guid>
      <description>&lt;p&gt;A trailing comma after the last argument of a function call. PHP 7.3 came out at the start of the month, and this is the feature I noticed first, because I meet the missing comma in every second diff:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;logger&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;order created&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;order_id&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$order&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A tiny thing that kills a whole class of noisy diffs, the ones where adding an argument touches the previous line too. Arrays got this years ago. Calls only now.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Rector: refactoring by machine</title>
      <link>https://useinov.com/posts/2018/rector-machine-refactoring/</link>
      <pubDate>Thu, 08 Feb 2018 15:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2018/rector-machine-refactoring/</guid>
      <description>&lt;p&gt;Search for &lt;code&gt;-&amp;gt;fetch(&lt;/code&gt; in a project with old Kohana code and count the hits. A method call on a model, a comment, a line in a test fixture, an unrelated class that happens to have a method with the same name. That is what regex refactoring looks like. Regex does not know a method call from a string literal.&lt;/p&gt;
&lt;p&gt;Rector does. A young tool I found this month: it parses PHP into an AST with nikic/php-parser, applies transformation rules, prints the code back. Rename a class across the whole project. Change a method call, add an argument everywhere. The AST sees that this &lt;code&gt;fetch&lt;/code&gt; is called on that type and touches only those places. Mechanical change becomes exact.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Sodium in PHP 7.2 core</title>
      <link>https://useinov.com/posts/2017/php-sodium/</link>
      <pubDate>Sun, 10 Dec 2017 20:20:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2017/php-sodium/</guid>
      <description>&lt;p&gt;PHP 7.2 came out on the last day of November. Mcrypt is out of core, libsodium is in. That trade alone makes it a good release.&lt;/p&gt;
&lt;p&gt;Search any forum for &amp;ldquo;php encrypt&amp;rdquo; and you find the same folk recipe: &lt;code&gt;openssl_encrypt&lt;/code&gt; with AES-256-CBC, an IV made from who knows what, no authentication of the ciphertext. Every choice in that recipe is a place to be wrong, and CBC without a MAC is wrong in a way that has published attacks. The developer is not careless. The API hands an application developer decisions that belong to a cryptographer.&lt;/p&gt;</description>
    </item>
    <item>
      <title>First run of PHPStan on legacy code</title>
      <link>https://useinov.com/posts/2017/phpstan-first-run/</link>
      <pubDate>Sat, 09 Sep 2017 15:30:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2017/phpstan-first-run/</guid>
      <description>&lt;p&gt;Level 0, a legacy codebase, a few hundred errors on the first run. That was my week with PHPStan. Most of the output was noise about magic the tool cannot see. But in the first hour of reading I found three real bugs, live in production for months.&lt;/p&gt;
&lt;p&gt;One: a repository method returns an entity or null, and a caller chains a method right on the result. The not-found branch was never written. It survived because that path needs a deleted record, and deleted records are rare. Rare is not never.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 7.1: nullable is honest</title>
      <link>https://useinov.com/posts/2016/php71-nullable-types/</link>
      <pubDate>Fri, 09 Dec 2016 20:35:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2016/php71-nullable-types/</guid>
      <description>&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// returns User or false, see wiki
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;findByEmail&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Every legacy codebase has this method. The comment is the type system. Half the callers check for &lt;code&gt;false&lt;/code&gt;, some check for &lt;code&gt;null&lt;/code&gt; because a sister method returns null, one caller checks nothing and works by luck.&lt;/p&gt;
&lt;p&gt;PHP 7.1 came out last week. Nullable types, &lt;code&gt;void&lt;/code&gt;, &lt;code&gt;iterable&lt;/code&gt;, multi-catch. Small features, but together they continue the direction 7.0 started: less implicit agreement, more signature. The one I care about is &lt;code&gt;?Type&lt;/code&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Doctrine and the 100 000 row import</title>
      <link>https://useinov.com/posts/2016/doctrine-batch-memory/</link>
      <pubDate>Sun, 19 Jun 2016 20:35:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2016/doctrine-batch-memory/</guid>
      <description>&lt;p&gt;Row sixty thousand. &lt;code&gt;Allowed memory size exhausted&lt;/code&gt;. The import script on one project died there, and the code was the obvious loop: read a row, &lt;code&gt;persist()&lt;/code&gt; an entity, next row, &lt;code&gt;flush()&lt;/code&gt; at the end. On a hundred test rows it worked perfectly. It was my loop.&lt;/p&gt;
&lt;p&gt;The reason is the Unit of Work. Doctrine keeps every managed entity in memory, plus a snapshot of its original data for change tracking. Persist a hundred thousand entities and you hold a hundred thousand objects twice. This is not a bug. It is the price of the ORM&amp;rsquo;s main feature, and on a normal web request the price is invisible because the request dies young.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Docker instead of a setup README</title>
      <link>https://useinov.com/posts/2016/docker-for-php/</link>
      <pubDate>Wed, 13 Apr 2016 21:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2016/docker-for-php/</guid>
      <description>&lt;p&gt;Two days. That is how long the new colleague spent last month getting one project to run on his laptop. The README is two pages: PHP with a specific set of extensions, Nginx, MySQL, Redis, and a paragraph that starts with &amp;ldquo;on OS X it is a bit different&amp;rdquo;. Every laptop in the team is a slightly different snowflake.&lt;/p&gt;
&lt;p&gt;So I finally tried Docker for local development. The compose file:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Middleware groups in Laravel 5.2</title>
      <link>https://useinov.com/posts/2016/laravel-middleware-groups/</link>
      <pubDate>Mon, 08 Feb 2016 21:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2016/laravel-middleware-groups/</guid>
      <description>&lt;p&gt;An IP whitelist check placed after auth. That was the whole bug. Every scanner bot on the internet was going through session start and a user lookup just to be told to go away. The fix was to move one line up in the Kernel.&lt;/p&gt;
&lt;p&gt;Laravel 5.2 makes that line easier to see. Middleware groups, and the request pipeline is finally written down in one place:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$middlewareGroups&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;web&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;\App\Http\Middleware\EncryptCookies&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;\Illuminate\Session\Middleware\StartSession&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;\App\Http\Middleware\VerifyCsrfToken&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;api&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s1&#34;&gt;&amp;#39;throttle:60,1&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s1&#34;&gt;&amp;#39;bindings&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before 5.2, sessions and CSRF were global middleware. They ran for everything, including API routes that have no use for cookies. Now &lt;code&gt;web&lt;/code&gt; and &lt;code&gt;api&lt;/code&gt; are two separate pipelines, and you can read each one top to bottom.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 7 measured on real code</title>
      <link>https://useinov.com/posts/2015/php7-measured/</link>
      <pubDate>Sat, 26 Dec 2015 21:35:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2015/php7-measured/</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kohana on PHP 7: whose problem is it</title>
      <link>https://useinov.com/posts/2015/kohana-on-php7/</link>
      <pubDate>Tue, 08 Sep 2015 10:45:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2015/kohana-on-php7/</guid>
      <description>&lt;p&gt;The test suite went green on the PHP 7 release candidate on the second try. I expected worse. Then I opened &lt;code&gt;modules/&lt;/code&gt; and the mood changed.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Run your tests on PHP 7 now</title>
      <link>https://useinov.com/posts/2015/php7-early-tests/</link>
      <pubDate>Wed, 08 Jul 2015 22:45:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2015/php7-early-tests/</guid>
      <description>&lt;p&gt;Three lines in &lt;code&gt;.travis.yml&lt;/code&gt;. That is the whole cost of knowing in July what will break on PHP 7 in December.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;php&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;5.6&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;nightly&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;allow_failures&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;php&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;nightly&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;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.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 5 and the death of the models folder</title>
      <link>https://useinov.com/posts/2015/laravel-five-structure/</link>
      <pubDate>Wed, 25 Feb 2015 22:20:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2015/laravel-five-structure/</guid>
      <description>&lt;p&gt;&lt;code&gt;app/models&lt;/code&gt; is gone. Laravel 5 came out at the start of the month, and of everything in the release the folder I notice most is the one that no longer exists.&lt;/p&gt;
&lt;p&gt;The old skeleton had &lt;code&gt;app/models&lt;/code&gt; and &lt;code&gt;app/controllers&lt;/code&gt;. You knew where things go because the framework told you. Laravel 5 removes that. There is &lt;code&gt;app/&lt;/code&gt;, it maps to the &lt;code&gt;App&lt;/code&gt; namespace through PSR-4, and inside it you organize classes however you want.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Host header is user input</title>
      <link>https://useinov.com/posts/2014/host-header-is-user-input/</link>
      <pubDate>Tue, 23 Dec 2014 19:20:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/host-header-is-user-input/</guid>
      <description>&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$link&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_SERVER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;HTTP_HOST&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/reset/&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I wrote this line. More than once. It builds the link for a password reset email, and it looks like it reads something about the server. It does not. &lt;code&gt;Host&lt;/code&gt; comes from the client, same as any other header.&lt;/p&gt;
&lt;p&gt;Kohana 3.3.4 shipped this month with a fix around exactly this, and the topic is bigger than one framework.&lt;/p&gt;
&lt;p&gt;The attack is short. Someone requests a password reset for your email address and puts his own domain into the &lt;code&gt;Host&lt;/code&gt; header. The application trusts the header, builds the link, sends it. You receive a real email from a real site with a link to &lt;code&gt;evil.example&lt;/code&gt; and your real token in the path. You click. He collects the token. Host header poisoning, and no framework protects you from it out of the box.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Symfony 2.6: dump() and boring upgrades</title>
      <link>https://useinov.com/posts/2014/symfony-26-vardumper/</link>
      <pubDate>Thu, 18 Dec 2014 22:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/symfony-26-vardumper/</guid>
      <description>&lt;p&gt;&lt;code&gt;dump()&lt;/code&gt; instead of &lt;code&gt;var_dump()&lt;/code&gt;. Symfony 2.6 came out at the end of November, and this is the visible gift: VarDumper. Collapsible output, clickable class names, works inside the toolbar. A small thing, used fifty times a day. Tools like this improve life more than big features do.&lt;/p&gt;
&lt;p&gt;But the important story in 2.6 is not a feature. The release notes read like a preparation checklist for Symfony 3: deprecations, everywhere.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Debug toolbar in production is a gift to strangers</title>
      <link>https://useinov.com/posts/2014/profiler-in-production/</link>
      <pubDate>Tue, 09 Dec 2014 21:00:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/profiler-in-production/</guid>
      <description>&lt;p&gt;&lt;code&gt;/_profiler&lt;/code&gt; on a live site. Type it after any Symfony domain and sometimes it answers. This autumn the story went around, and I will not retell it. The lesson is bigger than one framework.&lt;/p&gt;
&lt;p&gt;Think what a profiler actually stores. Every SQL query with parameters. Cookies and session data. Routes, controller names, request headers. Sometimes config values. A full X-ray of your application, nicely formatted, with search.&lt;/p&gt;
&lt;p&gt;Now count how many sites have &lt;code&gt;/_profiler&lt;/code&gt; or a debugbar open to the world because someone deployed with &lt;code&gt;app_dev.php&lt;/code&gt;, or left &lt;code&gt;debug = true&lt;/code&gt; in the production config. Not a rare exotic mistake. I saw it. I made it once, on a staging server that stayed reachable from outside longer than anyone planned.&lt;/p&gt;</description>
    </item>
    <item>
      <title>PHP 5.6: small syntax, big TLS surprise</title>
      <link>https://useinov.com/posts/2014/php-56-upgrade/</link>
      <pubDate>Fri, 05 Dec 2014 19:45:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/php-56-upgrade/</guid>
      <description>&lt;p&gt;A nightly cron failed silently for two days. That was our PHP 5.6 upgrade.&lt;/p&gt;
&lt;p&gt;5.6 is out since the end of August, and we moved one production project this month. The syntax part is pleasant and minor. Variadics:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;log_all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$level&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$messages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Argument unpacking with &lt;code&gt;...$args&lt;/code&gt; on the call side. Constant expressions in defaults. &lt;code&gt;use function&lt;/code&gt; for importing functions. All nice. None of it changes your architecture. You will use variadics maybe twice a year and be happy both times.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Services instead of fat controllers</title>
      <link>https://useinov.com/posts/2014/services-instead-of-fat-controllers/</link>
      <pubDate>Tue, 02 Dec 2014 22:20:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/services-instead-of-fat-controllers/</guid>
      <description>&lt;p&gt;120 lines in one action. Order confirmation: load the order, charge the card, send the email, render the page. It ran fine for a year. Then a console script needed the same operation, and the action turned out to be glued to SMTP, to the database and to the HTTP request. All at once.&lt;/p&gt;
&lt;p&gt;Symfony people talk about dependency injection so much it sounds like religion. It is one simple idea: a class receives its dependencies and does not create them.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Queues in Laravel 4.2: the user should not wait for your SMTP</title>
      <link>https://useinov.com/posts/2014/laravel-queues/</link>
      <pubDate>Thu, 27 Nov 2014 20:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/laravel-queues/</guid>
      <description>&lt;p&gt;1.8 seconds for one registration request. I put a timer around it on a project last month. 1.5 of those seconds was the welcome email going out over SMTP.&lt;/p&gt;
&lt;p&gt;The user waits almost two seconds and looks at a spinner, for a handshake with a mail server he will never hear about.&lt;/p&gt;
&lt;p&gt;Laravel 4.2 makes the fix one line:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Queue&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;SendWelcomeEmail&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The controller returns in 300 ms. A worker picks the job up and sends the email. &lt;code&gt;php artisan queue:listen&lt;/code&gt; to start, beanstalkd or Redis behind it, the &lt;code&gt;failed_jobs&lt;/code&gt; table for jobs that died.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Composer next to Kohana bootstrap</title>
      <link>https://useinov.com/posts/2014/composer-next-to-kohana/</link>
      <pubDate>Sun, 23 Nov 2014 21:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/composer-next-to-kohana/</guid>
      <description>&lt;p&gt;One line in &lt;code&gt;bootstrap.php&lt;/code&gt;, above &lt;code&gt;Kohana::init()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DOCROOT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;vendor/autoload.php&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That is the whole integration. Kohana keeps its modules and its underscore autoloader. Composer brings Packagist. Two autoloaders in one process, and they do not collide: Composer packages use namespaces, Kohana classes have underscores, the lookups never meet.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;require&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;guzzlehttp/guzzle&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;~5.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This setup runs in production on one of my projects for a few months. Zero problems from the autoloading side. The problems, when they came, were from me.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kohana is not developed anymore. Now what</title>
      <link>https://useinov.com/posts/2014/kohana-is-over-now-what/</link>
      <pubDate>Wed, 19 Nov 2014 19:30:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/kohana-is-over-now-what/</guid>
      <description>&lt;p&gt;The commit log of kohana/kohana on GitHub. I opened it in the summer and counted: a few merges a month, then fewer, then nothing. This year the official development basically stopped. Our projects did not stop working on that day. But the question came to my inbox three times since, so here is the answer.&lt;/p&gt;
&lt;p&gt;What to do with a working Kohana product depends on which of four situations you are in.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Cascading filesystem: the best idea in Kohana</title>
      <link>https://useinov.com/posts/2014/cascading-filesystem/</link>
      <pubDate>Sun, 16 Nov 2014 22:05:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/cascading-filesystem/</guid>
      <description>&lt;p&gt;&lt;code&gt;Kohana::find_file()&lt;/code&gt;. One function, maybe forty lines. If I could keep only one thing from Kohana, this is the one.&lt;/p&gt;
&lt;p&gt;It looks for every file in order: &lt;code&gt;application/&lt;/code&gt;, then modules, then &lt;code&gt;system/&lt;/code&gt;. First found wins. Config, views, classes, i18n, everything goes through the same lookup.&lt;/p&gt;
&lt;p&gt;In practice: a module ships a config file, you put a file with the same name into &lt;code&gt;application/config/&lt;/code&gt;, change one key. The module does not know. Nothing under &lt;code&gt;modules/&lt;/code&gt; is touched.&lt;/p&gt;</description>
    </item>
    <item>
      <title>HMVC in Kohana: a request inside a request</title>
      <link>https://useinov.com/posts/2014/kohana-hmvc/</link>
      <pubDate>Mon, 10 Nov 2014 21:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/kohana-hmvc/</guid>
      <description>&lt;p&gt;&lt;code&gt;Request::factory(&#39;widgets/stats&#39;)&lt;/code&gt; inside a controller. A colleague looked at this line last week and asked the question I hear most often about Kohana: you already have a request, why make another one?&lt;/p&gt;
&lt;p&gt;I keep answering it at the desk. So I will write it down once. Short notes, mostly backend, mostly PHP. This is the first.&lt;/p&gt;
&lt;p&gt;The answer is composition. Take a dashboard. A stats block, a recent orders block, a notifications block. Each one needs its own data and its own logic. You can put all of it into one action. It works. In a month it is 300 lines and nobody wants to open it.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
