<?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/" xmlns:blog="https://jonesrussell.github.io/blog/ns"><channel><title>Psr14 on Web Developer Blog</title><link>https://jonesrussell.github.io/blog/tags/psr14/</link><description>Recent content in Psr14 on Web Developer Blog</description><image><title>Web Developer Blog</title><url>https://jonesrussell.github.io/blog/images/og-default.png</url><link>https://jonesrussell.github.io/blog/images/og-default.png</link></image><generator>Hugo -- 0.168.0</generator><language>en-us</language><lastBuildDate>Mon, 19 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jonesrussell.github.io/blog/tags/psr14/feed.xml" rel="self" type="application/rss+xml"/><item><title>A PSR-14 dispatcher does not promise Symfony's second argument</title><link>https://jonesrussell.github.io/blog/psr-14-dispatcher-second-argument/</link><pubDate>Mon, 19 Oct 2026 00:00:00 +0000</pubDate><guid>https://jonesrussell.github.io/blog/psr-14-dispatcher-second-argument/</guid><category>php</category><blog:tag>php</blog:tag><blog:tag>psr14</blog:tag><blog:tag>symfony</blog:tag><blog:tag>waaseyaa</blog:tag><description>Keep event dispatch calls within the interface you inject, using a small Waaseyaa fix as a practical example.</description><content:encoded><![CDATA[<p>Ahnii!</p>
<p>If your constructor accepts <code>Psr\EventDispatcher\EventDispatcherInterface</code>, call <code>dispatch()</code> with one argument. <a href="https://github.com/symfony/event-dispatcher/blob/v7.3.0/EventDispatcher.php">Symfony&rsquo;s dispatcher</a> supports an optional event name, but the PSR interface does not promise that parameter.</p>
<p>A small <a href="https://github.com/waaseyaa/framework/commit/4ac878ed2999ca9c6860c90a277b0f75430f9f84">Waaseyaa fix</a> shows how an implementation-specific convenience can slip into code that declares a portable dependency.</p>
<h2 id="read-the-interface-you-inject">Read the interface you inject</h2>
<p>The <a href="https://www.php-fig.org/psr/psr-14/">PSR-14 dispatcher interface</a> declares this method:</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">dispatch</span>(<span style="color:#a6e22e">object</span> $event);
</span></span></code></pre></div><p>The event object is the only argument in that contract. A concrete dispatcher may offer more, but callers depending on the PSR interface cannot assume those additions exist.</p>
<p>For example, this call relies on an extra parameter:</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>$dispatcher<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">dispatch</span>($event, $event<span style="color:#f92672">::</span><span style="color:#a6e22e">class</span>);
</span></span></code></pre></div><p>Symfony accepts an optional event name in that position. In its implementation, leaving the name out uses the event object&rsquo;s class. When the second argument is already that class name, it is redundant for Symfony&rsquo;s normal dispatch behavior.</p>
<p>The portable call is shorter:</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>$dispatcher<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">dispatch</span>($event);
</span></span></code></pre></div><p>This stays within the PSR interface. Listener selection still belongs to the dispatcher and its listener provider; PSR-14 does not prescribe a second naming argument.</p>
<h2 id="the-waaseyaa-change">The Waaseyaa change</h2>
<p>In the linked revision, <code>EntityRepository</code> depends on the PSR dispatcher interface. Its persistence path dispatched an <code>EntityPersistedEvent</code> and also supplied <code>EntityPersistedEvent::class</code> as the second argument.</p>
<p>The repair removed that argument. The messaging subscriber already registered its listener under the event class, so the call could use Symfony&rsquo;s default class-based name.</p>
<p>The event stayed at the same point in the persistence transaction. Removing an argument did not move the listener to a later notification phase or change when the transaction commits.</p>
<p>That distinction matters when reviewing a small compatibility fix: check both the call signature and the behavior surrounding the call.</p>
<h2 id="when-you-need-a-custom-event-name">When you need a custom event name</h2>
<p>An application may intentionally use names that differ from the event class. In that case, make the requirement explicit.</p>
<p><a href="https://github.com/symfony/event-dispatcher-contracts/blob/v3.6.0/EventDispatcherInterface.php">Symfony&rsquo;s event-dispatcher contract</a> exposes the optional event-name parameter. Depending on that contract tells readers and static analysis which behavior your code requires. It also makes the Symfony dependency visible.</p>
<p>Before changing a dispatch call, check:</p>
<ul>
<li><strong>The imported interface.</strong> PSR and Symfony both have an <code>EventDispatcherInterface</code>; the namespace matters.</li>
<li><strong>The listener registration.</strong> Confirm whether listeners use the event class or a custom name.</li>
<li><strong>The surrounding behavior.</strong> Preserve transaction boundaries, listener ordering and exception handling unless the change explicitly targets them.</li>
<li><strong>The verification.</strong> Run static analysis and the focused dispatch tests. Successful execution with one concrete dispatcher does not establish compatibility with every implementation of the interface.</li>
</ul>
<p>For the broader event model, see the <a href="/blog/psr-14-event-dispatcher/">PSR-14 walkthrough</a>. For a small dependency fix, start with the interface named in your constructor and make the call match it.</p>
<p>Baamaapii</p>
]]></content:encoded></item></channel></rss>