<?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>Oop on Guillaume Delré</title><link>https://guillaumedelre.github.io/tags/oop/</link><description>Recent content in Oop on Guillaume Delré</description><generator>Hugo</generator><language>en</language><lastBuildDate>Sun, 05 Jan 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/tags/oop/index.xml" rel="self" type="application/rss+xml"/><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></channel></rss>