<p dir="auto">Hello,</p>
<p dir="auto">The upgrade from 2.8 to 3.0 caused the TraceableEventDispatcher to fail to remove listeners it wraps during <code class="notranslate">preProcess</code>, causing the listener function to be called twice, when in dev environment.</p>
<p dir="auto">I have made some tests and both following scenarios lead to the same behaviour:</p>
<ul dir="auto">
<li>Event dispatched directly from a controller (<em>wings.dispatcher</em> is a simple alias to <em>event_dispatcher</em>):</li>
</ul>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="protected function dispatch($eventName, RestEvent $event) {
   $this-&gt;get('wings.dispatcher')-&gt;dispatch($eventName, $event);
}"><pre class="notranslate"><span class="pl-k">protected</span> <span class="pl-k">function</span> <span class="pl-en">dispatch</span>(<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>, <span class="pl-smi"><span class="pl-smi">RestEvent</span></span> <span class="pl-s1"><span class="pl-c1">$</span>event</span>) {
   <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'wings.dispatcher'</span>)-&gt;<span class="pl-en">dispatch</span>(<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>, <span class="pl-s1"><span class="pl-c1">$</span>event</span>);
}</pre></div>
<ul dir="auto">
<li>Event dispatched from an injected service accepting the <em>wings.dispatcher</em> as a constructor argument:</li>
</ul>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="    /**
     *
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    /**
     *
     *
     * @param EntityManager            $entityManager
     * @param EventDispatcherInterface $dispatcher
     * @param string $class
     */
    public function __construct(EntityManager $entityManager,
                                EventDispatcherInterface $dispatcher, $class) {
        parent::__construct($entityManager, $class);

        $this-&gt;entityManager = $entityManager;
        $this-&gt;dispatcher    = $dispatcher;
    }

    /**
     *
     * @param string     $eventName
     * @param EventArgs  $eventArgs
     */
    protected function dispatch($eventName, Event $eventArgs) {
        $this-&gt;dispatcher-&gt;dispatch($eventName, $eventArgs);
    }"><pre class="notranslate">    <span class="pl-c">/**</span>
<span class="pl-c">     *</span>
<span class="pl-c">     * @var EventDispatcherInterface</span>
<span class="pl-c">     */</span>
    <span class="pl-k">private</span> <span class="pl-s1"><span class="pl-c1">$</span>dispatcher</span>;

    <span class="pl-c">/**</span>
<span class="pl-c">     *</span>
<span class="pl-c">     *</span>
<span class="pl-c">     * @param EntityManager            $entityManager</span>
<span class="pl-c">     * @param EventDispatcherInterface $dispatcher</span>
<span class="pl-c">     * @param string $class</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">__construct</span>(<span class="pl-smi"><span class="pl-smi">EntityManager</span></span> <span class="pl-s1"><span class="pl-c1">$</span>entityManager</span>,
                                <span class="pl-smi"><span class="pl-smi">EventDispatcherInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>dispatcher</span>, <span class="pl-s1"><span class="pl-c1">$</span>class</span>) {
        <span class="pl-smi">parent</span>::<span class="pl-en">__construct</span>(<span class="pl-s1"><span class="pl-c1">$</span>entityManager</span>, <span class="pl-s1"><span class="pl-c1">$</span>class</span>);

        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">entityManager</span> = <span class="pl-s1"><span class="pl-c1">$</span>entityManager</span>;
        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">dispatcher</span>    = <span class="pl-s1"><span class="pl-c1">$</span>dispatcher</span>;
    }

    <span class="pl-c">/**</span>
<span class="pl-c">     *</span>
<span class="pl-c">     * @param string     $eventName</span>
<span class="pl-c">     * @param EventArgs  $eventArgs</span>
<span class="pl-c">     */</span>
    <span class="pl-k">protected</span> <span class="pl-k">function</span> <span class="pl-en">dispatch</span>(<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>, <span class="pl-smi"><span class="pl-smi">Event</span></span> <span class="pl-s1"><span class="pl-c1">$</span>eventArgs</span>) {
        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">dispatcher</span>-&gt;<span class="pl-en">dispatch</span>(<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>, <span class="pl-s1"><span class="pl-c1">$</span>eventArgs</span>);
    }</pre></div>
