<?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>Webhook on Guillaume Delré</title><link>https://guillaumedelre.github.io/tags/webhook/</link><description>Recent content in Webhook on Guillaume Delré</description><generator>Hugo</generator><language>en</language><lastBuildDate>Wed, 10 Jan 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/tags/webhook/index.xml" rel="self" type="application/rss+xml"/><item><title>Symfony 6.4 LTS: AssetMapper, Scheduler, Webhook, and the long-term release</title><link>https://guillaumedelre.github.io/2024/01/10/symfony-6.4-lts-assetmapper-scheduler-webhook-and-the-long-term-release/</link><pubDate>Wed, 10 Jan 2024 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2024/01/10/symfony-6.4-lts-assetmapper-scheduler-webhook-and-the-long-term-release/</guid><description>Part 8 of 11 in &amp;quot;Symfony Releases&amp;quot;: Symfony 6.4 LTS stabilizes AssetMapper — a bundler-free frontend approach — alongside the Scheduler and Webhook components.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 6.4 landed November 29, 2023. It&rsquo;s an LTS with a story: four components that shipped as experimental in earlier releases are now stable. The biggest deal is AssetMapper.</p>
<h2 id="assetmapper">AssetMapper</h2>
<p>Modern frontend tooling in Symfony meant Webpack Encore. Encore works: it handles transpilation, bundling, versioning, hot reload. It also requires Node.js, a separate build step, and a non-trivial amount of configuration for what is often a pretty modest frontend.</p>
<p>AssetMapper takes a different position. Modern browsers support ES modules natively. Instead of bundling, ship the files as-is, let the browser resolve imports through an importmap, and manage vendor dependencies through downloaded files rather than npm packages.</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-bash" data-lang="bash"><span style="display:flex;"><span>composer require symfony/asset-mapper
</span></span><span style="display:flex;"><span>php bin/console importmap:require lodash
</span></span></code></pre></div><p>No Node.js. No npm. No build step. JavaScript and CSS files are versioned and served directly, with a digest in the URL for cache busting. For apps where the frontend is not the primary engineering concern, this removes an entire toolchain from the equation.</p>
<p>6.4 adds CSS files to the importmap, automatic CSS preloading via WebLink, and commands to audit and update vendor dependencies. The package.json experience, minus npm.</p>
<h2 id="scheduler">Scheduler</h2>
<p>The Scheduler component (periodic and cron-style task scheduling without an external job runner) exits experimental and becomes stable. The API uses 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">#[AsCronTask(&#39;0 * * * *&#39;)]
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">HourlyReport</span> <span style="color:#66d9ef">implements</span> <span style="color:#a6e22e">ScheduledTaskInterface</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">run</span>()<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>Backed by Messenger transports, tasks run in any environment where a worker is running. For many use cases, this replaces the classic <code>cron</code> entry + console command pattern.</p>
<h2 id="webhook-and-remoteevent">Webhook and RemoteEvent</h2>
<p>Also graduating from experimental: the Webhook component handles incoming webhooks from external services. Instead of writing raw controllers that parse payloads and dispatch events by hand, you configure parsers for known services (Stripe, GitHub, Mailgun) and get typed events.</p>
<h2 id="clock3-datepoint">:clock3: DatePoint</h2>
<p>A new <code>DatePoint</code> class in the Clock component: an immutable <code>DateTime</code> wrapper that throws exceptions on invalid modifiers instead of silently returning <code>false</code>. Small thing, but meaningful for code that manipulates dates and actually wants to know when something goes wrong.</p>
<h2 id="the-support-window">The support window</h2>
<p>6.4 LTS gets bug fixes until November 2026 and security fixes until November 2027. The path from 6.4 to 7.4 (the next LTS) runs through the 6.4 deprecation notices, as usual.</p>
<h2 id="routes-without-magic-strings">Routes without magic strings</h2>
<p>FQCN-based route aliases are now generated automatically. If a controller method has a single route, Symfony creates an alias using its fully qualified class name:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// Previously: only &#39;blog_index&#39; worked
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Now: both work identically
</span></span></span><span style="display:flex;"><span>$this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">urlGenerator</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">generate</span>(<span style="color:#e6db74">&#39;blog_index&#39;</span>);
</span></span><span style="display:flex;"><span>$this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">urlGenerator</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">generate</span>(<span style="color:#a6e22e">BlogController</span><span style="color:#f92672">::</span><span style="color:#a6e22e">class</span><span style="color:#f92672">.</span><span style="color:#e6db74">&#39;::index&#39;</span>);
</span></span></code></pre></div><p>For invokable controllers, the alias is just the class name. The practical benefit is IDE navigation and refactoring safety: you&rsquo;re referencing a class constant, not a string that can silently drift.</p>
<h2 id="two-new-di-attributes">Two new DI attributes</h2>
<p><code>#[AutowireLocator]</code> and <code>#[AutowireIterator]</code> join the DI attribute family. Instead of configuring service locators and tagged iterables in YAML, you just declare them on constructor parameters:</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">#[AutowireLocator([FooHandler::class, BarHandler::class])]
</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></code></pre></div><p>Aliases, optional services (prefixed with <code>?</code>), and parameter injection via <code>SubscribedService</code> are all supported. The locator lazy-loads, so only the handlers you actually call get instantiated.</p>
<h2 id="messenger-gets-built-in-handlers">Messenger gets built-in handlers</h2>
<p>Three new message classes cover common tasks that previously required custom handlers.</p>
<p><code>RunProcessMessage</code> dispatches a <code>Process</code> command through the bus. <code>RunCommandMessage</code> does the same for console commands. Both return a context object with the exit code and output. <code>PingWebhookMessage</code> pings a URL, which is useful for monitoring scheduled tasks without spinning up a dedicated health-check service:</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>$this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">bus</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">dispatch</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">RunCommandMessage</span>(<span style="color:#e6db74">&#39;cache:clear&#39;</span>));
</span></span><span style="display:flex;"><span>$this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">bus</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">dispatch</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">PingWebhookMessage</span>(<span style="color:#e6db74">&#39;GET&#39;</span>, <span style="color:#e6db74">&#39;https://healthchecks.io/ping/abc123&#39;</span>));
</span></span></code></pre></div><p>The subprocess inheritance problem also got addressed with <code>PhpSubprocess</code>. When you run PHP with a custom memory limit (<code>-d memory_limit=-1</code>), child processes launched with <code>Process</code> don&rsquo;t inherit it. <code>PhpSubprocess</code> does:</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>$sub <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">PhpSubprocess</span>([<span style="color:#e6db74">&#39;bin/console&#39;</span>, <span style="color:#e6db74">&#39;app:heavy-import&#39;</span>]);
</span></span></code></pre></div><h2 id="security-three-fixes-for-real-situations">Security: three fixes for real situations</h2>
<p>The profiler now shows how security badges were resolved during authentication: which ones passed, which failed, and why. Before, you had to add debug output manually when a custom authenticator wasn&rsquo;t behaving.</p>
<p>Login throttling via RateLimiter now hashes PII in logs automatically. IP addresses and usernames get hashed with the kernel secret before they&rsquo;re written. No config needed, no regex on log lines.</p>
<p>Firewall patterns now accept arrays:</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:#f92672">firewalls</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">no_security</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">pattern</span>:
</span></span><span style="display:flex;"><span>            - <span style="color:#e6db74">&#34;^/register$&#34;</span>
</span></span><span style="display:flex;"><span>            - <span style="color:#e6db74">&#34;^/api/webhooks/&#34;</span>
</span></span></code></pre></div><p>No more regex gymnastics for multi-path exclusions.</p>
<h2 id="logout-without-a-dummy-controller">Logout without a dummy controller</h2>
<p>The logout route used to require a controller that did nothing but throw an exception, with a comment explaining that yes, this is intentional. 6.4 eliminates that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># config/routes/security.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">_security_logout</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">resource</span>: <span style="color:#ae81ff">security.route_loader.logout</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">type</span>: <span style="color:#ae81ff">service</span>
</span></span></code></pre></div><p>The route loader handles it. The dummy controller is gone. Flex updates the recipe.</p>
<h2 id="the-serializer-in-better-shape">The serializer in better shape</h2>
<p>Three serializer improvements that each solve a real problem.</p>
<p>Class-level <code>#[Groups]</code> attribute: apply a group to the entire class, then override per property. Useful when a resource has a default serialization group and a few fields that need finer control.</p>
<p>Translatable objects now have a dedicated normalizer. Translatable strings (wrapping Doctrine&rsquo;s <code>TranslatableInterface</code>) get translated to the locale passed via <code>NORMALIZATION_LOCALE_KEY</code> during normalization. Before this, you had to write a custom normalizer.</p>
<p>In debug mode, JSON decoding errors now use <code>seld/jsonlint</code> for better messages. Instead of &ldquo;Syntax error&rdquo;, you get the line and what actually went wrong:</p>
<pre tabindex="0"><code>Parse error on line 1: {&#39;foo&#39;: &#39;bar&#39;}
           ^ Invalid string, used single quotes instead of double quotes
</code></pre><h2 id="profilers-for-the-things-that-werent-http-requests">Profilers for the things that weren&rsquo;t HTTP requests</h2>
<p>The command profiler extends the existing profiler to console commands. Add <code>--profile</code> to any command and get a full profiler entry: input/output, execution time, memory, database queries, log messages. Commands that used to need <code>--verbose</code> plus manual timing now have the same debugging experience as HTTP requests.</p>
<p>The workflow profiler does the same for state machines. A new panel shows a graphical representation of your workflows and which transitions fired during the request. Zero configuration.</p>
<h2 id="the-dx-accumulation">The DX accumulation</h2>
<p>Several smaller additions that compound.</p>
<p><code>renderBlock()</code> and <code>renderBlockView()</code> on <code>AbstractController</code> let you render a named Twig block and return it as a <code>Response</code> or string. Handy for Turbo Stream responses where you want to update a fragment without a full controller action.</p>
<p>The <code>defined</code> env var processor returns a boolean rather than the value: <code>true</code> if the variable exists and is non-empty, <code>false</code> otherwise. Useful for feature flags driven by environment variables:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">parameters</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">is_feature_enabled</span>: <span style="color:#e6db74">&#39;%env(defined:FEATURE_FLAG_KEY)%&#39;</span>
</span></span></code></pre></div><p><code>HttpClient</code> now accepts <code>max_retries</code> per request, overriding the global retry strategy. The Finder component&rsquo;s <code>filter()</code> method accepts a second argument to prune entire directories early, which matters when you&rsquo;re searching large trees.</p>
<p>The <code>BrowserKit</code> <code>click()</code> method now accepts server parameters as extra headers, useful in functional tests that need to simulate authenticated API calls while following links.</p>
<h2 id="impersonation-becomes-usable-in-templates">Impersonation becomes usable in templates</h2>
<p>Two new Twig helpers: <code>impersonation_path()</code> and <code>impersonation_url()</code>. They generate the correct URLs including the switch-user query parameter, which is configurable and has no business being hardcoded in templates. Pair them with the existing <code>impersonation_exit_path()</code> for the full admin impersonation flow.</p>
<h2 id="locale-control-everywhere-it-was-missing">Locale control, everywhere it was missing</h2>
<p>Three gaps filled. <code>TemplatedEmail</code> now has a <code>locale()</code> method for rendering emails in the recipient&rsquo;s language. The locale switcher&rsquo;s <code>runWithLocale()</code> now passes the locale as an argument to the callback, so you don&rsquo;t have to capture it from the outer scope. And <code>app.enabledLocales</code> is available in Twig, so you can build language switchers without hardcoding locale lists.</p>
<h2 id="deploying-to-read-only-filesystems">Deploying to read-only filesystems</h2>
<p><code>APP_BUILD_DIR</code> is now an environment variable recognized by the kernel. Set it to redirect compiled artifacts (router cache, Doctrine proxies, preloaded translations) to a directory that exists, even when the default cache directory doesn&rsquo;t. The <code>MicroKernelTrait</code> uses it automatically. The <code>WarmableInterface</code> gained a <code>$buildDir</code> parameter to support this separation: custom cache warmers that write read-only artifacts should update accordingly.</p>
]]></content:encoded></item></channel></rss>