diff --git a/src/Symfony/Bundle/AsseticBundle/Command/DumpCommand.php b/src/Symfony/Bundle/AsseticBundle/Command/DumpCommand.php index d4d08243d2..54f9fea2a0 100644 --- a/src/Symfony/Bundle/AsseticBundle/Command/DumpCommand.php +++ b/src/Symfony/Bundle/AsseticBundle/Command/DumpCommand.php @@ -14,11 +14,12 @@ namespace Symfony\Bundle\AsseticBundle\Command; use Assetic\Asset\AssetInterface; use Assetic\Factory\LazyAssetManager; use Symfony\Bundle\FrameworkBundle\Command\Command; +use Symfony\Bundle\AsseticBundle\Event\WriteEvent; +use Symfony\Bundle\AsseticBundle\Events; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\EventDispatcher\Event; /** * Dumps assets to the filesystem. @@ -46,7 +47,8 @@ class DumpCommand extends Command $am = $this->container->get('assetic.asset_manager'); // notify an event so custom stream wrappers can be registered lazily - $this->container->get('event_dispatcher')->notify(new Event(null, 'assetic.write', array('path' => $basePath))); + $event = new WriteEvent($basePath); + $this->container->get('event_dispatcher')->dispatch(Events::onAsseticWrite, $writeEvent); if ($input->getOption('watch')) { return $this->watch($am, $basePath, $output, $this->container->getParameter('kernel.debug')); diff --git a/src/Symfony/Bundle/AsseticBundle/Event/WriteEvent.php b/src/Symfony/Bundle/AsseticBundle/Event/WriteEvent.php new file mode 100644 index 0000000000..2d3ff8e35e --- /dev/null +++ b/src/Symfony/Bundle/AsseticBundle/Event/WriteEvent.php @@ -0,0 +1,32 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle\Event; + +use Symfony\Component\EventDispatcher\Event; + +/** + * @author Bernhard Schussek + */ +class WriteEvent extends Event +{ + private $targetPath; + + public function __construct($targetPath = null) + { + $this->targetPath = $targetPath; + } + + public function getTargetPath() + { + return $this->targetPath; + } +} diff --git a/src/Symfony/Bundle/AsseticBundle/Events.php b/src/Symfony/Bundle/AsseticBundle/Events.php new file mode 100644 index 0000000000..3fc8011973 --- /dev/null +++ b/src/Symfony/Bundle/AsseticBundle/Events.php @@ -0,0 +1,20 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Symfony\Bundle\AsseticBundle; + +/** + * @author Bernhard Schussek + */ +final class Events +{ + const onAsseticWrite = 'onAsseticWrite'; +} diff --git a/src/Symfony/Bundle/DoctrineBundle/ConnectionFactory.php b/src/Symfony/Bundle/DoctrineBundle/ConnectionFactory.php index 6f265f405c..2f485d1b7f 100644 --- a/src/Symfony/Bundle/DoctrineBundle/ConnectionFactory.php +++ b/src/Symfony/Bundle/DoctrineBundle/ConnectionFactory.php @@ -36,7 +36,7 @@ class ConnectionFactory /** * Create a connection by name. - * + * * @param string $connectionName * @return Doctrine\DBAL\Connection */ diff --git a/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/Compiler/RegisterEventListenersAndSubscribersPass.php b/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/Compiler/RegisterEventListenersAndSubscribersPass.php index fe25624e92..71c568901e 100644 --- a/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/Compiler/RegisterEventListenersAndSubscribersPass.php +++ b/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/Compiler/RegisterEventListenersAndSubscribersPass.php @@ -32,7 +32,7 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface ); foreach ($subscribers as $id => $instances) { - $definition->addMethodCall('addEventSubscriber', array(new Reference($id))); + $definition->addMethodCall('addSubscriber', array(new Reference($id))); } } @@ -52,7 +52,7 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface } if (0 < count($events)) { - $definition->addMethodCall('addEventListener', array( + $definition->addMethodCall('addListener', array( $events, new Reference($listenerId), )); diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/Command/InfoDoctrineODMCommand.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/Command/InfoDoctrineODMCommand.php index dbac5f59f2..413e4182d0 100644 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/Command/InfoDoctrineODMCommand.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/Command/InfoDoctrineODMCommand.php @@ -3,7 +3,7 @@ /* * This file is part of the Symfony package. * - * (c) Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/DependencyInjection/Compiler/RegisterEventListenersAndSubscribersPass.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/DependencyInjection/Compiler/RegisterEventListenersAndSubscribersPass.php index 6a8a6c68fd..8ce9a65afb 100644 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/DependencyInjection/Compiler/RegisterEventListenersAndSubscribersPass.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/DependencyInjection/Compiler/RegisterEventListenersAndSubscribersPass.php @@ -29,7 +29,7 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface ); foreach ($subscribers as $id => $instances) { - $definition->addMethodCall('addEventSubscriber', array(new Reference($id))); + $definition->addMethodCall('addSubscriber', array(new Reference($id))); } } @@ -49,7 +49,7 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface } if (0 < count($events)) { - $definition->addMethodCall('addEventListener', array( + $definition->addMethodCall('addListener', array( $events, new Reference($listenerId), )); diff --git a/src/Symfony/Bundle/FrameworkBundle/ContainerAwareEventDispatcher.php b/src/Symfony/Bundle/FrameworkBundle/ContainerAwareEventDispatcher.php new file mode 100644 index 0000000000..6931e4cd21 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/ContainerAwareEventDispatcher.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\Event; + +/** + * Lazily loads listeners and subscribers from the dependency injection + * container + * + * @author Fabien Potencier + * @author Bernhard Schussek + */ +class ContainerAwareEventDispatcher extends EventDispatcher +{ + /** + * The container from where services are loaded + * @var ContainerInterface + */ + private $container; + + /** + * The service IDs of the event listeners and subscribers + * @var array + */ + private $listenerIds; + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Adds a service as event listener + * + * @param string|array $events One or more events for which the listener + * is added + * @param string $serviceId The ID of the listener service + * @param integer $priority The higher this value, the earlier an event + * listener will be triggered in the chain. + * Defaults to 0. + */ + public function addListenerService($events, $serviceId, $priority = 0) + { + if (!is_string($serviceId)) { + throw new \InvalidArgumentException('Expected a string argument'); + } + + foreach ((array)$events as $event) { + // Prevent duplicate entries + $this->listenerIds[$event][$serviceId] = $priority; + } + } + + /** + * {@inheritDoc} + * + * Lazily loads listeners for this event from the dependency injection + * container. + */ + public function dispatch($eventName, Event $event = null) + { + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $serviceId => $priority) { + $this->addListener($eventName, $this->container->get($serviceId), $priority); + } + } + + parent::dispatch($eventName, $event); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Debug/EventDispatcher.php b/src/Symfony/Bundle/FrameworkBundle/Debug/EventDispatcher.php deleted file mode 100644 index 24b6f56961..0000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Debug/EventDispatcher.php +++ /dev/null @@ -1,166 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Debug; - -use Symfony\Bundle\FrameworkBundle\EventDispatcher as BaseEventDispatcher; -use Symfony\Component\EventDispatcher\EventInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\HttpKernel\Debug\EventDispatcherTraceableInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; - -/** - * EventDispatcher extends the original EventDispatcher class to add some debugging tools. - * - * @author Fabien Potencier - */ -class EventDispatcher extends BaseEventDispatcher implements EventDispatcherTraceableInterface -{ - protected $logger; - protected $called; - - /** - * Constructor. - * - * @param ContainerInterface $container A ContainerInterface instance - * @param LoggerInterface $logger A LoggerInterface instance - */ - public function __construct(ContainerInterface $container, LoggerInterface $logger = null) - { - parent::__construct($container); - - $this->logger = $logger; - $this->called = array(); - } - - /** - * {@inheritDoc} - */ - public function notify(EventInterface $event) - { - foreach ($this->getListeners($event->getName()) as $listener) { - $this->addCall($event, $listener, 'notify'); - - if (is_array($listener) && is_string($listener[0])) { - $listener[0] = $this->container->get($listener[0]); - } - - call_user_func($listener, $event); - } - } - - /** - * {@inheritDoc} - */ - public function notifyUntil(EventInterface $event) - { - foreach ($this->getListeners($event->getName()) as $i => $listener) { - $this->addCall($event, $listener, 'notifyUntil'); - - if (is_array($listener) && is_string($listener[0])) { - $listener[0] = $this->container->get($listener[0]); - } - - $ret = call_user_func($listener, $event); - if ($event->isProcessed()) { - if (null !== $this->logger) { - $this->logger->debug(sprintf('Listener "%s" processed the event "%s"', $this->listenerToString($listener), $event->getName())); - - $listeners = $this->getListeners($event->getName()); - while (++$i < count($listeners)) { - $this->logger->debug(sprintf('Listener "%s" was not called for event "%s"', $this->listenerToString($listeners[$i]), $event->getName())); - } - } - - return $ret; - } - } - } - - /** - * {@inheritDoc} - */ - public function filter(EventInterface $event, $value) - { - foreach ($this->getListeners($event->getName()) as $listener) { - $this->addCall($event, $listener, 'filter'); - - if (is_array($listener) && is_string($listener[0])) { - $listener[0] = $this->container->get($listener[0]); - } - - $value = call_user_func($listener, $event, $value); - } - - return $value; - } - - /** - * {@inheritDoc} - */ - public function getCalledListeners() - { - return $this->called; - } - - /** - * {@inheritDoc} - */ - public function getNotCalledListeners() - { - $notCalled = array(); - - foreach (array_keys($this->listeners) as $name) { - foreach ($this->getListeners($name) as $listener) { - $listener = $this->listenerToString($listener); - - if (!isset($this->called[$name.'.'.$listener])) { - $notCalled[] = array( - 'event' => $name, - 'listener' => $listener, - ); - } - } - } - - return $notCalled; - } - - protected function listenerToString($listener) - { - if (is_object($listener) && $listener instanceof \Closure) { - return 'Closure'; - } - - if (is_string($listener)) { - return $listener; - } - - if (is_array($listener)) { - return is_object($listener[0]) ? sprintf('%s::%s', get_class($listener[0]), $listener[1]) : implode(':', $listener); - } - } - - protected function addCall(EventInterface $event, $listener, $type) - { - $listener = $this->listenerToString($listener); - - if (null !== $this->logger) { - $this->logger->debug(sprintf('Notified event "%s" to listener "%s" (%s)', $event->getName(), $listener, $type)); - } - - $this->called[$event->getName().'.'.$listener] = array( - 'event' => $event->getName(), - 'caller' => null !== $event->getSubject() ? get_class($event->getSubject()) : null, - 'listener' => $listener, - ); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Debug/TraceableEventDispatcher.php b/src/Symfony/Bundle/FrameworkBundle/Debug/TraceableEventDispatcher.php new file mode 100644 index 0000000000..24747f6683 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Debug/TraceableEventDispatcher.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Debug; + +use Symfony\Bundle\FrameworkBundle\ContainerAwareEventDispatcher; +use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcherInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\EventDispatcher\Event; + +/** + * Extends the ContainerAwareEventDispatcher to add some debugging tools. + * + * @author Fabien Potencier + */ +class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements TraceableEventDispatcherInterface +{ + protected $logger; + protected $called; + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(ContainerInterface $container, LoggerInterface $logger = null) + { + parent::__construct($container); + + $this->logger = $logger; + $this->called = array(); + } + + /** + * {@inheritDoc} + */ + protected function triggerListener($listener, $eventName, Event $event) + { + parent::triggerListener($listener, $eventName, $event); + + $listenerString = $this->listenerToString($listener); + + if (null !== $this->logger) { + $this->logger->debug(sprintf('Notified event "%s" to listener "%s"', $eventName, $listenerString)); + } + + $this->called[$eventName.'.'.$listenerString] = array( + 'class' => $listenerString, + 'event' => $eventName, + ); + + if ($event->isPropagationStopped() && null !== $this->logger) { + $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s"', $this->listenerToString($listener), $eventName)); + + $skippedListeners = $this->getListeners($eventName); + $skipped = false; + + foreach ($skippedListeners as $skippedListener) { + if ($skipped) { + $this->logger->debug(sprintf('Listener "%s" was not called for event "%s"', $this->listenerToString($skippedListener), $eventName)); + } + + if ($skippedListener === $listener) { + $skipped = false; + } + } + } + } + + /** + * {@inheritDoc} + */ + public function getCalledListeners() + { + return $this->called; + } + + /** + * {@inheritDoc} + */ + public function getNotCalledListeners() + { + $notCalled = array(); + foreach (array_keys($this->getListeners()) as $name) { + foreach ($this->getListeners($name) as $listener) { + $listener = $this->listenerToString($listener); + if (!isset($this->called[$name.'.'.$listener])) { + $notCalled[] = array( + 'class' => $listener, + 'event' => $name, + ); + } + } + } + + return $notCalled; + } + + protected function listenerToString($listener) + { + if (is_object($listener)) { + if ($listener instanceof \Closure) { + return 'Closure'; + } + + return get_class($listener); + } + + if (is_array($listener)) { + return is_object($listener[0]) ? get_class($listener[0]) : implode('::', $listener); + } + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RegisterKernelListenersPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RegisterKernelListenersPass.php index 0f7d9cb756..91ddd4fc7d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RegisterKernelListenersPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RegisterKernelListenersPass.php @@ -24,30 +24,18 @@ class RegisterKernelListenersPass implements CompilerPassInterface } $listeners = array(); + $definition = $container->getDefinition('event_dispatcher'); + foreach ($container->findTaggedServiceIds('kernel.listener') as $id => $events) { foreach ($events as $event) { $priority = isset($event['priority']) ? $event['priority'] : 0; + if (!isset($event['event'])) { throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "kernel.listener" tags.', $id)); } - if (!isset($event['method'])) { - throw new \InvalidArgumentException(sprintf('Service "%s" must define the "method" attribute on "kernel.listener" tags.', $id)); - } - if (!isset($listeners[$event['event']][$priority])) { - if (!isset($listeners[$event['event']])) { - $listeners[$event['event']] = array(); - } - $listeners[$event['event']][$priority] = array(); - } - - $listeners[$event['event']][$priority][] = array($id, $event['method']); + $definition->addMethodCall('addListenerService', array($event['event'], $id, $priority)); } } - - $container - ->getDefinition('event_dispatcher') - ->addMethodCall('registerKernelListeners', array($listeners)) - ; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 99135736c5..70a306620a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -140,17 +140,24 @@ class FrameworkExtension extends Extension 'Symfony\\Component\\HttpKernel\\ResponseListener', 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver', 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface', + 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent', + 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent', + 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent', + 'Symfony\\Component\\HttpKernel\\Events', + + 'Symfony\\Component\\EventDispatcher\\EventDispatcher', + 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface', + 'Symfony\\Component\\EventDispatcher\\Event', + 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface', 'Symfony\\Bundle\\FrameworkBundle\\RequestListener', 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser', 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver', 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller', - - 'Symfony\\Component\\EventDispatcher\\EventInterface', - 'Symfony\\Component\\EventDispatcher\\Event', - 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface', - 'Symfony\\Component\\EventDispatcher\\EventDispatcher', - 'Symfony\\Bundle\\FrameworkBundle\\EventDispatcher', + 'Symfony\\Bundle\\FrameworkBundle\\ContainerAwareEventDispatcher', )); } diff --git a/src/Symfony/Bundle/FrameworkBundle/EventDispatcher.php b/src/Symfony/Bundle/FrameworkBundle/EventDispatcher.php deleted file mode 100644 index 4f1ad5dab3..0000000000 --- a/src/Symfony/Bundle/FrameworkBundle/EventDispatcher.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle; - -use Symfony\Component\EventDispatcher\EventDispatcher as BaseEventDispatcher; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\EventDispatcher\EventInterface; - -/** - * This EventDispatcher automatically gets the kernel listeners injected - * - * @author Fabien Potencier - */ -class EventDispatcher extends BaseEventDispatcher -{ - protected $container; - - /** - * Constructor. - * - * @param ContainerInterface $container A ContainerInterface instance - */ - public function __construct(ContainerInterface $container) - { - $this->container = $container; - } - - public function registerKernelListeners(array $listeners) - { - $this->listeners = $listeners; - } - - /** - * {@inheritdoc} - */ - public function notify(EventInterface $event) - { - foreach ($this->getListeners($event->getName()) as $listener) { - if (is_array($listener) && is_string($listener[0])) { - $listener[0] = $this->container->get($listener[0]); - } - call_user_func($listener, $event); - } - } - - /** - * {@inheritdoc} - */ - public function notifyUntil(EventInterface $event) - { - foreach ($this->getListeners($event->getName()) as $listener) { - if (is_array($listener) && is_string($listener[0])) { - $listener[0] = $this->container->get($listener[0]); - } - - $ret = call_user_func($listener, $event); - if ($event->isProcessed()) { - return $ret; - } - } - } - - /** - * {@inheritdoc} - */ - public function filter(EventInterface $event, $value) - { - foreach ($this->getListeners($event->getName()) as $listener) { - if (is_array($listener) && is_string($listener[0])) { - $listener[0] = $this->container->get($listener[0]); - } - $value = call_user_func($listener, $event, $value); - } - - return $value; - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/HttpFoundation/SessionListener.php b/src/Symfony/Bundle/FrameworkBundle/HttpFoundation/SessionListener.php index a9477e451c..58a5356c6f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/HttpFoundation/SessionListener.php +++ b/src/Symfony/Bundle/FrameworkBundle/HttpFoundation/SessionListener.php @@ -11,10 +11,10 @@ namespace Symfony\Bundle\FrameworkBundle\HttpFoundation; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; /** * SessionListener. @@ -27,23 +27,18 @@ class SessionListener { /** * Checks if session was initialized and saves if current request is master - * Runs on 'core.response' in test environment + * Runs on 'onCoreResponse' in test environment * - * @param EventInterface $event - * @param Response $response - * - * @return Response + * @param FilterResponseEvent $event */ - public function filter(EventInterface $event, Response $response) + public function onCoreResponse(FilterResponseEvent $event) { - if ($request = $event->get('request')) { - if (HttpKernelInterface::MASTER_REQUEST === $event->get('request_type')) { + if ($request = $event->getRequest()) { + if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) { if ($session = $request->getSession()) { $session->save(); } } } - - return $response; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/HttpKernel.php b/src/Symfony/Bundle/FrameworkBundle/HttpKernel.php index acdf419bd2..51edce231f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/HttpKernel.php +++ b/src/Symfony/Bundle/FrameworkBundle/HttpKernel.php @@ -16,18 +16,14 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; */ class HttpKernel extends BaseHttpKernel { - protected $container; - protected $esiSupport; + private $container; + private $esiSupport; - public function __construct(ContainerInterface $container, ControllerResolverInterface $controllerResolver) + public function __construct(EventDispatcherInterface $dispatcher, ContainerInterface $container, ControllerResolverInterface $controllerResolver) { + parent::__construct($dispatcher, $controllerResolver); + $this->container = $container; - $this->resolver = $controllerResolver; - } - - public function setEventDispatcher(EventDispatcherInterface $dispatcher) - { - $this->dispatcher = $dispatcher; } public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) diff --git a/src/Symfony/Bundle/FrameworkBundle/Profiler/ProfilerListener.php b/src/Symfony/Bundle/FrameworkBundle/Profiler/ProfilerListener.php index 9d7a815a37..191a97ff64 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Profiler/ProfilerListener.php +++ b/src/Symfony/Bundle/FrameworkBundle/Profiler/ProfilerListener.php @@ -11,17 +11,16 @@ namespace Symfony\Bundle\FrameworkBundle\Profiler; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpFoundation\RequestMatcherInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * ProfilerListener collects data for the current request by listening to the core.response event. - * - * The handleException method must be connected to the core.exception event. - * The handleResponse method must be connected to the core.response event. + * ProfilerListener collects data for the current request by listening to the onCoreResponse event. * * @author Fabien Potencier */ @@ -47,49 +46,47 @@ class ProfilerListener } /** - * Handles the core.request event + * Handles the onCoreRequest event * * This method initialize the profiler to be able to get it as a scoped - * service when handleResponse() will collect the sub request + * service when onCoreResponse() will collect the sub request * - * @param EventInterface $event An EventInterface instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public function handleRequest(EventInterface $event) + public function onCoreRequest(GetResponseEvent $event) { $this->container->get('profiler'); } /** - * Handles the core.exception event. + * Handles the onCoreException event. * - * @param EventInterface $event An EventInterface instance + * @param GetResponseForExceptionEvent $event A GetResponseForExceptionEvent instance */ - public function handleException(EventInterface $event) + public function onCoreException(GetResponseForExceptionEvent $event) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->get('request_type')) { - return false; + if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + return; } - $this->exception = $event->get('exception'); - - return false; + $this->exception = $event->getException(); } /** - * Handles the core.response event. + * Handles the onCoreResponse event. * - * @param EventInterface $event An EventInterface instance - * - * @return Response $response A Response instance + * @param FilterResponseEvent $event A FilterResponseEvent instance */ - public function handleResponse(EventInterface $event, Response $response) + public function onCoreResponse(FilterResponseEvent $event) { - if (null !== $this->matcher && !$this->matcher->matches($event->get('request'))) { + $response = $event->getResponse(); + + if (null !== $this->matcher && !$this->matcher->matches($event->getRequest())) { return $response; } if ($this->onlyException && null === $this->exception) { - return $response; + return; } $profiler = $this->container->get('profiler'); @@ -98,9 +95,7 @@ class ProfilerListener $profiler->setParent($parent['request']['profiler']->getToken()); } - $profiler->collect($event->get('request'), $response, $this->exception); + $profiler->collect($event->getRequest(), $event->getResponse(), $this->exception); $this->exception = null; - - return $response; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/RequestListener.php b/src/Symfony/Bundle/FrameworkBundle/RequestListener.php index 0936794605..9d50c32065 100644 --- a/src/Symfony/Bundle/FrameworkBundle/RequestListener.php +++ b/src/Symfony/Bundle/FrameworkBundle/RequestListener.php @@ -13,16 +13,14 @@ namespace Symfony\Bundle\FrameworkBundle; use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * RequestListener. * - * The handle method must be connected to the core.request event. - * * @author Fabien Potencier */ class RequestListener @@ -38,10 +36,10 @@ class RequestListener $this->logger = $logger; } - public function handle(EventInterface $event) + public function onCoreRequest(GetResponseEvent $event) { - $request = $event->get('request'); - $master = HttpKernelInterface::MASTER_REQUEST === $event->get('request_type'); + $request = $event->getRequest(); + $master = HttpKernelInterface::MASTER_REQUEST === $event->getRequestType(); $this->initializeSession($request, $master); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml index 3adec67c81..c1fefe3eaa 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - Symfony\Bundle\FrameworkBundle\Debug\EventDispatcher + Symfony\Bundle\FrameworkBundle\Debug\TraceableEventDispatcher diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml index 148affda71..a23791d5a0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml @@ -13,7 +13,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml index 15da6e8a0d..0659d3b023 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml @@ -23,9 +23,9 @@ - - - + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml index 0e4c6ddabc..3a1f259b44 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - Symfony\Bundle\FrameworkBundle\EventDispatcher + Symfony\Bundle\FrameworkBundle\ContainerAwareEventDispatcher Symfony\Bundle\FrameworkBundle\HttpKernel Symfony\Component\HttpKernel\Debug\ErrorHandler null @@ -25,9 +25,9 @@ + - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml index 8a9051f2a0..757ad7faee 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml @@ -25,7 +25,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml index 6c48bec18d..61637e7166 100755 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml @@ -26,19 +26,19 @@ - + - + %kernel.charset% - + %exception_listener.controller% diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/HttpFoundation/SessionListenerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/HttpFoundation/SessionListenerTest.php index 4bb0d8543b..b9d86cc827 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/HttpFoundation/SessionListenerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/HttpFoundation/SessionListenerTest.php @@ -12,10 +12,10 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\HttpFoundation; use Symfony\Bundle\FrameworkBundle\HttpFoundation\SessionListener; -use Symfony\Component\EventDispatcher\Event; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; /** * SessionListenerTest. @@ -53,10 +53,12 @@ class SessionListenerTest extends \PHPUnit_Framework_TestCase { $request->setSession($this->session); $response = new Response(); + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $event = new FilterResponseEvent($kernel, $request, $type, $response); - $this->assertSame($response, $this->listener->filter(new Event( - $this, 'core.response', array('request' => $request, 'request_type' => $type) - ), $response)); + $this->listener->onCoreResponse($event); + + $this->assertSame($response, $event->getResponse()); } private function sessionMustNotBeSaved() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/HttpKernelTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/HttpKernelTest.php index e6d33ffac7..9942684384 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/HttpKernelTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/HttpKernelTest.php @@ -5,9 +5,9 @@ namespace Symfony\Bundle\FrameworkBundle\Tests; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Bundle\FrameworkBundle\HttpKernel; +use Symfony\Component\EventDispatcher\EventDispatcher; class HttpKernelTest extends \PHPUnit_Framework_TestCase { @@ -38,8 +38,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase $dispatcher = new EventDispatcher(); $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); - $kernel = new HttpKernel($container, $resolver); - $kernel->setEventDispatcher($dispatcher); + $kernel = new HttpKernel($dispatcher, $container, $resolver); $controller = function() use($expected) { @@ -87,8 +86,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase $dispatcher = new EventDispatcher(); $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); - $kernel = new HttpKernel($container, $resolver); - $kernel->setEventDispatcher($dispatcher); + $kernel = new HttpKernel($dispatcher, $container, $resolver); $controller = function() use ($expected) { diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index d78770188a..7d2b44b953 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -106,7 +106,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml index 343f82b76d..9f0cd54c40 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml @@ -51,7 +51,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/ResponseListener.php b/src/Symfony/Bundle/SecurityBundle/ResponseListener.php index 93b1c8af6d..00e56c4141 100644 --- a/src/Symfony/Bundle/SecurityBundle/ResponseListener.php +++ b/src/Symfony/Bundle/SecurityBundle/ResponseListener.php @@ -3,7 +3,7 @@ namespace Symfony\Bundle\SecurityBundle; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; /** @@ -13,15 +13,13 @@ use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; */ class ResponseListener { - public function handle(EventInterface $event, Response $response) + public function onCoreResponse(FilterResponseEvent $event) { - $request = $event->get('request'); - if (!$request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)) { - return $response; + $request = $event->getRequest(); + $response = $event->getResponse(); + + if ($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)) { + $response->headers->setCookie($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)); } - - $response->headers->setCookie($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)); - - return $response; } } \ No newline at end of file diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml index 6d837c5b02..6ca5ba0419 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml @@ -11,7 +11,7 @@ - + %debug.toolbar.intercept_redirects% diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig index 98148c4b96..eb55d9070a 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig @@ -12,15 +12,13 @@ + - - {% for elements in collector.calledlisteners %} + - - {% endfor %}
Class EventCallerListener
{{ elements.class|abbr_class }} {{ elements.event }}{{ elements.caller|abbr_class }}{{ elements.listener|abbr_method }}
@@ -35,8 +33,8 @@ {% for elements in collector.notcalledlisteners %} + {{ elements.class|abbr_class }} {{ elements.event }} - {{ elements.listener|abbr_method }} {% endfor %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/WebDebugToolbarListener.php b/src/Symfony/Bundle/WebProfilerBundle/WebDebugToolbarListener.php index e83f72cbb3..bd83a4e706 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/WebDebugToolbarListener.php +++ b/src/Symfony/Bundle/WebProfilerBundle/WebDebugToolbarListener.php @@ -11,17 +11,17 @@ namespace Symfony\Bundle\WebProfilerBundle; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Bundle\FrameworkBundle\HttpKernel; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Bundle\TwigBundle\TwigEngine; /** * WebDebugToolbarListener injects the Web Debug Toolbar. * - * The handle method must be connected to the core.response event. + * The handle method must be connected to the onCoreResponse event. * * The WDT is only injected on well-formed HTML (with a proper tag). * This means that the WDT is never included in sub-requests or ESI requests. @@ -41,13 +41,15 @@ class WebDebugToolbarListener $this->interceptRedirects = $interceptRedirects; } - public function handle(EventInterface $event, Response $response) + public function onCoreResponse(FilterResponseEvent $event) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->get('request_type')) { - return $response; + if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + return; } - $request = $event->get('request'); + $response = $event->getResponse(); + $request = $event->getRequest(); + if ($response->headers->has('X-Debug-Token') && $response->isRedirect() && $this->interceptRedirects) { // keep current flashes for one more request $request->getSession()->setFlashes($request->getSession()->getFlashes()); @@ -66,12 +68,10 @@ class WebDebugToolbarListener || 'html' !== $request->getRequestFormat() || $request->isXmlHttpRequest() ) { - return $response; + return; } $this->injectToolbar($response); - - return $response; } /** diff --git a/src/Symfony/Component/EventDispatcher/Event.php b/src/Symfony/Component/EventDispatcher/Event.php index 21a1ce83ad..91c43561ba 100644 --- a/src/Symfony/Component/EventDispatcher/Event.php +++ b/src/Symfony/Component/EventDispatcher/Event.php @@ -1,136 +1,70 @@ - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information, see + * . */ namespace Symfony\Component\EventDispatcher; /** - * Event. + * Event is the base class for classes containing event data. * - * @author Fabien Potencier + * This class contains no event data. It is used by events that do not pass + * state information to an event handler when an event is raised. + * + * You can call the method stopPropagation() to abort the execution of + * further listeners in your event listener. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek */ -class Event implements EventInterface +class Event { - protected $processed = false; - protected $subject; - protected $name; - protected $parameters; + /** + * @var Boolean Whether no further event listeners should be triggered + */ + private $propagationStopped = false; /** - * Constructs a new Event. + * Returns whether further event listeners should be triggered. * - * @param mixed $subject The subject - * @param string $name The event name - * @param array $parameters An array of parameters + * @see Event::stopPropagation + * @return Boolean Whether propagation was already stopped for this event. */ - public function __construct($subject, $name, $parameters = array()) + public function isPropagationStopped() { - $this->subject = $subject; - $this->name = $name; - $this->parameters = $parameters; + return $this->propagationStopped; } /** - * Returns the subject. + * Stops the propagation of the event to further event listeners. * - * @return mixed The subject + * If multiple event listeners are connected to the same event, no + * further event listener will be triggered once any trigger calls + * stopPropagation(). */ - public function getSubject() + public function stopPropagation() { - return $this->subject; - } - - /** - * Returns the event name. - * - * @return string The event name - */ - public function getName() - { - return $this->name; - } - - /** - * Sets the processed flag to true. - * - * This method must be called by listeners when - * it has processed the event (it is only meaningful - * when the event has been notified with the notifyUntil() - * dispatcher method. - */ - public function setProcessed() - { - $this->processed = true; - } - - /** - * Returns whether the event has been processed by a listener or not. - * - * This method is only meaningful for events notified - * with notifyUntil(). - * - * @return Boolean true if the event has been processed, false otherwise - */ - public function isProcessed() - { - return $this->processed; - } - - /** - * Returns the event parameters. - * - * @return array The event parameters - */ - public function all() - { - return $this->parameters; - } - - /** - * Returns true if the parameter exists. - * - * @param string $name The parameter name - * - * @return Boolean true if the parameter exists, false otherwise - */ - public function has($name) - { - return array_key_exists($name, $this->parameters); - } - - /** - * Returns a parameter value. - * - * @param string $name The parameter name - * - * @return mixed The parameter value - * - * @throws \InvalidArgumentException When parameter doesn't exists for this event - */ - public function get($name) - { - if (!array_key_exists($name, $this->parameters)) { - throw new \InvalidArgumentException(sprintf('The event "%s" has no "%s" parameter.', $this->name, $name)); - } - - return $this->parameters[$name]; - } - - /** - * Sets a parameter. - * - * @param string $name The parameter name - * @param mixed $value The parameter value - */ - public function set($name, $value) - { - $this->parameters[$name] = $value; + $this->propagationStopped = true; } } diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcher.php b/src/Symfony/Component/EventDispatcher/EventDispatcher.php index 5218ca66e7..c7e752ce8f 100644 --- a/src/Symfony/Component/EventDispatcher/EventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -1,147 +1,204 @@ - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information, see + * . */ namespace Symfony\Component\EventDispatcher; /** - * EventDispatcher implements a dispatcher object. + * The EventDispatcherInterface is the central point of Symfony's event listener system. * - * @author Fabien Potencier + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek */ class EventDispatcher implements EventDispatcherInterface { - protected $listeners = array(); + /** + * Map of registered listeners. + * => ( => ) + * + * @var array + */ + private $listeners = array(); /** - * Connects a listener to a given event name. + * Map of priorities by the object hashes of their listeners. + * => ( => ) * - * Listeners with a higher priority are executed first. + * This property is used for listener sorting. * - * @param string $name An event name - * @param mixed $listener A PHP callable - * @param integer $priority The priority (between -10 and 10 -- defaults to 0) + * @var array */ - public function connect($name, $listener, $priority = 0) + private $priorities = array(); + + /** + * Stores which event listener lists are currently sorted. + * => + * + * @var array + */ + private $sorted = array(); + + /** + * @see EventDispatcherInterface::dispatch + */ + public function dispatch($eventName, Event $event = null) { - if (!isset($this->listeners[$name][$priority])) { - if (!isset($this->listeners[$name])) { - $this->listeners[$name] = array(); + if (isset($this->listeners[$eventName])) { + if (null === $event) { + $event = new Event(); } - $this->listeners[$name][$priority] = array(); - } - $this->listeners[$name][$priority][] = $listener; - } + $this->sortListeners($eventName); - /** - * Disconnects one, or all listeners for the given event name. - * - * @param string $name An event name - * @param mixed|null $listener The listener to remove, or null to remove all - * - * @return void - */ - public function disconnect($name, $listener = null) - { - if (!isset($this->listeners[$name])) { - return; - } + foreach ($this->listeners[$eventName] as $listener) { + $this->triggerListener($listener, $eventName, $event); - if (null === $listener) { - unset($this->listeners[$name]); - return; - } - - foreach ($this->listeners[$name] as $priority => $callables) { - foreach ($callables as $i => $callable) { - if ($listener === $callable) { - unset($this->listeners[$name][$priority][$i]); + if ($event->isPropagationStopped()) { + break; } } } } /** - * Notifies all listeners of a given event. - * - * @param EventInterface $event An EventInterface instance + * @see EventDispatcherInterface::getListeners */ - public function notify(EventInterface $event) + public function getListeners($eventName = null) { - foreach ($this->getListeners($event->getName()) as $listener) { - call_user_func($listener, $event); + if ($eventName) { + $this->sortListeners($eventName); + + return $this->listeners[$eventName]; + } + + foreach ($this->listeners as $eventName => $listeners) { + $this->sortListeners($eventName); + } + + return $this->listeners; + } + + /** + * @see EventDispatcherInterface::hasListeners + */ + public function hasListeners($eventName) + { + return isset($this->listeners[$eventName]) && $this->listeners[$eventName]; + } + + /** + * @see EventDispatcherInterface::addListener + */ + public function addListener($eventNames, $listener, $priority = 0) + { + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + + foreach ((array) $eventNames as $eventName) { + if (!isset($this->listeners[$eventName])) { + $this->listeners[$eventName] = array(); + $this->priorities[$eventName] = array(); + } + + // Prevents duplicate listeners on same event (same instance only) + $this->listeners[$eventName][$hash] = $listener; + $this->priorities[$eventName][$hash] = $priority; + $this->sorted[$eventName] = false; } } /** - * Notifies all listeners of a given event until one processes the event. - * - * @param EventInterface $event An EventInterface instance - * - * @return mixed The returned value of the listener that processed the event + * @see EventDispatcherInterface::removeListener */ - public function notifyUntil(EventInterface $event) + public function removeListener($eventNames, $listener) { - foreach ($this->getListeners($event->getName()) as $listener) { - $ret = call_user_func($listener, $event); - if ($event->isProcessed()) { - return $ret; + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + + foreach ((array) $eventNames as $eventName) { + // Check if actually have this listener associated + if (isset($this->listeners[$eventName][$hash])) { + unset($this->listeners[$eventName][$hash]); + unset($this->priorities[$eventName][$hash]); } } } /** - * Filters a value by calling all listeners of a given event. - * - * @param EventInterface $event An EventInterface instance - * @param mixed $value The value to be filtered - * - * @return mixed The filtered value + * @see EventDispatcherInterface::addSubscriber */ - public function filter(EventInterface $event, $value) + public function addSubscriber(EventSubscriberInterface $subscriber, $priority = 0) { - foreach ($this->getListeners($event->getName()) as $listener) { - $value = call_user_func($listener, $event, $value); - } - - return $value; + $this->addListener($subscriber->getSubscribedEvents(), $subscriber, $priority); } /** - * Returns true if the given event name has some listeners. + * Triggers the listener method for an event. * - * @param string $name The event name + * This method can be overridden to add functionality that is executed + * for each listener. * - * @return Boolean true if some listeners are connected, false otherwise + * @param object $listener The event listener on which to invoke the listener method. + * @param string $eventName The name of the event to dispatch. The name of the event is + * the name of the method that is invoked on listeners. + * @param Event $event The event arguments to pass to the event handlers/listeners. */ - public function hasListeners($name) + protected function triggerListener($listener, $eventName, Event $event) { - return (Boolean) count($this->getListeners($name)); + if ($listener instanceof \Closure) { + $listener->__invoke($event); + } else { + $listener->$eventName($event); + } } /** - * Returns all listeners associated with a given event name. + * Sorts the internal list of listeners for the given event by priority. * - * @param string $name The event name + * Calling this method multiple times will not cause overhead unless you + * add new listeners. As long as no listener is added, the list for an + * event name won't be sorted twice. * - * @return array An array of listeners + * @param string $event The name of the event. */ - public function getListeners($name) + private function sortListeners($eventName) { - if (!isset($this->listeners[$name])) { - return array(); + if (!$this->sorted[$eventName]) { + $p = $this->priorities[$eventName]; + + uasort($this->listeners[$eventName], function ($a, $b) use ($p) { + $order = $p[spl_object_hash($b)] - $p[spl_object_hash($a)]; + + // for the same priority, force the first registered one to stay first + return 0 === $order ? 1 : $order; + }); + + $this->sorted[$eventName] = true; } - - krsort($this->listeners[$name]); - - return call_user_func_array('array_merge', $this->listeners[$name]); } } diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php b/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php index 3b321532ca..2ce076a540 100644 --- a/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php +++ b/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php @@ -12,81 +12,72 @@ namespace Symfony\Component\EventDispatcher; /** - * EventDispatcherInterface describes an event dispatcher class. + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. * - * @see http://developer.apple.com/documentation/Cocoa/Conceptual/Notifications/index.html Apple's Cocoa framework - * - * @author Fabien Potencier + * @author Bernhard Schussek */ interface EventDispatcherInterface { /** - * Connects a listener to a given event name. + * Dispatches an event to all registered listeners. * - * Listeners with a higher priority are executed first. - * - * @param string $name An event name - * @param mixed $listener A PHP callable - * @param integer $priority The priority (between -10 and 10 -- defaults to 0) + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + * @param Event $event The event to pass to the event handlers/listeners. + * If not supplied, an empty Event instance is created. */ - function connect($name, $listener, $priority = 0); + function dispatch($eventName, Event $event = null); /** - * Disconnects one, or all listeners for the given event name. + * Adds an event listener that listens on the specified events. * - * @param string $name An event name - * @param mixed|null $listener The listener to remove, or null to remove all - * - * @return void + * @param string|array $eventNames The event(s) to listen on. + * @param object $listener The listener object. + * @param integer $priority The higher this value, the earlier an event + * listener will be triggered in the chain. + * Defaults to 0. */ - function disconnect($name, $listener = null); + function addListener($eventNames, $listener, $priority = 0); /** - * Notifies all listeners of a given event. + * Adds an event subscriber. The subscriber is asked for all the events he is + * interested in and added as a listener for these events. * - * @param EventInterface $event An EventInterface instance + * @param EventSubscriberInterface $subscriber The subscriber. + * @param integer $priority The higher this value, the earlier an event + * listener will be triggered in the chain. + * Defaults to 0. */ - function notify(EventInterface $event); + function addSubscriber(EventSubscriberInterface $subscriber, $priority = 0); /** - * Notifies all listeners of a given event until one processes the event. + * Removes an event listener from the specified events. * - * A listener tells the dispatcher that it has processed the event - * by calling the setProcessed() method on it. - * - * It can then return a value that will be forwarded to the caller. - * - * @param EventInterface $event An EventInterface instance - * - * @return mixed The returned value of the listener that processed the event + * @param string|array $eventNames The event(s) to remove a listener from. + * @param object $listener The listener object to remove. */ - function notifyUntil(EventInterface $event); + function removeListener($eventNames, $listener); /** - * Filters a value by calling all listeners of a given event. + * Gets the listeners of a specific event or all listeners. * - * @param EventInterface $event An EventInterface instance - * @param mixed $value The value to be filtered + * @param string $eventName The name of the event. * - * @return mixed The filtered value + * @return array The event listeners for the specified event, or all event + * listeners by event name. */ - function filter(EventInterface $event, $value); + function getListeners($eventName = null); /** - * Returns true if the given event name has some listeners. + * Checks whether an event has any registered listeners. * - * @param string $name The event name + * @param string $eventName The name of the event. * - * @return Boolean true if some listeners are connected, false otherwise + * @return Boolean TRUE if the specified event has any listeners, FALSE + * otherwise. */ - function hasListeners($name); - - /** - * Returns all listeners associated with a given event name. - * - * @param string $name The event name - * - * @return array An array of listeners - */ - function getListeners($name); + function hasListeners($eventName); } diff --git a/src/Symfony/Component/EventDispatcher/EventInterface.php b/src/Symfony/Component/EventDispatcher/EventInterface.php deleted file mode 100644 index 4ac4ffebd3..0000000000 --- a/src/Symfony/Component/EventDispatcher/EventInterface.php +++ /dev/null @@ -1,89 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher; - -/** - * EventInterface. - * - * @author Fabien Potencier - */ -interface EventInterface -{ - /** - * Returns the subject. - * - * @return mixed The subject - */ - function getSubject(); - - /** - * Returns the event name. - * - * @return string The event name - */ - function getName(); - - /** - * Sets the processed flag to true. - * - * This method must be called by listeners when - * it has processed the event (it is only meaningful - * when the event has been notified with the notifyUntil() - * dispatcher method. - */ - function setProcessed(); - - /** - * Returns whether the event has been processed by a listener or not. - * - * This method is only meaningful for events notified - * with notifyUntil(). - * - * @return Boolean true if the event has been processed, false otherwise - */ - function isProcessed(); - - /** - * Returns the event parameters. - * - * @return array The event parameters - */ - function all(); - - /** - * Returns true if the parameter exists. - * - * @param string $name The parameter name - * - * @return Boolean true if the parameter exists, false otherwise - */ - function has($name); - - /** - * Returns a parameter value. - * - * @param string $name The parameter name - * - * @return mixed The parameter value - * - * @throws \InvalidArgumentException When parameter doesn't exists for this event - */ - function get($name); - - /** - * Sets a parameter. - * - * @param string $name The parameter name - * @param mixed $value The parameter value - */ - function set($name, $value); -} diff --git a/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php b/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php new file mode 100644 index 0000000000..9ad999c3cf --- /dev/null +++ b/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php @@ -0,0 +1,45 @@ +. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + */ +interface EventSubscriberInterface +{ + /** + * Returns an array of events this subscriber wants to listen to. + * + * @return array + */ + static function getSubscribedEvents(); +} diff --git a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php index 4cd4c1e28d..5b5d762213 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php @@ -11,10 +11,10 @@ namespace Symfony\Component\HttpKernel\DataCollector; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Debug\EventDispatcherTraceableInterface; +use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * EventDataCollector. @@ -27,7 +27,7 @@ class EventDataCollector extends DataCollector public function setEventDispatcher(EventDispatcherInterface $dispatcher) { - if ($dispatcher instanceof EventDispatcherTraceableInterface) { + if ($dispatcher instanceof TraceableEventDispatcherInterface) { $this->dispatcher = $dispatcher; } } @@ -48,7 +48,7 @@ class EventDataCollector extends DataCollector * * @return array An array of called listeners * - * @see EventDispatcherTraceableInterface + * @see TraceableEventDispatcherInterface */ public function getCalledListeners() { @@ -60,7 +60,7 @@ class EventDataCollector extends DataCollector * * @return array An array of not called listeners * - * @see EventDispatcherTraceableInterface + * @see TraceableEventDispatcherInterface */ public function getNotCalledListeners() { diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php index 6fbef25c86..df6c234697 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php @@ -48,7 +48,7 @@ class LoggerDataCollector extends DataCollector * * @return array An array of called events * - * @see EventDispatcherTraceableInterface + * @see TraceableEventDispatcherInterface */ public function countErrors() { diff --git a/src/Symfony/Component/HttpKernel/Debug/ExceptionListener.php b/src/Symfony/Component/HttpKernel/Debug/ExceptionListener.php index e6d1e3d577..d2e5fe52ee 100644 --- a/src/Symfony/Component/HttpKernel/Debug/ExceptionListener.php +++ b/src/Symfony/Component/HttpKernel/Debug/ExceptionListener.php @@ -11,9 +11,9 @@ namespace Symfony\Component\HttpKernel\Debug; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Exception\FlattenException; use Symfony\Component\HttpFoundation\Request; @@ -21,8 +21,6 @@ use Symfony\Component\HttpFoundation\Request; /** * ExceptionListener. * - * The handle method must be connected to the core.exception event. - * * @author Fabien Potencier */ class ExceptionListener @@ -36,7 +34,7 @@ class ExceptionListener $this->logger = $logger; } - public function handle(EventInterface $event) + public function onCoreException(GetResponseForExceptionEvent $event) { static $handling; @@ -46,8 +44,8 @@ class ExceptionListener $handling = true; - $exception = $event->get('exception'); - $request = $event->get('request'); + $exception = $event->getException(); + $request = $event->getRequest(); if (null !== $this->logger) { $this->logger->err(sprintf('%s: %s (uncaught exception)', get_class($exception), $exception->getMessage())); @@ -68,7 +66,7 @@ class ExceptionListener $request = $request->duplicate(null, null, $attributes); try { - $response = $event->getSubject()->handle($request, HttpKernelInterface::SUB_REQUEST, true); + $response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, true); } catch (\Exception $e) { $message = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $e->getMessage()); if (null !== $this->logger) { @@ -79,15 +77,13 @@ class ExceptionListener // set handling to false otherwise it wont be able to handle further more $handling = false; - + // re-throw the exception as this is a catch-all throw $exception; } - $event->setProcessed(); + $event->setResponse($response); $handling = false; - - return $response; } } diff --git a/src/Symfony/Component/HttpKernel/Debug/EventDispatcherTraceableInterface.php b/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcherInterface.php similarity index 93% rename from src/Symfony/Component/HttpKernel/Debug/EventDispatcherTraceableInterface.php rename to src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcherInterface.php index 57b51c440d..622acd652a 100644 --- a/src/Symfony/Component/HttpKernel/Debug/EventDispatcherTraceableInterface.php +++ b/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcherInterface.php @@ -14,7 +14,7 @@ namespace Symfony\Component\HttpKernel\Debug; /** * @author Fabien Potencier */ -interface EventDispatcherTraceableInterface +interface TraceableEventDispatcherInterface { /** * Gets the called listeners. diff --git a/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php b/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php new file mode 100644 index 0000000000..d75d802a4e --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Allows to filter a controller callable + * + * You can call getController() to retrieve the current controller. With + * setController() you can set a new controller that is used in for processing + * a request. + * + * Controllers should be callables. + * + * @author Bernhard Schussek + */ +class FilterControllerEvent extends KernelEvent +{ + /** + * The current controller + * @var callable + */ + private $controller; + + public function __construct(HttpKernelInterface $kernel, $controller, Request $request, $requestType) + { + parent::__construct($kernel, $request, $requestType); + + $this->setController($controller); + } + + /** + * Returns the current controller + * + * @return callable + */ + public function getController() + { + return $this->controller; + } + + /** + * Sets a new controller + * + * @param callable $controller + */ + public function setController($controller) + { + // controller must be a callable + if (!is_callable($controller)) { + throw new \LogicException(sprintf('The controller must be a callable (%s given).', $this->varToString($controller))); + } + + $this->controller = $controller; + } + + private function varToString($var) + { + if (is_object($var)) { + return sprintf('[object](%s)', get_class($var)); + } + + if (is_array($var)) { + $a = array(); + foreach ($var as $k => $v) { + $a[] = sprintf('%s => %s', $k, $this->varToString($v)); + } + + return sprintf("[array](%s)", implode(', ', $a)); + } + + if (is_resource($var)) { + return '[resource]'; + } + + return str_replace("\n", '', var_export((string) $var, true)); + } +} \ No newline at end of file diff --git a/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php b/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php new file mode 100644 index 0000000000..3489b510ff --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Allows to filter a Response object + * + * You can call getResponse() to retrieve the current response. With + * setResponse() you can set a new response that will be returned to the + * browser. + * + * @author Bernhard Schussek + */ +class FilterResponseEvent extends KernelEvent +{ + /** + * The current response object + * @var Symfony\Component\HttpFoundation\Response + */ + private $response; + + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, Response $response) + { + parent::__construct($kernel, $request, $requestType); + + $this->setResponse($response); + } + + /** + * Returns the current response object + * + * @return Symfony\Component\HttpFoundation\Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * Sets a new response object + * + * @param Symfony\Component\HttpFoundation\Response $response + */ + public function setResponse(Response $response) + { + $this->response = $response; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php b/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php new file mode 100644 index 0000000000..6990427591 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Allows to create a response for a request + * + * Call setResponse() to set the response that will be returned for the + * current request. The propagation of this event is stopped as soon as a + * response is set. + * + * @author Bernhard Schussek + */ +class GetResponseEvent extends KernelEvent +{ + /** + * The response object + * @var Symfony\Component\HttpFoundation\Response + */ + private $response; + + /** + * Returns the response object + * + * @return Symfony\Component\HttpFoundation\Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * Sets a response and stops event propagation + * + * @param Symfony\Component\HttpFoundation\Response $response + */ + public function setResponse(Response $response) + { + $this->response = $response; + + $this->stopPropagation(); + } + + /** + * Returns whether a response was set + * + * @return Boolean Whether a response was set + */ + public function hasResponse() + { + return null !== $this->response; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php b/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php new file mode 100644 index 0000000000..da4fdc36a1 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Allows to create a response for the return value of a controller + * + * Call setResponse() to set the response that will be returned for the + * current request. The propagation of this event is stopped as soon as a + * response is set. + * + * @author Bernhard Schussek + */ +class GetResponseForControllerResultEvent extends GetResponseEvent +{ + /** + * The return value of the controller + * @var mixed + */ + private $controllerResult; + + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, $controllerResult) + { + parent::__construct($kernel, $request, $requestType); + + $this->controllerResult = $controllerResult; + } + + /** + * Returns the return value of the controller + * + * @return mixed The controller return value + */ + public function getControllerResult() + { + return $this->controllerResult; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php b/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php new file mode 100644 index 0000000000..26c0e803d2 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Allows to create a response for a thrown exception + * + * Call setResponse() to set the response that will be returned for the + * current request. The propagation of this event is stopped as soon as a + * response is set. + * + * You can also call setException() to replace the thrown exception. This + * exception will be thrown if no response is set during processing of this + * event. + * + * @author Bernhard Schussek + */ +class GetResponseForExceptionEvent extends GetResponseEvent +{ + /** + * The exception object + * @var \Exception + */ + private $exception; + + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, \Exception $e) + { + parent::__construct($kernel, $request, $requestType); + + $this->setException($e); + } + + /** + * Returns the thrown exception + * + * @return \Exception The thrown exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Replaces the thrown exception + * + * This exception will be thrown if no response is set in the event. + * + * @param \Exception $exception The thrown exception + */ + public function setException(\Exception $exception) + { + $this->exception = $exception; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/HttpKernel/Event/KernelEvent.php b/src/Symfony/Component/HttpKernel/Event/KernelEvent.php new file mode 100644 index 0000000000..4475f0c0c8 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Event/KernelEvent.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\EventDispatcher\Event; + +/** + * Base class for events thrown in the HttpKernel component + * + * @author Bernhard Schussek + */ +class KernelEvent extends Event +{ + /** + * The kernel in which this event was thrown + * @var Symfony\Component\HttpKernel\HttpKernelInterface + */ + private $kernel; + + /** + * The request the kernel is currently processing + * @var Symfony\Component\HttpFoundation\Request + */ + private $request; + + /** + * The request type the kernel is currently processing. One of + * HttpKernelInterface::MASTER_REQUEST and HttpKernelInterface::SUB_REQUEST + * @var integer + */ + private $requestType; + + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType) + { + $this->kernel = $kernel; + $this->request = $request; + $this->requestType = $requestType; + } + + /** + * Returns the kernel in which this event was thrown + * + * @return Symfony\Component\HttpKernel\HttpKernelInterface + */ + public function getKernel() + { + return $this->kernel; + } + + /** + * Returns the request the kernel is currently processing + * + * @return Symfony\Component\HttpFoundation\Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * Returns the request type the kernel is currently processing + * + * @return integer One of HttpKernelInterface::MASTER_REQUEST and + * HttpKernelInterface::SUB_REQUEST + */ + public function getRequestType() + { + return $this->requestType; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/HttpKernel/Events.php b/src/Symfony/Component/HttpKernel/Events.php new file mode 100644 index 0000000000..49ab0327e5 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Events.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +/** + * Contains all events thrown in the HttpKernel component + * + * @author Bernhard Schussek + */ +final class Events +{ + /** + * The onCoreRequest event occurs at the very beginning of request + * dispatching + * + * This event allows you to create a response for a request before any + * other code in the framework is executed. The event listener method + * receives a Symfony\Component\HttpKernel\Event\GetResponseEvent + * instance. + * + * @var string + */ + const onCoreRequest = 'onCoreRequest'; + + /** + * The onCoreException event occurs when an uncaught exception appears + * + * This event allows you to create a response for a thrown exception or + * to modify the thrown exception. The event listener method receives + * a Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent + * instance. + * + * @var string + */ + const onCoreException = 'onCoreException'; + + /** + * The onCoreView event occurs when the return value of a controller + * is not a Response instance + * + * This event allows you to create a response for the return value of the + * controller. The event listener method receives a + * Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent + * instance. + * + * @var string + */ + const onCoreView = 'onCoreView'; + + /** + * The onCoreController event occurs once a controller was found for + * handling a request + * + * This event allows you to change the controller that will handle the + * request. The event listener method receives a + * Symfony\Component\HttpKernel\Event\FilterControllerEvent instance. + * + * @var string + */ + const onCoreController = 'onCoreController'; + + /** + * The onCoreController event occurs once a reponse was created for + * replying to a request + * + * This event allows you to modify or replace the response that will be + * replied. The event listener method receives a + * Symfony\Component\HttpKernel\Event\FilterResponseEvent instance. + * + * @var string + */ + const onCoreResponse = 'onCoreResponse'; +} \ No newline at end of file diff --git a/src/Symfony/Component/HttpKernel/HttpCache/EsiListener.php b/src/Symfony/Component/HttpKernel/HttpCache/EsiListener.php index 2a2dec30b6..0f02a711fa 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/EsiListener.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/EsiListener.php @@ -13,19 +13,16 @@ namespace Symfony\Component\HttpKernel\HttpCache; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; /** * EsiListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for ESI. * - * The filter method must be connected to the core.response event. - * * @author Fabien Potencier */ class EsiListener { - protected $dispatcher; + protected $i; protected $esi; /** @@ -41,17 +38,14 @@ class EsiListener /** * Filters the Response. * - * @param EventInterface $event An EventInterface instance - * @param Response $response A Response instance + * @param FilterResponseEvent $event A FilterResponseEvent instance */ - public function filter(EventInterface $event, Response $response) + public function onCoreResponse(FilterResponseEvent $event) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->get('request_type') || null === $this->esi) { - return $response; + if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType() || null === $this->esi) { + return; } - $this->esi->addSurrogateControl($response); - - return $response; + $this->esi->addSurrogateControl($event->getResponse()); } } diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index 2e6d5b1937..c6f6d714f5 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -11,12 +11,16 @@ namespace Symfony\Component\HttpKernel; -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * HttpKernel notifies events to convert a Request object to a Response one. @@ -25,13 +29,13 @@ use Symfony\Component\HttpFoundation\Response; */ class HttpKernel implements HttpKernelInterface { - protected $dispatcher; - protected $resolver; + private $dispatcher; + private $resolver; /** * Constructor * - * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance * @param ControllerResolverInterface $resolver A ControllerResolverInterface instance */ public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver) @@ -84,10 +88,11 @@ class HttpKernel implements HttpKernelInterface protected function handleRaw(Request $request, $type = self::MASTER_REQUEST) { // request - $event = new Event($this, 'core.request', array('request_type' => $type, 'request' => $request)); - $response = $this->dispatcher->notifyUntil($event); - if ($event->isProcessed()) { - return $this->filterResponse($response, $request, 'A "core.request" listener returned a non response object.', $type); + $event = new GetResponseEvent($this, $request, $type); + $this->dispatcher->dispatch(Events::onCoreRequest, $event); + + if ($event->hasResponse()) { + return $this->filterResponse($event->getResponse(), $request, $type); } // load controller @@ -95,13 +100,9 @@ class HttpKernel implements HttpKernelInterface throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". Maybe you forgot to add the matching route in your routing configuration?', $request->getPathInfo())); } - $event = new Event($this, 'core.controller', array('request_type' => $type, 'request' => $request)); - $controller = $this->dispatcher->filter($event, $controller); - - // controller must be a callable - if (!is_callable($controller)) { - throw new \LogicException(sprintf('The controller must be a callable (%s given).', $this->varToString($controller))); - } + $event = new FilterControllerEvent($this, $controller, $request, $type); + $this->dispatcher->dispatch(Events::onCoreController, $event); + $controller = $event->getController(); // controller arguments $arguments = $this->resolver->getArguments($request, $controller); @@ -111,14 +112,19 @@ class HttpKernel implements HttpKernelInterface // view if (!$response instanceof Response) { - $event = new Event($this, 'core.view', array('request_type' => $type, 'request' => $request, 'controller_value' => $response)); - $retval = $this->dispatcher->notifyUntil($event); - if ($event->isProcessed()) { - $response = $retval; + $event = new GetResponseForControllerResultEvent($this, $request, $type, $response); + $this->dispatcher->dispatch(Events::onCoreView, $event); + + if ($event->hasResponse()) { + $response = $event->getResponse(); + } + + if (!$response instanceof Response) { + throw new \LogicException(sprintf('The controller must return a response (%s given).', $this->varToString($response))); } } - return $this->filterResponse($response, $request, sprintf('The controller must return a response (%s given).', $this->varToString($response)), $type); + return $this->filterResponse($response, $request, $type); } /** @@ -132,19 +138,13 @@ class HttpKernel implements HttpKernelInterface * * @throws \RuntimeException if the passed object is not a Response instance */ - protected function filterResponse($response, $request, $message, $type) + protected function filterResponse(Response $response, Request $request, $type) { - if (!$response instanceof Response) { - throw new \RuntimeException($message); - } + $event = new FilterResponseEvent($this, $request, $type, $response); - $response = $this->dispatcher->filter(new Event($this, 'core.response', array('request_type' => $type, 'request' => $request)), $response); + $this->dispatcher->dispatch(Events::onCoreResponse, $event); - if (!$response instanceof Response) { - throw new \RuntimeException('A "core.response" listener returned a non response object.'); - } - - return $response; + return $event->getResponse(); } /** @@ -158,13 +158,14 @@ class HttpKernel implements HttpKernelInterface */ protected function handleException(\Exception $e, $request, $type) { - $event = new Event($this, 'core.exception', array('request_type' => $type, 'request' => $request, 'exception' => $e)); - $response = $this->dispatcher->notifyUntil($event); - if (!$event->isProcessed()) { + $event = new GetResponseForExceptionEvent($this, $request, $type, $e); + $this->dispatcher->dispatch(Events::onCoreException, $event); + + if (!$event->hasResponse()) { throw $e; } - return $this->filterResponse($response, $request, 'A "core.exception" listener returned a non response object.', $type); + return $this->filterResponse($event->getResponse(), $request, $type); } protected function varToString($var) diff --git a/src/Symfony/Component/HttpKernel/ResponseListener.php b/src/Symfony/Component/HttpKernel/ResponseListener.php index e155529ba6..4b72643757 100644 --- a/src/Symfony/Component/HttpKernel/ResponseListener.php +++ b/src/Symfony/Component/HttpKernel/ResponseListener.php @@ -11,14 +11,12 @@ namespace Symfony\Component\HttpKernel; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpFoundation\Response; /** * ResponseListener fixes the Response Content-Type. * - * The filter method must be connected to the core.response event. - * * @author Fabien Potencier */ class ResponseListener @@ -33,29 +31,28 @@ class ResponseListener /** * Filters the Response. * - * @param EventInterface $event An EventInterface instance - * @param Response $response A Response instance + * @param FilterResponseEvent $event A FilterResponseEvent instance */ - public function filter(EventInterface $event, Response $response) + public function onCoreResponse(FilterResponseEvent $event) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->get('request_type')) { - return $response; + if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + return; } + $response = $event->getResponse(); + if (null === $response->getCharset()) { $response->setCharset($this->charset); } if ($response->headers->has('Content-Type')) { - return $response; + return; } - $request = $event->get('request'); + $request = $event->getRequest(); $format = $request->getRequestFormat(); if ((null !== $format) && $mimeType = $request->getMimeType($format)) { $response->headers->set('Content-Type', $mimeType); } - - return $response; } } diff --git a/src/Symfony/Component/Security/Http/Authentication/AuthenticationFailureHandlerInterface.php b/src/Symfony/Component/Security/Http/Authentication/AuthenticationFailureHandlerInterface.php index 8defef6230..02db734f0f 100644 --- a/src/Symfony/Component/Security/Http/Authentication/AuthenticationFailureHandlerInterface.php +++ b/src/Symfony/Component/Security/Http/Authentication/AuthenticationFailureHandlerInterface.php @@ -2,8 +2,8 @@ namespace Symfony\Component\Security\Http\Authentication; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -22,12 +22,12 @@ interface AuthenticationFailureHandlerInterface * called by authentication listeners inheriting from * AbstractAuthenticationListener. * - * @param EventInterface $event the "core.security" event, this event always + * @param GetResponseEvent $event the "onCoreRequest" event, this event always * has the kernel as target * @param Request $request * @param AuthenticationException $exception * * @return Response the response to return */ - function onAuthenticationFailure(EventInterface $event, Request $request, AuthenticationException $exception); + function onAuthenticationFailure(GetResponseEvent $event, Request $request, AuthenticationException $exception); } \ No newline at end of file diff --git a/src/Symfony/Component/Security/Http/Authentication/AuthenticationSuccessHandlerInterface.php b/src/Symfony/Component/Security/Http/Authentication/AuthenticationSuccessHandlerInterface.php index 235eb946a4..0af47c95d5 100644 --- a/src/Symfony/Component/Security/Http/Authentication/AuthenticationSuccessHandlerInterface.php +++ b/src/Symfony/Component/Security/Http/Authentication/AuthenticationSuccessHandlerInterface.php @@ -2,7 +2,7 @@ namespace Symfony\Component\Security\Http\Authentication; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\HttpFoundation\Request; @@ -22,12 +22,12 @@ interface AuthenticationSuccessHandlerInterface * is called by authentication listeners inheriting from * AbstractAuthenticationListener. * - * @param EventInterface $event the "core.security" event, this event always + * @param GetResponseEvent $event the "onCoreRequest" event, this event always * has the kernel as target * @param Request $request * @param TokenInterface $token * * @return Response the response to return */ - function onAuthenticationSuccess(EventInterface $event, Request $request, TokenInterface $token); + function onAuthenticationSuccess(GetResponseEvent $event, Request $request, TokenInterface $token); } \ No newline at end of file diff --git a/src/Symfony/Component/Security/Http/Authorization/AccessDeniedHandlerInterface.php b/src/Symfony/Component/Security/Http/Authorization/AccessDeniedHandlerInterface.php index 7a1bcf4a87..c33353bec9 100644 --- a/src/Symfony/Component/Security/Http/Authorization/AccessDeniedHandlerInterface.php +++ b/src/Symfony/Component/Security/Http/Authorization/AccessDeniedHandlerInterface.php @@ -3,8 +3,9 @@ namespace Symfony\Component\Security\Http\Authorization; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; /** * This is used by the ExceptionListener to translate an AccessDeniedException @@ -17,11 +18,11 @@ interface AccessDeniedHandlerInterface /** * Handles an access denied failure. * - * @param EventInterface $event - * @param Request $request - * @param AccessDeniedException $accessDeniedException + * @param GetResponseForExceptionEvent $event + * @param Request $request + * @param AccessDeniedException $accessDeniedException * * @return Response may return null */ - function handle(EventInterface $event, Request $request, AccessDeniedException $accessDeniedException); -} \ No newline at end of file + function handle(GetResponseForExceptionEvent $event, Request $request, AccessDeniedException $accessDeniedException); +} diff --git a/src/Symfony/Component/Security/Http/EntryPoint/AuthenticationEntryPointInterface.php b/src/Symfony/Component/Security/Http/EntryPoint/AuthenticationEntryPointInterface.php index 6bf01c32f6..3095b5cac0 100644 --- a/src/Symfony/Component/Security/Http/EntryPoint/AuthenticationEntryPointInterface.php +++ b/src/Symfony/Component/Security/Http/EntryPoint/AuthenticationEntryPointInterface.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Security\Http\EntryPoint; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\HttpFoundation\Request; @@ -26,9 +26,9 @@ interface AuthenticationEntryPointInterface /** * Starts the authentication scheme. * - * @param EventInterface $event The "core.security" event + * @param GetResponseEvent $event The "onCoreRequest" event * @param object $request The request that resulted in an AuthenticationException * @param AuthenticationException $authException The exception that started the authentication process */ - function start(EventInterface $event, Request $request, AuthenticationException $authException = null); + function start(GetResponseEvent $event, Request $request, AuthenticationException $authException = null); } diff --git a/src/Symfony/Component/Security/Http/EntryPoint/BasicAuthenticationEntryPoint.php b/src/Symfony/Component/Security/Http/EntryPoint/BasicAuthenticationEntryPoint.php index 8f824265b2..8ac0f648ee 100644 --- a/src/Symfony/Component/Security/Http/EntryPoint/BasicAuthenticationEntryPoint.php +++ b/src/Symfony/Component/Security/Http/EntryPoint/BasicAuthenticationEntryPoint.php @@ -11,11 +11,11 @@ namespace Symfony\Component\Security\Http\EntryPoint; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; /** * BasicAuthenticationEntryPoint starts an HTTP Basic authentication. @@ -31,7 +31,7 @@ class BasicAuthenticationEntryPoint implements AuthenticationEntryPointInterface $this->realmName = $realmName; } - public function start(EventInterface $event, Request $request, AuthenticationException $authException = null) + public function start(GetResponseEvent $event, Request $request, AuthenticationException $authException = null) { $response = new Response(); $response->headers->set('WWW-Authenticate', sprintf('Basic realm="%s"', $this->realmName)); diff --git a/src/Symfony/Component/Security/Http/EntryPoint/DigestAuthenticationEntryPoint.php b/src/Symfony/Component/Security/Http/EntryPoint/DigestAuthenticationEntryPoint.php index a4488abf07..81f99b17d2 100644 --- a/src/Symfony/Component/Security/Http/EntryPoint/DigestAuthenticationEntryPoint.php +++ b/src/Symfony/Component/Security/Http/EntryPoint/DigestAuthenticationEntryPoint.php @@ -11,13 +11,13 @@ namespace Symfony\Component\Security\Http\EntryPoint; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\Security\Core\Exception\NonceExpiredException; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; /** * DigestAuthenticationEntryPoint starts an HTTP Digest authentication. @@ -39,7 +39,7 @@ class DigestAuthenticationEntryPoint implements AuthenticationEntryPointInterfac $this->logger = $logger; } - public function start(EventInterface $event, Request $request, AuthenticationException $authException = null) + public function start(GetResponseEvent $event, Request $request, AuthenticationException $authException = null) { $expiryTime = microtime(true) + $this->nonceValiditySeconds * 1000; $signatureValue = md5($expiryTime.':'.$this->key); diff --git a/src/Symfony/Component/Security/Http/EntryPoint/FormAuthenticationEntryPoint.php b/src/Symfony/Component/Security/Http/EntryPoint/FormAuthenticationEntryPoint.php index e43eca4ded..899de47272 100644 --- a/src/Symfony/Component/Security/Http/EntryPoint/FormAuthenticationEntryPoint.php +++ b/src/Symfony/Component/Security/Http/EntryPoint/FormAuthenticationEntryPoint.php @@ -11,13 +11,13 @@ namespace Symfony\Component\Security\Http\EntryPoint; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; /** * FormAuthenticationEntryPoint starts an authentication via a login form. @@ -44,10 +44,10 @@ class FormAuthenticationEntryPoint implements AuthenticationEntryPointInterface /** * {@inheritdoc} */ - public function start(EventInterface $event, Request $request, AuthenticationException $authException = null) + public function start(GetResponseEvent $event, Request $request, AuthenticationException $authException = null) { if ($this->useForward) { - return $event->getSubject()->handle(Request::create($this->loginPath), HttpKernelInterface::SUB_REQUEST); + return $event->getKernel()->handle(Request::create($this->loginPath), HttpKernelInterface::SUB_REQUEST); } return new RedirectResponse(0 !== strpos($this->loginPath, 'http') ? $request->getUriForPath($this->loginPath) : $this->loginPath, 302); diff --git a/src/Symfony/Component/Security/Http/EntryPoint/RetryAuthenticationEntryPoint.php b/src/Symfony/Component/Security/Http/EntryPoint/RetryAuthenticationEntryPoint.php index 48959bf908..5439f19eab 100644 --- a/src/Symfony/Component/Security/Http/EntryPoint/RetryAuthenticationEntryPoint.php +++ b/src/Symfony/Component/Security/Http/EntryPoint/RetryAuthenticationEntryPoint.php @@ -11,12 +11,12 @@ namespace Symfony\Component\Security\Http\EntryPoint; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; /** * RetryAuthenticationEntryPoint redirects URL based on the configured scheme. @@ -36,7 +36,7 @@ class RetryAuthenticationEntryPoint implements AuthenticationEntryPointInterface $this->httpsPort = $httpsPort; } - public function start(EventInterface $event, Request $request, AuthenticationException $authException = null) + public function start(GetResponseEvent $event, Request $request, AuthenticationException $authException = null) { $scheme = $request->isSecure() ? 'http' : 'https'; if ('http' === $scheme && 80 != $this->httpPort) { diff --git a/src/Symfony/Component/Security/Http/Event/InteractiveLoginEvent.php b/src/Symfony/Component/Security/Http/Event/InteractiveLoginEvent.php new file mode 100644 index 0000000000..8331bf0f93 --- /dev/null +++ b/src/Symfony/Component/Security/Http/Event/InteractiveLoginEvent.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\Event; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\EventDispatcher\Event; + +class InteractiveLoginEvent extends Event +{ + private $request; + + private $authenticationToken; + + public function __construct(Request $request, $authenticationToken) + { + $this->request = $request; + $this->authenticationToken = $authenticationToken; + } + + public function getRequest() + { + return $this->request; + } + + public function getAuthenticationToken() + { + return $this->authenticationToken; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/Security/Http/Event/SwitchUserEvent.php b/src/Symfony/Component/Security/Http/Event/SwitchUserEvent.php new file mode 100644 index 0000000000..03ca003b8a --- /dev/null +++ b/src/Symfony/Component/Security/Http/Event/SwitchUserEvent.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\Event; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\EventDispatcher\Event; + +class SwitchUserEvent extends Event +{ + private $request; + + private $targetUser; + + public function __construct(Request $request, UserInterface $targetUser) + { + $this->request = $request; + $this->targetUser = $targetUser; + } + + public function getRequest() + { + return $this->request; + } + + public function getTargetUser() + { + return $this->targetUser; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/Security/Http/Events.php b/src/Symfony/Component/Security/Http/Events.php new file mode 100644 index 0000000000..c0aa65d258 --- /dev/null +++ b/src/Symfony/Component/Security/Http/Events.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http; + +final class Events +{ + const onSecurityInteractiveLogin = 'onSecurityInteractiveLogin'; + + const onSecuritySwitchUser = 'onSecuritySwitchUser'; +} \ No newline at end of file diff --git a/src/Symfony/Component/Security/Http/Firewall.php b/src/Symfony/Component/Security/Http/Firewall.php index bd77f6d892..7bdda2a353 100644 --- a/src/Symfony/Component/Security/Http/Firewall.php +++ b/src/Symfony/Component/Security/Http/Firewall.php @@ -11,11 +11,11 @@ namespace Symfony\Component\Security\Http; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; -use Symfony\Component\EventDispatcher\Event; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Events; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Firewall uses a FirewallMap to register security listeners for the given @@ -25,13 +25,13 @@ use Symfony\Component\HttpFoundation\Request; * (a Basic authentication for the /api, and a web based authentication for * everything else for instance). * - * The handle method must be connected to the core.request event. - * * @author Fabien Potencier */ class Firewall { private $map; + private $dispatcher; + private $currentListeners; /** * Constructor. @@ -42,21 +42,22 @@ class Firewall { $this->map = $map; $this->dispatcher = $dispatcher; + $this->currentListeners = array(); } /** * Handles security. * - * @param EventInterface $event An EventInterface instance + * @param GetResponseEvent $event An GetResponseEvent instance */ - public function handle(EventInterface $event) + public function onCoreRequest(GetResponseEvent $event) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->get('request_type')) { + if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { return; } // register listeners for this firewall - list($listeners, $exception) = $this->map->getListeners($event->get('request')); + list($listeners, $exception) = $this->map->getListeners($event->getRequest()); if (null !== $exception) { $exception->register($this->dispatcher); } @@ -65,8 +66,8 @@ class Firewall foreach ($listeners as $listener) { $response = $listener->handle($event); - if ($event->isProcessed()) { - return $response; + if ($event->hasResponse()) { + break; } } } diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php index 14ae36f950..c36fbad298 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php @@ -11,22 +11,24 @@ namespace Symfony\Component\Security\Http\Firewall; -use Symfony\Component\EventDispatcher\Event; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Events as KernelEvents; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; +use Symfony\Component\Security\Http\Events; /** * The AbstractAuthenticationListener is the preferred base class for all @@ -53,7 +55,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface protected $providerKey; private $securityContext; private $sessionStrategy; - private $eventDispatcher; + private $dispatcher; private $successHandler; private $failureHandler; private $rememberMeServices; @@ -66,7 +68,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface * @param array $options An array of options for the processing of a successful, or failed authentication attempt * @param LoggerInterface $logger A LoggerInterface instance */ - public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null) + public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); @@ -89,7 +91,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface 'failure_forward' => false, ), $options); $this->logger = $logger; - $this->eventDispatcher = $eventDispatcher; + $this->dispatcher = $dispatcher; } /** @@ -105,11 +107,11 @@ abstract class AbstractAuthenticationListener implements ListenerInterface /** * Handles form based authentication. * - * @param Event $event An Event instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public final function handle(EventInterface $event) + public final function handle(GetResponseEvent $event) { - $request = $event->get('request'); + $request = $event->getRequest(); if (!$this->requiresAuthentication($request)) { return; @@ -129,13 +131,11 @@ abstract class AbstractAuthenticationListener implements ListenerInterface } else { throw new \RuntimeException('attemptAuthentication() must either return a Response, an implementation of TokenInterface, or null.'); } - } catch (AuthenticationException $failed) { - $response = $this->onFailure($event, $request, $failed); + } catch (AuthenticationException $e) { + $response = $this->onFailure($event, $request, $e); } - $event->setProcessed(); - - return $response; + $event->setResponse($response); } /** @@ -165,7 +165,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface */ abstract protected function attemptAuthentication(Request $request); - private function onFailure($event, Request $request, AuthenticationException $failed) + private function onFailure(GetResponseEvent $event, Request $request, AuthenticationException $failed) { if (null !== $this->logger) { $this->logger->debug(sprintf('Authentication request failed: %s', $failed->getMessage())); @@ -201,7 +201,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface return new RedirectResponse(0 !== strpos($this->options['failure_path'], 'http') ? $request->getUriForPath($this->options['failure_path']) : $this->options['failure_path'], 302); } - private function onSuccess(EventInterface $event, Request $request, TokenInterface $token) + private function onSuccess(GetResponseEvent $event, Request $request, TokenInterface $token) { if (null !== $this->logger) { $this->logger->debug('User has been authenticated successfully'); @@ -213,8 +213,9 @@ abstract class AbstractAuthenticationListener implements ListenerInterface $session->remove(SecurityContextInterface::AUTHENTICATION_ERROR); $session->remove(SecurityContextInterface::LAST_USERNAME); - if (null !== $this->eventDispatcher) { - $this->eventDispatcher->notify(new Event($this, 'security.interactive_login', array('request' => $request, 'token' => $token))); + if (null !== $this->dispatcher) { + $loginEvent = new InteractiveLoginEvent($request, $token); + $this->dispatcher->dispatch(Events::onSecurityInteractiveLogin, $loginEvent); } if (null !== $this->successHandler) { diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php index bba0466da3..88faa27037 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php @@ -11,15 +11,17 @@ namespace Symfony\Component\Security\Http\Firewall; -use Symfony\Component\EventDispatcher\Event; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; +use Symfony\Component\Security\Http\Events; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Events as KernelEvents; +use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * AbstractPreAuthenticatedListener is the base class for all listener that @@ -34,25 +36,25 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface private $securityContext; private $authenticationManager; private $providerKey; - private $eventDispatcher; + private $dispatcher; - public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null) + public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { $this->securityContext = $securityContext; $this->authenticationManager = $authenticationManager; $this->providerKey = $providerKey; $this->logger = $logger; - $this->eventDispatcher = $eventDispatcher; + $this->dispatcher = $dispatcher; } /** * Handles X509 authentication. * - * @param EventInterface $event An EventInterface instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public final function handle(EventInterface $event) + public final function handle(GetResponseEvent $event) { - $request = $event->get('request'); + $request = $event->getRequest(); if (null !== $this->logger) { $this->logger->debug(sprintf('Checking secure context token: %s', $this->securityContext->getToken())); @@ -78,8 +80,9 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface } $this->securityContext->setToken($token); - if (null !== $this->eventDispatcher) { - $this->eventDispatcher->notify(new Event($this, 'security.interactive_login', array('request' => $request, 'token' => $token))); + if (null !== $this->dispatcher) { + $loginEvent = new InteractiveLoginEvent($request, $token); + $this->dispatcher->dispatch(Events::onSecurityInteractiveLogin, $loginEvent); } } catch (AuthenticationException $failed) { $this->securityContext->setToken(null); diff --git a/src/Symfony/Component/Security/Http/Firewall/AccessListener.php b/src/Symfony/Component/Security/Http/Firewall/AccessListener.php index a4faeb5d94..bbcd9329aa 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AccessListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AccessListener.php @@ -16,8 +16,8 @@ use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface use Symfony\Component\Security\Http\AccessMap; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Events; use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; use Symfony\Component\Security\Core\Exception\AccessDeniedException; @@ -46,15 +46,15 @@ class AccessListener implements ListenerInterface /** * Handles access authorization. * - * @param EventInterface $event An EventInterface instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public function handle(EventInterface $event) + public function handle(GetResponseEvent $event) { if (null === $token = $this->context->getToken()) { throw new AuthenticationCredentialsNotFoundException('A Token was not found in the SecurityContext.'); } - $request = $event->get('request'); + $request = $event->getRequest(); list($attributes, $channel) = $this->map->getPatterns($request); diff --git a/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php index 47ca58fb7f..c5c2376d23 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php @@ -13,8 +13,8 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Events; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; /** @@ -39,9 +39,9 @@ class AnonymousAuthenticationListener implements ListenerInterface /** * Handles anonymous authentication. * - * @param EventInterface $event An EventInterface instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public function handle(EventInterface $event) + public function handle(GetResponseEvent $event) { if (null !== $this->context->getToken()) { return; diff --git a/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php index b9a764a325..efeb963e78 100644 --- a/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php @@ -15,8 +15,8 @@ use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Events; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\AuthenticationException; @@ -51,11 +51,11 @@ class BasicAuthenticationListener implements ListenerInterface /** * Handles basic authentication. * - * @param EventInterface $event An EventInterface instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public function handle(EventInterface $event) + public function handle(GetResponseEvent $event) { - $request = $event->get('request'); + $request = $event->getRequest(); if (false === $username = $request->server->get('PHP_AUTH_USER', false)) { return; diff --git a/src/Symfony/Component/Security/Http/Firewall/ChannelListener.php b/src/Symfony/Component/Security/Http/Firewall/ChannelListener.php index 92a9c3b9c0..64176e1ad5 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ChannelListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ChannelListener.php @@ -14,8 +14,8 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Http\AccessMap; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Events; /** * ChannelListener switches the HTTP protocol based on the access control @@ -39,11 +39,11 @@ class ChannelListener implements ListenerInterface /** * Handles channel management. * - * @param EventInterface $event An EventInterface instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public function handle(EventInterface $event) + public function handle(GetResponseEvent $event) { - $request = $event->get('request'); + $request = $event->getRequest(); list($attributes, $channel) = $this->map->getPatterns($request); @@ -52,9 +52,11 @@ class ChannelListener implements ListenerInterface $this->logger->debug('Redirecting to HTTPS'); } - $event->setProcessed(); + $response = $this->authenticationEntryPoint->start($event, $request); - return $this->authenticationEntryPoint->start($event, $request); + $event->setResponse($response); + + return; } if ('http' === $channel && $request->isSecure()) { @@ -62,9 +64,9 @@ class ChannelListener implements ListenerInterface $this->logger->debug('Redirecting to HTTP'); } - $event->setProcessed(); + $response = $this->authenticationEntryPoint->start($event, $request); - return $this->authenticationEntryPoint->start($event, $request); + $event->setResponse($response); } } } diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 548876a223..a20eaab597 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -11,17 +11,19 @@ namespace Symfony\Component\Security\Http\Firewall; -use Symfony\Component\EventDispatcher\EventInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Events; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\SecurityContext; use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * ContextListener manages the SecurityContext persistence through a session. @@ -36,7 +38,7 @@ class ContextListener implements ListenerInterface private $logger; private $userProviders; - public function __construct(SecurityContext $context, array $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null) + public function __construct(SecurityContext $context, array $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { if (empty($contextKey)) { throw new \InvalidArgumentException('$contextKey must not be empty.'); @@ -46,19 +48,19 @@ class ContextListener implements ListenerInterface $this->userProviders = $userProviders; $this->contextKey = $contextKey; - if (null !== $eventDispatcher) { - $eventDispatcher->connect('core.response', array($this, 'write'), 0); + if (null !== $dispatcher) { + $dispatcher->addListener(Events::onCoreResponse, $this); } } /** * Reads the SecurityContext from the session. * - * @param EventInterface $event An EventInterface instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public function handle(EventInterface $event) + public function handle(GetResponseEvent $event) { - $request = $event->get('request'); + $request = $event->getRequest(); $session = $request->hasSession() ? $request->getSession() : null; @@ -82,29 +84,27 @@ class ContextListener implements ListenerInterface /** * Writes the SecurityContext to the session. * - * @param EventInterface $event An EventInterface instance + * @param FilterResponseEvent $event A FilterResponseEvent instance */ - public function write(EventInterface $event, Response $response) + public function onCoreResponse(FilterResponseEvent $event) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->get('request_type')) { - return $response; + if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + return; } if (null === $token = $this->context->getToken()) { - return $response; + return; } if (null === $token || $token instanceof AnonymousToken) { - return $response; + return; } if (null !== $this->logger) { $this->logger->debug('Write SecurityContext in the session'); } - $event->get('request')->getSession()->set('_security_'.$this->contextKey, serialize($token)); - - return $response; + $event->getRequest()->getSession()->set('_security_'.$this->contextKey, serialize($token)); } /** diff --git a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php index c64cf1c50f..f550c07d8e 100644 --- a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php @@ -15,8 +15,8 @@ use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Events; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; @@ -54,11 +54,11 @@ class DigestAuthenticationListener implements ListenerInterface /** * Handles digest authentication. * - * @param EventInterface $event An EventInterface instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public function handle(EventInterface $event) + public function handle(GetResponseEvent $event) { - $request = $event->get('request'); + $request = $event->getRequest(); if (!$header = $request->server->get('PHP_AUTH_DIGEST')) { return; @@ -121,7 +121,7 @@ class DigestAuthenticationListener implements ListenerInterface $this->securityContext->setToken(new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey)); } - private function fail(EventInterface $event, Request $request, AuthenticationException $authException) + private function fail(GetResponseEvent $event, Request $request, AuthenticationException $authException) { $this->securityContext->setToken(null); diff --git a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php index 7d1b892bd8..efe29f47fc 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php @@ -17,14 +17,15 @@ use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Exception\InsufficientAuthenticationException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Events; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * ExceptionListener catches authentication exception and converts them to @@ -52,25 +53,24 @@ class ExceptionListener } /** - * Registers a core.exception listener to take care of security exceptions. + * Registers a onCoreException listener to take care of security exceptions. * * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance - * @param integer $priority The priority */ public function register(EventDispatcherInterface $dispatcher) { - $dispatcher->connect('core.exception', array($this, 'handleException'), 0); + $dispatcher->addListener(Events::onCoreException, $this); } /** * Handles security related exceptions. * - * @param EventInterface $event An EventInterface instance + * @param GetResponseForExceptionEvent $event An GetResponseForExceptionEvent instance */ - public function handleException(EventInterface $event) + public function onCoreException(GetResponseForExceptionEvent $event) { - $exception = $event->get('exception'); - $request = $event->get('request'); + $exception = $event->getException(); + $request = $event->getRequest(); if ($exception instanceof AuthenticationException) { if (null !== $this->logger) { @@ -118,7 +118,7 @@ class ExceptionListener $subRequest = Request::create($this->errorPage); $subRequest->attributes->set(SecurityContextInterface::ACCESS_DENIED_ERROR, $exception); - $response = $event->getSubject()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true); + $response = $event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true); $response->setStatusCode(403); } } catch (\Exception $e) { @@ -126,7 +126,7 @@ class ExceptionListener $this->logger->err(sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $e->getMessage())); } - $event->set('exception', new \RuntimeException('Exception thrown when handling an exception.', 0, $e)); + $event->setException(new \RuntimeException('Exception thrown when handling an exception.', 0, $e)); return; } @@ -135,12 +135,10 @@ class ExceptionListener return; } - $event->setProcessed(); - - return $response; + $event->setResponse($response); } - private function startAuthentication(EventInterface $event, Request $request, AuthenticationException $authException) + private function startAuthentication(GetResponseForExceptionEvent $event, Request $request, AuthenticationException $authException) { $this->context->setToken(null); diff --git a/src/Symfony/Component/Security/Http/Firewall/ListenerInterface.php b/src/Symfony/Component/Security/Http/Firewall/ListenerInterface.php index 44d2902d62..9d5084ef3f 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ListenerInterface.php +++ b/src/Symfony/Component/Security/Http/Firewall/ListenerInterface.php @@ -11,8 +11,7 @@ namespace Symfony\Component\Security\Http\Firewall; -use Symfony\Component\EventDispatcher\EventInterface; - +use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -25,7 +24,7 @@ interface ListenerInterface /** * This interface must be implemented by firewall listeners. * - * @param EventInterface $dispatcher + * @param GetResponseEvent $event */ - function handle(EventInterface $event); + function handle(GetResponseEvent $event); } \ No newline at end of file diff --git a/src/Symfony/Component/Security/Http/Firewall/LogoutListener.php b/src/Symfony/Component/Security/Http/Firewall/LogoutListener.php index bdb92bcd60..ce757c8b44 100644 --- a/src/Symfony/Component/Security/Http/Firewall/LogoutListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/LogoutListener.php @@ -15,10 +15,10 @@ use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface; use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface; use Symfony\Component\Security\Core\SecurityContextInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\Kernel\Event\GetResponseEvent; +use Symfony\Component\Kernel\Events; /** * LogoutListener logout users. @@ -63,11 +63,11 @@ class LogoutListener implements ListenerInterface /** * Performs the logout if requested * - * @param EventInterface $event An EventInterface instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public function handle(EventInterface $event) + public function handle(GetResponseEvent $event) { - $request = $event->get('request'); + $request = $event->getRequest(); if ($this->logoutPath !== $request->getPathInfo()) { return; @@ -92,8 +92,6 @@ class LogoutListener implements ListenerInterface $this->securityContext->setToken(null); - $event->setProcessed(); - - return $response; + $event->setResponse($response); } } diff --git a/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php b/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php index 725755d9b7..c13845c24f 100644 --- a/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php @@ -2,18 +2,21 @@ namespace Symfony\Component\Security\Http\Firewall; -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Events as KernelEvents; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\Security\Core\SecurityContext; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; +use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; +use Symfony\Component\Security\Http\Events; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /* * This file is part of the Symfony framework. @@ -35,7 +38,7 @@ class RememberMeListener implements ListenerInterface private $rememberMeServices; private $authenticationManager; private $logger; - private $eventDispatcher; + private $dispatcher; /** * Constructor @@ -45,27 +48,27 @@ class RememberMeListener implements ListenerInterface * @param AuthenticationManagerInterface $authenticationManager * @param LoggerInterface $logger */ - public function __construct(SecurityContext $securityContext, RememberMeServicesInterface $rememberMeServices, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null) + public function __construct(SecurityContext $securityContext, RememberMeServicesInterface $rememberMeServices, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { $this->securityContext = $securityContext; $this->rememberMeServices = $rememberMeServices; $this->authenticationManager = $authenticationManager; $this->logger = $logger; - $this->eventDispatcher = $eventDispatcher; + $this->dispatcher = $dispatcher; } /** * Handles remember-me cookie based authentication. * - * @param Event $event An Event instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public function handle(EventInterface $event) + public function handle(GetResponseEvent $event) { if (null !== $this->securityContext->getToken()) { return; } - $request = $event->get('request'); + $request = $event->getRequest(); if (null === $token = $this->rememberMeServices->autoLogin($request)) { return; } @@ -74,8 +77,9 @@ class RememberMeListener implements ListenerInterface $token = $this->authenticationManager->authenticate($token); $this->securityContext->setToken($token); - if (null !== $this->eventDispatcher) { - $this->eventDispatcher->notify(new Event($this, 'security.interactive_login', array('request' => $request, 'token' => $token))); + if (null !== $this->dispatcher) { + $loginEvent = new InteractiveLoginEvent($request, $token); + $this->dispatcher->dispatch(Events::onSecurityInteractiveLogin, $loginEvent); } if (null !== $this->logger) { diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php index e1925bd6d7..f9219f789c 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php @@ -11,14 +11,13 @@ namespace Symfony\Component\Security\Http\Firewall; -use Symfony\Component\EventDispatcher\Event; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Events; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -27,6 +26,9 @@ use Symfony\Component\Security\Core\Role\SwitchUserRole; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Http\Event\SwitchUserEvent; +use Symfony\Component\Security\Http\Events; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * SwitchUserListener allows a user to impersonate another one temporarily @@ -44,12 +46,12 @@ class SwitchUserListener implements ListenerInterface private $usernameParameter; private $role; private $logger; - private $eventDispatcher; + private $dispatcher; /** * Constructor. */ - public function __construct(SecurityContextInterface $securityContext, UserProviderInterface $provider, UserCheckerInterface $userChecker, $providerKey, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, $usernameParameter = '_switch_user', $role = 'ROLE_ALLOWED_TO_SWITCH', EventDispatcherInterface $eventDispatcher = null) + public function __construct(SecurityContextInterface $securityContext, UserProviderInterface $provider, UserCheckerInterface $userChecker, $providerKey, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, $usernameParameter = '_switch_user', $role = 'ROLE_ALLOWED_TO_SWITCH', EventDispatcherInterface $dispatcher = null) { if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); @@ -63,17 +65,17 @@ class SwitchUserListener implements ListenerInterface $this->usernameParameter = $usernameParameter; $this->role = $role; $this->logger = $logger; - $this->eventDispatcher = $eventDispatcher; + $this->dispatcher = $dispatcher; } /** * Handles digest authentication. * - * @param EventInterface $event An EventInterface instance + * @param GetResponseEvent $event A GetResponseEvent instance */ - public function handle(EventInterface $event) + public function handle(GetResponseEvent $event) { - $request = $event->get('request'); + $request = $event->getRequest(); if (!$request->get($this->usernameParameter)) { return; @@ -94,9 +96,7 @@ class SwitchUserListener implements ListenerInterface $request->server->set('QUERY_STRING', ''); $response = new RedirectResponse($request->getUri(), 302); - $event->setProcessed(); - - return $response; + $event->setResponse($response); } /** @@ -129,8 +129,9 @@ class SwitchUserListener implements ListenerInterface $token = new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey, $roles); - if (null !== $this->eventDispatcher) { - $this->eventDispatcher->notify(new Event($this, 'security.switch_user', array('request' => $request, 'target_user' => $token->getUser()))); + if (null !== $this->dispatcher) { + $switchEvent = new SwitchUserEvent($request, $token->getUser()); + $this->dispatcher->dispatch(Events::onSecuritySwitchUser, $switchEvent); } return $token; @@ -149,8 +150,9 @@ class SwitchUserListener implements ListenerInterface throw new AuthenticationCredentialsNotFoundException(sprintf('Could not find original Token object.')); } - if (null !== $this->eventDispatcher) { - $this->eventDispatcher->notify(new Event($this, 'security.switch_user', array('request' => $request, 'target_user' => $original->getUser()))); + if (null !== $this->dispatcher) { + $switchEvent = new SwitchUserEvent($request, $original->getUser()); + $this->dispatcher->dispatch(Events::onSecuritySwitchUser, $switchEvent); } return $original; diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index 2402105e92..5a84391d5b 100644 --- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -11,8 +11,6 @@ namespace Symfony\Component\Security\Http\Firewall; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; - use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Log\LoggerInterface; @@ -23,6 +21,7 @@ use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterfac use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * UsernamePasswordFormAuthenticationListener is the default implementation of @@ -37,7 +36,7 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL /** * {@inheritdoc} */ - public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null, CsrfProviderInterface $csrfProvider = null) + public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null) { parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $providerKey, array_merge(array( 'username_parameter' => '_username', @@ -45,7 +44,7 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL 'csrf_parameter' => '_csrf_token', 'csrf_page_id' => 'form_login', 'post_only' => true, - ), $options), $successHandler, $failureHandler, $logger, $eventDispatcher); + ), $options), $successHandler, $failureHandler, $logger, $dispatcher); $this->csrfProvider = $csrfProvider; } diff --git a/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php index 54e3d3faf3..2f4b78a7cb 100644 --- a/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/X509AuthenticationListener.php @@ -11,12 +11,12 @@ namespace Symfony\Component\Security\Http\Firewall; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Exception\BadCredentialsException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * X509 authentication listener. @@ -28,9 +28,9 @@ class X509AuthenticationListener extends AbstractPreAuthenticatedListener private $userKey; private $credentialKey; - public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, $userKey = 'SSL_CLIENT_S_DN_Email', $credentialKey = 'SSL_CLIENT_S_DN', LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null) + public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, $userKey = 'SSL_CLIENT_S_DN_Email', $credentialKey = 'SSL_CLIENT_S_DN', LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { - parent::__construct($securityContext, $authenticationManager, $providerKey, $logger, $eventDispatcher); + parent::__construct($securityContext, $authenticationManager, $providerKey, $logger, $dispatcher); $this->userKey = $userKey; $this->credentialKey = $credentialKey; diff --git a/src/Symfony/Component/Security/Http/Logout/LogoutSuccessHandlerInterface.php b/src/Symfony/Component/Security/Http/Logout/LogoutSuccessHandlerInterface.php index 346784bdd8..21dbb818ff 100644 --- a/src/Symfony/Component/Security/Http/Logout/LogoutSuccessHandlerInterface.php +++ b/src/Symfony/Component/Security/Http/Logout/LogoutSuccessHandlerInterface.php @@ -3,7 +3,7 @@ namespace Symfony\Component\Security\Http\Logout; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\EventDispatcher\EventInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; /** * LogoutSuccesshandlerInterface. @@ -21,9 +21,9 @@ interface LogoutSuccessHandlerInterface /** * Creates a Response object to send upon a successful logout. * - * @param EventInterface $event + * @param GetResponseEvent $event * @param Request $request * @return Response never null */ - function onLogoutSuccess(EventInterface $event, Request $request); + function onLogoutSuccess(GetResponseEvent $event, Request $request); } \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php b/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php index 2b7125885c..a221a44105 100644 --- a/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php +++ b/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php @@ -13,126 +13,187 @@ namespace Symfony\Tests\Component\EventDispatcher; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; class EventDispatcherTest extends \PHPUnit_Framework_TestCase { - public function testConnectAndDisconnect() + /* Some pseudo events */ + const preFoo = 'preFoo'; + const postFoo = 'postFoo'; + const preBar = 'preBar'; + const postBar = 'postBar'; + + private $dispatcher; + + private $listener; + + protected function setUp() { - $dispatcher = new EventDispatcher(); - - $dispatcher->connect('bar', 'listenToBar'); - $this->assertEquals(array('listenToBar'), $dispatcher->getListeners('bar'), '->connect() connects a listener to an event name'); - $dispatcher->connect('bar', 'listenToBarBar'); - $this->assertEquals(array('listenToBar', 'listenToBarBar'), $dispatcher->getListeners('bar'), '->connect() can connect several listeners for the same event name'); - - $dispatcher->connect('barbar', 'listenToBarBar'); - - $dispatcher->disconnect('bar'); - $this->assertEquals(array(), $dispatcher->getListeners('bar'), '->disconnect() without a listener disconnects all listeners of for an event name'); - $this->assertEquals(array('listenToBarBar'), $dispatcher->getListeners('barbar'), '->disconnect() without a listener disconnects all listeners of for an event name'); + $this->dispatcher = new EventDispatcher(); + $this->listener = new TestEventListener(); } - public function testGetHasListeners() + public function testInitialState() { - $dispatcher = new EventDispatcher(); - - $this->assertFalse($dispatcher->hasListeners('foo'), '->hasListeners() returns false if the event has no listener'); - $dispatcher->connect('foo', 'listenToFoo'); - $this->assertEquals(true, $dispatcher->hasListeners('foo'), '->hasListeners() returns true if the event has some listeners'); - $dispatcher->disconnect('foo', 'listenToFoo'); - $this->assertFalse($dispatcher->hasListeners('foo'), '->hasListeners() returns false if the event has no listener'); - - $dispatcher->connect('bar', 'listenToBar'); - $this->assertEquals(array('listenToBar'), $dispatcher->getListeners('bar'), '->getListeners() returns an array of listeners connected to the given event name'); - $this->assertEquals(array(), $dispatcher->getListeners('foobar'), '->getListeners() returns an empty array if no listener are connected to the given event name'); + $this->assertEquals(array(), $this->dispatcher->getListeners()); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); } - public function testNotify() + public function testAddListener() { - $listener = new Listener(); - $dispatcher = new EventDispatcher(); - $dispatcher->connect('foo', array($listener, 'listenToFoo')); - $dispatcher->connect('foo', array($listener, 'listenToFooBis')); - $e = $dispatcher->notify($event = new Event(new \stdClass(), 'foo')); - $this->assertEquals('listenToFoolistenToFooBis', $listener->getValue(), '->notify() notifies all registered listeners in order'); - - $listener->reset(); - $dispatcher = new EventDispatcher(); - $dispatcher->connect('foo', array($listener, 'listenToFooBis')); - $dispatcher->connect('foo', array($listener, 'listenToFoo')); - $dispatcher->notify(new Event(new \stdClass(), 'foo')); - $this->assertEquals('listenToFooBislistenToFoo', $listener->getValue(), '->notify() notifies all registered listeners in order'); + $this->dispatcher->addListener(array('preFoo', 'postFoo'), $this->listener); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->assertEquals(1, count($this->dispatcher->getListeners(self::preFoo))); + $this->assertEquals(1, count($this->dispatcher->getListeners(self::postFoo))); + $this->assertEquals(2, count($this->dispatcher->getListeners())); } - public function testNotifyUntil() + public function testGetListenersSortsByPriority() { - $listener = new Listener(); - $dispatcher = new EventDispatcher(); - $dispatcher->connect('foo', array($listener, 'listenToFoo')); - $dispatcher->connect('foo', array($listener, 'listenToFooBis')); - $dispatcher->notifyUntil($event = new Event(new \stdClass(), 'foo')); - $this->assertEquals('listenToFoolistenToFooBis', $listener->getValue(), '->notifyUntil() notifies all registered listeners in order and stops when the event is processed'); + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); - $listener->reset(); - $dispatcher = new EventDispatcher(); - $dispatcher->connect('foo', array($listener, 'listenToFooBis')); - $dispatcher->connect('foo', array($listener, 'listenToFoo')); - $dispatcher->notifyUntil($event = new Event(new \stdClass(), 'foo')); - $this->assertEquals('listenToFooBis', $listener->getValue(), '->notifyUntil() notifies all registered listeners in order and stops when the event is processed'); + $this->dispatcher->addListener('preFoo', $listener1, -10); + $this->dispatcher->addListener('preFoo', $listener2); + $this->dispatcher->addListener('preFoo', $listener3, 10); + + $expected = array( + spl_object_hash($listener3) => $listener3, + spl_object_hash($listener2) => $listener2, + spl_object_hash($listener1) => $listener1, + ); + + $this->assertSame($expected, $this->dispatcher->getListeners('preFoo')); } - public function testFilter() + public function testGetAllListenersSortsByPriority() { - $listener = new Listener(); - $dispatcher = new EventDispatcher(); - $dispatcher->connect('foo', array($listener, 'filterFoo')); - $dispatcher->connect('foo', array($listener, 'filterFooBis')); - $ret = $dispatcher->filter($event = new Event(new \stdClass(), 'foo'), 'foo'); - $this->assertEquals('-*foo*-', $ret, '->filter() returns the filtered value'); + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); - $listener->reset(); - $dispatcher = new EventDispatcher(); - $dispatcher->connect('foo', array($listener, 'filterFooBis')); - $dispatcher->connect('foo', array($listener, 'filterFoo')); - $ret = $dispatcher->filter($event = new Event(new \stdClass(), 'foo'), 'foo'); - $this->assertEquals('*-foo-*', $ret, '->filter() returns the filtered value'); + $this->dispatcher->addListener('preFoo', $listener1, -10); + $this->dispatcher->addListener('preFoo', $listener2); + $this->dispatcher->addListener('preFoo', $listener3, 10); + $this->dispatcher->addListener('postFoo', $listener4, -10); + $this->dispatcher->addListener('postFoo', $listener5); + $this->dispatcher->addListener('postFoo', $listener6, 10); + + $expected = array( + 'preFoo' => array( + spl_object_hash($listener3) => $listener3, + spl_object_hash($listener2) => $listener2, + spl_object_hash($listener1) => $listener1, + ), + 'postFoo' => array( + spl_object_hash($listener6) => $listener6, + spl_object_hash($listener5) => $listener5, + spl_object_hash($listener4) => $listener4, + ), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners()); + } + + public function testDispatch() + { + $this->dispatcher->addListener(array('preFoo', 'postFoo'), $this->listener); + $this->dispatcher->dispatch(self::preFoo); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + $invoked++; + }; + $this->dispatcher->addListener(array('preFoo', 'postFoo'), $listener); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener; + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->dispatcher->addListener('postFoo', $this->listener, 10); + $this->dispatcher->addListener('postFoo', $otherListener); + $this->dispatcher->dispatch(self::postFoo); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->dispatcher->addListener('preFoo', $listener1, -10); + $this->dispatcher->addListener('preFoo', $listener2); + $this->dispatcher->addListener('preFoo', $listener3, 10); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->dispatcher->addListener(array('preBar'), $this->listener); + $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener(array('preBar'), $this->listener); + $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); } } -class Listener +class TestEventListener { - protected - $value = ''; + public $preFooInvoked = false; + public $postFooInvoked = false; - function filterFoo(Event $event, $foo) + /* Listener methods */ + + public function preFoo(Event $e) { - return "*$foo*"; + $this->preFooInvoked = true; } - function filterFooBis(Event $event, $foo) + public function postFoo(Event $e) { - return "-$foo-"; - } + $this->postFooInvoked = true; - function listenToFoo(Event $event) - { - $this->value .= 'listenToFoo'; - } - - function listenToFooBis(Event $event) - { - $this->value .= 'listenToFooBis'; - - $event->setProcessed(); - } - - function getValue() - { - return $this->value; - } - - function reset() - { - $this->value = ''; + $e->stopPropagation(); + } +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('preFoo', 'postFoo'); } } diff --git a/tests/Symfony/Tests/Component/EventDispatcher/EventTest.php b/tests/Symfony/Tests/Component/EventDispatcher/EventTest.php deleted file mode 100644 index 64e34ff220..0000000000 --- a/tests/Symfony/Tests/Component/EventDispatcher/EventTest.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Tests\Component\EventDispatcher; - -use Symfony\Component\EventDispatcher\Event; - -class EventTest extends \PHPUnit_Framework_TestCase -{ - protected $subject; - protected $parameters; - - public function testGetSubject() - { - $event = $this->createEvent(); - $this->assertEquals($this->subject, $event->getSubject(), '->getSubject() returns the event subject'); - } - - public function testGetName() - { - $this->assertEquals('name', $this->createEvent()->getName(), '->getName() returns the event name'); - } - - public function testParameters() - { - $event = $this->createEvent(); - - $this->assertEquals($this->parameters, $event->all(), '->all() returns the event parameters'); - $this->assertEquals('bar', $event->get('foo'), '->get() returns the value of a parameter'); - $event->set('foo', 'foo'); - $this->assertEquals('foo', $event->get('foo'), '->set() changes the value of a parameter'); - $this->assertTrue($event->has('foo'), '->has() returns true if the parameter is defined'); - $this->assertFalse($event->has('oof'), '->has() returns false if the parameter is not defined'); - - try { - $event->get('foobar'); - $this->fail('->get() throws an \InvalidArgumentException exception when the parameter does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws an \InvalidArgumentException exception when the parameter does not exist'); - $this->assertEquals('The event "name" has no "foobar" parameter.', $e->getMessage(), '->get() throws an \InvalidArgumentException exception when the parameter does not exist'); - } - $event = new Event($this->subject, 'name', $this->parameters); - } - - public function testSetIsProcessed() - { - $event = $this->createEvent(); - $this->assertFalse($event->isProcessed(), '->isProcessed() returns false by default'); - $event->setProcessed(); - $this->assertTrue($event->isProcessed(), '->isProcessed() returns true if the event has been processed'); - } - - protected function createEvent() - { - $this->subject = new \stdClass(); - $this->parameters = array('foo' => 'bar'); - - return new Event($this->subject, 'name', $this->parameters); - } -} diff --git a/tests/Symfony/Tests/Component/HttpKernel/ClientTest.php b/tests/Symfony/Tests/Component/HttpKernel/ClientTest.php index a6186628d8..07a94d3c39 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/ClientTest.php +++ b/tests/Symfony/Tests/Component/HttpKernel/ClientTest.php @@ -15,8 +15,6 @@ use Symfony\Component\HttpKernel\Client; use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\Event; require_once __DIR__.'/TestHttpKernel.php'; diff --git a/tests/Symfony/Tests/Component/HttpKernel/DataCollector/EventDataCollectorTest.php b/tests/Symfony/Tests/Component/HttpKernel/DataCollector/EventDataCollectorTest.php index cfa90cb944..2841f12784 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/DataCollector/EventDataCollectorTest.php +++ b/tests/Symfony/Tests/Component/HttpKernel/DataCollector/EventDataCollectorTest.php @@ -14,27 +14,26 @@ namespace Symfony\Tests\Component\HttpKernel\DataCollector; use Symfony\Component\HttpKernel\DataCollector\EventDataCollector; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Debug\EventDispatcherTraceableInterface; +use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcher; - class EventDataCollectorTest extends \PHPUnit_Framework_TestCase { public function testCollect() { $c = new EventDataCollector(); $c->setEventDispatcher(new TestEventDispatcher()); - + $c->collect(new Request(), new Response()); - + $this->assertSame('events',$c->getName()); $this->assertSame(array('foo'),$c->getCalledListeners()); $this->assertSame(array('bar'),$c->getNotCalledListeners()); } - + } -class TestEventDispatcher extends EventDispatcher implements EventDispatcherTraceableInterface +class TestEventDispatcher extends EventDispatcher implements TraceableEventDispatcherInterface { function getCalledListeners() { diff --git a/tests/Symfony/Tests/Component/HttpKernel/Debug/ExceptionListenerTest.php b/tests/Symfony/Tests/Component/HttpKernel/Debug/ExceptionListenerTest.php index 7a9fbaabdc..c07212f7b5 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/Debug/ExceptionListenerTest.php +++ b/tests/Symfony/Tests/Component/HttpKernel/Debug/ExceptionListenerTest.php @@ -11,11 +11,13 @@ namespace Symfony\Tests\Component\HttpKernel\Debug; +use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Debug\ExceptionListener; -use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Debug\ErrorException; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Tests\Component\HttpKernel\Logger; /** @@ -29,72 +31,74 @@ class ExceptionListenerTest extends \PHPUnit_Framework_TestCase { $logger = new TestLogger(); $l = new ExceptionListener('foo', $logger); - - $_logger = new \ReflectionProperty(get_class($l),'logger'); + + $_logger = new \ReflectionProperty(get_class($l), 'logger'); $_logger->setAccessible(true); - $_controller = new \ReflectionProperty(get_class($l),'controller'); + $_controller = new \ReflectionProperty(get_class($l), 'controller'); $_controller->setAccessible(true); - - $this->assertSame($logger,$_logger->getValue($l)); - $this->assertSame('foo',$_controller->getValue($l)); + + $this->assertSame($logger, $_logger->getValue($l)); + $this->assertSame('foo', $_controller->getValue($l)); } - + /** * @dataProvider provider */ - public function testHandleWithoutLogger($event,$event2) + public function testHandleWithoutLogger($event, $event2) { //store the current error_log, and set the new one to dev/null $error_log = ini_get('error_log'); - ini_set('error_log','/dev/null'); - - $l = new ExceptionListener('foo'); + ini_set('error_log', '/dev/null'); - $this->assertEquals('foo', $l->handle($event)); - - try{ - $response = $l->handle($event2); - }catch(\Exception $e){ - $this->assertSame('foo',$e->getMessage()); + $l = new ExceptionListener('foo'); + $l->onCoreException($event); + + $this->assertEquals(new Response('foo'), $event->getResponse()); + + try { + $l->onCoreException($event2); + } catch(\Exception $e) { + $this->assertSame('foo', $e->getMessage()); } - + //restore the old error_log - ini_set('error_log',$error_log); + ini_set('error_log', $error_log); } - + /** * @dataProvider provider */ public function testHandleWithLogger($event, $event2) { $logger = new TestLogger(); - - $l = new ExceptionListener('foo',$logger); - - $this->assertSame('foo', $l->handle($event)); - - try{ - $response = $l->handle($event2); - }catch(\Exception $e){ - $this->assertSame('foo',$e->getMessage()); + + $l = new ExceptionListener('foo', $logger); + $l->onCoreException($event); + + $this->assertEquals(new Response('foo'), $event->getResponse()); + + try { + $l->onCoreException($event2); + } catch(\Exception $e) { + $this->assertSame('foo', $e->getMessage()); } - - $this->assertEquals(3,$logger->countErrors()); - $this->assertEquals(3,count($logger->getLogs('err'))); + + $this->assertEquals(3, $logger->countErrors()); + $this->assertEquals(3, count($logger->getLogs('err'))); } - + public function provider() { - $args = array('exception'=>new ErrorException('foo'),'request'=>new Request()); - - $event = new Event(new Subject(),'bar',$args); - $event2 = new Event(new SubjectException(),'bar',$args); + $request = new Request(); + $exception = new ErrorException('foo'); + $event = new GetResponseForExceptionEvent(new TestKernel(), $request, 'foo', $exception); + $event2 = new GetResponseForExceptionEvent(new TestKernelThatThrowsException(), $request, 'foo', $exception); return array( - array($event,$event2) + array($event, $event2) ); } - + } class TestLogger extends Logger implements DebugLoggerInterface @@ -103,25 +107,25 @@ class TestLogger extends Logger implements DebugLoggerInterface { return count($this->logs['err']); } - + public function getDebugLogger() { return new static(); } } -class Subject +class TestKernel implements HttpKernelInterface { - public function handle() + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) { - return 'foo'; + return new Response('foo'); } - + } -class SubjectException +class TestKernelThatThrowsException implements HttpKernelInterface { - public function handle() + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) { throw new \Exception('bar'); } diff --git a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/EsiListenerTest.php b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/EsiListenerTest.php index b8247b3e92..bf23bc18c1 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/EsiListenerTest.php +++ b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/EsiListenerTest.php @@ -13,46 +13,54 @@ namespace Symfony\Tests\Component\HttpKernel\HttpCache; use Symfony\Component\HttpKernel\HttpCache\Esi; use Symfony\Component\HttpKernel\HttpCache\EsiListener; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Events; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\EventDispatcher\EventDispatcher; class EsiListenerTest extends \PHPUnit_Framework_TestCase { public function testFilterDoesNothingForSubRequests() { $dispatcher = new EventDispatcher(); + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $response = new Response('foo '); $listener = new EsiListener(new Esi()); - $dispatcher->connect('core.response', array($listener, 'filter')); - $event = new Event(null, 'core.response', array('request_type' => HttpKernelInterface::SUB_REQUEST)); - $dispatcher->filter($event, $response = new Response('foo ')); + $dispatcher->addListener(Events::onCoreResponse, $listener); + $event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::SUB_REQUEST, $response); + $dispatcher->dispatch(Events::onCoreResponse, $event); - $this->assertEquals('', $response->headers->get('Surrogate-Control')); + $this->assertEquals('', $event->getResponse()->headers->get('Surrogate-Control')); } public function testFilterWhenThereIsSomeEsiIncludes() { $dispatcher = new EventDispatcher(); + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $response = new Response('foo '); $listener = new EsiListener(new Esi()); - $dispatcher->connect('core.response', array($listener, 'filter')); - $event = new Event(null, 'core.response', array('request_type' => HttpKernelInterface::MASTER_REQUEST)); - $dispatcher->filter($event, $response = new Response('foo ')); + $dispatcher->addListener(Events::onCoreResponse, $listener); + $event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response); + $dispatcher->dispatch(Events::onCoreResponse, $event); - $this->assertEquals('content="ESI/1.0"', $response->headers->get('Surrogate-Control')); + $this->assertEquals('content="ESI/1.0"', $event->getResponse()->headers->get('Surrogate-Control')); } public function testFilterWhenThereIsNoEsiIncludes() { $dispatcher = new EventDispatcher(); + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $response = new Response('foo'); $listener = new EsiListener(new Esi()); - $dispatcher->connect('core.response', array($listener, 'filter')); - $event = new Event(null, 'core.response', array('request_type' => HttpKernelInterface::MASTER_REQUEST)); - $dispatcher->filter($event, $response = new Response('foo')); + $dispatcher->addListener(Events::onCoreResponse, $listener); + $event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response); + $dispatcher->dispatch(Events::onCoreResponse, $event); - $this->assertEquals('', $response->headers->get('Surrogate-Control')); + $this->assertEquals('', $event->getResponse()->headers->get('Surrogate-Control')); } } diff --git a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/TestHttpKernel.php b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/TestHttpKernel.php index 9b4c077a89..cb14f94f29 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/TestHttpKernel.php +++ b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/TestHttpKernel.php @@ -15,8 +15,8 @@ use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; class TestHttpKernel extends HttpKernel implements ControllerResolverInterface { diff --git a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/TestMultipleHttpKernel.php b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/TestMultipleHttpKernel.php index 1fe2a6fe13..5b6e0f8428 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/TestMultipleHttpKernel.php +++ b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/TestMultipleHttpKernel.php @@ -15,8 +15,8 @@ use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; class TestMultipleHttpKernel extends HttpKernel implements ControllerResolverInterface { diff --git a/tests/Symfony/Tests/Component/HttpKernel/HttpKernelTest.php b/tests/Symfony/Tests/Component/HttpKernel/HttpKernelTest.php index 19f4983da0..2e86b88b83 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/HttpKernelTest.php +++ b/tests/Symfony/Tests/Component/HttpKernel/HttpKernelTest.php @@ -13,6 +13,7 @@ namespace Symfony\Tests\Component\HttpKernel; use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Events; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\EventDispatcher\EventDispatcher; @@ -42,11 +43,9 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase public function testHandleWhenControllerThrowsAnExceptionAndRawIsFalse() { $dispatcher = new EventDispatcher(); - $dispatcher->connect('core.exception', function ($event) + $dispatcher->addListener(Events::onCoreException, function ($event) { - $event->setProcessed(); - - return new Response($event->get('exception')->getMessage()); + $event->setResponse(new Response($event->getException()->getMessage())); }); $kernel = new HttpKernel($dispatcher, $this->getResolver(function () { throw new \RuntimeException('foo'); })); @@ -57,11 +56,9 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase public function testHandleWhenAListenerReturnsAResponse() { $dispatcher = new EventDispatcher(); - $dispatcher->connect('core.request', function ($event) + $dispatcher->addListener(Events::onCoreRequest, function ($event) { - $event->setProcessed(); - - return new Response('hello'); + $event->setResponse(new Response('hello')); }); $kernel = new HttpKernel($dispatcher, $this->getResolver()); @@ -92,7 +89,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase } /** - * @expectedException RuntimeException + * @expectedException LogicException */ public function testHandleWhenControllerDoesNotReturnAResponse() { @@ -105,55 +102,21 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase public function testHandleWhenControllerDoesNotReturnAResponseButAViewIsRegistered() { $dispatcher = new EventDispatcher(); - $dispatcher->connect('core.view', function ($event) + $dispatcher->addListener(Events::onCoreView, function ($event) { - $event->setProcessed(); - - return new Response($event->get('controller_value')); + $event->setResponse(new Response($event->getControllerResult())); }); $kernel = new HttpKernel($dispatcher, $this->getResolver(function () { return 'foo'; })); $this->assertEquals('foo', $kernel->handle(new Request())->getContent()); } - /** - * @expectedException RuntimeException - */ - public function testHandleWhenAViewDoesNotReturnAResponse() - { - $dispatcher = new EventDispatcher(); - $dispatcher->connect('core.view', function ($event) - { - $event->setProcessed(); - - return $event->get('controller_value'); - }); - $kernel = new HttpKernel($dispatcher, $this->getResolver(function () { return 'foo'; })); - - $kernel->handle(new Request()); - } - - /** - * @expectedException RuntimeException - */ - public function testHandleWhenAResponseListenerDoesNotReturnAResponse() - { - $dispatcher = new EventDispatcher(); - $dispatcher->connect('core.response', function ($event, $response) - { - return 'foo'; - }); - $kernel = new HttpKernel($dispatcher, $this->getResolver()); - - $kernel->handle(new Request()); - } - public function testHandleWithAResponseListener() { $dispatcher = new EventDispatcher(); - $dispatcher->connect('core.response', function ($event, $response) + $dispatcher->addListener(Events::onCoreResponse, function ($event) { - return new Response('foo'); + $event->setResponse(new Response('foo')); }); $kernel = new HttpKernel($dispatcher, $this->getResolver()); diff --git a/tests/Symfony/Tests/Component/HttpKernel/ResponseListenerTest.php b/tests/Symfony/Tests/Component/HttpKernel/ResponseListenerTest.php index fc6922adba..c6d6b732fd 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/ResponseListenerTest.php +++ b/tests/Symfony/Tests/Component/HttpKernel/ResponseListenerTest.php @@ -12,56 +12,68 @@ namespace Symfony\Tests\Component\HttpKernel; use Symfony\Component\HttpKernel\ResponseListener; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\Event; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Events; +use Symfony\Component\EventDispatcher\EventDispatcher; class ResponseListenerTest extends \PHPUnit_Framework_TestCase { + private $dispatcher; + + private $kernel; + + protected function setUp() + { + $this->dispatcher = new EventDispatcher(); + $listener = new ResponseListener('UTF-8'); + $this->dispatcher->addListener(Events::onCoreResponse, $listener); + + $this->kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + + } public function testFilterDoesNothingForSubRequests() { - $event = new Event(null, 'core.response', array('request_type' => HttpKernelInterface::SUB_REQUEST)); - $this->getDispatcher()->filter($event, $response = new Response('foo')); + $response = new Response('foo'); - $this->assertEquals('', $response->headers->get('content-type')); + $event = new FilterResponseEvent($this->kernel, new Request(), HttpKernelInterface::SUB_REQUEST, $response); + $this->dispatcher->dispatch(Events::onCoreResponse, $event); + + $this->assertEquals('', $event->getResponse()->headers->get('content-type')); } public function testFilterDoesNothingIfContentTypeIsSet() { - $event = new Event(null, 'core.response', array('request_type' => HttpKernelInterface::MASTER_REQUEST)); $response = new Response('foo'); $response->headers->set('Content-Type', 'text/plain'); - $this->getDispatcher()->filter($event, $response); - $this->assertEquals('text/plain', $response->headers->get('content-type')); + $event = new FilterResponseEvent($this->kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response); + $this->dispatcher->dispatch(Events::onCoreResponse, $event); + + $this->assertEquals('text/plain', $event->getResponse()->headers->get('content-type')); } public function testFilterDoesNothingIfRequestFormatIsNotDefined() { - $event = new Event(null, 'core.response', array('request_type' => HttpKernelInterface::MASTER_REQUEST, 'request' => Request::create('/'))); - $this->getDispatcher()->filter($event, $response = new Response('foo')); + $response = new Response('foo'); - $this->assertEquals('', $response->headers->get('content-type')); + $event = new FilterResponseEvent($this->kernel, Request::create('/'), HttpKernelInterface::MASTER_REQUEST, $response); + $this->dispatcher->dispatch(Events::onCoreResponse, $event); + + $this->assertEquals('', $event->getResponse()->headers->get('content-type')); } public function testFilterSetContentType() { + $response = new Response('foo'); $request = Request::create('/'); $request->setRequestFormat('json'); - $event = new Event(null, 'core.response', array('request_type' => HttpKernelInterface::MASTER_REQUEST, 'request' => $request)); - $this->getDispatcher()->filter($event, $response = new Response('foo')); - $this->assertEquals('application/json', $response->headers->get('content-type')); - } + $event = new FilterResponseEvent($this->kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response); + $this->dispatcher->dispatch(Events::onCoreResponse, $event); - protected function getDispatcher() - { - $dispatcher = new EventDispatcher(); - $listener = new ResponseListener('UTF-8'); - $dispatcher->connect('core.response', array($listener, 'filter')); - - return $dispatcher; + $this->assertEquals('application/json', $event->getResponse()->headers->get('content-type')); } } diff --git a/tests/Symfony/Tests/Component/HttpKernel/TestHttpKernel.php b/tests/Symfony/Tests/Component/HttpKernel/TestHttpKernel.php index bd11f7b80f..35eda847da 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/TestHttpKernel.php +++ b/tests/Symfony/Tests/Component/HttpKernel/TestHttpKernel.php @@ -14,8 +14,8 @@ namespace Symfony\Tests\Component\HttpKernel; use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; class TestHttpKernel extends HttpKernel implements ControllerResolverInterface { diff --git a/tests/Symfony/Tests/Component/Security/Http/Firewall/RememberMeListenerTest.php b/tests/Symfony/Tests/Component/Security/Http/Firewall/RememberMeListenerTest.php index 2ab8efbb1a..f0b2b395c5 100644 --- a/tests/Symfony/Tests/Component/Security/Http/Firewall/RememberMeListenerTest.php +++ b/tests/Symfony/Tests/Component/Security/Http/Firewall/RememberMeListenerTest.php @@ -3,6 +3,7 @@ namespace Symfony\Tests\Component\Security\Http\Firewall; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Events; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\Security\Core\Exception\AuthenticationException; @@ -11,7 +12,7 @@ use Symfony\Component\HttpFoundation\Request; class RememberMeListenerTest extends \PHPUnit_Framework_TestCase { - public function testCheckCookiesDoesNotTryToPopulateNonEmptySecurityContext() + public function testOnCoreSecurityDoesNotTryToPopulateNonEmptySecurityContext() { list($listener, $context, $service,,) = $this->getListener(); @@ -26,10 +27,10 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase ->method('setToken') ; - $this->assertNull($listener->handle($this->getEvent())); + $this->assertNull($listener->handle($this->getGetResponseEvent())); } - public function testCheckCookiesDoesNothingWhenNoCookieIsSet() + public function testOnCoreSecurityDoesNothingWhenNoCookieIsSet() { list($listener, $context, $service,,) = $this->getListener(); @@ -45,18 +46,17 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue(null)) ; - $event = $this->getEvent(); + $event = $this->getGetResponseEvent(); $event ->expects($this->once()) - ->method('get') - ->with('request') + ->method('getRequest') ->will($this->returnValue(new Request())) ; $this->assertNull($listener->handle($event)); } - public function testCheckCookiesIgnoresAuthenticationExceptionThrownByAuthenticationManagerImplementation() + public function testOnCoreSecurityIgnoresAuthenticationExceptionThrownByAuthenticationManagerImplementation() { list($listener, $context, $service, $manager,) = $this->getListener(); @@ -84,18 +84,17 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase ->will($this->throwException($exception)) ; - $event = $this->getEvent(); + $event = $this->getGetResponseEvent(); $event ->expects($this->once()) - ->method('get') - ->with('request') + ->method('getRequest') ->will($this->returnValue(new Request())) ; $listener->handle($event); } - public function testCheckCookies() + public function testOnCoreSecurity() { list($listener, $context, $service, $manager,) = $this->getListener(); @@ -124,20 +123,24 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue($token)) ; - $event = $this->getEvent(); + $event = $this->getGetResponseEvent(); $event ->expects($this->once()) - ->method('get') - ->with('request') + ->method('getRequest') ->will($this->returnValue(new Request())) ; $listener->handle($event); } - protected function getEvent() + protected function getGetResponseEvent() { - return $this->getMock('Symfony\Component\EventDispatcher\Event', array(), array(), '', false); + return $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + } + + protected function getFilterResponseEvent() + { + return $this->getMock('Symfony\Component\HttpKernel\Event\FilterResponseEvent', array(), array(), '', false); } protected function getListener()