<p dir="auto">I've been debugging into TraceableEventDispatcher <code class="notranslate">preProcess</code>:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="private function preProcess($eventName)
    {
        foreach ($this-&gt;dispatcher-&gt;getListeners($eventName) as $listener) {
            $info = $this-&gt;getListenerInfo($listener, $eventName);
            $name = isset($info['class']) ? $info['class'] : $info['type'];
            $wrappedListener = new WrappedListener($listener, $name, $this-&gt;stopwatch, $this);
            $this-&gt;wrappedListeners[$eventName][] = $wrappedListener;
            $this-&gt;dispatcher-&gt;removeListener($eventName, $listener);
            $this-&gt;dispatcher-&gt;addListener($eventName, $wrappedListener, $info['priority']);
        }
    }"><pre class="notranslate"><span class="pl-k">private</span> <span class="pl-k">function</span> <span class="pl-en">preProcess</span>(<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>)
    {
        <span class="pl-k">foreach</span> (<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">dispatcher</span>-&gt;<span class="pl-en">getListeners</span>(<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>) <span class="pl-k">as</span> <span class="pl-s1"><span class="pl-c1">$</span>listener</span>) {
            <span class="pl-s1"><span class="pl-c1">$</span>info</span> = <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">getListenerInfo</span>(<span class="pl-s1"><span class="pl-c1">$</span>listener</span>, <span class="pl-s1"><span class="pl-c1">$</span>eventName</span>);
            <span class="pl-s1"><span class="pl-c1">$</span>name</span> = isset(<span class="pl-s1"><span class="pl-c1">$</span>info</span>[<span class="pl-s">'class'</span>]) ? <span class="pl-s1"><span class="pl-c1">$</span>info</span>[<span class="pl-s">'class'</span>] : <span class="pl-s1"><span class="pl-c1">$</span>info</span>[<span class="pl-s">'type'</span>];
            <span class="pl-s1"><span class="pl-c1">$</span>wrappedListener</span> = <span class="pl-k">new</span> <span class="pl-v">WrappedListener</span>(<span class="pl-s1"><span class="pl-c1">$</span>listener</span>, <span class="pl-s1"><span class="pl-c1">$</span>name</span>, <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">stopwatch</span>, <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>);
            <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">wrappedListeners</span>[<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>][] = <span class="pl-s1"><span class="pl-c1">$</span>wrappedListener</span>;
            <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">dispatcher</span>-&gt;<span class="pl-en">removeListener</span>(<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>, <span class="pl-s1"><span class="pl-c1">$</span>listener</span>);
            <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">dispatcher</span>-&gt;<span class="pl-en">addListener</span>(<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>, <span class="pl-s1"><span class="pl-c1">$</span>wrappedListener</span>, <span class="pl-s1"><span class="pl-c1">$</span>info</span>[<span class="pl-s">'priority'</span>]);
        }
    }</pre></div>
<p dir="auto">During the call <code class="notranslate">$this-&gt;dispatcher-&gt;removeListener($eventName, $listener);</code>, the underlying ContainerAwareEventDispatcher executes the following code:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public function removeListener($eventName, $listener)
    {
        $this-&gt;lazyLoad($eventName);
        if (isset($this-&gt;listenerIds[$eventName])) {
            foreach ($this-&gt;listenerIds[$eventName] as $i =&gt; list($serviceId, $method, $priority)) {
                $key = $serviceId.'.'.$method;
                if (isset($this-&gt;listeners[$eventName][$key]) &amp;&amp; $listener === array($this-&gt;listeners[$eventName][$key], $method)) {
                    unset($this-&gt;listeners[$eventName][$key]);
                    if (empty($this-&gt;listeners[$eventName])) {
                        unset($this-&gt;listeners[$eventName]);
                    }
                    unset($this-&gt;listenerIds[$eventName][$i]);
                    if (empty($this-&gt;listenerIds[$eventName])) {
                        unset($this-&gt;listenerIds[$eventName]);
                    }
                }
            }
        }
        parent::removeListener($eventName, $listener);
    }"><pre class="notranslate"><span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">removeListener</span>(<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>, <span class="pl-s1"><span class="pl-c1">$</span>listener</span>)
    {
        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">lazyLoad</span>(<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>);
        <span class="pl-k">if</span> (isset(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">listenerIds</span>[<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>])) {
            <span class="pl-k">foreach</span> (<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">listenerIds</span>[<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>] <span class="pl-k">as</span> <span class="pl-s1"><span class="pl-c1">$</span>i</span> =&gt; <span class="pl-en">list</span>(<span class="pl-s1"><span class="pl-c1">$</span>serviceId</span>, <span class="pl-s1"><span class="pl-c1">$</span>method</span>, <span class="pl-s1"><span class="pl-c1">$</span>priority</span>)) {
                <span class="pl-s1"><span class="pl-c1">$</span>key</span> = <span class="pl-s1"><span class="pl-c1">$</span>serviceId</span>.<span class="pl-s">'.'</span>.<span class="pl-s1"><span class="pl-c1">$</span>method</span>;
                <span class="pl-k">if</span> (isset(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">listeners</span>[<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>][<span class="pl-s1"><span class="pl-c1">$</span>key</span>]) &amp;&amp; <span class="pl-s1"><span class="pl-c1">$</span>listener</span> === <span class="pl-en">array</span>(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">listeners</span>[<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>][<span class="pl-s1"><span class="pl-c1">$</span>key</span>], <span class="pl-s1"><span class="pl-c1">$</span>method</span>)) {
                    unset(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">listeners</span>[<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>][<span class="pl-s1"><span class="pl-c1">$</span>key</span>]);
                    <span class="pl-k">if</span> (empty(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">listeners</span>[<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>])) {
                        unset(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">listeners</span>[<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>]);
                    }
                    unset(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">listenerIds</span>[<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>][<span class="pl-s1"><span class="pl-c1">$</span>i</span>]);
                    <span class="pl-k">if</span> (empty(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">listenerIds</span>[<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>])) {
                        unset(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">listenerIds</span>[<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>]);
                    }
                }
            }
        }
        <span class="pl-smi">parent</span>::<span class="pl-en">removeListener</span>(<span class="pl-s1"><span class="pl-c1">$</span>eventName</span>, <span class="pl-s1"><span class="pl-c1">$</span>listener</span>);
    }</pre></div>
<p dir="auto">The second part of the expression <code class="notranslate">if (isset($this-&gt;listeners[$eventName][$key]) &amp;&amp; $listener === array($this-&gt;listeners[$eventName][$key], $method))</code> returns now <em>false</em> in 3.0 while it's returning <em>true</em> in v2.8.</p>
<p dir="auto">Apart of the minor changes for the list() usage, it seems the code between the two versions is identical, up to the <em>EventDispatcher</em> class. I've checked all the methods involved (<code class="notranslate">addListener</code>,<code class="notranslate">getListeners</code>, <code class="notranslate">removeListener</code>) and I can't find any difference that could lead to the different result.</p>
<p dir="auto">The final test I did to prove I was not messing up with something it's been to test a Signup procedure in dev environment with v3.0 and received two signup mails. Changed composer.json to v2.8, updated, and run the same scenario and received only one mail.</p>
<p dir="auto">In prod environment, when no TraceableEventDispatcher is involved, either v2.8 or v3.0 works fine.</p>
<p dir="auto">Use of a broken or else risky cryptographic algorithm  md5() in symfony/debug/ErrorHandler.php</p>
<p dir="auto">This function uses the !php_standard_ns.md5() function, which uses a hash algorithm that is considered weak. In recent years, researchers have demonstrated ways to breach many uses of previously-thought-safe hash functions such as MD5.</p>