1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\EventDispatcher;
13
14use Psr\EventDispatcher\StoppableEventInterface;
15use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
16use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface;
17
18/**
19 * A helper class to provide BC/FC with the legacy signature of EventDispatcherInterface::dispatch().
20 *
21 * This class should be deprecated in Symfony 5.1
22 *
23 * @author Nicolas Grekas <p@tchwork.com>
24 */
25final class LegacyEventDispatcherProxy implements EventDispatcherInterface
26{
27    private $dispatcher;
28
29    public static function decorate(?ContractsEventDispatcherInterface $dispatcher): ?ContractsEventDispatcherInterface
30    {
31        if (null === $dispatcher) {
32            return null;
33        }
34        $r = new \ReflectionMethod($dispatcher, 'dispatch');
35        $param2 = $r->getParameters()[1] ?? null;
36
37        if (!$param2 || !$param2->hasType() || $param2->getType()->isBuiltin()) {
38            return $dispatcher;
39        }
40
41        @trigger_error(sprintf('The signature of the "%s::dispatch()" method should be updated to "dispatch($event, string $eventName = null)", not doing so is deprecated since Symfony 4.3.', $r->class), \E_USER_DEPRECATED);
42
43        $self = new self();
44        $self->dispatcher = $dispatcher;
45
46        return $self;
47    }
48
49    /**
50     * {@inheritdoc}
51     *
52     * @param string|null $eventName
53     *
54     * @return object
55     */
56    public function dispatch($event/*, string $eventName = null*/)
57    {
58        $eventName = 1 < \func_num_args() ? func_get_arg(1) : null;
59
60        if (\is_object($event)) {
61            $eventName = $eventName ?? \get_class($event);
62        } elseif (\is_string($event) && (null === $eventName || $eventName instanceof ContractsEvent || $eventName instanceof Event)) {
63            @trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.', ContractsEventDispatcherInterface::class), \E_USER_DEPRECATED);
64            $swap = $event;
65            $event = $eventName ?? new Event();
66            $eventName = $swap;
67        } else {
68            throw new \TypeError(sprintf('Argument 1 passed to "%s::dispatch()" must be an object, "%s" given.', ContractsEventDispatcherInterface::class, \is_object($event) ? \get_class($event) : \gettype($event)));
69        }
70
71        $listeners = $this->getListeners($eventName);
72        $stoppable = $event instanceof Event || $event instanceof ContractsEvent || $event instanceof StoppableEventInterface;
73
74        foreach ($listeners as $listener) {
75            if ($stoppable && $event->isPropagationStopped()) {
76                break;
77            }
78            $listener($event, $eventName, $this);
79        }
80
81        return $event;
82    }
83
84    /**
85     * {@inheritdoc}
86     */
87    public function addListener($eventName, $listener, $priority = 0)
88    {
89        return $this->dispatcher->addListener($eventName, $listener, $priority);
90    }
91
92    /**
93     * {@inheritdoc}
94     */
95    public function addSubscriber(EventSubscriberInterface $subscriber)
96    {
97        return $this->dispatcher->addSubscriber($subscriber);
98    }
99
100    /**
101     * {@inheritdoc}
102     */
103    public function removeListener($eventName, $listener)
104    {
105        return $this->dispatcher->removeListener($eventName, $listener);
106    }
107
108    /**
109     * {@inheritdoc}
110     */
111    public function removeSubscriber(EventSubscriberInterface $subscriber)
112    {
113        return $this->dispatcher->removeSubscriber($subscriber);
114    }
115
116    /**
117     * {@inheritdoc}
118     */
119    public function getListeners($eventName = null): array
120    {
121        return $this->dispatcher->getListeners($eventName);
122    }
123
124    /**
125     * {@inheritdoc}
126     */
127    public function getListenerPriority($eventName, $listener): ?int
128    {
129        return $this->dispatcher->getListenerPriority($eventName, $listener);
130    }
131
132    /**
133     * {@inheritdoc}
134     */
135    public function hasListeners($eventName = null): bool
136    {
137        return $this->dispatcher->hasListeners($eventName);
138    }
139
140    /**
141     * Proxies all method calls to the original event dispatcher.
142     */
143    public function __call($method, $arguments)
144    {
145        return $this->dispatcher->{$method}(...$arguments);
146    }
147}
148