<?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 Releases on Guillaume Delré</title><link>https://guillaumedelre.github.io/series/php-releases/</link><description>Recent content in PHP Releases on Guillaume Delré</description><generator>Hugo</generator><language>en</language><lastBuildDate>Sun, 04 Jan 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/series/php-releases/index.xml" rel="self" type="application/rss+xml"/><item><title>PHP 8.5: the pipe operator, a URI library, and a lot of cleanup</title><link>https://guillaumedelre.github.io/2026/01/04/php-8.5-the-pipe-operator-a-uri-library-and-a-lot-of-cleanup/</link><pubDate>Sun, 04 Jan 2026 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2026/01/04/php-8.5-the-pipe-operator-a-uri-library-and-a-lot-of-cleanup/</guid><description>Part 11 of 11 in &amp;quot;PHP Releases&amp;quot;: PHP 8.5 adds a pipe operator for readable functional pipelines and a native URI class that ends fragile string parsing.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 8.5 shipped November 20th. Two features define this release: the pipe operator and the URI extension. They solve different problems, but both share the same motivation: making common operations less awkward to express.</p>
<h2 id="the-pipe-operator">The pipe operator</h2>
<p>Functional pipelines in PHP have always been a mess. Chaining transformations meant either nesting function calls inside out, or breaking them into intermediate variables:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// before — read right to left
</span></span></span><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_sum</span>(<span style="color:#a6e22e">array_map</span>(<span style="color:#e6db74">&#39;strlen&#39;</span>, <span style="color:#a6e22e">array_filter</span>($strings, <span style="color:#e6db74">&#39;strlen&#39;</span>)));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// or verbose but readable
</span></span></span><span style="display:flex;"><span>$filtered   <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_filter</span>($strings, <span style="color:#e6db74">&#39;strlen&#39;</span>);
</span></span><span style="display:flex;"><span>$lengths    <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_map</span>(<span style="color:#e6db74">&#39;strlen&#39;</span>, $filtered);
</span></span><span style="display:flex;"><span>$result     <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_sum</span>($lengths);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// after — read left to right
</span></span></span><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> $strings
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">array_filter</span>(<span style="color:#f92672">?</span>, <span style="color:#e6db74">&#39;strlen&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">array_map</span>(<span style="color:#e6db74">&#39;strlen&#39;</span>, <span style="color:#f92672">?</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">array_sum</span>(<span style="color:#f92672">?</span>);
</span></span></code></pre></div><p>The <code>|&gt;</code> operator passes the left-hand value into the right-hand expression. The <code>?</code> placeholder marks where it goes. Pipelines now read in the order operations happen: left to right, top to bottom.</p>
<p>This pairs well with first-class callables from PHP 8.1. The two features compose nicely:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> $input <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">trim</span>(<span style="color:#f92672">...</span>) <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">strtolower</span>(<span style="color:#f92672">...</span>) <span style="color:#f92672">|&gt;</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">normalize</span>(<span style="color:#f92672">...</span>);
</span></span></code></pre></div><h2 id="the-uri-extension">The URI extension</h2>
<p>Handling URIs in PHP has always meant either reaching for a third-party library or cobbling together <code>parse_url()</code> (returns an array, not an object), <code>http_build_query()</code>, and manual string concatenation.</p>
<p>The new <code>Uri</code> extension gives you a proper object-oriented API:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$uri <span style="color:#f92672">=</span> <span style="color:#a6e22e">Uri\Uri</span><span style="color:#f92672">::</span><span style="color:#a6e22e">parse</span>(<span style="color:#e6db74">&#39;https://example.com/path?query=value#fragment&#39;</span>);
</span></span><span style="display:flex;"><span>$modified <span style="color:#f92672">=</span> $uri<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">withPath</span>(<span style="color:#e6db74">&#39;/new-path&#39;</span>)<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">withQuery</span>(<span style="color:#e6db74">&#39;key=val&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $modified; <span style="color:#75715e">// https://example.com/new-path?key=val#fragment
</span></span></span></code></pre></div><p>Immutable value objects, RFC-compliant parsing, modify individual components without parsing and reconstructing the whole string. Long overdue.</p>
<h2 id="nodiscard">#[\NoDiscard]</h2>
<p>A new attribute that generates a warning when the return value is ignored:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">#[\NoDiscard(&#34;Use the returned collection, the original is unchanged&#34;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">filter</span>(<span style="color:#a6e22e">callable</span> $fn)<span style="color:#f92672">:</span> <span style="color:#66d9ef">static</span> { <span style="color:#f92672">...</span> }
</span></span></code></pre></div><p>Useful for immutable methods where ignoring the return value is almost certainly a bug. Common in other languages for years, now in PHP where it belongs.</p>
<h2 id="clone-with">clone with</h2>
<p>Cloning an object with modified properties without using property hooks or a custom <code>with()</code> method:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$updated <span style="color:#f92672">=</span> <span style="color:#66d9ef">clone</span>($point) <span style="color:#a6e22e">with</span> { <span style="color:#a6e22e">x</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">10</span>, <span style="color:#a6e22e">y</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">20</span> };
</span></span></code></pre></div><p>Clean syntax for a pattern readonly objects needed: you clone to &ldquo;modify&rdquo; since direct mutation isn&rsquo;t allowed.</p>
<p>PHP 8.5 has a functional streak. The pipe operator and URI extension together make data transformation code meaningfully easier to read. The language keeps moving in a consistent direction.</p>
<h2 id="closures-in-constant-expressions">Closures in constant expressions</h2>
<p>A constraint that&rsquo;s been baked in since PHP 5: constant expressions (attribute arguments, property defaults, parameter defaults, <code>const</code> declarations) couldn&rsquo;t contain closures or first-class callables. 8.5 removes that.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">#[Validate(fn($v) =&gt; $v &gt; 0)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#a6e22e">int</span> $count <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">NORMALIZER</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">strtolower</span>(<span style="color:#f92672">...</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Config</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">readonly</span> <span style="color:#a6e22e">Closure</span> $transform <span style="color:#f92672">=</span> <span style="color:#a6e22e">trim</span>(<span style="color:#f92672">...</span>),
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This is the missing piece that makes attributes genuinely expressive for validation and transformation rules. Before 8.5, you had to pass class names or string references to attributes and let the framework look them up. Now the callable lives directly in the attribute.</p>
<h2 id="attributes-on-constants">Attributes on constants</h2>
<p>The <code>#[\Deprecated]</code> attribute from 8.4 couldn&rsquo;t be applied to <code>const</code> declarations. 8.5 adds attribute support for constants generally:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">OLD_LIMIT</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">100</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#[\Deprecated(&#39;Use RATE_LIMIT instead&#39;, since: &#39;3.0&#39;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">API_TIMEOUT</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">30</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">RATE_LIMIT</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">60</span>;
</span></span></code></pre></div><p><code>ReflectionConstant</code>, a new reflection class in 8.5, exposes <code>getAttributes()</code> so tools can read them. Combined with closures in constant expressions, attributes on constants become a real metadata layer for compile-time values.</p>
<h2 id="override-extends-to-properties">#[\Override] extends to properties</h2>
<p>PHP 8.3 brought <code>#[\Override]</code> for methods. 8.5 extends it to properties:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Base</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">string</span> $name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;default&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Derived</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Base</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[\Override]
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">string</span> $name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;derived&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If the property doesn&rsquo;t exist in the parent, PHP throws an error. Particularly useful with property hooks from 8.4: you can now signal that a hooked property is intentionally overriding a parent&rsquo;s.</p>
<h2 id="static-asymmetric-visibility">Static asymmetric visibility</h2>
<p>8.4 introduced asymmetric visibility (<code>public private(set)</code>) for instance properties. 8.5 brings that to <code>static</code> properties too:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Registry</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">private</span>(<span style="color:#a6e22e">set</span>) <span style="color:#66d9ef">array</span> $items <span style="color:#f92672">=</span> [];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">register</span>(<span style="color:#a6e22e">string</span> $key, <span style="color:#a6e22e">mixed</span> $value)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">self</span><span style="color:#f92672">::</span>$items[$key] <span style="color:#f92672">=</span> $value;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">Registry</span><span style="color:#f92672">::</span>$items[<span style="color:#e6db74">&#39;foo&#39;</span>]; <span style="color:#75715e">// readable
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">Registry</span><span style="color:#f92672">::</span>$items[<span style="color:#e6db74">&#39;bar&#39;</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">// Error: cannot write outside class
</span></span></span></code></pre></div><p>Straightforward pattern: expose a static collection for reading, block external mutation.</p>
<h2 id="constructor-promotion-for-final-properties">Constructor promotion for final properties</h2>
<p>Property promotion in constructors has existed since PHP 8.0. The <code>final</code> modifier on promoted properties was the missing piece, 8.5 adds it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ValueObject</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">final</span> <span style="color:#a6e22e">readonly</span> <span style="color:#a6e22e">string</span> $id,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">final</span> <span style="color:#a6e22e">readonly</span> <span style="color:#a6e22e">string</span> $name,
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>A subclass can&rsquo;t override <code>$id</code> or <code>$name</code> with a property of the same name. The <code>final readonly</code> combination on promoted properties makes value objects as locked down as possible without sealing the whole class.</p>
<h2 id="casts-in-constant-expressions">Casts in constant expressions</h2>
<p>Another gap in constant expressions: no type casts. 8.5 allows them:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">PRECISION</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">int</span>) <span style="color:#ae81ff">3.7</span>;      <span style="color:#75715e">// 3
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">THRESHOLD</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">float</span>) <span style="color:#e6db74">&#39;1.5&#39;</span>;  <span style="color:#75715e">// 1.5
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">FLAG</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">bool</span>) <span style="color:#ae81ff">1</span>;            <span style="color:#75715e">// true
</span></span></span></code></pre></div><p>Sounds minor until you have configuration constants derived from environment variables that need type coercion right at the declaration.</p>
<h2 id="fatal-errors-include-backtraces">Fatal errors include backtraces</h2>
<p>Before 8.5, a fatal error (out-of-memory, stack overflow, type error in certain contexts) produced a message with no context about where in the code it happened. Finding the cause meant inserting debug logging and reproducing.</p>
<p>8.5 adds stack backtraces to fatal error messages, in the same format as exception backtraces. A new INI directive, <code>fatal_error_backtraces</code>, controls the behavior. It&rsquo;s on by default.</p>
<h2 id="array_first-and-array_last">array_first() and array_last()</h2>
<p>PHP has had <code>reset()</code> and <code>end()</code> for accessing the first and last elements of an array since PHP 3. Both mutate the array&rsquo;s internal pointer (not safe to call on a reference), and they return <code>false</code> for empty arrays in a way that&rsquo;s indistinguishable from a stored <code>false</code> value.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$values <span style="color:#f92672">=</span> [<span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">20</span>, <span style="color:#ae81ff">30</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$first <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_first</span>($values);  <span style="color:#75715e">// 10
</span></span></span><span style="display:flex;"><span>$last  <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_last</span>($values);   <span style="color:#75715e">// 30
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$first <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_first</span>([]);       <span style="color:#75715e">// null
</span></span></span></code></pre></div><p>The new functions return <code>null</code> for empty arrays, don&rsquo;t touch the internal pointer, and work on any array expression without needing a variable. <code>reset($this-&gt;getItems())</code> was a deprecation warning waiting to happen.</p>
<h2 id="get_error_handler-and-get_exception_handler">get_error_handler() and get_exception_handler()</h2>
<p>PHP has <code>set_error_handler()</code> and <code>set_exception_handler()</code>. Getting the current handler meant either storing it yourself before setting it, or calling <code>set_error_handler(null)</code> and capturing what came back, which also cleared the handler in the process.</p>
<p>8.5 adds:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$current <span style="color:#f92672">=</span> <span style="color:#a6e22e">get_error_handler</span>();
</span></span><span style="display:flex;"><span>$current <span style="color:#f92672">=</span> <span style="color:#a6e22e">get_exception_handler</span>();
</span></span></code></pre></div><p>Handy in middleware chains where you want to wrap the existing handler without losing it, or in tests where you want to verify a handler was actually installed.</p>
<h2 id="intllistformatter">IntlListFormatter</h2>
<p>Formatting a list with locale-appropriate conjunctions has always needed manual string assembly. 8.5 adds <code>IntlListFormatter</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$formatter <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">IntlListFormatter</span>(<span style="color:#e6db74">&#39;en_US&#39;</span>, <span style="color:#a6e22e">IntlListFormatter</span><span style="color:#f92672">::</span><span style="color:#a6e22e">TYPE_AND</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $formatter<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">format</span>([<span style="color:#e6db74">&#39;apples&#39;</span>, <span style="color:#e6db74">&#39;oranges&#39;</span>, <span style="color:#e6db74">&#39;pears&#39;</span>]);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// &#34;apples, oranges, and pears&#34;
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$formatter <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">IntlListFormatter</span>(<span style="color:#e6db74">&#39;fr_FR&#39;</span>, <span style="color:#a6e22e">IntlListFormatter</span><span style="color:#f92672">::</span><span style="color:#a6e22e">TYPE_OR</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $formatter<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">format</span>([<span style="color:#e6db74">&#39;rouge&#39;</span>, <span style="color:#e6db74">&#39;bleu&#39;</span>, <span style="color:#e6db74">&#39;vert&#39;</span>]);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// &#34;rouge, bleu ou vert&#34;
</span></span></span></code></pre></div><p>The class wraps ICU&rsquo;s <code>ListFormatter</code>. Three types: <code>TYPE_AND</code>, <code>TYPE_OR</code>, <code>TYPE_UNITS</code>. Width constants control whether you get &ldquo;and&rdquo; or &ldquo;&amp;&rdquo;. Oxford comma handling, locale-specific conjunction placement, all handled by ICU.</p>
<h2 id="filter_throw_on_failure-for-filter_var">FILTER_THROW_ON_FAILURE for filter_var()</h2>
<p><code>filter_var()</code> returns <code>false</code> on validation failure, which produces the classic <code>false vs null vs 0</code> ambiguity when you&rsquo;re filtering untrusted input. A new flag changes that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>    $email <span style="color:#f92672">=</span> <span style="color:#a6e22e">filter_var</span>($input, <span style="color:#a6e22e">FILTER_VALIDATE_EMAIL</span>, <span style="color:#a6e22e">FILTER_THROW_ON_FAILURE</span>);
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">Filter\FilterFailedException</span> $e) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// explicitly invalid, not ambiguously false
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The <code>Filter\FilterFailedException</code> and <code>Filter\FilterException</code> classes are new in 8.5. The flag can&rsquo;t be combined with <code>FILTER_NULL_ON_FAILURE</code>: the behaviors are mutually exclusive.</p>
<h2 id="deprecations-that-clean-up-years-of-technical-debt">Deprecations that clean up years of technical debt</h2>
<p>The backtick operator (<code>`command`</code> as an alias for <code>shell_exec()</code>) is deprecated. It&rsquo;s an obscure syntax that surprises anyone reading the code and is inconsistent with every other PHP function call.</p>
<p>Non-canonical cast names (<code>(boolean)</code>, <code>(integer)</code>, <code>(double)</code>, <code>(binary)</code>) are deprecated in favor of their short forms: <code>(bool)</code>, <code>(int)</code>, <code>(float)</code>, <code>(string)</code>. The long forms have been undocumented for years; 8.5 starts the formal removal.</p>
<p>Semicolon-terminated <code>case</code> statements are deprecated:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// deprecated
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">switch</span> ($x) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#ae81ff">1</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">break</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// correct
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">switch</span> ($x) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#ae81ff">1</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">break</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The semicolon form has been syntactically valid since PHP 4 but nobody uses it on purpose. It&rsquo;s a typo PHP happened to accept.</p>
<p><code>__sleep()</code> and <code>__wakeup()</code> are deprecated in favor of <code>__serialize()</code> and <code>__unserialize()</code>, which return and receive arrays and compose correctly with inheritance. The old methods had messy semantics around property visibility.</p>
<h2 id="max_memory_limit-caps-runaway-allocations">max_memory_limit caps runaway allocations</h2>
<p>A new startup-only INI directive: <code>max_memory_limit</code>. It sets a ceiling that <code>memory_limit</code> can&rsquo;t exceed at runtime. If a script calls <code>ini_set('memory_limit', '10G')</code> and <code>max_memory_limit</code> is <code>512M</code>, PHP warns and caps the value.</p>
<p>Useful in shared hosting environments, or anywhere you want to make sure a bug or a malicious payload can&rsquo;t convince PHP to raise its own limit and eat the whole machine&rsquo;s RAM.</p>
<h2 id="opcache-is-always-present">Opcache is always present</h2>
<p>In 8.5, Opcache is always compiled into the PHP binary and always loaded. The old situation (Opcache as a loadable extension that might or might not be present depending on build configuration) is gone.</p>
<p>You can still disable it: <code>opcache.enable=0</code> works fine. What changes is the guarantee that the Opcache API (<code>opcache_get_status()</code>, <code>opcache_invalidate()</code>, etc.) is always available, regardless of how PHP was compiled. Any code that checks <code>extension_loaded('opcache')</code> before calling Opcache functions can drop the check.</p>
]]></content:encoded></item><item><title>PHP 8.4: property hooks and the end of the getter/setter ceremony</title><link>https://guillaumedelre.github.io/2025/01/05/php-8.4-property-hooks-and-the-end-of-the-getter/setter-ceremony/</link><pubDate>Sun, 05 Jan 2025 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2025/01/05/php-8.4-property-hooks-and-the-end-of-the-getter/setter-ceremony/</guid><description>Part 10 of 11 in &amp;quot;PHP Releases&amp;quot;: PHP 8.4 brings property hooks: get/set logic directly on properties, replacing twenty years of getter/setter boilerplate.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 8.4 released November 21st. Property hooks are the feature. Everything else, and there&rsquo;s quite a bit of it, is secondary.</p>
<h2 id="property-hooks">Property hooks</h2>
<p>For twenty years, if you wanted behavior on property access in PHP you had to write getters and setters:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">string</span> $_name;
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getName</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> { <span style="color:#66d9ef">return</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">_name</span>; }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">setName</span>(<span style="color:#a6e22e">string</span> $name)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">_name</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">strtoupper</span>($name);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>PHP 8.4 adds hooks directly on the property:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">string</span> $name {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">set</span>(<span style="color:#a6e22e">string</span> $name) {
</span></span><span style="display:flex;"><span>            $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">strtoupper</span>($name);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>You can define <code>get</code> and <code>set</code> hooks independently. A property with only a <code>get</code> hook is computed on access:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Circle</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">float</span> $area {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">get</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">M_PI</span> <span style="color:#f92672">*</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">radius</span> <span style="color:#f92672">**</span> <span style="color:#ae81ff">2</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(<span style="color:#66d9ef">public</span> <span style="color:#a6e22e">float</span> $radius) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>No backing storage, no explicit getter method, full IDE support. Interfaces can declare properties with hooks too, which means contracts can now specify behavior on property access, something that was flat-out impossible before.</p>
<h2 id="asymmetric-visibility">Asymmetric visibility</h2>
<p>A lighter option for when you just want public read, private write:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Version</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">private</span>(<span style="color:#a6e22e">set</span>) <span style="color:#a6e22e">string</span> $value <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;1.0.0&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$v <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Version</span>();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $v<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">value</span>;      <span style="color:#75715e">// works
</span></span></span><span style="display:flex;"><span>$v<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;2.0&#39;</span>;  <span style="color:#75715e">// Error
</span></span></span></code></pre></div><p>Kills the <code>private $x</code> + <code>public getX()</code> pattern for read-only public properties without needing full readonly semantics.</p>
<h2 id="array_find-and-friends">array_find() and friends</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$first <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_find</span>($users, <span style="color:#a6e22e">fn</span>($u) <span style="color:#f92672">=&gt;</span> $u<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">isActive</span>());
</span></span><span style="display:flex;"><span>$any   <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_any</span>($users, <span style="color:#a6e22e">fn</span>($u) <span style="color:#f92672">=&gt;</span> $u<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">isPremium</span>());
</span></span><span style="display:flex;"><span>$all   <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_all</span>($users, <span style="color:#a6e22e">fn</span>($u) <span style="color:#f92672">=&gt;</span> $u<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">isVerified</span>());
</span></span></code></pre></div><p>These have been in every other language&rsquo;s standard library for decades. In PHP, you had to use <code>array_filter()</code> + index access or write a manual loop. They exist now: <code>array_find()</code>, <code>array_find_key()</code>, <code>array_any()</code>, <code>array_all()</code>.</p>
<h2 id="instantiation-without-extra-parentheses">Instantiation without extra parentheses</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// before
</span></span></span><span style="display:flex;"><span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MyClass</span>())<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">method</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// after
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MyClass</span>()<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">method</span>();
</span></span></code></pre></div><p>A syntax restriction that was always annoying and never justified is gone.</p>
<h2 id="lazy-objects">Lazy objects</h2>
<p>Objects whose initialization is deferred until first property access:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$user <span style="color:#f92672">=</span> $reflector<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">newLazyProxy</span>(<span style="color:#a6e22e">fn</span>() <span style="color:#f92672">=&gt;</span> $repository<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">find</span>($id));
</span></span><span style="display:flex;"><span><span style="color:#75715e">// No database call yet
</span></span></span><span style="display:flex;"><span>$user<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">name</span>; <span style="color:#75715e">// Now the proxy initializes
</span></span></span></code></pre></div><p>The direct audience is framework ORM and DI container authors, not application developers. But the effect shows up in every app that uses Doctrine or Symfony: lazy loading implemented at the language level rather than through code generation.</p>
<p>PHP 8.4 is a language that barely resembles the PHP 5 most of us started with. Property hooks in particular: they&rsquo;re not a workaround, they&rsquo;re a design feature.</p>
<h2 id="deprecated-for-your-own-code">#[\Deprecated] for your own code</h2>
<p>PHP has emitted deprecation notices for built-in functions for years. 8.4 lets you wire the same mechanism into your own code:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ApiClient</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[\Deprecated(
</span></span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">message</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Use fetchJson() instead&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">since</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;2.0&#39;</span>,
</span></span><span style="display:flex;"><span>    )]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">get</span>(<span style="color:#a6e22e">string</span> $url)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> { <span style="color:#f92672">...</span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Calling a deprecated method now emits <code>E_USER_DEPRECATED</code>, just like calling <code>mysql_connect()</code>. IDEs pick it up, static analyzers flag it, the error log captures it. Before this, the only option was a <code>@deprecated</code> PHPDoc comment: fine for IDEs, completely invisible to the engine.</p>
<h2 id="bcmathnumber-makes-arbitrary-precision-usable">BcMath\Number makes arbitrary precision usable</h2>
<p>The <code>bcmath</code> functions have been in PHP since forever, but their procedural API makes chaining anything painful. 8.4 adds <code>BcMath\Number</code>, an object wrapper with operator overloading:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$a <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">BcMath\Number</span>(<span style="color:#e6db74">&#39;10.5&#39;</span>);
</span></span><span style="display:flex;"><span>$b <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">BcMath\Number</span>(<span style="color:#e6db74">&#39;3.2&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> $a <span style="color:#f92672">+</span> $b;             <span style="color:#75715e">// BcMath\Number(&#39;13.7&#39;)
</span></span></span><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> $a <span style="color:#f92672">*</span> $b <span style="color:#f92672">-</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">BcMath\Number</span>(<span style="color:#e6db74">&#39;1&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $result;                  <span style="color:#75715e">// 32.6
</span></span></span></code></pre></div><p>The <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>**</code>, <code>%</code> operators all work. The object is immutable. Scale propagates automatically through operations. Financial calculations, which used to mean chains of <code>bcadd(bcmul(...), ...)</code>, now just read like arithmetic.</p>
<p>New procedural functions complete the picture: <code>bcceil()</code>, <code>bcfloor()</code>, <code>bcround()</code>, <code>bcdivmod()</code>.</p>
<h2 id="roundingmode-enum-replaces-php_round_-constants">RoundingMode enum replaces PHP_ROUND_* constants</h2>
<p><code>round()</code> has always taken a <code>$mode</code> int from a set of <code>PHP_ROUND_*</code> constants. 8.4 replaces that with a <code>RoundingMode</code> enum with cleaner names and four additional modes that weren&rsquo;t available before:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.5</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">HalfAwayFromZero</span>);  <span style="color:#75715e">// 3
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.5</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">HalfTowardsZero</span>);   <span style="color:#75715e">// 2
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.5</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">HalfEven</span>);          <span style="color:#75715e">// 2 (banker&#39;s rounding)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.5</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">HalfOdd</span>);           <span style="color:#75715e">// 3
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// The four new modes (only available via the enum)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.3</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">TowardsZero</span>);       <span style="color:#75715e">// 2
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.7</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">AwayFromZero</span>);      <span style="color:#75715e">// 3
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.3</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">PositiveInfinity</span>);  <span style="color:#75715e">// 3
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#ae81ff">2.3</span>, <span style="color:#a6e22e">mode</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">RoundingMode</span><span style="color:#f92672">::</span><span style="color:#a6e22e">NegativeInfinity</span>);  <span style="color:#75715e">// 2
</span></span></span></code></pre></div><p>The old <code>PHP_ROUND_*</code> constants still work. The enum is the path forward.</p>
<h2 id="multibyte-string-functions-that-should-have-existed">Multibyte string functions that should have existed</h2>
<p><code>mb_trim()</code>, <code>mb_ltrim()</code>, <code>mb_rtrim()</code>: trim functions that respect multibyte character boundaries, not just ASCII whitespace. Also new: <code>mb_ucfirst()</code> and <code>mb_lcfirst()</code> for proper title-casing of multibyte strings.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$s <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;\u{200B}hello\u{200B}&#34;</span>; <span style="color:#75715e">// Zero-width spaces
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">mb_trim</span>($s);              <span style="color:#75715e">// &#34;hello&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">mb_ucfirst</span>(<span style="color:#e6db74">&#39;über&#39;</span>);       <span style="color:#75715e">// &#34;Über&#34;
</span></span></span></code></pre></div><p>These fill gaps that have been sitting there since <code>mbstring</code> was introduced.</p>
<h2 id="request_parse_body-for-non-post-requests">request_parse_body() for non-POST requests</h2>
<p>PHP automatically parses <code>application/x-www-form-urlencoded</code> and <code>multipart/form-data</code> into <code>$_POST</code> and <code>$_FILES</code>, but only for POST requests. PATCH and PUT requests with the same content types needed manual parsing with <code>file_get_contents('php://input')</code> and custom code.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// Inside a PATCH handler
</span></span></span><span style="display:flex;"><span>[$_POST, $_FILES] <span style="color:#f92672">=</span> <span style="color:#a6e22e">request_parse_body</span>();
</span></span></code></pre></div><p>The function returns a tuple. Same parsing logic PHP uses for POST, now available for any HTTP method.</p>
<h2 id="a-new-dom-api-that-follows-the-spec">A new DOM API that follows the spec</h2>
<p>The existing <code>DOMDocument</code> API was built on an older DOM level 3 spec with PHP-specific quirks layered on top. 8.4 adds a parallel <code>Dom\</code> namespace that implements the WHATWG Living Standard:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$doc <span style="color:#f92672">=</span> <span style="color:#a6e22e">Dom\HTMLDocument</span><span style="color:#f92672">::</span><span style="color:#a6e22e">createFromString</span>(<span style="color:#e6db74">&#39;&lt;p class=&#34;lead&#34;&gt;Hello&lt;/p&gt;&#39;</span>);
</span></span><span style="display:flex;"><span>$p <span style="color:#f92672">=</span> $doc<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">querySelector</span>(<span style="color:#e6db74">&#39;p&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $p<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">classList</span>;  <span style="color:#75715e">// &#34;lead&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $p<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">id</span>;         <span style="color:#75715e">// &#34;&#34;
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$doc2 <span style="color:#f92672">=</span> <span style="color:#a6e22e">Dom\HTMLDocument</span><span style="color:#f92672">::</span><span style="color:#a6e22e">createFromFile</span>(<span style="color:#e6db74">&#39;page.html&#39;</span>);
</span></span></code></pre></div><p><code>Dom\HTMLDocument</code> parses HTML5 correctly, tag soup included. <code>Dom\XMLDocument</code> handles strict XML. The new classes are strict about types, return proper node types, and expose modern properties like <code>classList</code>, <code>id</code>, <code>className</code>. The old <code>DOMDocument</code> stays, unchanged, for backward compatibility.</p>
<h2 id="pdo-gets-driver-specific-subclasses">PDO gets driver-specific subclasses</h2>
<p><code>PDO::connect()</code> and direct instantiation now return driver-specific subclasses when available:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$pdo <span style="color:#f92672">=</span> <span style="color:#a6e22e">PDO</span><span style="color:#f92672">::</span><span style="color:#a6e22e">connect</span>(<span style="color:#e6db74">&#39;mysql:host=localhost;dbname=test&#39;</span>, <span style="color:#e6db74">&#39;user&#39;</span>, <span style="color:#e6db74">&#39;pass&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// $pdo is now a Pdo\Mysql instance
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$pdo <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Pdo\Pgsql</span>(<span style="color:#e6db74">&#39;pgsql:host=localhost;dbname=test&#39;</span>, <span style="color:#e6db74">&#39;user&#39;</span>, <span style="color:#e6db74">&#39;pass&#39;</span>);
</span></span></code></pre></div><p>Each driver subclass (<code>Pdo\Mysql</code>, <code>Pdo\Pgsql</code>, <code>Pdo\Sqlite</code>, <code>Pdo\Firebird</code>, <code>Pdo\Odbc</code>, <code>Pdo\DbLib</code>) can expose driver-specific methods without polluting the base <code>PDO</code> interface. Doctrine, Laravel, and similar ORMs can now type-hint against the specific driver class when they need driver-specific behavior.</p>
<h2 id="openssl-gets-modern-key-support">OpenSSL gets modern key support</h2>
<p><code>openssl_pkey_new()</code> and related functions now support Curve25519 and Curve448, the modern elliptic curves that have replaced older NIST curves in most security recommendations:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$key <span style="color:#f92672">=</span> <span style="color:#a6e22e">openssl_pkey_new</span>([<span style="color:#e6db74">&#39;curve_name&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;ed25519&#39;</span>, <span style="color:#e6db74">&#39;private_key_type&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">OPENSSL_KEYTYPE_EC</span>]);
</span></span><span style="display:flex;"><span>$details <span style="color:#f92672">=</span> <span style="color:#a6e22e">openssl_pkey_get_details</span>($key);
</span></span></code></pre></div><p><code>x25519</code> and <code>x448</code> for key exchange, <code>ed25519</code> and <code>ed448</code> for signatures. All four now work with <code>openssl_sign()</code> and <code>openssl_verify()</code>.</p>
<h2 id="pcre-variable-length-lookbehind">PCRE: variable-length lookbehind</h2>
<p>The bundled PCRE2 library update (10.44) brings variable-length lookbehind assertions, something Perl and Python regex engines had and PHP couldn&rsquo;t do:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// Match &#34;bar&#34; only when preceded by &#34;foo&#34; or &#34;foooo&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">preg_match</span>(<span style="color:#e6db74">&#39;/(?&lt;=foo+)bar/&#39;</span>, <span style="color:#e6db74">&#39;foooobar&#39;</span>, $matches);
</span></span></code></pre></div><p>Lookbehind assertions used to require a fixed-width pattern. Now they can match patterns of variable length. The <code>r</code> modifier (<code>PCRE2_EXTRA_CASELESS_RESTRICT</code>) is also new: it prevents mixing ASCII and non-ASCII characters in case-insensitive matches, closing a class of Unicode confusion attacks.</p>
<h2 id="datetime-gets-microseconds-and-timestamp-factory">DateTime gets microseconds and timestamp factory</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$dt <span style="color:#f92672">=</span> <span style="color:#a6e22e">DateTimeImmutable</span><span style="color:#f92672">::</span><span style="color:#a6e22e">createFromTimestamp</span>(<span style="color:#ae81ff">1700000000.5</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $dt<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getMicrosecond</span>(); <span style="color:#75715e">// 500000
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$with_micros <span style="color:#f92672">=</span> $dt<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">setMicrosecond</span>(<span style="color:#ae81ff">123456</span>);
</span></span></code></pre></div><p><code>createFromTimestamp()</code> accepts a float for sub-second precision. <code>getMicrosecond()</code> and <code>setMicrosecond()</code> round out the API for the microsecond component that&rsquo;s been inside <code>DateTime</code> internally but inaccessible directly.</p>
<h2 id="fpow-for-ieee-754-compliance">fpow() for IEEE 754 compliance</h2>
<p><code>pow(0, -2)</code> in PHP has historically returned an implementation-defined value. 8.4 deprecates <code>pow()</code> with a zero base and negative exponent and introduces <code>fpow()</code>, which strictly follows IEEE 754: <code>fpow(0, -2)</code> returns <code>INF</code>, as the standard defines:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">fpow</span>(<span style="color:#ae81ff">2.0</span>, <span style="color:#ae81ff">3.0</span>);   <span style="color:#75715e">// 8.0
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">fpow</span>(<span style="color:#ae81ff">0.0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1.0</span>);  <span style="color:#75715e">// INF
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">fpow</span>(<span style="color:#f92672">-</span><span style="color:#ae81ff">1.0</span>, <span style="color:#a6e22e">INF</span>);  <span style="color:#75715e">// 1.0
</span></span></span></code></pre></div><p>Worth knowing in any code doing mathematical computations where IEEE compliance matters.</p>
<h2 id="the-cost-of-bcrypt-goes-up">The cost of bcrypt goes up</h2>
<p>The default cost for <code>password_hash()</code> with <code>PASSWORD_BCRYPT</code> went from <code>10</code> to <code>12</code>. This hits any code calling <code>password_hash($pass, PASSWORD_BCRYPT)</code> without an explicit cost. The goal is to keep the default roughly &ldquo;a few hundred milliseconds on modern hardware&rdquo; as hardware gets faster.</p>
<p>If you store bcrypt hashes and upgrade to 8.4, existing hashes stay valid: <code>password_verify()</code> reads the cost from the hash itself. New hashes use cost 12. <code>password_needs_rehash()</code> returns true for old hashes if you pass <code>['cost' =&gt; 12]</code>, so you can upgrade them on next login.</p>
<h2 id="deprecations-that-matter">Deprecations that matter</h2>
<p>Implicitly nullable parameters are deprecated. If a parameter has a default of <code>null</code>, the type has to say so explicitly:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// Deprecated in 8.4
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">foo</span>(<span style="color:#a6e22e">string</span> $s <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span>) {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Correct
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">foo</span>(<span style="color:#f92672">?</span><span style="color:#a6e22e">string</span> $s <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span>) {}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">foo</span>(<span style="color:#a6e22e">string</span><span style="color:#f92672">|</span><span style="color:#66d9ef">null</span> $s <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span>) {}
</span></span></code></pre></div><p><code>trigger_error()</code> with <code>E_USER_ERROR</code> is deprecated: replace it with an exception or <code>exit()</code>. The <code>E_USER_ERROR</code> level was always an awkward hybrid between a recoverable error and a fatal one, and nobody was sure which.</p>
<p><code>lcg_value()</code> is deprecated too. Use <code>Random\Randomizer::getFloat()</code> instead. The LCG generator had poor randomness properties and no seeding control.</p>
]]></content:encoded></item><item><title>PHP 8.3: typed constants and the small wins that stick</title><link>https://guillaumedelre.github.io/2024/01/07/php-8.3-typed-constants-and-the-small-wins-that-stick/</link><pubDate>Sun, 07 Jan 2024 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2024/01/07/php-8.3-typed-constants-and-the-small-wins-that-stick/</guid><description>Part 9 of 11 in &amp;quot;PHP Releases&amp;quot;: PHP 8.3 adds typed class constants, a json_validate function, and a cleaner way to fetch class constants dynamically.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 8.3 landed November 23rd. Quiet release by PHP standards: no enum-sized shift, no JIT. What it does have is a focused set of improvements that close long-standing gaps in the type system and add functions that should have existed years ago.</p>
<h2 id="typed-class-constants">Typed class constants</h2>
<p>Class constants have been untyped since their introduction. PHP 8.3 fixes that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">HasVersion</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">string</span> <span style="color:#a6e22e">VERSION</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">App</span> <span style="color:#66d9ef">implements</span> <span style="color:#a6e22e">HasVersion</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">string</span> <span style="color:#a6e22e">VERSION</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;1.0.0&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Without typed constants, an interface constant could be overridden with a completely different type in an implementing class and nothing would complain. Typed constants close that gap, and on interface-driven codebases the impact is immediate.</p>
<h2 id="dynamic-class-constant-access">Dynamic class constant access</h2>
<p>A gap that required a workaround since constants were introduced:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;STATUS&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">MyClass</span><span style="color:#f92672">::</span>{$name}; <span style="color:#75715e">// now works
</span></span></span></code></pre></div><p>Before, accessing a constant with a dynamic name meant calling <code>constant('MyClass::STATUS')</code>. The new syntax is consistent with how PHP already handles variable variables and dynamic method calls.</p>
<h2 id="readonly-can-now-be-amended-in-clone">readonly can now be amended in clone</h2>
<p>A specific but genuinely annoying limitation of 8.1 readonly: you couldn&rsquo;t clone an object and change a readonly property. 8.3 adds the ability to reinitialize readonly properties during cloning, which makes immutable value objects usable in a lot more patterns.</p>
<h2 id="json_validate">json_validate()</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">json_validate</span>($string)) {
</span></span><span style="display:flex;"><span>    $data <span style="color:#f92672">=</span> <span style="color:#a6e22e">json_decode</span>($string);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Before 8.3, the only way to validate a JSON string was to decode it and check for errors. <code>json_validate()</code> checks without allocating the decoded structure, which matters when you only need to know if the string is valid JSON, not what&rsquo;s in it.</p>
<h2 id="randomizer-improvements">Randomizer improvements</h2>
<p><code>getBytesFromString()</code> generates a random string composed only of characters from a given set:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$rng <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Random\Randomizer</span>();
</span></span><span style="display:flex;"><span>$token <span style="color:#f92672">=</span> $rng<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getBytesFromString</span>(<span style="color:#e6db74">&#39;abcdefghijklmnopqrstuvwxyz0123456789&#39;</span>, <span style="color:#ae81ff">32</span>);
</span></span></code></pre></div><p>The previous approach: <code>str_split</code>, <code>array_map</code>, random selection, <code>implode</code>. It worked, but it was longer than it had any right to be.</p>
<p>8.3 is for the teams that adopt PHP versions quickly and want the incremental improvements. The typed constants alone are worth it on any codebase with interface constants.</p>
<h2 id="override-makes-inheritance-explicit">#[\Override] makes inheritance explicit</h2>
<p>Before 8.3, nothing stopped you from writing a method you thought was overriding a parent&rsquo;s, when you had a typo in the name or the parent had quietly removed it. Silent bugs, zero feedback from the engine.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Cache</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[\Override]
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">get</span>(<span style="color:#a6e22e">string</span> $key)<span style="color:#f92672">:</span> <span style="color:#a6e22e">mixed</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Engine verifies this method exists in a parent or interface
</span></span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If the method doesn&rsquo;t exist in any parent class or implemented interface, PHP throws an error. Same concept as Java&rsquo;s <code>@Override</code> or C#&rsquo;s <code>override</code>, finally in PHP.</p>
<h2 id="final-on-trait-methods">final on trait methods</h2>
<p>Traits have always had rough edges in PHP&rsquo;s OOP model. One specific problem: a class using a trait could override any of its methods, undermining whatever guarantees the trait was trying to provide. 8.3 lets the trait itself mark a method as final:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">trait</span> <span style="color:#a6e22e">Singleton</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getInstance</span>()<span style="color:#f92672">:</span> <span style="color:#66d9ef">static</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now a class using the trait cannot override <code>getInstance()</code>. The guarantee holds.</p>
<h2 id="anonymous-classes-can-be-readonly">Anonymous classes can be readonly</h2>
<p>PHP 8.1 brought readonly classes. Anonymous classes were left out for some reason. 8.3 fixes that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$point <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">readonly</span> <span style="color:#a6e22e">class</span>(<span style="color:#ae81ff">3</span>, <span style="color:#ae81ff">4</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">float</span> $x,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">float</span> $y,
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>Handy when you need a throwaway immutable value object without the ceremony of naming it.</p>
<h2 id="static-variable-initializers-accept-expressions">Static variable initializers accept expressions</h2>
<p>A small but long-standing restriction: static variable initializers only accepted constant expressions, no function calls. 8.3 drops that constraint:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">connection</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">PDO</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">static</span> $pdo <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">PDO</span>(<span style="color:#a6e22e">getenv</span>(<span style="color:#e6db74">&#39;DATABASE_URL&#39;</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> $pdo;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The initializer runs once on first call, the static variable persists. Achievable with a null-check before, this is just cleaner.</p>
<h2 id="mb_str_pad-finally-exists">mb_str_pad() finally exists</h2>
<p><code>str_pad()</code> has always been byte-aware, not character-aware. For multibyte strings (Arabic, Japanese, accented characters) it produced wrong output. 8.3 finally adds the multibyte variant:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$padded <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_str_pad</span>(<span style="color:#e6db74">&#39;日本&#39;</span>, <span style="color:#ae81ff">10</span>, <span style="color:#e6db74">&#39;*&#39;</span>, <span style="color:#a6e22e">STR_PAD_BOTH</span>);
</span></span></code></pre></div><p>The function respects character boundaries, not byte counts.</p>
<h2 id="str_increment-and-str_decrement">str_increment() and str_decrement()</h2>
<p>PHP&rsquo;s <code>++</code> operator on strings has a history of quirks: it increments letter sequences (<code>'a'</code> → <code>'b'</code>, <code>'z'</code> → <code>'aa'</code>), but <code>--</code> never worked symmetrically. The behavior was surprising enough that 8.3 deprecates <code>++</code>/<code>--</code> on non-alphanumeric strings and introduces explicit functions:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">str_increment</span>(<span style="color:#e6db74">&#39;a&#39;</span>);  <span style="color:#75715e">// b
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">str_increment</span>(<span style="color:#e6db74">&#39;Az&#39;</span>); <span style="color:#75715e">// Ba
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">str_decrement</span>(<span style="color:#e6db74">&#39;b&#39;</span>);  <span style="color:#75715e">// a
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">str_decrement</span>(<span style="color:#e6db74">&#39;Ba&#39;</span>); <span style="color:#75715e">// Az
</span></span></span></code></pre></div><p>The functions make the intent obvious and the behavior predictable.</p>
<h2 id="randomrandomizer-gets-float-support">Random\Randomizer gets float support</h2>
<p>8.3 fills in the float side of the Randomizer API:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$rng <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Random\Randomizer</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// A float in [0.0, 1.0)
</span></span></span><span style="display:flex;"><span>$f <span style="color:#f92672">=</span> $rng<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">nextFloat</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// A float in a specific range with controlled boundary inclusion
</span></span></span><span style="display:flex;"><span>$f <span style="color:#f92672">=</span> $rng<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getFloat</span>(<span style="color:#ae81ff">1.5</span>, <span style="color:#ae81ff">3.5</span>, <span style="color:#a6e22e">Random\IntervalBoundary</span><span style="color:#f92672">::</span><span style="color:#a6e22e">ClosedOpen</span>);
</span></span></code></pre></div><p><code>IntervalBoundary</code> is a new enum with four values: <code>ClosedOpen</code>, <code>ClosedClosed</code>, <code>OpenClosed</code>, <code>OpenOpen</code>. This matters for statistical correctness: the naive approach of <code>rand() / getrandmax()</code> doesn&rsquo;t produce a uniform distribution over floats.</p>
<h2 id="the-date-exception-hierarchy">The Date exception hierarchy</h2>
<p>Date/time errors in PHP used to throw generic exceptions with no way to tell &ldquo;malformed string&rdquo; from &ldquo;invalid timezone&rdquo; without parsing the message yourself. 8.3 adds a proper hierarchy:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">DateTimeImmutable</span>(<span style="color:#e6db74">&#39;not a date&#39;</span>);
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">DateMalformedStringException</span> $e) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// specifically a parsing failure
</span></span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">DateException</span> $e) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// other date-related errors
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The full tree: <code>DateError</code> (engine-level), <code>DateException</code> (base), with specific subclasses for invalid timezone, malformed interval string, malformed period string, and malformed date string.</p>
<h2 id="gc_status-tells-you-more">gc_status() tells you more</h2>
<p><code>gc_status()</code> now returns eight additional fields: <code>running</code>, <code>protected</code>, <code>full</code>, <code>buffer_size</code>, and timing breakdowns (<code>application_time</code>, <code>collector_time</code>, <code>destructor_time</code>, <code>free_time</code>). If you&rsquo;re profiling memory pressure or GC pauses, this data was previously unavailable without pulling in an extension.</p>
<h2 id="strrchr-grows-a-direction-argument">strrchr() grows a direction argument</h2>
<p><code>strrchr()</code> (find the last occurrence of a character, return from there to end) now accepts a <code>$before_needle</code> boolean, matching the API of <code>strstr()</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$path <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;/var/www/html/index.php&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">strrchr</span>($path, <span style="color:#e6db74">&#39;/&#39;</span>, <span style="color:#a6e22e">before_needle</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>);  <span style="color:#75715e">// /var/www/html
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">strrchr</span>($path, <span style="color:#e6db74">&#39;/&#39;</span>);                        <span style="color:#75715e">// /index.php
</span></span></span></code></pre></div><p>A function that&rsquo;s been in PHP since 1994, finally consistent with its sibling.</p>
<h2 id="deprecations-worth-noting">Deprecations worth noting</h2>
<p><code>get_class()</code> and <code>get_parent_class()</code> without arguments now emit deprecation notices. The argumentless forms relied on implicit <code>$this</code> context, which was easy to misread. Pass the object explicitly.</p>
<p><code>assert_options()</code> and the <code>ASSERT_*</code> constants are deprecated in favor of the <code>zend.assertions</code> INI directive, which is the right tool for controlling assertion behavior across environments.</p>
<p>The <code>++</code>/<code>--</code> operators on empty strings and non-numeric non-alphanumeric strings now emit deprecation warnings. The behavior was undefined territory. 8.3 starts the migration toward defined behavior in 9.0.</p>
<h2 id="stack-overflow-protection">Stack overflow protection</h2>
<p>Two new INI directives: <code>zend.max_allowed_stack_size</code> sets a hard limit on PHP&rsquo;s stack depth, and <code>zend.reserved_stack_size</code> sets aside a buffer for cleanup after a limit is hit. Before 8.3, deeply recursive code could just crash at the OS level. Now PHP catches it and throws an <code>Error</code> with a useful message.</p>
]]></content:encoded></item><item><title>PHP 8.2: readonly classes and the deprecation that matters</title><link>https://guillaumedelre.github.io/2023/01/22/php-8.2-readonly-classes-and-the-deprecation-that-matters/</link><pubDate>Sun, 22 Jan 2023 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2023/01/22/php-8.2-readonly-classes-and-the-deprecation-that-matters/</guid><description>Part 8 of 11 in &amp;quot;PHP Releases&amp;quot;: PHP 8.2 introduces readonly classes, deprecates dynamic properties, and adds disjunctive normal form types.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 8.2 dropped December 8th. Readonly classes are the headline. The deprecation of dynamic properties is the one that actually requires your attention.</p>
<h2 id="dynamic-properties-deprecated">Dynamic properties deprecated</h2>
<p>PHP has always allowed adding properties to objects that weren&rsquo;t declared in the class:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span> {}
</span></span><span style="display:flex;"><span>$user <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">User</span>();
</span></span><span style="display:flex;"><span>$user<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Alice&#39;</span>; <span style="color:#75715e">// no declaration, no error... until now
</span></span></span></code></pre></div><p>In 8.2, this triggers a deprecation notice. In PHP 9.0 it becomes a fatal error. The grace period exists, but the migration clock is running.</p>
<p>The reasoning is solid: dynamic properties are a classic source of typos that silently pass (write <code>$user-&gt;nmae</code> and PHP just creates a new property instead of complaining). Explicit declarations make the class contract clear and make tooling actually useful.</p>
<p>Migration is mostly mechanical: declare the properties, or slap <code>#[AllowDynamicProperties]</code> on legacy classes you can&rsquo;t touch yet.</p>
<h2 id="readonly-classes">Readonly classes</h2>
<p>8.1 added <code>readonly</code> for individual properties. 8.2 adds it to the class declaration itself:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">readonly</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Point</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">float</span> $x,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">float</span> $y,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">float</span> $z,
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>All promoted and explicitly declared properties become readonly automatically. Value objects (coordinates, money amounts, identifiers) are the obvious target. The syntax is clean and the intent reads clearly.</p>
<p>One constraint: readonly classes can&rsquo;t have non-typed properties, which were already a bad idea with readonly anyway.</p>
<h2 id="dnf-types">DNF types</h2>
<p>Disjunctive Normal Form types let you combine union and intersection types:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">process</span>(<span style="color:#a6e22e">Countable</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Iterator</span><span style="color:#f92672">|</span><span style="color:#66d9ef">null</span> $collection)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> { <span style="color:#f92672">...</span> }
</span></span></code></pre></div><p><code>(Countable&amp;Iterator)|null</code>: an object that implements both interfaces, or null. This covers type expressions that 8.0 union types and 8.1 intersection types each got partway to but couldn&rsquo;t represent together.</p>
<h2 id="the-random-extension">The Random extension</h2>
<p>A dedicated <code>Random</code> extension replaces the scattered <code>rand()</code>, <code>mt_rand()</code>, <code>random_int()</code> functions with an object-oriented API:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$rng <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Random\Randomizer</span>();
</span></span><span style="display:flex;"><span>$rng<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getInt</span>(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">100</span>);
</span></span><span style="display:flex;"><span>$rng<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">shuffleArray</span>($items);
</span></span></code></pre></div><p>Engines are swappable: <code>Mersenne Twister</code>, <code>PCG64</code>, <code>Xoshiro256StarStar</code>, or <code>CryptoSafeEngine</code> for security-sensitive contexts. Same code, seeded deterministic engine in tests, cryptographic engine in production.</p>
<p>8.2 is a consolidation release. The dynamic properties deprecation is the one decision you need to make now.</p>
<h2 id="null-false-and-true-as-standalone-types"><code>null</code>, <code>false</code>, and <code>true</code> as standalone types</h2>
<p>PHP has had <code>nullable</code> types since 7.1 and union types since 8.0, but <code>null</code> as a standalone type declaration wasn&rsquo;t valid. 8.2 fixes that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">alwaysNull</span>()<span style="color:#f92672">:</span> <span style="color:#66d9ef">null</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">null</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">disabled</span>()<span style="color:#f92672">:</span> <span style="color:#66d9ef">false</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">enabled</span>()<span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>false</code> and <code>true</code> as standalone types are useful when you need to be precise about what a function can actually return. It&rsquo;s narrow but correct: a function that returns <code>false</code> on failure and a string on success should declare <code>string|false</code>, and now both sides of that union are real types.</p>
<h2 id="constants-in-traits">Constants in traits</h2>
<p>Traits could hold properties and methods. Constants were the odd gap. 8.2 closes it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">trait</span> <span style="color:#a6e22e">Timestamps</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">DATE_FORMAT</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Y-m-d H:i:s&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">formatCreatedAt</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">createdAt</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">format</span>(<span style="color:#a6e22e">self</span><span style="color:#f92672">::</span><span style="color:#a6e22e">DATE_FORMAT</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Article</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">use</span> <span style="color:#a6e22e">Timestamps</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">Article</span><span style="color:#f92672">::</span><span style="color:#a6e22e">DATE_FORMAT</span>; <span style="color:#75715e">// &#39;Y-m-d H:i:s&#39;
</span></span></span></code></pre></div><p>The constant belongs to the class that uses the trait, not the trait itself, so you can&rsquo;t access <code>Timestamps::DATE_FORMAT</code> directly. Expected scoping behavior, consistent with how trait methods already work.</p>
<h2 id="sensitiveparameter"><code>#[SensitiveParameter]</code></h2>
<p>Stack traces have always been a liability: function arguments get logged verbatim, which means passwords and tokens end up in your error logs and monitoring dashboards. 8.2 adds an attribute to stop that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">authenticate</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">string</span> $user,
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[\SensitiveParameter] string $password,
</span></span></span><span style="display:flex;"><span>)<span style="color:#f92672">:</span> <span style="color:#a6e22e">bool</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// if this throws, the stack trace shows:
</span></span></span><span style="display:flex;"><span>    <span style="color:#75715e">// authenticate(&#39;alice&#39;, Object(SensitiveParameterValue))
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">hash</span>(<span style="color:#e6db74">&#39;sha256&#39;</span>, $password) <span style="color:#f92672">===</span> <span style="color:#a6e22e">getStoredHash</span>($user);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The parameter value in the trace gets replaced with a <code>SensitiveParameterValue</code> object. One attribute, zero excuses not to add it to every function that touches credentials.</p>
<h2 id="deprecated-string-interpolation-syntaxes">Deprecated string interpolation syntaxes</h2>
<p>Two ways to interpolate expressions inside strings are deprecated in 8.2:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;world&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// These are deprecated:
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;Hello </span><span style="color:#e6db74">${</span>name<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>;       <span style="color:#75715e">// use &#34;$name&#34; or &#34;{$name}&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;Hello </span><span style="color:#e6db74">${</span>getName()<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>;  <span style="color:#75715e">// use &#34;{$this-&gt;getName()}&#34;
</span></span></span></code></pre></div><p>The <code>${...}</code> forms created ambiguity between variable variables and expressions. The cleaner <code>{$...}</code> syntax has always been there and does the same thing. This is mostly a search-and-replace job in codebases that picked up the deprecated forms out of habit.</p>
<h2 id="utf8_encode-and-utf8_decode-deprecated"><code>utf8_encode()</code> and <code>utf8_decode()</code> deprecated</h2>
<p>These two functions are deprecated in 8.2 and gone in 9.0. Their behavior was always narrower than the names suggested: <code>utf8_encode()</code> converts ISO-8859-1 to UTF-8, not &ldquo;any encoding to UTF-8.&rdquo;</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// Deprecated in 8.2:
</span></span></span><span style="display:flex;"><span>$utf8 <span style="color:#f92672">=</span> <span style="color:#a6e22e">utf8_encode</span>($latin1String);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Use instead:
</span></span></span><span style="display:flex;"><span>$utf8 <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_convert_encoding</span>($latin1String, <span style="color:#e6db74">&#39;UTF-8&#39;</span>, <span style="color:#e6db74">&#39;ISO-8859-1&#39;</span>);
</span></span></code></pre></div><p><code>mb_convert_encoding()</code> or <code>iconv()</code> handle the general case. If you&rsquo;re actually dealing with Latin-1 input, the replacement is a direct swap.</p>
<h2 id="locale-independent-string-functions">Locale-independent string functions</h2>
<p>Several string functions silently varied behavior based on the system locale, producing different results in production versus a dev container. In 8.2, they&rsquo;re locale-independent and ASCII-only:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// strtolower, strtoupper, stristr, stripos, strripos,
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// lcfirst, ucfirst, ucwords, str_ireplace now do ASCII case conversion only.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// For locale-aware behavior, use mb_* equivalents:
</span></span></span><span style="display:flex;"><span>$lowered <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_strtolower</span>($text, <span style="color:#e6db74">&#39;UTF-8&#39;</span>);
</span></span></code></pre></div><p>This is a correctness fix. If your code was relying on locale-sensitive behavior from these functions, it was already broken on systems with different locale configurations. 8.2 makes the behavior deterministic everywhere, which is what you actually wanted.</p>
<h2 id="str_split-on-empty-string"><code>str_split()</code> on empty string</h2>
<p>A quiet behavior change worth noting:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// PHP 8.1: str_split(&#39;&#39;) === [&#39;&#39;]
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// PHP 8.2: str_split(&#39;&#39;) === []
</span></span></span></code></pre></div><p>The new behavior makes more sense: splitting nothing produces nothing. If you&rsquo;re checking <code>count(str_split($input))</code>, an empty input no longer produces a count of 1.</p>
]]></content:encoded></item><item><title>PHP 8.1: enums, fibers, and the type system growing up</title><link>https://guillaumedelre.github.io/2022/01/09/php-8.1-enums-fibers-and-the-type-system-growing-up/</link><pubDate>Sun, 09 Jan 2022 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2022/01/09/php-8.1-enums-fibers-and-the-type-system-growing-up/</guid><description>Part 7 of 11 in &amp;quot;PHP Releases&amp;quot;: PHP 8.1 adds native enums, fibers for cooperative multitasking, readonly properties, and intersection types.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 8.1 released November 25th. It follows 8.0&rsquo;s sweeping overhaul with something different: fewer features, but each one thought through rather than bolted on.</p>
<h2 id="enums">Enums</h2>
<p>This is the one that changes codebases the moment you upgrade. Before 8.1, PHP enumerations were either class constants, strings, or integers with nothing enforcing them:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// before: nothing stops Status::INVALID from being passed
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">ACTIVE</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;active&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">INACTIVE</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;inactive&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// after
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">enum</span> <span style="color:#a6e22e">Status</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">Active</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;active&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">Inactive</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;inactive&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">activate</span>(<span style="color:#a6e22e">Status</span> $status)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> { <span style="color:#f92672">...</span> }
</span></span></code></pre></div><p>PHP enums are objects, not scalars. They support methods, interfaces, and constants. Backed enums (with a string or int value) serialize cleanly and map to database columns naturally. Pure enums (no backing type) enforce domain concepts without worrying about serialization.</p>
<p>The immediate effect: every status field, every finite set of states in every codebase I maintain became an enum candidate. The type system finally has a native way to express the thing every PHP project has been faking for years.</p>
<h2 id="fibers">Fibers</h2>
<p>Fibers are a cooperative concurrency primitive: you can pause and resume execution of a function, yielding control without threads.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$fiber <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Fiber</span>(<span style="color:#66d9ef">function</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    $value <span style="color:#f92672">=</span> <span style="color:#a6e22e">Fiber</span><span style="color:#f92672">::</span><span style="color:#a6e22e">suspend</span>(<span style="color:#e6db74">&#39;first&#39;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;Resumed with: </span><span style="color:#e6db74">{</span>$value<span style="color:#e6db74">}</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>;
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> $fiber<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">start</span>();    <span style="color:#75715e">// &#39;first&#39;
</span></span></span><span style="display:flex;"><span>$fiber<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">resume</span>(<span style="color:#e6db74">&#39;hello&#39;</span>);      <span style="color:#75715e">// &#34;Resumed with: hello&#34;
</span></span></span></code></pre></div><p>Fibers are the foundation async libraries like ReactPHP and Amp have needed from the runtime for a while. For most application developers the direct API matters less than the libraries built on top of it, but understanding fibers explains what those libraries are doing underneath.</p>
<h2 id="pencil2-readonly-properties">:pencil2: Readonly properties</h2>
<p>8.0 brought constructor promotion. 8.1 adds <code>readonly</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">readonly</span> <span style="color:#a6e22e">int</span> $id,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">readonly</span> <span style="color:#a6e22e">string</span> $name,
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>A <code>readonly</code> property can be written exactly once, during initialization. After that, any write throws an <code>Error</code>. Combined with constructor promotion, value objects and DTOs become concise and actually mean what they say.</p>
<h2 id="first-class-callable-syntax">First-class callable syntax</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$fn <span style="color:#f92672">=</span> <span style="color:#a6e22e">strlen</span>(<span style="color:#f92672">...</span>);
</span></span><span style="display:flex;"><span>$fn <span style="color:#f92672">=</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">process</span>(<span style="color:#f92672">...</span>);
</span></span><span style="display:flex;"><span>$fn <span style="color:#f92672">=</span> <span style="color:#a6e22e">MyClass</span><span style="color:#f92672">::</span><span style="color:#a6e22e">create</span>(<span style="color:#f92672">...</span>);
</span></span></code></pre></div><p><code>...</code> after a callable creates a <code>Closure</code> without <code>Closure::fromCallable()</code> boilerplate. Useful when passing methods as callbacks.</p>
<p>8.1 is precise. Enums alone justify the upgrade.</p>
<h2 id="intersection-types">Intersection types</h2>
<p>Union types landed in 8.0. Intersection types follow in 8.1. Where a union says &ldquo;one of these&rdquo;, an intersection says &ldquo;all of these&rdquo;:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">process</span>(<span style="color:#a6e22e">Countable</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Iterator</span> $collection)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">foreach</span> ($collection <span style="color:#66d9ef">as</span> $item) { <span style="color:#75715e">/* ... */</span> }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">count</span>($collection);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>One constraint: intersection types can&rsquo;t be mixed with union types in the same declaration (that arrives in 8.2 as DNF types). But this already unlocks precise type-checking for objects that must satisfy multiple interfaces at once, a pattern frameworks use constantly that had to stay untyped until now.</p>
<h2 id="the-never-return-type">The <code>never</code> return type</h2>
<p>A function that never returns (it always throws or exits) now has a type to say so:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">redirect</span>(<span style="color:#a6e22e">string</span> $url)<span style="color:#f92672">:</span> <span style="color:#a6e22e">never</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">header</span>(<span style="color:#e6db74">&#34;Location: </span><span style="color:#e6db74">{</span>$url<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">exit</span>();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">fail</span>(<span style="color:#a6e22e">string</span> $message)<span style="color:#f92672">:</span> <span style="color:#a6e22e">never</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\RuntimeException</span>($message);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The practical benefit: static analyzers can prove that code after a <code>never</code> function is unreachable, and callers know there&rsquo;s no return value to handle. Before this, it lived in docblocks with no enforcement.</p>
<h2 id="final-class-constants">Final class constants</h2>
<p>Before 8.1, any subclass could quietly override a parent&rsquo;s class constant. Now you can put a stop to that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Base</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">VERSION</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;1.0&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Child</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Base</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Fatal error: Cannot override final constant Base::VERSION
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">VERSION</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;2.0&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Relatedly, interface constants are now overridable by implementing classes by default. A separate behavior fix that had been inconsistent since interfaces were introduced.</p>
<h2 id="new-in-initializers"><code>new</code> in initializers</h2>
<p>Default parameter values used to be restricted to scalars and arrays. 8.1 drops that restriction:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Logger</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">Handler</span> $handler <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">NullHandler</span>(),
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">createUser</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Validator</span> $validator <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">DefaultValidator</span>(),
</span></span><span style="display:flex;"><span>)<span style="color:#f92672">:</span> <span style="color:#a6e22e">User</span> { <span style="color:#75715e">/* ... */</span> }
</span></span></code></pre></div><p>Same goes for attribute arguments and static variable initializers. Which means dependency injection with sensible defaults no longer needs a null check and lazy instantiation inside the method body.</p>
<h2 id="array-unpacking-with-string-keys">Array unpacking with string keys</h2>
<p>Array unpacking via the spread operator only worked with integer-keyed arrays before 8.1. String keys work now too:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$defaults <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#39;color&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;red&#39;</span>, <span style="color:#e6db74">&#39;size&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;M&#39;</span>];
</span></span><span style="display:flex;"><span>$custom <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#39;size&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;L&#39;</span>, <span style="color:#e6db74">&#39;weight&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;200g&#39;</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$merged <span style="color:#f92672">=</span> [<span style="color:#f92672">...</span>$defaults, <span style="color:#f92672">...</span>$custom];
</span></span><span style="display:flex;"><span><span style="color:#75715e">// [&#39;color&#39; =&gt; &#39;red&#39;, &#39;size&#39; =&gt; &#39;L&#39;, &#39;weight&#39; =&gt; &#39;200g&#39;]
</span></span></span></code></pre></div><p>Later keys override earlier ones. Same behavior as <code>array_merge()</code>, but expressed inline. Performance difference is marginal; readability difference is not.</p>
<h2 id="fsync-and-fdatasync"><code>fsync</code> and <code>fdatasync</code></h2>
<p>Two functions that had no good reason to be missing from a filesystem-oriented language:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$fp <span style="color:#f92672">=</span> <span style="color:#a6e22e">fopen</span>(<span style="color:#e6db74">&#39;/tmp/important.dat&#39;</span>, <span style="color:#e6db74">&#39;w&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">fwrite</span>($fp, $data);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">fsync</span>($fp);   <span style="color:#75715e">// flush OS buffers to physical storage
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">fclose</span>($fp);
</span></span></code></pre></div><p><code>fdatasync()</code> does the same but skips metadata sync when you only care about the data being durable. Both return <code>false</code> on failure. If you&rsquo;re writing anything that needs crash safety, you needed these.</p>
<h2 id="passing-null-to-non-nullable-built-in-parameters">Passing <code>null</code> to non-nullable built-in parameters</h2>
<p>A quieter but consequential change: built-in functions that accept strings, integers, etc. have always silently swallowed <code>null</code> and coerced it. In 8.1, that starts emitting a deprecation notice.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">str_contains</span>(<span style="color:#e6db74">&#34;foobar&#34;</span>, <span style="color:#66d9ef">null</span>);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Deprecated: Passing null to parameter #2 ($needle) of type string is deprecated
</span></span></span></code></pre></div><p>This aligns built-in functions with user-defined functions, which already refused nullable arguments for non-nullable parameters. PHP 9.0 turns this into a hard error. If you&rsquo;re passing <code>null</code> into string functions, now is a better time to fix it than during a production incident.</p>
<h2 id="mysqli-exceptions-by-default">MySQLi exceptions by default</h2>
<p>Before 8.1, MySQLi failed silently unless you explicitly called <code>mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)</code>. That&rsquo;s now the default:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// This throws \mysqli_sql_exception on connection failure in 8.1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Previously returned false and set an error you had to check manually
</span></span></span><span style="display:flex;"><span>$connection <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">mysqli</span>(<span style="color:#e6db74">&#39;localhost&#39;</span>, <span style="color:#e6db74">&#39;user&#39;</span>, <span style="color:#e6db74">&#39;wrong_password&#39;</span>, <span style="color:#e6db74">&#39;db&#39;</span>);
</span></span></code></pre></div><p>Every codebase that catches MySQLi errors by checking return values needs to be reviewed. The silent failures that caused hard-to-diagnose bugs now throw exceptions, which is the right behavior, just potentially surprising if you hit it mid-upgrade.</p>
]]></content:encoded></item><item><title>PHP 8.0: match, named arguments, attributes, and JIT</title><link>https://guillaumedelre.github.io/2021/01/10/php-8.0-match-named-arguments-attributes-and-jit/</link><pubDate>Sun, 10 Jan 2021 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2021/01/10/php-8.0-match-named-arguments-attributes-and-jit/</guid><description>Part 6 of 11 in &amp;quot;PHP Releases&amp;quot;: PHP 8.0 reshapes the language: JIT compiler, named arguments, match expressions, union types, and nullsafe operator.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 8.0 shipped November 26th. I&rsquo;ve been running it for six weeks on a side project and a greenfield service at work. It&rsquo;s the most significant PHP release since 7.0, and in some ways more impactful, because the changes pile on top of each other in useful ways.</p>
<h2 id="jit">JIT</h2>
<p>The Just-In-Time compiler was the headline announcement. The reality in production is more nuanced: for typical web apps (database queries, HTTP calls, template rendering) the gains are modest, because those workloads are I/O bound, not compute bound. Where JIT actually shines is CPU-intensive code: image manipulation, data transformation, mathematical computation.</p>
<p>For most web apps, the performance improvement comes from the overall engine work in 8.0, not JIT specifically. Still worth enabling though: it costs nothing on I/O-bound work.</p>
<h2 id="match-expressions">Match expressions</h2>
<p><code>switch</code> has three problems: it uses loose comparison, it falls through by default, and it can&rsquo;t be used as an expression. <code>match</code> fixes all three:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> <span style="color:#a6e22e">match</span>($status) {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;active&#39;</span>, <span style="color:#e6db74">&#39;pending&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;processing&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;done&#39;</span>             <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;finished&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">default</span>            <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\UnexpectedValueException</span>($status),
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>Strict comparison. No fall-through. Expression that returns a value. Non-exhaustive match throws. After one week with <code>match</code> I stopped writing <code>switch</code>.</p>
<h2 id="named-arguments">Named arguments</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">array_slice</span>(<span style="color:#66d9ef">array</span><span style="color:#f92672">:</span> $users, <span style="color:#a6e22e">offset</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0</span>, <span style="color:#a6e22e">length</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">10</span>, <span style="color:#a6e22e">preserve_keys</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>);
</span></span></code></pre></div><p>Named arguments let you pass arguments in any order and skip optional ones. The obvious win is readability on functions with multiple boolean flags. The less obvious win: named arguments survive PHP version upgrades even when parameter order changes, because you&rsquo;re naming what you mean.</p>
<h2 id="attributes">Attributes</h2>
<p>Out with docblock annotations (the <code>@Route</code>, <code>@ORM\Column</code> style that frameworks have relied on for years), in with first-class PHP syntax:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">#[Route(&#39;/users&#39;, methods: [&#39;GET&#39;])]
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#[IsGranted(&#39;ROLE_ADMIN&#39;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">list</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Response</span> { <span style="color:#f92672">...</span> }
</span></span></code></pre></div><p>Attributes are validated by the engine, not parsed from strings. IDE support just works, no plugin magic needed. For Symfony and Doctrine users, this is the real daily win of PHP 8.0.</p>
<h2 id="constructor-promotion">Constructor promotion</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">readonly</span> <span style="color:#a6e22e">int</span> $id,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">string</span> $name,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">private</span> <span style="color:#f92672">?</span><span style="color:#a6e22e">string</span> $email <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span>,
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Properties declared and assigned in one line in the constructor signature. The most immediate refactoring win in 8.0: every data class I&rsquo;ve touched since upgrading is half the lines it used to be.</p>
<h2 id="nullsafe-operator">Nullsafe operator</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$city <span style="color:#f92672">=</span> $user<span style="color:#f92672">?-&gt;</span><span style="color:#a6e22e">getAddress</span>()<span style="color:#f92672">?-&gt;</span><span style="color:#a6e22e">getCity</span>()<span style="color:#f92672">?-&gt;</span><span style="color:#a6e22e">getName</span>();
</span></span></code></pre></div><p><code>null</code> at any point in the chain short-circuits the rest and returns <code>null</code>. The alternative was nested null checks or a chain of early returns. This composes naturally.</p>
<h2 id="union-types">Union types</h2>
<p>Named arguments make function signatures more explicit at the call site. Union types make them more honest at the declaration site:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">processInput</span>(<span style="color:#a6e22e">int</span><span style="color:#f92672">|</span><span style="color:#a6e22e">float</span><span style="color:#f92672">|</span><span style="color:#a6e22e">string</span> $value)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span><span style="color:#f92672">|</span><span style="color:#a6e22e">int</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">is_string</span>($value)) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">strlen</span>($value);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> (<span style="color:#a6e22e">int</span>) <span style="color:#a6e22e">round</span>($value);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The union <code>int|float|string</code> is a literal OR. The engine enforces it on entry and exit. Before 8.0, &ldquo;this parameter accepts int or float&rdquo; lived in a docblock that nothing enforced. There&rsquo;s also <code>null</code> as a type component: <code>?string</code> is just syntactic sugar for <code>string|null</code>, both are valid.</p>
<p>One special case: <code>false</code>. PHP has a bunch of built-in functions that return a typed value on success and <code>false</code> on failure. The 8.0 type system accommodates that: <code>array|false</code>, <code>string|false</code>. It&rsquo;s an honest acknowledgment that the codebase can&rsquo;t be rewritten overnight.</p>
<h2 id="static-return-type">static return type</h2>
<p><code>static</code> as a return type was possible informally through docblocks, but 8.0 makes it official. The distinction between <code>self</code> and <code>static</code> matters in inheritance:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Builder</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">protected</span> <span style="color:#66d9ef">array</span> $config <span style="color:#f92672">=</span> [];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">set</span>(<span style="color:#a6e22e">string</span> $key, <span style="color:#a6e22e">mixed</span> $value)<span style="color:#f92672">:</span> <span style="color:#66d9ef">static</span> {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">config</span>[$key] <span style="color:#f92672">=</span> $value;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> $this;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SpecialBuilder</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Builder</span> {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> (<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">SpecialBuilder</span>())<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">set</span>(<span style="color:#e6db74">&#39;foo&#39;</span>, <span style="color:#e6db74">&#39;bar&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// $result is SpecialBuilder, not Builder
</span></span></span></code></pre></div><p>With <code>self</code> as the return type, that chain would return <code>Builder</code>, breaking fluent interfaces in subclasses. <code>static</code> makes fluent APIs work correctly across inheritance hierarchies without manual overrides.</p>
<h2 id="mixed-type">mixed type</h2>
<p><code>mixed</code> was a docblock convention for years. 8.0 makes it a real type that shows up in signatures:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">debug</span>(<span style="color:#a6e22e">mixed</span> $value)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">var_dump</span>($value);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>It accepts everything: <code>null</code>, objects, resources, scalars, arrays. Semantically it&rsquo;s the same as having no type declaration, but it&rsquo;s explicit rather than absent. The difference between &ldquo;this parameter is untyped&rdquo; and &ldquo;this parameter intentionally accepts anything.&rdquo; Worth using when you&rsquo;re writing a general-purpose utility that would be dishonest with a narrower type.</p>
<h2 id="throw-as-expression">throw as expression</h2>
<p>Before 8.0, <code>throw</code> was a statement. Sounds like a pedantic distinction until you hit the places where you actually want an expression:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// In a ternary:
</span></span></span><span style="display:flex;"><span>$value <span style="color:#f92672">=</span> $input <span style="color:#f92672">??</span> <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\InvalidArgumentException</span>(<span style="color:#e6db74">&#39;input required&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// In an arrow function:
</span></span></span><span style="display:flex;"><span>$getId <span style="color:#f92672">=</span> <span style="color:#a6e22e">fn</span>(<span style="color:#a6e22e">User</span> $u) <span style="color:#f92672">=&gt;</span> $u<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">id</span> <span style="color:#f92672">??</span> <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\RuntimeException</span>(<span style="color:#e6db74">&#39;no id&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// In a match arm (which is already an expression):
</span></span></span><span style="display:flex;"><span>$status <span style="color:#f92672">=</span> <span style="color:#a6e22e">match</span>($code) {
</span></span><span style="display:flex;"><span>    <span style="color:#ae81ff">200</span>     <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;ok&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#ae81ff">404</span>     <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;not found&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">default</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\UnexpectedValueException</span>(<span style="color:#e6db74">&#34;unknown code: </span><span style="color:#e6db74">$code</span><span style="color:#e6db74">&#34;</span>),
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>The last one is particularly useful: match without a default will throw <code>UnhandledMatchError</code> automatically, but sometimes you want to control the exception type and message.</p>
<h2 id="catch-without-a-variable">catch without a variable</h2>
<p>Small quality-of-life fix. When you catch an exception but don&rsquo;t actually use the object, 8.0 lets you omit the variable:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>    $result <span style="color:#f92672">=</span> $cache<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">get</span>($key);
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">CacheMissException</span>) {
</span></span><span style="display:flex;"><span>    $result <span style="color:#f92672">=</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">compute</span>($key);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Before 8.0, you had to write <code>catch (CacheMissException $e)</code> and then either use <code>$e</code> or live with the IDE warning about an unused variable. Neither was satisfying.</p>
<h2 id="string-functions-that-should-have-existed-years-ago">String functions that should have existed years ago</h2>
<p>Three functions that every PHP developer has written manually at least once:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">str_contains</span>(<span style="color:#e6db74">&#39;hello world&#39;</span>, <span style="color:#e6db74">&#39;world&#39;</span>);  <span style="color:#75715e">// true
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">str_starts_with</span>(<span style="color:#e6db74">&#39;hello world&#39;</span>, <span style="color:#e6db74">&#39;hell&#39;</span>); <span style="color:#75715e">// true
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">str_ends_with</span>(<span style="color:#e6db74">&#39;hello world&#39;</span>, <span style="color:#e6db74">&#39;world&#39;</span>); <span style="color:#75715e">// true
</span></span></span></code></pre></div><p>Before 8.0, the go-to approaches were <code>strpos() !== false</code>, <code>strncmp()</code>, or <code>substr() ===</code>, all of which require stopping to remember the semantics. These new functions are just direct and readable. No regex, no offset arithmetic.</p>
<h2 id="stable-sort">Stable sort</h2>
<p>PHP&rsquo;s sorting functions weren&rsquo;t stable before 8.0. &ldquo;Not stable&rdquo; means elements that compare as equal could end up in any order relative to each other. In practice this caused subtle bugs in UI code that needed consistent ordering, pagination that shifted between loads, and tests that only passed by luck.</p>
<p>8.0 guarantees stability across all sorting functions: <code>sort()</code>, <code>usort()</code>, <code>array_multisort()</code>, and the rest. Equal elements keep their original relative position. This is the behavior most people assumed was already there.</p>
<h2 id="weakmap">WeakMap</h2>
<p>7.4 brought <code>WeakReference</code> for single objects. 8.0 brings <code>WeakMap</code>: a map where both the keys (objects) and their associated data can be garbage collected when no other reference to the key object exists:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RequestCache</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">WeakMap</span> $cache;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>() {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">cache</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">WeakMap</span>();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">get</span>(<span style="color:#a6e22e">Request</span> $request)<span style="color:#f92672">:</span> <span style="color:#a6e22e">Response</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">cache</span>[$request] <span style="color:#f92672">??=</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">compute</span>($request);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The moment <code>$request</code> is no longer referenced anywhere else, the entry disappears from the map. No manual cleanup needed. It&rsquo;s the right pattern for memoization and computed property caches where you don&rsquo;t want to be the sole reason an object stays alive.</p>
<h2 id="new-exception-types">New exception types</h2>
<p><code>ValueError</code> is thrown when a function gets the right type but an invalid value, as opposed to <code>TypeError</code> which fires on wrong types:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">array_chunk</span>([], <span style="color:#f92672">-</span><span style="color:#ae81ff">5</span>); <span style="color:#75715e">// ValueError: array_chunk(): Argument #2 ($length) must be greater than 0
</span></span></span></code></pre></div><p>Before 8.0, many of these were warnings that returned <code>false</code> or <code>null</code>. Now they throw. The engine is stricter, which means you catch problems earlier instead of getting weird results somewhere downstream.</p>
<h2 id="get_debug_type-and-fdiv">get_debug_type() and fdiv()</h2>
<p>Two utility functions worth knowing.</p>
<p><code>get_debug_type()</code> returns a normalized string representation of any value, handy for error messages:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">get_debug_type</span>(<span style="color:#ae81ff">1</span>);          <span style="color:#75715e">// &#34;int&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">get_debug_type</span>(<span style="color:#ae81ff">1.0</span>);        <span style="color:#75715e">// &#34;float&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">get_debug_type</span>(<span style="color:#66d9ef">null</span>);       <span style="color:#75715e">// &#34;null&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">get_debug_type</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Foo</span>());  <span style="color:#75715e">// &#34;Foo&#34; (not &#34;object&#34;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">get_debug_type</span>([]);         <span style="color:#75715e">// &#34;array&#34;
</span></span></span></code></pre></div><p>The difference from <code>gettype()</code>: it returns class names for objects and uses normalized names (<code>&quot;int&quot;</code> not <code>&quot;integer&quot;</code>). Exactly what you want when building an exception message that says what you got versus what you expected.</p>
<p><code>fdiv()</code> performs floating-point division following IEEE 754, meaning division by zero returns <code>INF</code>, <code>-INF</code>, or <code>NAN</code> instead of a warning:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">fdiv</span>(<span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">0</span>);   <span style="color:#75715e">// INF
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">fdiv</span>(<span style="color:#f92672">-</span><span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">0</span>);  <span style="color:#75715e">// -INF
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">fdiv</span>(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>);    <span style="color:#75715e">// NAN
</span></span></span></code></pre></div><h2 id="the-changes-that-break-things">The changes that break things</h2>
<p>8.0 also ships a few changes that aren&rsquo;t features, they&rsquo;re corrections.</p>
<p>The big one: <code>0 == &quot;foo&quot;</code> is now <code>false</code>. In PHP 7, comparing an integer to a non-numeric string would cast the string to 0, so <code>0 == &quot;anything-non-numeric&quot;</code> evaluated to <code>true</code>. That was a persistent source of bugs and security headaches. PHP 8 flips it: the integer gets cast to a string instead:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>(<span style="color:#ae81ff">0</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;foo&#34;</span>);  <span style="color:#75715e">// bool(false) in 8.0, bool(true) in 7.x
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>(<span style="color:#ae81ff">0</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;&#34;</span>);     <span style="color:#75715e">// bool(false) in 8.0, bool(true) in 7.x
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>(<span style="color:#ae81ff">0</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;0&#34;</span>);    <span style="color:#75715e">// bool(true) in both (&#34;0&#34; is numeric)
</span></span></span></code></pre></div><p>If you relied on this intentionally, you already knew it was sketchy. If you didn&rsquo;t know you relied on it, 8.0 will find those code paths for you.</p>
<p>Several functions that used to return resources now return proper objects: <code>curl_init()</code> returns a <code>CurlHandle</code>, <code>imagecreate()</code> returns a <code>GdImage</code>, <code>xml_parser_create()</code> returns an <code>XMLParser</code>. Code that checks <code>is_resource($curl)</code> will break, because <code>is_resource()</code> returns <code>false</code> for these objects. The fix is to check against <code>false</code> (the return value on failure) rather than checking the type of the success case.</p>
<p>PHP 8.0 is the kind of release where the features reinforce each other. Attributes play well with constructor promotion. Match pairs naturally with union types. The string functions cut noise that was hiding intent. The corrections are occasionally breaking, but they push the language toward consistency it should have had years ago.</p>
]]></content:encoded></item><item><title>PHP 7.4: typed properties and the arrow function you actually want</title><link>https://guillaumedelre.github.io/2020/01/12/php-7.4-typed-properties-and-the-arrow-function-you-actually-want/</link><pubDate>Sun, 12 Jan 2020 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2020/01/12/php-7.4-typed-properties-and-the-arrow-function-you-actually-want/</guid><description>Part 5 of 11 in &amp;quot;PHP Releases&amp;quot;: PHP 7.4 brings typed properties and concise arrow functions — the last 7.x release and the clearest preview of PHP 8.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 7.4 landed November 28th. It&rsquo;s the last 7.x release before PHP 8.0, and it feels like it. The features are substantial enough to stand on their own, but they also read as groundwork for what&rsquo;s coming.</p>
<h2 id="typed-properties">Typed properties</h2>
<p>This is the one. Since PHP 7.0, you could type function parameters and return values. But class properties? Still untyped:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">int</span> $id;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">string</span> $name;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#f92672">?</span><span style="color:#a6e22e">DateTimeInterface</span> $deletedAt;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>7.4 changes that. Typed properties enforce types at assignment, not just at call sites. Classes become self-documenting in a way that docblocks never quite managed, and the engine catches type errors before they propagate through half your stack.</p>
<p>One subtlety: typed properties are <code>uninitialized</code> by default (not <code>null</code>). Accessing an uninitialized property throws an <code>Error</code>. This trips people up: <code>?string</code> doesn&rsquo;t imply a default of <code>null</code>. You still need an explicit <code>= null</code> for that.</p>
<h2 id="arrow-functions">Arrow functions</h2>
<p>Closures in PHP have always required explicitly importing outer scope variables with <code>use</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$multiplier <span style="color:#f92672">=</span> <span style="color:#ae81ff">3</span>;
</span></span><span style="display:flex;"><span>$fn <span style="color:#f92672">=</span> <span style="color:#a6e22e">fn</span>($x) <span style="color:#f92672">=&gt;</span> $x <span style="color:#f92672">*</span> $multiplier; <span style="color:#75715e">// no use() needed
</span></span></span></code></pre></div><p>Arrow functions capture the enclosing scope automatically. Single expression, implicit return, no boilerplate. They don&rsquo;t replace full closures for complex logic, but for short callbacks they eliminate a class of noise that had been accumulating for years.</p>
<h2 id="opcache-preloading">Opcache preloading</h2>
<p>For long-lived PHP-FPM setups, preloading allows a script to load and compile PHP files into opcache memory at server startup. Those files are available to all requests without compilation overhead.</p>
<p>The gain varies by application. On large frameworks where the same files are loaded on every request, it&rsquo;s real. On smaller apps, negligible. Worth benchmarking before adding the configuration complexity.</p>
<h2 id="the-small-ones-that-add-up">The small ones that add up</h2>
<p>The features mentioned in passing deserve more than a line. The null coalescing assignment operator <code>??=</code> solves a pattern that was annoying enough to write every single time but never annoying enough to bother abstracting:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$config[<span style="color:#e6db74">&#39;timeout&#39;</span>] <span style="color:#f92672">??=</span> <span style="color:#ae81ff">30</span>;
</span></span><span style="display:flex;"><span><span style="color:#75715e">// equivalent to: $config[&#39;timeout&#39;] = $config[&#39;timeout&#39;] ?? 30;
</span></span></span></code></pre></div><p>Spread operator in array literals does what you&rsquo;d expect from the function call version — unpack an iterable into an array literal:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$defaults <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#39;color&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;blue&#39;</span>, <span style="color:#e6db74">&#39;size&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;M&#39;</span>];
</span></span><span style="display:flex;"><span>$options <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#39;size&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;L&#39;</span>, <span style="color:#f92672">...</span>$defaults, <span style="color:#e6db74">&#39;weight&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#ae81ff">1.2</span>];
</span></span><span style="display:flex;"><span><span style="color:#75715e">// [&#39;size&#39; =&gt; &#39;M&#39;, &#39;color&#39; =&gt; &#39;blue&#39;, &#39;weight&#39; =&gt; 1.2]
</span></span></span></code></pre></div><p>Note: string keys weren&rsquo;t supported in 7.4 for array unpacking. That came later.</p>
<p>Covariant return types and contravariant parameter types close a gap that made some inheritance patterns needlessly awkward. A child class can now narrow its return type to a subtype of the parent&rsquo;s, without hitting a fatal error:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Producer</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">get</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Iterator</span> {}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ChildProducer</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Producer</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">get</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">ArrayIterator</span> {} <span style="color:#75715e">// ArrayIterator implements Iterator
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="reading-numbers-at-3am">Reading numbers at 3am</h2>
<p>The numeric literal separator is one of those features you don&rsquo;t know you wanted until the first time you write a large constant and immediately lose track of the magnitude:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$earthMass    <span style="color:#f92672">=</span> <span style="color:#ae81ff">5_972_168_000_000_000_000_000_000</span>; <span style="color:#75715e">// kg
</span></span></span><span style="display:flex;"><span>$lightSpeed   <span style="color:#f92672">=</span> <span style="color:#ae81ff">299_792_458</span>;                        <span style="color:#75715e">// m/s
</span></span></span><span style="display:flex;"><span>$planck       <span style="color:#f92672">=</span> <span style="color:#ae81ff">6.626</span><span style="color:#a6e22e">_070_15e</span><span style="color:#f92672">-</span><span style="color:#ae81ff">34</span>;                  <span style="color:#75715e">// J·s
</span></span></span><span style="display:flex;"><span>$hexMask      <span style="color:#f92672">=</span> <span style="color:#ae81ff">0xFF_EC_D5_08</span>;
</span></span><span style="display:flex;"><span>$binaryFlags  <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span><span style="color:#a6e22e">b0001_1111_0010_0000</span>;
</span></span></code></pre></div><p>The underscore is purely syntactic. The engine strips it before parsing the value. You can put it anywhere between digits, though convention follows the natural grouping of the number system you&rsquo;re working in.</p>
<h2 id="holding-without-owning">Holding without owning</h2>
<p><code>WeakReference</code> lets you hold a reference to an object without preventing the garbage collector from destroying it. The use case is caches and registries: you want to know an object is alive, but you don&rsquo;t want to be the reason it stays alive:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$object <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">HeavyObject</span>();
</span></span><span style="display:flex;"><span>$ref <span style="color:#f92672">=</span> <span style="color:#a6e22e">WeakReference</span><span style="color:#f92672">::</span><span style="color:#a6e22e">create</span>($object);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>($ref<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">get</span>()); <span style="color:#75715e">// object(HeavyObject)
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">unset</span>($object);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>($ref<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">get</span>()); <span style="color:#75715e">// NULL — GC collected it
</span></span></span></code></pre></div><p>Before 7.4 you had <code>WeakRef</code> via an extension, and some frameworks were doing <code>SplObjectStorage</code> tricks that didn&rsquo;t quite behave the same way. The native class is just straightforward.</p>
<h2 id="serialization-without-surprise">Serialization without surprise</h2>
<p>Custom object serialization before 7.4 went through the <code>Serializable</code> interface: implement <code>serialize()</code> and <code>unserialize()</code>, return a string, reconstruct from it. The problem is that <code>serialize()</code> triggered <code>__sleep()</code>, <code>unserialize()</code> triggered <code>__wakeup()</code>, and the interaction between these hooks was fragile, especially in inheritance hierarchies.</p>
<p>7.4 introduces <code>__serialize()</code> and <code>__unserialize()</code>, which work with arrays instead of strings and don&rsquo;t interact with the old hooks:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Session</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">string</span> $token;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">\DateTime</span> $createdAt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__serialize</span>()<span style="color:#f92672">:</span> <span style="color:#66d9ef">array</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> [<span style="color:#e6db74">&#39;token&#39;</span> <span style="color:#f92672">=&gt;</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">token</span>, <span style="color:#e6db74">&#39;created&#39;</span> <span style="color:#f92672">=&gt;</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">createdAt</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getTimestamp</span>()];
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__unserialize</span>(<span style="color:#66d9ef">array</span> $data)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">token</span> <span style="color:#f92672">=</span> $data[<span style="color:#e6db74">&#39;token&#39;</span>];
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">createdAt</span> <span style="color:#f92672">=</span> (<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\DateTime</span>())<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">setTimestamp</span>($data[<span style="color:#e6db74">&#39;created&#39;</span>]);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>When both the new and old methods exist on the same class, <code>__serialize()</code> wins. The old <code>Serializable</code> interface gets deprecated in 8.1.</p>
<h2 id="what-the-standard-library-quietly-got">What the standard library quietly got</h2>
<p><code>mb_str_split()</code> does what <code>str_split()</code> does but correctly for multibyte strings. The gap was genuinely embarrassing for a language used in as many locales as PHP:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">mb_str_split</span>(<span style="color:#e6db74">&#39;héllo&#39;</span>, <span style="color:#ae81ff">1</span>); <span style="color:#75715e">// [&#39;h&#39;, &#39;é&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;]
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">str_split</span>(<span style="color:#e6db74">&#39;héllo&#39;</span>, <span style="color:#ae81ff">1</span>);    <span style="color:#75715e">// [&#39;h&#39;, &#39;Ã&#39;, &#39;©&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;] — broken
</span></span></span></code></pre></div><p><code>strip_tags()</code> now accepts an array of allowed tags, which is cleaner than the string format it used to require:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">strip_tags</span>($html, [<span style="color:#e6db74">&#39;p&#39;</span>, <span style="color:#e6db74">&#39;br&#39;</span>, <span style="color:#e6db74">&#39;strong&#39;</span>]); <span style="color:#75715e">// was: &#39;&lt;p&gt;&lt;br&gt;&lt;strong&gt;&#39;
</span></span></span></code></pre></div><p><code>proc_open()</code> now accepts an array command, bypassing shell interpretation entirely. Same idea as Python&rsquo;s <code>subprocess</code> with <code>shell=False</code>. Worth knowing whenever you&rsquo;re passing user-supplied arguments to an external process.</p>
<h2 id="the-ffi-chapter">The FFI chapter</h2>
<p>The Foreign Function Interface extension landed in 7.4 after spending time in a feature branch. It lets PHP call native C functions by loading a shared library and declaring the signatures:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$ffi <span style="color:#f92672">=</span> <span style="color:#a6e22e">FFI</span><span style="color:#f92672">::</span><span style="color:#a6e22e">cdef</span>(<span style="color:#e6db74">&#34;int strlen(const char *s);&#34;</span>, <span style="color:#e6db74">&#34;libc.so.6&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>($ffi<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">strlen</span>(<span style="color:#e6db74">&#34;hello&#34;</span>)); <span style="color:#75715e">// int(5)
</span></span></span></code></pre></div><p>The practical applications are narrow but real: calling platform APIs with no PHP binding, wrapping performance-critical C code without writing a full extension, or just poking at native libraries directly. Not a replacement for proper extensions in production, but it removes the &ldquo;write a C extension&rdquo; barrier for exploration.</p>
<h2 id="what-got-deprecated">What got deprecated</h2>
<p>A few things that should have been cleaned up a while ago finally got the deprecation treatment in 7.4.</p>
<p>Nested ternaries without parentheses have been ambiguous forever. PHP evaluated them left-to-right while pretty much every other language with a ternary evaluates right-to-left:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// Was ambiguous, now deprecated:
</span></span></span><span style="display:flex;"><span>$a <span style="color:#f92672">?</span> $b <span style="color:#f92672">:</span> $c <span style="color:#f92672">?</span> $d <span style="color:#f92672">:</span> $e;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Make it explicit:
</span></span></span><span style="display:flex;"><span>($a <span style="color:#f92672">?</span> $b <span style="color:#f92672">:</span> $c) <span style="color:#f92672">?</span> $d <span style="color:#f92672">:</span> $e;
</span></span></code></pre></div><p>Curly brace offset access for strings and arrays — <code>$str{0}</code> instead of <code>$str[0]</code> — is deprecated and gone in 8.0. It was always an alias, never a separate feature.</p>
<p><code>implode()</code> with reversed argument order (array first, glue second) is deprecated. The function has accepted both orders since the beginning, which was a mistake. The correct order is <code>implode(string $separator, array $array)</code>.</p>
<h2 id="what-comes-next">What comes next</h2>
<p>7.4 is the last 7.x release. The deprecations are mostly ground-clearing for removals in 8.0. The RFC backlog for 8.0 is substantial: JIT, attributes, named arguments, match expressions. 7.4 is a solid place to land while waiting for all that to arrive.</p>
]]></content:encoded></item><item><title>PHP 7.3: small wins that add up</title><link>https://guillaumedelre.github.io/2019/01/20/php-7.3-small-wins-that-add-up/</link><pubDate>Sun, 20 Jan 2019 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2019/01/20/php-7.3-small-wins-that-add-up/</guid><description>Part 4 of 11 in &amp;quot;PHP Releases&amp;quot;: PHP 7.3 brings flexible heredoc syntax, trailing commas in function calls, and quality-of-life fixes that add up quickly.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 7.3 shipped December 6th. No single killer feature. It&rsquo;s a collection of quality-of-life improvements that individually feel minor but together make daily work noticeably less annoying.</p>
<h2 id="flexible-heredoc-and-nowdoc">Flexible heredoc and nowdoc</h2>
<p>Until 7.3, the closing marker of a heredoc had to be at column zero. That forced awkward de-indentation in otherwise well-formatted code:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// before
</span></span></span><span style="display:flex;"><span>$html <span style="color:#f92672">=</span> <span style="color:#e6db74">&lt;&lt;&lt;</span><span style="color:#e6db74">HTML</span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    &lt;div&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        &lt;p&gt;Hello&lt;/p&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    &lt;/div&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">HTML; // had to be at column 0, ugly
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">// after
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">$html = &lt;&lt;&lt;HTML
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    &lt;div&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        &lt;p&gt;Hello&lt;/p&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    &lt;/div&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    </span><span style="color:#e6db74">HTML</span>;
</span></span></code></pre></div><p>The closing marker can now be indented to match the surrounding code, and that indentation is stripped from the content. This looks cosmetic. It&rsquo;s not. Heredocs in nested contexts (class methods, conditionals) were visually jarring before. Now they fit.</p>
<h2 id="1234-array_key_first-and-array_key_last">:1234: array_key_first() and array_key_last()</h2>
<p>This existed forever as a workaround:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$first <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_keys</span>($array)[<span style="color:#ae81ff">0</span>];
</span></span></code></pre></div><p>7.3 adds the obvious helpers:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$first <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_key_first</span>($array);
</span></span><span style="display:flex;"><span>$last  <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_key_last</span>($array);
</span></span></code></pre></div><p>And <code>is_countable()</code> to safely check before calling <code>count()</code> on something that might not implement <code>Countable</code>. Functions that should have existed years ago.</p>
<h2 id="pcre2">PCRE2</h2>
<p>The regular expression engine was migrated from PCRE to PCRE2. Mostly invisible for existing patterns, but PCRE2 is actively maintained and handles edge cases better. The main practical impact: some patterns that previously produced undefined behavior now throw errors. That&rsquo;s the correct behavior, even if it surprises you on the first upgrade.</p>
<h2 id="trailing-commas-in-function-calls">Trailing commas in function calls</h2>
<p>7.2 allowed trailing commas in grouped namespace imports. 7.3 extends this to function and method calls:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$result <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_merge</span>(
</span></span><span style="display:flex;"><span>    $defaults,
</span></span><span style="display:flex;"><span>    $overrides,
</span></span><span style="display:flex;"><span>    $extras, <span style="color:#75715e">// no more removing this comma before the closing paren
</span></span></span><span style="display:flex;"><span>);
</span></span></code></pre></div><p>This matters most with multiline calls. Adding or removing an argument no longer means touching the adjacent line. Diffs stay honest, rebases get a little less painful.</p>
<h2 id="reference-assignments-in-array-destructuring">Reference assignments in array destructuring</h2>
<p>Array destructuring gained the ability to capture references instead of copies:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$data <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#39;Alice&#39;</span>, <span style="color:#ae81ff">42</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[<span style="color:#f92672">&amp;</span>$name, $age] <span style="color:#f92672">=</span> $data;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Bob&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>($data[<span style="color:#ae81ff">0</span>]); <span style="color:#75715e">// string(3) &#34;Bob&#34;
</span></span></span></code></pre></div><p>Nested references work too:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>[$a, [<span style="color:#f92672">&amp;</span>$b]] <span style="color:#f92672">=</span> [<span style="color:#ae81ff">1</span>, [<span style="color:#ae81ff">2</span>]];
</span></span></code></pre></div><p>More niche than trailing commas, but the right tool when you need to alias deep into a structure without a pile of intermediate assignments.</p>
<h2 id="instanceof-with-literals-is-now-legal"><code>instanceof</code> with literals is now legal</h2>
<p>Previously, using <code>instanceof</code> with a literal on the left side was a parse error. 7.3 makes it valid:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">var_dump</span>(<span style="color:#66d9ef">null</span> <span style="color:#a6e22e">instanceof</span> <span style="color:#66d9ef">stdClass</span>); <span style="color:#75715e">// bool(false)
</span></span></span></code></pre></div><p>It always evaluates to <code>false</code>, which is exactly correct. The benefit is that code that conditionally constructs a value and then checks its type no longer needs to extract the value into a variable first. Useful in generated code and test helpers.</p>
<h2 id="json_decode-and-json_encode-can-throw-now"><code>json_decode()</code> and <code>json_encode()</code> can throw now</h2>
<p>Before 7.3, JSON errors were silent unless you remembered to check <code>json_last_error()</code>. Easy to forget, easy to get wrong:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$data <span style="color:#f92672">=</span> <span style="color:#a6e22e">json_decode</span>($response);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">json_last_error</span>() <span style="color:#f92672">!==</span> <span style="color:#a6e22e">JSON_ERROR_NONE</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// most people forgot this part
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>7.3 adds <code>JSON_THROW_ON_ERROR</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$data <span style="color:#f92672">=</span> <span style="color:#a6e22e">json_decode</span>($response, <span style="color:#66d9ef">true</span>, <span style="color:#ae81ff">512</span>, <span style="color:#a6e22e">JSON_THROW_ON_ERROR</span>);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// throws JsonException on malformed input
</span></span></span></code></pre></div><p><code>JsonException</code> extends <code>RuntimeException</code>. Catch it specifically or let it propagate. Should have worked this way from day one.</p>
<h2 id="setcookie-with-an-options-array"><code>setcookie()</code> with an options array</h2>
<p>The old <code>setcookie()</code> signature is a relic: seven positional arguments, most of which you leave as defaults just to reach the one you actually want. 7.3 adds an alternative form that takes an associative array:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">setcookie</span>(<span style="color:#e6db74">&#39;session&#39;</span>, $token, [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;expires&#39;</span>  <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">time</span>() <span style="color:#f92672">+</span> <span style="color:#ae81ff">3600</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;path&#39;</span>     <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;/&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;secure&#39;</span>   <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;httponly&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;samesite&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;Lax&#39;</span>,
</span></span><span style="display:flex;"><span>]);
</span></span></code></pre></div><p>The <code>samesite</code> option is the real reason this was added — the old positional signature had no slot for it. <code>session_set_cookie_params()</code> received the same treatment, and a new <code>session.cookie_samesite</code> ini directive covers the default.</p>
<h2 id="hrtime-for-benchmarking-that-actually-measures-time"><code>hrtime()</code> for benchmarking that actually measures time</h2>
<p><code>microtime()</code> reads wall clock time. Fine for most cases. But it&rsquo;s affected by NTP adjustments, and its resolution is implementation-dependent. <code>hrtime()</code> reads the monotonic high-resolution clock:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$start <span style="color:#f92672">=</span> <span style="color:#a6e22e">hrtime</span>(<span style="color:#66d9ef">true</span>);  <span style="color:#75715e">// nanoseconds as integer
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">doWork</span>();
</span></span><span style="display:flex;"><span>$elapsed <span style="color:#f92672">=</span> <span style="color:#a6e22e">hrtime</span>(<span style="color:#66d9ef">true</span>) <span style="color:#f92672">-</span> $start;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $elapsed <span style="color:#f92672">/</span> <span style="color:#ae81ff">1e6</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#34; ms</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>;
</span></span></code></pre></div><p>Without the <code>true</code> argument it returns <code>[seconds, nanoseconds]</code> as a two-element array. Use this for microbenchmarks, or anywhere clock drift would silently corrupt your measurement.</p>
<h2 id="gc_status--looking-inside-the-garbage-collector"><code>gc_status()</code> — looking inside the garbage collector</h2>
<p>PHP&rsquo;s cyclic garbage collector runs when a buffer of potential cycles fills up. Until 7.3 you had no easy way to see what it was actually doing. <code>gc_status()</code> exposes the internal state:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$status <span style="color:#f92672">=</span> <span style="color:#a6e22e">gc_status</span>();
</span></span><span style="display:flex;"><span><span style="color:#75715e">// [
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//   &#39;runs&#39;       =&gt; 3,
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//   &#39;collected&#39;  =&gt; 127,
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//   &#39;threshold&#39;  =&gt; 10001,
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//   &#39;roots&#39;      =&gt; 42,
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// ]
</span></span></span></code></pre></div><p>Not something most app code needs. Useful when you&rsquo;re trying to figure out why memory keeps climbing under specific workloads.</p>
<h2 id="compileerror-joins-the-exception-hierarchy"><code>CompileError</code> joins the exception hierarchy</h2>
<p>Parse errors have been catchable as <code>ParseError</code> since PHP 7.0. 7.3 introduces <code>CompileError</code> as the parent class for compile-time failures, with <code>ParseError</code> becoming a subclass of it:</p>
<pre tabindex="0"><code>Error
└── CompileError
    └── ParseError
</code></pre><p>In practice, code that catches <code>ParseError</code> still works. The new class just gives future compile-time errors (that aren&rsquo;t parse errors) a proper home in the hierarchy.</p>
<h2 id="bcscale-as-a-getter"><code>bcscale()</code> as a getter</h2>
<p>The BC Math scale was always settable via <code>bcscale($n)</code>. Getting the current scale required tracking it yourself. 7.3 makes <code>bcscale()</code> work without arguments:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">bcscale</span>(<span style="color:#ae81ff">4</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#a6e22e">bcscale</span>(); <span style="color:#75715e">// 4
</span></span></span></code></pre></div><p>Minor. Worth knowing if you&rsquo;re writing library code that needs to respect or restore whoever called it&rsquo;s scale setting.</p>
<h2 id="the-continue-inside-switch-warning">The <code>continue</code> inside <code>switch</code> warning</h2>
<p>This one is a correctness fix that looks like a deprecation. In PHP, <code>continue</code> inside a <code>switch</code> has always behaved like <code>break</code> — it exits the switch, not the enclosing loop. Developers coming from other languages often write this expecting to skip to the next loop iteration:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span> ($items <span style="color:#66d9ef">as</span> $item) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">switch</span> ($item<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">type</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#e6db74">&#39;skip&#39;</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">continue</span>; <span style="color:#75715e">// WRONG: exits the switch, not the foreach
</span></span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>7.3 adds a warning for this pattern. The fix is <code>continue 2</code> to explicitly target the enclosing loop. The behavior hasn&rsquo;t changed. The silence has.</p>
<h2 id="deprecations">Deprecations</h2>
<p>Case-insensitive constants declared via <code>define()</code> are deprecated:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">define</span>(<span style="color:#e6db74">&#39;MY_CONST&#39;</span>, <span style="color:#ae81ff">42</span>, <span style="color:#66d9ef">true</span>); <span style="color:#75715e">// third argument deprecated
</span></span></span></code></pre></div><p>Passing a non-string needle to <code>strpos()</code>, <code>strstr()</code>, and related functions is deprecated. In PHP 8 these will interpret the needle as a string, not an ASCII codepoint. If you&rsquo;re passing integers to these functions intentionally, <code>chr($n)</code> is the explicit form.</p>
<p><code>fgetss()</code> is deprecated — it was <code>fgets()</code> with HTML/PHP tags stripped. Use <code>fgets()</code> and strip tags explicitly if you need them stripped. The <code>string.strip_tags</code> stream filter goes with it.</p>
<p>7.3 is the kind of release you appreciate in hindsight. Nothing individually dramatic, but after six months with it the heredoc fix alone has paid back the upgrade cost in readability. Sometimes boring is exactly right.</p>
]]></content:encoded></item><item><title>PHP 7.2: goodbye mcrypt, hello sodium</title><link>https://guillaumedelre.github.io/2018/01/14/php-7.2-goodbye-mcrypt-hello-sodium/</link><pubDate>Sun, 14 Jan 2018 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2018/01/14/php-7.2-goodbye-mcrypt-hello-sodium/</guid><description>Part 3 of 11 in &amp;quot;PHP Releases&amp;quot;: PHP 7.2 removes the unmaintained mcrypt extension and bundles libsodium — finally modernizing PHP&amp;#39;s cryptography story.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 7.2 released November 30th. The headline isn&rsquo;t a language feature, it&rsquo;s a removal. <code>mcrypt</code> is gone.</p>
<p>This is good news, even if it doesn&rsquo;t feel that way when you&rsquo;re the one migrating.</p>
<h2 id="the-mcrypt-problem">The mcrypt problem</h2>
<p><code>mcrypt</code> has been unmaintained since 2007. More than a decade of stagnation in a cryptography library. It was deprecated in 7.1, and 7.2 removes it entirely. The replacement is <code>sodium</code>, now bundled as a core extension.</p>
<p>Sodium is the PHP binding for <a href="https://libsodium.org" target="_blank" rel="noopener noreferrer">libsodium</a>, a modern cryptographic library built around safe defaults. Where mcrypt required you to pick the right cipher, mode, and padding (and get it wrong silently), sodium&rsquo;s API makes dangerous choices structurally hard. <code>sodium_crypto_secretbox()</code> for symmetric encryption, <code>sodium_crypto_box()</code> for asymmetric, <code>sodium_crypto_sign()</code> for signatures. The names tell you what you&rsquo;re doing.</p>
<p>If you have mcrypt code in production, the migration is unavoidable. Worth doing carefully too: cryptography code that &ldquo;still works&rdquo; can be silently broken in ways you won&rsquo;t notice until it&rsquo;s too late.</p>
<h2 id="the-object-type-hint">The object type hint</h2>
<p>7.2 adds <code>object</code> as a parameter and return type:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">serialize</span>(<span style="color:#a6e22e">object</span> $data)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// accepts any object
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>It&rsquo;s broad — any object satisfies it — but it&rsquo;s better than no type at all when you genuinely don&rsquo;t care about the specific class. Complements the existing <code>array</code>, <code>callable</code>, and class-specific hints.</p>
<h2 id="argon2-in-password_hash">Argon2 in password_hash</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$hash <span style="color:#f92672">=</span> <span style="color:#a6e22e">password_hash</span>($password, <span style="color:#a6e22e">PASSWORD_ARGON2I</span>);
</span></span></code></pre></div><p><code>PASSWORD_BCRYPT</code> was the default and still reasonable, but Argon2 won the Password Hashing Competition for a reason: it&rsquo;s memory-hard, which makes GPU-based cracking significantly more expensive. Worth switching for new apps.</p>
<p>7.2 is more of a security release than a language one. Remove mcrypt, add sodium, and you&rsquo;ve moved the platform to a place where you can actually trust it with sensitive data. The language features are incremental. The infrastructure shift is not.</p>
<h2 id="parameter-types-you-can-now-drop-on-purpose">Parameter types you can now drop on purpose</h2>
<p>7.2 formalizes something that was previously just a smell: when you implement an interface or override a method, you can now omit the parameter type entirely. This is valid contravariance under the Liskov substitution principle.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">Serializable</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">serialize</span>(<span style="color:#66d9ef">array</span> $data)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">JsonSerializer</span> <span style="color:#66d9ef">implements</span> <span style="color:#a6e22e">Serializable</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">serialize</span>($data)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> { <span style="color:#75715e">// no type — accepts more, still valid
</span></span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">json_encode</span>($data);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>It reads oddly at first. But it&rsquo;s logically sound: a method that accepts anything is strictly more permissive than one that only accepts arrays. The type system agrees, even if your code reviewer raises an eyebrow.</p>
<h2 id="abstract-methods-that-evolve">Abstract methods that evolve</h2>
<p>When an abstract class extends another abstract class, it can now override the abstract method with a different signature. The constraint is directional: parameters can be widened (contravariant), return types can be narrowed (covariant).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">BaseProcessor</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">process</span>(<span style="color:#a6e22e">string</span> $input);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TypedProcessor</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">BaseProcessor</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">abstract</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">process</span>($input)<span style="color:#f92672">:</span> <span style="color:#a6e22e">int</span>; <span style="color:#75715e">// parameter widened, return type added
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This was rejected before 7.2. It unlocks intermediate abstractions without forcing every leaf class to repeat the same signature.</p>
<h2 id="trailing-commas-in-grouped-use-statements">Trailing commas in grouped use statements</h2>
<p>Small, but I notice its absence when it&rsquo;s missing. Grouped namespace imports can now have a trailing comma on the last item:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">use</span> <span style="color:#a6e22e">App\Services\</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">UserService</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">OrderService</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">NotificationService</span>, <span style="color:#75715e">// comma here — finally
</span></span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>This means you can reorder or add lines without touching the previous last entry. Git diffs get cleaner, merge conflicts get rarer.</p>
<h2 id="count-grew-a-conscience"><code>count()</code> grew a conscience</h2>
<p>Before 7.2, <code>count(null)</code> returned 0. Silently. No warning. That&rsquo;s exactly the kind of thing that buries a bug for months. Now it emits <code>E_WARNING</code> when you pass something that isn&rsquo;t an array or a <code>Countable</code> object.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">count</span>(<span style="color:#66d9ef">null</span>);  <span style="color:#75715e">// Warning: count(): Parameter must be an array or an object that implements Countable
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">count</span>(<span style="color:#ae81ff">42</span>);    <span style="color:#75715e">// same
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">count</span>(<span style="color:#e6db74">&#34;hi&#34;</span>);  <span style="color:#75715e">// same
</span></span></span></code></pre></div><p>The behavior didn&rsquo;t change for valid inputs. Only the silence was broken. That&rsquo;s the correct direction.</p>
<h2 id="spl_object_id--the-thing-you-were-emulating-with-splobjectstorage"><code>spl_object_id()</code> — the thing you were emulating with SplObjectStorage</h2>
<p>If you&rsquo;ve ever built a map keyed on object identity, you&rsquo;ve written something like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$storage <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">SplObjectStorage</span>();
</span></span><span style="display:flex;"><span>$storage[$obj] <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span></code></pre></div><p>7.2 adds <code>spl_object_id()</code>, which returns a unique integer for the lifetime of an object. It&rsquo;s the same internal handle <code>SplObjectStorage</code> uses, made directly accessible:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$id <span style="color:#f92672">=</span> <span style="color:#a6e22e">spl_object_id</span>($obj); <span style="color:#75715e">// e.g. 42
</span></span></span><span style="display:flex;"><span>$map[$id] <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;something&#39;</span>;
</span></span></code></pre></div><p>The integer is reused after the object is destroyed, so don&rsquo;t hold onto it past the object&rsquo;s lifetime. Within a well-scoped context though, it&rsquo;s a cheap identity key.</p>
<h2 id="pdo-national-character-strings">PDO: national character strings</h2>
<p>When working with databases that distinguish between regular and national character string types (Oracle, SQL Server), 7.2 adds the flags you needed:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$stmt <span style="color:#f92672">=</span> $pdo<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">prepare</span>(<span style="color:#e6db74">&#34;SELECT * FROM users WHERE name = ?&#34;</span>);
</span></span><span style="display:flex;"><span>$stmt<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">bindValue</span>(<span style="color:#ae81ff">1</span>, <span style="color:#e6db74">&#39;Ångström&#39;</span>, <span style="color:#a6e22e">PDO</span><span style="color:#f92672">::</span><span style="color:#a6e22e">PARAM_STR</span> <span style="color:#f92672">|</span> <span style="color:#a6e22e">PDO</span><span style="color:#f92672">::</span><span style="color:#a6e22e">PARAM_STR_NATL</span>);
</span></span></code></pre></div><p>Or set a connection-level default:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$pdo<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">setAttribute</span>(<span style="color:#a6e22e">PDO</span><span style="color:#f92672">::</span><span style="color:#a6e22e">ATTR_DEFAULT_STR_PARAM</span>, <span style="color:#a6e22e">PDO</span><span style="color:#f92672">::</span><span style="color:#a6e22e">PARAM_STR_NATL</span>);
</span></span></code></pre></div><p><code>PDO::PARAM_STR_NATL</code> signals that the string is a national character type (NCHAR/NVARCHAR). Obscure, yes. Essential if you&rsquo;ve ever watched your Unicode data come out mangled because the driver had no idea about the difference.</p>
<h2 id="gd-got-bmp-support-and-clipping-rectangles">GD got BMP support and clipping rectangles</h2>
<p>Two things worth knowing. First, BMP files are now first-class citizens in the GD extension:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$image <span style="color:#f92672">=</span> <span style="color:#a6e22e">imagecreatefrombmp</span>(<span style="color:#e6db74">&#39;photo.bmp&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">imagebmp</span>($image, <span style="color:#e6db74">&#39;output.bmp&#39;</span>);
</span></span></code></pre></div><p>Second, you can now define a clipping rectangle so that drawing operations only affect a portion of the image:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">imagesetclip</span>($image, <span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">150</span>); <span style="color:#75715e">// x1, y1, x2, y2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// everything drawn outside this rectangle is silently ignored
</span></span></span></code></pre></div><p>Neither feature reshapes how most apps work, but both replace &ldquo;install an extra library&rdquo; with &ldquo;it&rsquo;s just in core now.&rdquo;</p>
<h2 id="mb_chr-and-mb_ord--unicodes-chr-and-ord"><code>mb_chr()</code> and <code>mb_ord()</code> — Unicode&rsquo;s <code>chr()</code> and <code>ord()</code></h2>
<p>PHP has had <code>chr()</code> and <code>ord()</code> forever. They work on bytes. For Unicode code points, you were on your own. 7.2 adds the multibyte equivalents:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$char <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_chr</span>(<span style="color:#ae81ff">0x1F600</span>); <span style="color:#75715e">// returns the 😀 emoji
</span></span></span><span style="display:flex;"><span>$code <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_ord</span>(<span style="color:#e6db74">&#39;é&#39;</span>);     <span style="color:#75715e">// returns 233
</span></span></span></code></pre></div><p>And <code>mb_scrub()</code>, which strips invalid byte sequences from a string rather than failing silently or throwing:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$clean <span style="color:#f92672">=</span> <span style="color:#a6e22e">mb_scrub</span>($untrustedInput, <span style="color:#e6db74">&#39;UTF-8&#39;</span>);
</span></span></code></pre></div><p>Handy at any external boundary: API responses, file uploads, database reads from legacy systems.</p>
<h2 id="deprecations-worth-knowing-before-74-arrives">Deprecations worth knowing before 7.4 arrives</h2>
<p>Several things were soft-deprecated in 7.2 that will become errors in later versions. The ones most likely to bite:</p>
<p><code>__autoload()</code> is deprecated. If you&rsquo;re still registering a global autoload function instead of using <code>spl_autoload_register()</code>, fix it before it becomes a fatal.</p>
<p><code>create_function()</code> is deprecated. It&rsquo;s a wrapper around <code>eval()</code> and was always dangerous. Use a closure:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// before
</span></span></span><span style="display:flex;"><span>$fn <span style="color:#f92672">=</span> <span style="color:#a6e22e">create_function</span>(<span style="color:#e6db74">&#39;$x&#39;</span>, <span style="color:#e6db74">&#39;return $x * 2;&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// after
</span></span></span><span style="display:flex;"><span>$fn <span style="color:#f92672">=</span> <span style="color:#a6e22e">fn</span>($x) <span style="color:#f92672">=&gt;</span> $x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>;
</span></span></code></pre></div><p><code>each()</code> is deprecated. The loop pattern it enabled is better written as <code>foreach</code>. There&rsquo;s no loss here.</p>
<p><code>parse_str()</code> without a second argument dumps parsed values into the local symbol table — a security issue that should never have been allowed. Always pass the output variable:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">parse_str</span>($queryString, $params); <span style="color:#75715e">// correct
</span></span></span></code></pre></div><p>The <code>(unset)</code> cast is deprecated because it always returns <code>null</code>, which you can just write as <code>null</code>. Completely pointless syntax that should never have existed.</p>
]]></content:encoded></item><item><title>PHP 7.1: a tighter type system and the small wins around it</title><link>https://guillaumedelre.github.io/2017/01/15/php-7.1-a-tighter-type-system-and-the-small-wins-around-it/</link><pubDate>Sun, 15 Jan 2017 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2017/01/15/php-7.1-a-tighter-type-system-and-the-small-wins-around-it/</guid><description>Part 2 of 11 in &amp;quot;PHP Releases&amp;quot;: PHP 7.1 filled the gaps left by 7.0: nullable types, void return, and destructuring — small additions that made the type system usable.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 7.1 shipped December 1st. No 2x performance headline, no engine rewrite. It fills in the gaps that 7.0 left in the type system, and those gaps were genuinely annoying.</p>
<h2 id="nullable-types">Nullable types</h2>
<p>7.0 let you declare <code>string $name</code> as a parameter type. What it didn&rsquo;t let you do was say &ldquo;this can also be null&rdquo;. You had to drop the type hint entirely or hack around it. 7.1 adds <code>?</code> prefix:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">findUser</span>(<span style="color:#f92672">?</span><span style="color:#a6e22e">int</span> $id)<span style="color:#f92672">:</span> <span style="color:#f92672">?</span><span style="color:#a6e22e">User</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> ($id <span style="color:#f92672">===</span> <span style="color:#66d9ef">null</span>) <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">null</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">repository</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">find</span>($id);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This sounds minor. It&rsquo;s not. Nullable types make the difference between a signature that tells you what a function does and one that lies by omission. Every codebase I&rsquo;ve worked on has functions that can return null. Now you can actually say so instead of hiding it in a docblock.</p>
<h2 id="void-return-type">Void return type</h2>
<p>The complement to nullable: a function that intentionally returns nothing:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">process</span>(<span style="color:#a6e22e">Order</span> $order)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">dispatcher</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">dispatch</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">OrderProcessed</span>($order));
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>void</code> makes the intent explicit and prevents accidentally returning a value from a function that shouldn&rsquo;t. Combined with nullable types, PHP&rsquo;s type system in 7.1 is quite a bit more expressive than 7.0.</p>
<h2 id="class-constant-visibility">Class constant visibility</h2>
<p>A small but welcome fix. Constants in classes were always public before 7.1. Now:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Config</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">DB_PASSWORD</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;secret&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">protected</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">VERSION</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;2.0&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">MAX_RETRIES</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">3</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Keeping implementation details private matters. This should have existed from the start.</p>
<h2 id="catching-multiple-exceptions">Catching multiple exceptions</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">InvalidArgumentException</span> <span style="color:#f92672">|</span> <span style="color:#a6e22e">RuntimeException</span> $e) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// handle both
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Saves a duplicate catch block when two exceptions need identical handling. Simple, useful.</p>
<h2 id="destructuring-arrays-without-list">Destructuring arrays without list()</h2>
<p><code>list()</code> has been in PHP since 4.0 and always felt slightly out of place syntactically. 7.1 adds a shorthand using <code>[]</code> that reads much more naturally:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>[$first, $second] <span style="color:#f92672">=</span> $coordinates;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span> ($rows <span style="color:#66d9ef">as</span> [$id, $name, $email]) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>It also gains key support, which makes destructuring associative arrays finally usable:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>[<span style="color:#e6db74">&#39;id&#39;</span> <span style="color:#f92672">=&gt;</span> $id, <span style="color:#e6db74">&#39;name&#39;</span> <span style="color:#f92672">=&gt;</span> $name] <span style="color:#f92672">=</span> $user;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span> ($records <span style="color:#66d9ef">as</span> [<span style="color:#e6db74">&#39;id&#39;</span> <span style="color:#f92672">=&gt;</span> $id, <span style="color:#e6db74">&#39;status&#39;</span> <span style="color:#f92672">=&gt;</span> $status]) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Before this, extracting named keys from an array meant either <code>extract()</code> (which dumps everything into scope and invites collisions) or a bunch of individual assignments. This is just cleaner.</p>
<h2 id="the-iterable-type">The iterable type</h2>
<p>If you write a function that accepts either an array or a generator, there was no clean type hint for that in 7.0. You either typed it as <code>array</code> and silently excluded generators, or dropped the hint entirely:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">processItems</span>(<span style="color:#a6e22e">iterable</span> $items)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">foreach</span> ($items <span style="color:#66d9ef">as</span> $item) {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span>($item);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>iterable</code> accepts anything you can <code>foreach</code> over: arrays and <code>Traversable</code> implementations. It also works as a return type. Not dramatic, but it closes a real gap.</p>
<h2 id="negative-string-offsets">Negative string offsets</h2>
<p>String indexing with <code>[]</code> or <code>{}</code> now accepts negative values, counting from the end:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$str <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;hello&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $str[<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>]; <span style="color:#75715e">// &#34;o&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $str[<span style="color:#f92672">-</span><span style="color:#ae81ff">2</span>]; <span style="color:#75715e">// &#34;l&#34;
</span></span></span></code></pre></div><p>Several string functions got the same treatment: <code>strpos()</code>, <code>substr()</code>, <code>substr_count()</code>, and others now accept a negative offset. Consistent with how Python has worked forever. Better late than never.</p>
<h2 id="closurefromcallable">Closure::fromCallable()</h2>
<p>Before this, converting a callable (like <code>[$object, 'method']</code> or a function name string) to a proper <code>Closure</code> required <code>Closure::bind()</code> or <code>bindTo()</code> with awkward scope handling. 7.1 adds a static factory method:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Processor</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">transform</span>(<span style="color:#a6e22e">string</span> $value)<span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">strtoupper</span>($value);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getTransformer</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Closure</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">Closure</span><span style="color:#f92672">::</span><span style="color:#a6e22e">fromCallable</span>([$this, <span style="color:#e6db74">&#39;transform&#39;</span>]);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The resulting closure captures the correct <code>$this</code> and scope. It&rsquo;s particularly useful when passing methods as callbacks to functions that expect <code>callable</code>, or when building pipelines.</p>
<h2 id="argumentcounterror">ArgumentCountError</h2>
<p>In PHP 7.0, calling a user-defined function with too few arguments generated a warning and execution continued with <code>null</code>-filled parameters. In 7.1, it throws an <code>ArgumentCountError</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">connect</span>(<span style="color:#a6e22e">string</span> $host, <span style="color:#a6e22e">int</span> $port)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> { <span style="color:#75715e">/* ... */</span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">connect</span>(<span style="color:#e6db74">&#39;localhost&#39;</span>); <span style="color:#75715e">// Throws ArgumentCountError
</span></span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">\ArgumentCountError</span> $e) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>ArgumentCountError</code> extends <code>TypeError</code>, which extends <code>Error</code>. Call sites that previously silently degraded now fail loudly. That&rsquo;s a migration risk if you have code that relied on the permissive behavior, but honestly, it&rsquo;s the right call.</p>
<p>7.1 is the kind of release that makes you trust a platform more. The core team was clearly paying attention to the friction, not just shipping headlines.</p>
]]></content:encoded></item><item><title>PHP 7.0: performance, types, and the features that stuck</title><link>https://guillaumedelre.github.io/2016/01/17/php-7.0-performance-types-and-the-features-that-stuck/</link><pubDate>Sun, 17 Jan 2016 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2016/01/17/php-7.0-performance-types-and-the-features-that-stuck/</guid><description>Part 1 of 11 in &amp;quot;PHP Releases&amp;quot;: PHP 7.0 doubled performance with a Zend Engine rewrite and finally brought scalar type hints to the language.</description><category>php-releases</category><content:encoded><![CDATA[<p>PHP 7.0 dropped on December 3rd. A month and a half in, I&rsquo;ve migrated two projects and the results are hard to ignore.</p>
<p>The headline number is 2x faster than PHP 5.6. That&rsquo;s not a benchmark cherry-pick — it&rsquo;s the median across real applications. The Zend Engine was rewritten to use a new internal value representation that cuts memory usage significantly and reduces allocations. On one project, average response time dropped by 40% with zero code changes. You just upgrade and it goes faster.</p>
<p>But performance isn&rsquo;t the most interesting part.</p>
<h2 id="types-finally">Types, finally</h2>
<p>PHP has had type hints for objects since 5.0, for arrays since 5.1. In 7.0, you can finally declare scalar types for function parameters and return values:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">add</span>(<span style="color:#a6e22e">int</span> $a, <span style="color:#a6e22e">int</span> $b)<span style="color:#f92672">:</span> <span style="color:#a6e22e">int</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> $a <span style="color:#f92672">+</span> $b;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>In strict mode (<code>declare(strict_types=1)</code>), passing a float to that function throws a <code>TypeError</code>. In the default coercive mode, PHP converts the value. That distinction matters: strict mode is per-file, so you can adopt it gradually without nuking your whole codebase at once.</p>
<p>Return type declarations are the other half. Putting intent in the signature rather than a docblock means the engine enforces it, not a code reviewer who might be half-asleep.</p>
<h2 id="the-null-coalescing-operator">The null coalescing operator</h2>
<p><code>??</code> is small but used constantly:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$username <span style="color:#f92672">=</span> $_GET[<span style="color:#e6db74">&#39;user&#39;</span>] <span style="color:#f92672">??</span> <span style="color:#e6db74">&#39;guest&#39;</span>;
</span></span></code></pre></div><p>That replaces <code>isset($_GET['user']) ? $_GET['user'] : 'guest'</code>. It chains too: <code>$a ?? $b ?? $c</code>. After years of <code>isset()</code> noise, this alone was worth upgrading.</p>
<h2 id="the-breaking-part">The breaking part</h2>
<p>The error handling overhaul is the real upgrade risk. Many fatal errors are now <code>Error</code> exceptions, catchable but different from <code>Exception</code>. Code that relied on fatal errors to halt execution silently now needs explicit handling. Legacy error suppression with <code>@</code> also works differently in places.</p>
<p>Read the migration guide before touching a production app. The payoff is real, but the gap between 5.6 and 7.0 is the widest PHP has ever had.</p>
<h2 id="the-spaceship-operator">The spaceship operator</h2>
<p><code>&lt;=&gt;</code> is a combined comparison operator that returns -1, 0, or 1. It&rsquo;s mostly there for sorting:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">usort</span>($users, <span style="color:#66d9ef">function</span> ($a, $b) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> $a<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">age</span> <span style="color:#f92672">&lt;=&gt;</span> $b<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">age</span>;
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Before this, a custom sort comparator was a small exercise in remembered arithmetic. <code>$a - $b</code> works for integers but silently breaks for floats. <code>&lt;=&gt;</code> does the right thing for every comparable type.</p>
<h2 id="anonymous-classes">Anonymous classes</h2>
<p>You can now instantiate a class defined inline, on the spot, without giving it a name:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$logger <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">class</span>($config) <span style="color:#66d9ef">implements</span> <span style="color:#a6e22e">LoggerInterface</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(<span style="color:#66d9ef">private</span> <span style="color:#66d9ef">array</span> $config) {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">string</span> $message)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">file_put_contents</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">config</span>[<span style="color:#e6db74">&#39;path&#39;</span>], $message <span style="color:#f92672">.</span> <span style="color:#a6e22e">PHP_EOL</span>, <span style="color:#a6e22e">FILE_APPEND</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>The canonical use case is test doubles and one-off interface implementations that don&rsquo;t deserve a file. It removes a real friction point: the gap between &ldquo;I need an object&rdquo; and &ldquo;I have to create a class file for a 10-line thing&rdquo;.</p>
<h2 id="cryptographically-secure-randomness">Cryptographically secure randomness</h2>
<p>PHP 5&rsquo;s <code>rand()</code> and <code>mt_rand()</code> were never meant for security. 7.0 adds two functions that are:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$token <span style="color:#f92672">=</span> <span style="color:#a6e22e">bin2hex</span>(<span style="color:#a6e22e">random_bytes</span>(<span style="color:#ae81ff">32</span>)); <span style="color:#75715e">// 64-character hex token
</span></span></span><span style="display:flex;"><span>$pin   <span style="color:#f92672">=</span> <span style="color:#a6e22e">random_int</span>(<span style="color:#ae81ff">100000</span>, <span style="color:#ae81ff">999999</span>);
</span></span></code></pre></div><p><code>random_bytes()</code> pulls from the OS CSPRNG. <code>random_int()</code> wraps that for integers. These replace every home-grown token generation scheme that was quietly doing it wrong, which is most of them.</p>
<h2 id="group-use-declarations">Group use declarations</h2>
<p>Before 7.0, importing five things from the same namespace meant five <code>use</code> statements. Now:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">use</span> <span style="color:#a6e22e">App\Model\</span>{<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">Order</span>, <span style="color:#a6e22e">Product</span>};
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">use</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">App\Helpers\</span>{<span style="color:#a6e22e">formatDate</span>, <span style="color:#a6e22e">slugify</span>};
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">use</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">App\Config\</span>{<span style="color:#a6e22e">MAX_RETRIES</span>, <span style="color:#a6e22e">TIMEOUT</span>};
</span></span></code></pre></div><p>Small ergonomic improvement, but it reduces the visual noise at the top of files with deep namespace hierarchies.</p>
<h2 id="generators-grew-up">Generators grew up</h2>
<p>Generators in 5.5 were interesting but incomplete. 7.0 adds two things. First, a generator can now have a return value, accessible after iteration ends:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">process</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Generator</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#e6db74">&#39;step 1&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#e6db74">&#39;step 2&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#39;done&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$gen <span style="color:#f92672">=</span> <span style="color:#a6e22e">process</span>();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span> ($gen <span style="color:#66d9ef">as</span> $step) { <span style="color:#75715e">/* ... */</span> }
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $gen<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getReturn</span>(); <span style="color:#75715e">// &#34;done&#34;
</span></span></span></code></pre></div><p>Second, <code>yield from</code> delegates to another generator or iterable, transparently passing values and return values through:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">inner</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Generator</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#ae81ff">1</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#ae81ff">2</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#39;inner done&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">outer</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">Generator</span> {
</span></span><span style="display:flex;"><span>    $result <span style="color:#f92672">=</span> <span style="color:#66d9ef">yield</span> <span style="color:#a6e22e">from</span> <span style="color:#a6e22e">inner</span>();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">echo</span> $result; <span style="color:#75715e">// &#34;inner done&#34;
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#ae81ff">3</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This makes composing generators practical without manually plumbing values between them.</p>
<h2 id="closurecall">Closure::call()</h2>
<p>A more direct way to bind a closure to an object and call it immediately:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Counter</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">int</span> $count <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$increment <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">int</span> $by)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> {
</span></span><span style="display:flex;"><span>    $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">count</span> <span style="color:#f92672">+=</span> $by;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$increment<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">call</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Counter</span>(), <span style="color:#ae81ff">5</span>);
</span></span></code></pre></div><p><code>bindTo()</code> existed before but required two steps. <code>call()</code> collapses them and is faster at runtime because it skips the intermediate closure creation.</p>
<h2 id="unicode-escape-syntax-in-strings">Unicode escape syntax in strings</h2>
<p>You can now embed Unicode characters directly in double-quoted strings or heredocs using a codepoint:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;\u{1F418}&#34;</span>; <span style="color:#75715e">// 🐘
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;\u{00E9}&#34;</span>;  <span style="color:#75715e">// é
</span></span></span></code></pre></div><p>Beats copy-pasting characters from a Unicode table into source files, which is what people were actually doing.</p>
<h2 id="safer-unserialize">Safer unserialize()</h2>
<p><code>unserialize()</code> has a long history of being a vector for object injection attacks. 7.0 adds an <code>allowed_classes</code> option:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$data <span style="color:#f92672">=</span> <span style="color:#a6e22e">unserialize</span>($input, [<span style="color:#e6db74">&#39;allowed_classes&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">false</span>]);
</span></span><span style="display:flex;"><span>$data <span style="color:#f92672">=</span> <span style="color:#a6e22e">unserialize</span>($input, [<span style="color:#e6db74">&#39;allowed_classes&#39;</span> <span style="color:#f92672">=&gt;</span> [<span style="color:#a6e22e">User</span><span style="color:#f92672">::</span><span style="color:#a6e22e">class</span>, <span style="color:#a6e22e">Order</span><span style="color:#f92672">::</span><span style="color:#a6e22e">class</span>]]);
</span></span></code></pre></div><p>Passing <code>false</code> prevents any object from being instantiated during deserialization. This is the default you want when deserializing untrusted input.</p>
<h2 id="1234-integer-division">:1234: Integer division</h2>
<p><code>intdiv()</code> is explicit integer division with no float intermediate:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$pages <span style="color:#f92672">=</span> <span style="color:#a6e22e">intdiv</span>(<span style="color:#a6e22e">count</span>($items), $perPage); <span style="color:#75715e">// int, no casting needed
</span></span></span></code></pre></div><p>Yes, you could cast the result of a division. <code>intdiv()</code> makes the intent clear and avoids the float precision edge cases that casting introduces for large numbers.</p>
<h2 id="constants-as-arrays">Constants as arrays</h2>
<p>Before 7.0, <code>define()</code> only accepted scalar values. Arrays worked with <code>const</code> at class or namespace scope but not with <code>define()</code>. Now they do:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">define</span>(<span style="color:#e6db74">&#39;HTTP_METHODS&#39;</span>, [<span style="color:#e6db74">&#39;GET&#39;</span>, <span style="color:#e6db74">&#39;POST&#39;</span>, <span style="color:#e6db74">&#39;PUT&#39;</span>, <span style="color:#e6db74">&#39;DELETE&#39;</span>, <span style="color:#e6db74">&#39;PATCH&#39;</span>]);
</span></span></code></pre></div><p>Useful for configuration that needs to be a constant but lives outside a class.</p>
<h2 id="assertions-with-teeth">Assertions with teeth</h2>
<p><code>assert()</code> got a proper redesign. In PHP 5, assertions were a runtime eval of strings. Now they can throw exceptions and be completely removed in production with zero overhead:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// In php.ini or at bootstrap:
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// assert.active = 1 (dev), 0 (prod)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// assert.exception = 1
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">assert</span>($user<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">isVerified</span>(), <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">\LogicException</span>(<span style="color:#e6db74">&#39;Unverified user reached checkout&#39;</span>));
</span></span></code></pre></div><p>When <code>assert.active = 0</code>, the expression is never evaluated. When it&rsquo;s on, a failing assertion throws the provided exception directly. This is finally a tool worth reaching for, without the embarrassment of admitting you used it.</p>
<h2 id="the-session_start-overhaul">The session_start() overhaul</h2>
<p><code>session_start()</code> now accepts an array of options that override <code>php.ini</code> directives for that call:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">session_start</span>([
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;cookie_lifetime&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#ae81ff">86400</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;cookie_secure&#39;</span>   <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;cookie_httponly&#39;</span>  <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;cookie_samesite&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;Lax&#39;</span>,
</span></span><span style="display:flex;"><span>]);
</span></span></code></pre></div><p>Before this, you either set options globally in <code>php.ini</code> or called <code>ini_set()</code> before <code>session_start()</code>. Neither was great when you needed different session configurations in different parts of an app.</p>
]]></content:encoded></item></channel></rss>