<?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>Workflow on Guillaume Delré</title><link>https://guillaumedelre.github.io/tags/workflow/</link><description>Recent content in Workflow on Guillaume Delré</description><generator>Hugo</generator><language>en</language><lastBuildDate>Fri, 12 Jan 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/tags/workflow/index.xml" rel="self" type="application/rss+xml"/><item><title>Symfony 7.0: PHP 8.2 minimum and annotations finally gone</title><link>https://guillaumedelre.github.io/2024/01/12/symfony-7.0-php-8.2-minimum-and-annotations-finally-gone/</link><pubDate>Fri, 12 Jan 2024 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2024/01/12/symfony-7.0-php-8.2-minimum-and-annotations-finally-gone/</guid><description>Part 9 of 11 in &amp;quot;Symfony Releases&amp;quot;: Symfony 7.0 requires PHP 8.2, drops Doctrine annotations entirely, and ships a rebuilt Workflow component.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 7.0 landed November 29, 2023, same day as 6.4. The pattern holds: the X.0 release cuts deprecated code and raises the PHP floor. 7.0 requires PHP 8.2 and removes everything that 6.4 flagged as deprecated.</p>
<p>The most visible removal: Doctrine annotations. <code>@Route</code>, <code>@ORM\Column</code>, <code>@Assert</code> - gone. Native PHP attributes have been the recommended approach since Symfony 5.2. 7.0 just makes it official.</p>
<h2 id="attributes-everywhere">Attributes everywhere</h2>
<p>The migration from annotations to attributes is mostly mechanical: syntax changes from <code>@</code> to <code>#[]</code>, and the class references move from Doctrine annotation classes to PHP attribute classes:</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><span style="color:#e6db74">/** @Route(&#39;/users&#39;, methods={&#34;GET&#34;}) */</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:#75715e">#[Route(&#39;/users&#39;, methods: [&#39;GET&#39;])]
</span></span></span></code></pre></div><p>The real win isn&rsquo;t just the syntax: attributes are validated by the PHP engine, not a docblock parser. IDEs can resolve them without custom plugins. Static analysis tools understand them natively. No more &ldquo;it fails silently at runtime because of a typo in a comment.&rdquo;</p>
<h2 id="workflow-with-php-attributes">Workflow with PHP attributes</h2>
<p>Workflow event listeners and guards can now be registered via 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">#[AsGuard(workflow: &#39;order&#39;, transition: &#39;ship&#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">canShip</span>(<span style="color:#a6e22e">Event</span> $event)<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> (<span style="color:#f92672">!</span>$event<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getSubject</span>()<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">isPaymentConfirmed</span>()) {
</span></span><span style="display:flex;"><span>        $event<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">setBlocked</span>(<span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The workflow profiler, a dedicated panel showing the current marking and available transitions, is a genuinely useful debugging tool if you&rsquo;re working with complex state machines.</p>
<h2 id="clock1-datepoint-in-the-clock-component">:clock1: DatePoint in the Clock component</h2>
<p><code>DatePoint</code>, the immutable <code>DateTime</code> with strict error handling introduced in 6.4, is now the recommended way to work with dates. Combine it with PHP 8.2&rsquo;s readonly properties and date value objects in domain code become almost trivially clean:</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">Order</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">DatePoint</span> $createdAt,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">public</span> <span style="color:#f92672">?</span><span style="color:#a6e22e">DatePoint</span> $shippedAt <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><h2 id="what-70-removes">What 7.0 removes</h2>
<p>The full removal list: Doctrine annotations support, the <code>Templating</code> component bridge, <code>ProxyManager</code> bridge, the <code>Monolog</code> bridge for versions below 3.0, and the Sendinblue transport (replaced by Brevo). PHP 8.0 and 8.1 support also ends. 8.2 is the floor now.</p>
<p>Upgrade from 6.4 with all deprecation notices fixed, and 7.0 is smooth. Skip that step and you&rsquo;re in for a bad time.</p>
<h2 id="scheduler-and-assetmapper-graduate">Scheduler and AssetMapper graduate</h2>
<p>Two components that shipped as experimental in 6.3 are now stable: Scheduler and AssetMapper. Stable means locked APIs, no more <code>@experimental</code> caveats, and they show up properly in the upgrade guide. You can actually rely on them now.</p>
<p>Scheduler gets <code>#[AsCronTask]</code> and <code>#[AsPeriodicTask]</code> for attribute-based task registration, runtime schedule modification with heap recalculation, <code>FailureEvent</code>, and a <code>--date</code> option on <code>schedule:debug</code>. AssetMapper adds CSS file support in importmap, an <code>outdated</code> command, an <code>audit</code> command, and automatic preloading via WebLink.</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">#[AsCronTask(&#39;0 2 * * *&#39;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">NightlyReportMessage</span> {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#[AsPeriodicTask(frequency: &#39;1 hour&#39;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">HourlyCleanupMessage</span> {}
</span></span></code></pre></div><h2 id="service-wiring-gets-two-new-attributes">Service wiring gets two new attributes</h2>
<p><code>#[AutowireLocator]</code> and <code>#[AutowireIterator]</code> landed in 6.4 and graduate to stable in 7.0. They replace the verbose XML/YAML tagged service locator config with something you can just put directly in 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:#66d9ef">class</span> <span style="color:#a6e22e">HandlerRegistry</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:#75715e">#[AutowireLocator(&#39;app.handler&#39;, indexAttribute: &#39;key&#39;)]
</span></span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">ContainerInterface</span> $handlers,
</span></span><span style="display:flex;"><span>    ) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>#[Target]</code> also gets smarter: when a service has a named autowiring alias like <code>invoice.lock.factory</code>, you can now write <code>#[Target('invoice')]</code> instead of the full alias name. Less noise when the type already tells you what you want.</p>
<h2 id="messenger-gets-more-precise-failure-handling">Messenger gets more precise failure handling</h2>
<p><code>RejectRedeliveredMessageException</code> tells the worker to not retry a message, which is handy when a message arrives twice because of a transport ack timeout and you need exactly-once semantics. <code>messenger:failed:remove --all</code> clears the entire failure transport in one shot, no loop required. Failed retries can also go directly to the failure transport, bypassing the retry queue entirely.</p>
<p>Multiple Redis Sentinel hosts are now supported in the DSN:</p>
<pre tabindex="0"><code>redis-sentinel://host1:26379,host2:26379,host3:26379/mymaster
</code></pre><h2 id="console-gets-signal-names-and-command-profiling">Console gets signal names and command profiling</h2>
<p><code>SignalMap</code> maps signal integers to their POSIX names. When a worker catches <code>SIGTERM</code>, the log now says <code>SIGTERM</code> instead of <code>15</code>. Small thing, real improvement. <code>ConsoleTerminateEvent</code> is dispatched even when the process exits via signal, which wasn&rsquo;t the case before 7.0.</p>
<p>Command profiling lands too: pass <code>--profile</code> to <code>bin/console</code> and the collected data goes straight into the Symfony profiler, browsable from the web UI.</p>
<h2 id="form-small-things-that-add-up">Form: small things that add up</h2>
<p><code>ChoiceType</code> gets a <code>duplicate_preferred_choices</code> option. Set it to <code>false</code> and you stop showing the same option twice when preferred choices overlap with the full list. <code>FormEvent::setData()</code> is deprecated for events where the data is already locked at that point in the lifecycle. The self-closing slash on <code>&lt;input&gt;</code> elements is also gone: <code>&lt;input&gt;</code> is a void element in HTML5 and the slash was technically invalid.</p>
<p>Enum support in forms is a nice one: <code>ChoiceType</code> renders backed enums directly, and translatable enums get their labels through the translator without any custom wiring.</p>
<h2 id="httpfoundation-small-but-useful">HttpFoundation: small but useful</h2>
<p><code>Response::send()</code> gets a <code>$flush</code> parameter. Pass <code>false</code> to buffer the output without flushing to the client, useful when chaining middleware that needs to inspect the response before it leaves the process.</p>
<p><code>UriSigner</code> moves from HttpKernel to HttpFoundation, where it belongs semantically. Same class name, different namespace.</p>
<p>Cookies get CHIPS support (Cookies Having Independent Partitioned State), the browser mechanism for cross-site cookies in a first-party partition. Only matters if you build embeddable widgets, but good to know it&rsquo;s there.</p>
<h2 id="translation-phrase-provider-and-tree-output">Translation: Phrase provider and tree output</h2>
<p>Phrase joins Crowdin and Lokalise as a supported translation provider. Configure it in <code>config/packages/translation.yaml</code> and the <code>translation:push</code> / <code>translation:pull</code> commands handle the sync.</p>
<p><code>translation:pull</code> gets an <code>--as-tree</code> option that writes translation files in nested YAML rather than flat dot-notation keys. Whether that&rsquo;s actually better depends entirely on your team.</p>
<p><code>LocaleSwitcher::runWithLocale()</code> now passes the current locale as an argument to the callback, saving you a <code>getLocale()</code> call inside:</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>$switcher<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">runWithLocale</span>(<span style="color:#e6db74">&#39;fr&#39;</span>, <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">string</span> $locale) <span style="color:#66d9ef">use</span> ($mailer) {
</span></span><span style="display:flex;"><span>    $mailer<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">send</span>($this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">buildEmail</span>($locale));
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h2 id="a-few-things-in-serializer-and-domcrawler">A few things in Serializer and DomCrawler</h2>
<p>The Serializer&rsquo;s <code>Context</code> attribute can now target specific classes, so a single DTO can behave differently during (de)serialization depending on which class holds the context. <code>TranslatableNormalizer</code> lands for normalizing objects that implement <code>TranslatableInterface</code>: the translator is called during normalization, not before.</p>
<p><code>Crawler::attr()</code> gains a <code>$default</code> parameter. Instead of null-checking the return value, pass a fallback:</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>$src <span style="color:#f92672">=</span> $crawler<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">attr</span>(<span style="color:#e6db74">&#39;src&#39;</span>, <span style="color:#e6db74">&#39;/placeholder.png&#39;</span>);
</span></span></code></pre></div><p><code>assertAnySelectorText()</code> and <code>assertAnySelectorTextContains()</code> join the DomCrawler assertion set. They pass if at least one matching element satisfies the condition, rather than requiring all of them to match.</p>
<h2 id="httpclient-har-responses-for-testing">HttpClient: HAR responses for testing</h2>
<p><code>MockResponse</code> now accepts HAR (HTTP Archive) files. Record real HTTP interactions in your browser or with a proxy, drop the <code>.har</code> file in your test fixtures, and replay 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>$client <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MockHttpClient</span>(<span style="color:#a6e22e">HarFileResponseFactory</span><span style="color:#f92672">::</span><span style="color:#a6e22e">createFromFile</span>(<span style="color:#66d9ef">__DIR__</span><span style="color:#f92672">.</span><span style="color:#e6db74">&#39;/fixtures/api.har&#39;</span>));
</span></span></code></pre></div><p>Much better than writing response stubs by hand when you&rsquo;re dealing with a complex API.</p>
]]></content:encoded></item></channel></rss>