<?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>Filesystem on Guillaume Delré</title><link>https://guillaumedelre.github.io/tags/filesystem/</link><description>Recent content in Filesystem on Guillaume Delré</description><generator>Hugo</generator><language>en</language><lastBuildDate>Wed, 12 Jan 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/tags/filesystem/index.xml" rel="self" type="application/rss+xml"/><item><title>Symfony 6.0: PHP 8.1 only, and the security system rebuilt</title><link>https://guillaumedelre.github.io/2022/01/12/symfony-6.0-php-8.1-only-and-the-security-system-rebuilt/</link><pubDate>Wed, 12 Jan 2022 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2022/01/12/symfony-6.0-php-8.1-only-and-the-security-system-rebuilt/</guid><description>Part 7 of 11 in &amp;quot;Symfony Releases&amp;quot;: Symfony 6.0 requires PHP 8.1, removes the legacy security system, and rebuilds authentication on a cleaner foundation.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 6.0 released November 29, 2021. The defining characteristic: PHP 8.1 is the minimum. Not supported, required. The releases team waited for PHP 8.1 to ship, then cut Symfony 6.0 the next day.</p>
<p>This isn&rsquo;t just a version bump. It&rsquo;s a commitment to build against the current language instead of the historical floor.</p>
<h2 id="the-security-system-finally-rebuilt">The security system, finally rebuilt</h2>
<p>The Symfony security component has two systems. The old one (<code>AnonymousToken</code>, <code>GuardAuthenticatorInterface</code>, a tangle of interfaces that made you implement methods you didn&rsquo;t need) had been deprecated. 6.0 removes it entirely.</p>
<p>The new security system (<code>security.enable_authenticator_manager: true</code> in 5.x) is now the only system. It&rsquo;s cleaner: one interface to implement, clear separation between authentication and authorization, passport-based credential checking. The upgrade from the old guard authenticators isn&rsquo;t painless, but the destination is a lot less confusing.</p>
<h2 id="the-filesystem-path-class">The Filesystem Path class</h2>
<p>Working with filesystem paths in PHP is basically a string manipulation problem. <code>__DIR__</code>, concatenation, <code>realpath()</code>, platform-specific separators: the standard library gives you primitives but no real model.</p>
<p>The new <code>Path</code> class handles 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><span style="color:#66d9ef">use</span> <span style="color:#a6e22e">Symfony\Component\Filesystem\Path</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Path</span><span style="color:#f92672">::</span><span style="color:#a6e22e">join</span>(<span style="color:#e6db74">&#39;/var/www&#39;</span>, <span style="color:#e6db74">&#39;html&#39;</span>, <span style="color:#e6db74">&#39;../uploads&#39;</span>); <span style="color:#75715e">// /var/www/uploads
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">Path</span><span style="color:#f92672">::</span><span style="color:#a6e22e">makeRelative</span>(<span style="color:#e6db74">&#39;/var/www/html&#39;</span>, <span style="color:#e6db74">&#39;/var/www&#39;</span>); <span style="color:#75715e">// html
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">Path</span><span style="color:#f92672">::</span><span style="color:#a6e22e">isAbsolute</span>(<span style="color:#e6db74">&#39;./relative/path&#39;</span>); <span style="color:#75715e">// false
</span></span></span></code></pre></div><p>Cross-platform, no side effects, no filesystem access needed. Also in 6.0: nested <code>.gitignore</code> pattern support in Finder.</p>
<h2 id="enums-in-the-form-system">Enums in the form system</h2>
<p>Building on 5.4&rsquo;s groundwork, 6.0 takes enum support further. <code>BackedEnum</code> values round-trip through forms and the serializer without custom transformers. The form component understands enum cases as choice options out of the box.</p>
<h2 id="what-60-removes">What 6.0 removes</h2>
<p>The removal list is extensive: the old security system, the <code>Templating</code> component, PHP annotations support (replaced by native attributes), Doctrine Cache support, <code>ContainerAwareTrait</code>. Six years of accumulated <code>@deprecated</code> markers, finally cleaned out.</p>
<p>Apps that took 5.4 deprecation warnings seriously had a clean upgrade path. Apps that didn&rsquo;t had work to do.</p>
<h2 id="tab-completion-was-always-the-gap">Tab completion was always the gap</h2>
<p>The Console component got shell autocompletion, and it&rsquo;s properly integrated: define a <code>complete()</code> method on your command, and Tab in Bash will suggest valid values for options and 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:#66d9ef">class</span> <span style="color:#a6e22e">DeployCommand</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Command</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">complete</span>(<span style="color:#a6e22e">CompletionInput</span> $input, <span style="color:#a6e22e">CompletionSuggestions</span> $suggestions)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> ($input<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">mustSuggestOptionValuesFor</span>(<span style="color:#e6db74">&#39;env&#39;</span>)) {
</span></span><span style="display:flex;"><span>            $suggestions<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">suggestValues</span>([<span style="color:#e6db74">&#39;prod&#39;</span>, <span style="color:#e6db74">&#39;staging&#39;</span>, <span style="color:#e6db74">&#39;dev&#39;</span>]);
</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>All built-in Symfony commands got completion too: <code>debug:router</code>, <code>cache:pool:clear</code>, <code>lint:yaml</code>, and about fifteen others. Run <code>bin/console completion bash &gt;&gt; ~/.bashrc</code> and you&rsquo;re done.</p>
<h2 id="messenger-now-with-attributes-and-batch-processing">Messenger, now with attributes and batch processing</h2>
<p>The <code>#[AsMessageHandler]</code> attribute replaces the old <code>MessageHandlerInterface</code>. Less boilerplate, and you can now configure transport affinity and priority directly on the attribute:</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">#[AsMessageHandler(fromTransport: &#39;async&#39;, priority: 10)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SendWelcomeEmailHandler</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">__invoke</span>(<span style="color:#a6e22e">UserRegistered</span> $message)<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span> { <span style="color:#f92672">...</span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The other significant addition: <code>BatchHandlerInterface</code>. When you&rsquo;re inserting a thousand rows, handling messages one by one is wasteful. Batch handlers collect messages and process them in groups. The default batch size is 10, controlled by <code>BatchHandlerTrait::shouldFlush()</code>. The <code>Acknowledger</code> handles individual success and failure within the batch.</p>
<p><code>reset_on_message: true</code> in the Messenger config resets container services between messages. Previously, a Monolog buffer could fill up across message handling and nobody noticed until production. This prevents that class of statefulness bug without requiring manual cleanup.</p>
<h2 id="the-di-container-gets-more-expressive">The DI container gets more expressive</h2>
<p>Three changes that matter in practice.</p>
<p>Union and intersection types now autowire. PHP 8.1 added intersection types, and Symfony 6.0 wires 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">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">NormalizerInterface</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">DenormalizerInterface</span> $serializer
</span></span><span style="display:flex;"><span>) {}
</span></span></code></pre></div><p>This works as long as both interfaces point to the same service through autowiring aliases.</p>
<p><code>TaggedIterator</code> and <code>TaggedLocator</code> attributes gained <code>defaultPriorityMethod</code> and <code>defaultIndexMethod</code> options. You no longer need YAML to express ordering or indexing for tagged services:</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">__construct</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#[TaggedIterator(tag: &#39;app.handler&#39;, defaultPriorityMethod: &#39;getPriority&#39;)]
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">iterable</span> $handlers,
</span></span><span style="display:flex;"><span>) {}
</span></span></code></pre></div><p><code>SubscribedService</code> (the attribute that replaces the implicit magic of <code>ServiceSubscriberTrait</code>) makes lazy service access explicit and typeable:</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">#[SubscribedService]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">private</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">mailer</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">MailerInterface</span>
</span></span><span style="display:flex;"><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">container</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">get</span>(<span style="color:#66d9ef">__METHOD__</span>);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="validation-gets-three-new-tools">Validation gets three new tools</h2>
<p><code>CssColor</code> validates CSS color values in whatever formats you care about: hex, RGB, HSL, named colors, or any mix. Useful for theme config fields where you want to accept <code>#ff0000</code> but not <code>red</code>, or vice versa.</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">#[Assert\CssColor(formats: Assert\CssColor::HEX_LONG)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">private</span> <span style="color:#a6e22e">string</span> $brandColor;
</span></span></code></pre></div><p><code>Cidr</code> validates CIDR notation for IPv4 and IPv6, with options to pin the version and constrain the netmask range. Infrastructure tools and network config forms finally have a first-class constraint.</p>
<p>The third addition isn&rsquo;t a new constraint. It&rsquo;s PHP 8.1 nested attributes making existing compound constraints usable without XML. <code>AtLeastOneOf</code>, <code>Collection</code>, <code>All</code>, <code>Sequentially</code>: all of these previously required annotation workarounds. Now they just work as attributes:</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">#[Assert\Collection(
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fields</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;email&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Assert\Email</span>(),
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;role&#39;</span>  <span style="color:#f92672">=&gt;</span> [<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Assert\NotBlank</span>(), <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Assert\Choice</span>([<span style="color:#e6db74">&#39;admin&#39;</span>, <span style="color:#e6db74">&#39;user&#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">private</span> <span style="color:#66d9ef">array</span> $payload;
</span></span></code></pre></div><h2 id="serializer-cleaned-up">Serializer, cleaned up</h2>
<p>Two things. First, serialization context is now configurable globally instead of being repeated on every <code>serialize()</code> 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-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># config/packages/serializer.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">serializer</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">default_context</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">enable_max_depth</span>: <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><p>Second, the <code>COLLECT_DENORMALIZATION_ERRORS</code> option changes how the serializer handles type errors on deserialization. Instead of throwing on the first problem, it collects all of them and surfaces them through <code>PartialDenormalizationException</code>. If you&rsquo;re writing an API that deserializes request bodies, this is the difference between returning &ldquo;first field that fails&rdquo; and &ldquo;all fields that fail&rdquo; in a single response.</p>
<h2 id="the-string-utilities-nobody-knew-they-needed">The string utilities nobody knew they needed</h2>
<p><code>trimPrefix()</code> and <code>trimSuffix()</code> on the <code>UnicodeString</code> / <code>ByteString</code> classes. Not glamorous, but stripping a known prefix with <code>ltrim()</code> is a subtle footgun: it strips characters, not strings. These are correct:</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:#66d9ef">function</span> <span style="color:#a6e22e">Symfony\Component\String\u</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">u</span>(<span style="color:#e6db74">&#39;file-image-001.png&#39;</span>)<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">trimPrefix</span>(<span style="color:#e6db74">&#39;file-&#39;</span>);   <span style="color:#75715e">// &#39;image-001.png&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">u</span>(<span style="color:#e6db74">&#39;report.html.twig&#39;</span>)<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">trimSuffix</span>(<span style="color:#e6db74">&#39;.twig&#39;</span>);     <span style="color:#75715e">// &#39;report.html&#39;
</span></span></span></code></pre></div><p>Also in this release: <code>NilUlid</code> for zero-value ULIDs, <code>perMonth()</code> and <code>perYear()</code> on RateLimiter for when hourly limits don&rsquo;t make sense, and <code>appendToFile()</code> in the Filesystem component gained an optional <code>LOCK_EX</code> parameter for concurrent writers.</p>
<h2 id="debugging-the-environment">Debugging the environment</h2>
<p><code>debug:dotenv</code> is a new console command that shows which <code>.env</code> files were loaded and where each value came from. When you have <code>.env</code>, <code>.env.local</code>, <code>.env.test</code>, and <code>.env.test.local</code> all fighting each other and something is wrong, this command tells you exactly which file won. It only shows up when the Dotenv component is in use, which is the case for any standard Symfony app.</p>
]]></content:encoded></item></channel></rss>