<?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-8.5 on MongrelDB</title><link>https://www.mongreldb.com/articles/tags/php-8.5/</link><description>Recent content in Php-8.5 on MongrelDB</description><image><title>MongrelDB</title><url>https://www.mongreldb.com/logo.png</url><link>https://www.mongreldb.com/logo.png</link></image><generator>Hugo</generator><language>en-US</language><lastBuildDate>Thu, 16 Jul 2026 09:00:00 -0500</lastBuildDate><atom:link href="https://www.mongreldb.com/articles/tags/php-8.5/index.xml" rel="self" type="application/rss+xml"/><item><title>PHP 8.4 Features We Use (and PHP 8.5 Features We Now Use)</title><link>https://www.mongreldb.com/articles/2026/07/php-8-4-features-we-use-and-php-8-5-features-we-look-forward-to/</link><pubDate>Thu, 16 Jul 2026 09:00:00 -0500</pubDate><guid>https://www.mongreldb.com/articles/2026/07/php-8-4-features-we-use-and-php-8-5-features-we-look-forward-to/</guid><description>PHP 8.4 is the floor of the pure PHP client, and PHP 8.5, released in November 2025, is where the language gives us enough to stop writing workarounds for cURL and readonly cloning.</description><content:encoded><![CDATA[<p>Every time a database client requires a C extension, a deployment story dies, because shared hosts disable <code>phpize</code>, CI images mismatch the ABI, and a developer somewhere is reading a PECL error at 11 PM instead of shipping a feature. That is the entire reason <code>visorcraft/mongreldb-php</code> is pure PHP, and the only way pure PHP works as a strategy is if the language gives you enough concrete features that you do not need to reach for native code to build a database client that feels modern. PHP 8.4 is the floor we ship on today, and PHP 8.5, released in November 2025, is the version that removes several of the remaining workarounds. This post is the tour of the features we use from 8.4, followed by the ones we are now adopting from 8.5, because the difference between a language version and a product decision is smaller than people pretend.</p>
<h2 id="asymmetric-visibility-and-the-shape-of-the-client">Asymmetric visibility and the shape of the client</h2>
<p>The <code>Database</code> class is the front door of the client, and it carries internal state that callers should read but never set: the base URL, the default transport, the current session token, and a few cached metadata objects. Before PHP 8.4, the honest choices were either public readonly properties, which expose the value for reading but still let anyone assign it during construction if the constructor is inside the class, or a pile of private properties with boilerplate getters that exist only to say &ldquo;this is not for you.&rdquo; Asymmetric visibility fixes this by letting the property be <code>public private(set)</code>, which means the world can read it and only the class can write it after construction, so internal mutation in methods like <code>withToken</code> still works while external callers are frozen out. This is exactly what a <code>Database</code> object needs.</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">final</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Database</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></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> $baseUrl,
</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">TransportInterface</span> $transport,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">private</span> <span style="color:#f92672">?</span><span style="color:#a6e22e">string</span> $sessionToken <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><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">withToken</span>(<span style="color:#a6e22e">string</span> $token)<span style="color:#f92672">:</span> <span style="color:#66d9ef">static</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        $clone <span style="color:#f92672">=</span> <span style="color:#66d9ef">clone</span> $this;
</span></span><span style="display:flex;"><span>        $clone<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">sessionToken</span> <span style="color:#f92672">=</span> $token;       <span style="color:#75715e">// private property, fine here
</span></span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> $clone;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This is not a cosmetic win; it is a stability win, because the public surface of the object now matches the contract we actually want, and there is no private getter a maintainer can forget to update when they rename a field. The client has a lot of value objects, <code>Column</code>, <code>Table</code>, <code>ResultSet</code>, <code>QueryPlan</code>, and asymmetric visibility keeps them honest without turning the source code into Java.</p>
<h2 id="readonly-properties-and-the-transport-configuration">Readonly properties and the transport configuration</h2>
<p>PHP 8.1 gave us readonly properties, PHP 8.2 added readonly classes, and by PHP 8.4 they are mature enough that we use them as the default for any object that is effectively a record. A transport configuration, for example, is a bundle of timeouts, retry counts, and header defaults that should be immutable once constructed, because a live request is already in flight and changing its timeout halfway through is a great way to get a bug nobody can reproduce. The combination of a <code>readonly</code> class and constructor property promotion means the entire configuration object collapses to a few lines.</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">TransportConfig</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></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">int</span> $connectTimeoutMs <span style="color:#f92672">=</span> <span style="color:#ae81ff">5000</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">int</span> $requestTimeoutMs <span style="color:#f92672">=</span> <span style="color:#ae81ff">30000</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">int</span> $maxRetries <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">array</span> $defaultHeaders <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>Readonly classes are not a free lunch, because <code>clone</code> becomes awkward and you need a <code>with</code> method or <code>clone</code> plus reassignment if you want a modified copy, but for transport config that is exactly what we want: the object is a snapshot, not a living document, and the database client treats it that way.</p>
<h2 id="array-find-functions-and-query-plan-navigation">Array find functions and query-plan navigation</h2>
<p>PHP 8.4 adds <code>array_find</code>, <code>array_find_key</code>, <code>array_any</code>, and <code>array_all</code>, and these are the kind of functions that sound trivial until you write the code that used to do the same thing with a loop and a flag. We hit them constantly when walking query-plan metadata, because a plan node is a nested array of operators, and we need to know whether any operator touches a vector index, or whether all operators are point lookups, or which node is the first one that references a JSON column. The old way was <code>array_filter(...)[0] ?? null</code> for find, and a hand-rolled <code>foreach</code> with <code>return false</code> for any/all, which is fine once but becomes noise when it appears twenty times in a client.</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: array_filter gives you all matches, then you take the first.
</span></span></span><span style="display:flex;"><span>$vectorNode <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_filter</span>($planNodes, <span style="color:#a6e22e">fn</span>($n) <span style="color:#f92672">=&gt;</span> $n[<span style="color:#e6db74">&#39;op&#39;</span>] <span style="color:#f92672">===</span> <span style="color:#e6db74">&#39;ann&#39;</span>)[<span style="color:#ae81ff">0</span>] <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">// After: intent is explicit, no allocation of the full filtered array.
</span></span></span><span style="display:flex;"><span>$vectorNode <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_find</span>($planNodes, <span style="color:#a6e22e">fn</span>($n) <span style="color:#f92672">=&gt;</span> $n[<span style="color:#e6db74">&#39;op&#39;</span>] <span style="color:#f92672">===</span> <span style="color:#e6db74">&#39;ann&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$hasVectorScan <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_any</span>($planNodes, <span style="color:#a6e22e">fn</span>($n) <span style="color:#f92672">=&gt;</span> $n[<span style="color:#e6db74">&#39;op&#39;</span>] <span style="color:#f92672">===</span> <span style="color:#e6db74">&#39;ann&#39;</span>);
</span></span><span style="display:flex;"><span>$allPointLookups <span style="color:#f92672">=</span> <span style="color:#a6e22e">array_all</span>($planNodes, <span style="color:#a6e22e">fn</span>($n) <span style="color:#f92672">=&gt;</span> $n[<span style="color:#e6db74">&#39;op&#39;</span>] <span style="color:#f92672">===</span> <span style="color:#e6db74">&#39;point&#39;</span>);
</span></span></code></pre></div><p>The performance difference is small for most client workloads, but the readability difference is large, and a readable client is one that gets more contributions and fewer misuses.</p>
<h2 id="property-hooks-and-lazy-hydration">Property hooks and lazy hydration</h2>
<p>PHP 8.4 introduces property hooks, which let you attach <code>get</code> and <code>set</code> logic directly to a property declaration without writing a backing field and a separate accessor. We use them for lazy hydration on the <code>ResultSet</code> rows, where each row is a thin wrapper over a raw JSON array until the caller actually asks for a typed value. The hook is not magic; it is just a place to put the conversion logic so that the object remains a plain object while still doing the work on demand.</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">final</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Row</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">private</span> <span style="color:#66d9ef">array</span> $data) {}
</span></span><span style="display:flex;"><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">get</span> <span style="color:#f92672">=&gt;</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">data</span>[<span style="color:#e6db74">&#39;name&#39;</span>] <span style="color:#f92672">??</span> <span style="color:#e6db74">&#39;&#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">public</span> <span style="color:#a6e22e">int</span> $id {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">get</span> <span style="color:#f92672">=&gt;</span> (<span style="color:#a6e22e">int</span>) ($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">data</span>[<span style="color:#e6db74">&#39;id&#39;</span>] <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>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">object</span> $document {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">get</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">json_decode</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">data</span>[<span style="color:#e6db74">&#39;document&#39;</span>] <span style="color:#f92672">??</span> <span style="color:#e6db74">&#39;{}&#39;</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The tradeoff is that property hooks can hide work, and a getter that does JSON decoding on every access is a trap if you call it in a loop. We keep the hooks thin and cache the heavier conversions in the constructor, so the hook is mostly a type-safe doorway, not a compute sink.</p>
<h2 id="ext-curl-in-php-84-the-http-foundation">ext-curl in PHP 8.4: the HTTP foundation</h2>
<p>The default transport is cURL, because cURL is the only HTTP client that is present on enough hosts that we can assume it exists, and PHP 8.0 gave us the OO <code>CurlHandle</code> and typed exceptions we use in the wrapper, with PHP 8.4 refinements to default encoding behavior keeping the implementation small. The client detects cURL at runtime, falls back to streams if it is missing, and lets you inject a PSR-18 or Symfony HttpClient adapter if you want something else, but the cURL path is the one we optimize for because it is the one that exists everywhere.</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">final</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CurlTransport</span> <span style="color:#66d9ef">implements</span> <span style="color:#a6e22e">TransportInterface</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">\CurlHandle</span> $handle;
</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">private</span> <span style="color:#a6e22e">TransportConfig</span> $config)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">curl_init</span>();
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">curl_setopt</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span>, <span style="color:#a6e22e">CURLOPT_RETURNTRANSFER</span>, <span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">curl_setopt</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span>, <span style="color:#a6e22e">CURLOPT_FOLLOWLOCATION</span>, <span style="color:#66d9ef">false</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">curl_setopt</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span>, <span style="color:#a6e22e">CURLOPT_CONNECTTIMEOUT_MS</span>, $config<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">connectTimeoutMs</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">curl_setopt</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span>, <span style="color:#a6e22e">CURLOPT_TIMEOUT_MS</span>, $config<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">requestTimeoutMs</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">request</span>(<span style="color:#a6e22e">string</span> $method, <span style="color:#a6e22e">string</span> $url, <span style="color:#a6e22e">string</span> $body <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>)<span style="color:#f92672">:</span> <span style="color:#a6e22e">Response</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">curl_setopt</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span>, <span style="color:#a6e22e">CURLOPT_CUSTOMREQUEST</span>, $method);
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">curl_setopt</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span>, <span style="color:#a6e22e">CURLOPT_URL</span>, $url);
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">curl_setopt</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span>, <span style="color:#a6e22e">CURLOPT_POSTFIELDS</span>, $body);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        $raw <span style="color:#f92672">=</span> <span style="color:#a6e22e">curl_exec</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> ($raw <span style="color:#f92672">===</span> <span style="color:#66d9ef">false</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">ConnectionException</span>(<span style="color:#a6e22e">curl_error</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</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">return</span> <span style="color:#a6e22e">Response</span><span style="color:#f92672">::</span><span style="color:#a6e22e">fromCurl</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">handle</span>, $raw);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The cURL handle is reused for the lifetime of the transport object, which is better than creating a new handle per request, but it is not yet true connection persistence across the whole process, which is where PHP 8.5 comes in.</p>
<h2 id="php-85-the-pipe-operator-and-persistent-curl-sharing">PHP 8.5: the pipe operator and persistent cURL sharing</h2>
<p>PHP 8.5 shipped in November 2025 with the pipe operator, persistent cURL share handles, and <code>clone</code> with modifications. These are not theoretical wins for the client; they are the features that let us simplify the code we already have.</p>
<p>The pipe operator lets you write <code>$value |&gt; $this-&gt;normalize(...) |&gt; $this-&gt;send(...)</code> instead of nesting calls right-to-left or assigning to a temporary variable. For a client that chains encoding, validation, and request building, the pipe operator makes the middle of the call stack readable without turning it into a fluent builder.</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.5
</span></span></span><span style="display:flex;"><span>$response <span style="color:#f92672">=</span> $requestBody
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">json_encode</span>(<span style="color:#f92672">...</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">|&gt;</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">transport</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">request</span>(<span style="color:#e6db74">&#39;POST&#39;</span>, $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">baseUrl</span> <span style="color:#f92672">.</span> <span style="color:#e6db74">&#39;/sql&#39;</span>, <span style="color:#f92672">...</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">Response</span><span style="color:#f92672">::</span><span style="color:#a6e22e">fromRaw</span>(<span style="color:#f92672">...</span>);
</span></span></code></pre></div><p>Persistent cURL share handles are the bigger deal. In PHP 8.4, the <code>CurlTransport</code> reuses a single <code>CurlHandle</code> for the lifetime of the transport object, but each transport object still starts a fresh TCP handshake. PHP 8.5 lets share handles persist across multiple PHP requests, which means an FPM or FrankenPHP worker can keep a warm connection to the database server instead of reopening it. We are wiring this into the transport layer so that the same <code>CurlTransport</code> can attach to a process-wide share handle when the runtime offers one, and the fallback for PHP 8.4 stays exactly the same.</p>
<p><code>clone</code> with modifications is the last piece. Readonly classes are great for value objects, but changing one field used to mean either a constructor spread with <code>get_object_vars</code> or a hand-written <code>with</code> method. PHP 8.5&rsquo;s <code>clone($this, ['field' =&gt; $value])</code> collapses that to a single expression, so the <code>withAlpha</code> style of method becomes small enough that we no longer need helper traits to keep it readable.</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.5
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">withAlpha</span>(<span style="color:#a6e22e">int</span> $alpha)<span style="color:#f92672">:</span> <span style="color:#a6e22e">self</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">clone</span>($this, [<span style="color:#e6db74">&#39;alpha&#39;</span> <span style="color:#f92672">=&gt;</span> $alpha]);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="the-backward-compatibility-tradeoff">The backward-compatibility tradeoff</h2>
<p>The client currently supports PHP 8.4 as its minimum, and PHP 8.5 is now stable and widely available in the distributions we track. Runtime features like persistent cURL share handles can be guarded with <code>function_exists</code> and <code>curl_share_init</code>, so the same package can run faster on PHP 8.5 without breaking PHP 8.4, but syntax features like the pipe operator cannot be runtime-guarded. That means the pipe operator and <code>clone</code> with modifications will arrive as part of a major version bump that raises the minimum to PHP 8.5, while the cURL sharing lands as a backward-compatible performance improvement for anyone already on 8.5.</p>
<h2 id="the-modern-equivalent">The modern equivalent</h2>
<p>In 2010, a PHP database client would have been a thin wrapper around <code>mysql_connect</code> and the main challenge was escaping strings correctly. In 2026, the challenge is building a client that is safe, typed, and deployable without native code, and PHP 8.4 is the first version where that feels natural rather than defensive. PHP 8.5 is now the version that removes the remaining reasons a database client would need to touch anything outside Composer, not by adding one big feature, but by making the small workarounds unnecessary. That is the goal, and the language is finally moving in the same direction.</p>
]]></content:encoded></item></channel></rss>