]> Raphaël G. Git Repositories - userbundle/blobdiff - Listener/LogoutListener.php
Replace logout success handler with default logout listener
[userbundle] / Listener / LogoutListener.php
diff --git a/Listener/LogoutListener.php b/Listener/LogoutListener.php
new file mode 100644 (file)
index 0000000..29b69e4
--- /dev/null
@@ -0,0 +1,221 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of the Rapsys UserBundle package.
+ *
+ * (c) Raphaël Gertz <symfony@rapsys.eu>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Rapsys\UserBundle\Listener;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Exception\InvalidParameterException;
+use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\RouterInterface;
+use Symfony\Component\Security\Http\Event\LogoutEvent;
+
+use Rapsys\UserBundle\RapsysUserBundle;
+
+/**
+ * {@inheritdoc}
+ */
+class LogoutListener implements EventSubscriberInterface {
+       /**
+        * Config array
+        */
+       protected $config;
+
+       /**
+        * Target url
+        */
+       private $targetUrl;
+
+       /**
+        * {@inheritdoc}
+        *
+        * @xxx Second argument will be replaced by security.firewalls.main.logout.target
+        * @see vendor/symfony/security-bundle/DependencyInjection/SecurityExtension.php +445
+        */
+       public function __construct(ContainerInterface $container, string $targetUrl, RouterInterface $router) {
+               //Set config
+               $this->config = $container->getParameter(RapsysUserBundle::getAlias());
+
+               //Set target url
+               $this->targetUrl = $targetUrl;
+
+               //Set router
+               $this->router = $router;
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function onLogout(LogoutEvent $event): void {
+               //Get request
+               $request = $event->getRequest();
+
+               //Retrieve logout route
+               $logout = $request->attributes->get('_route');
+
+               //Extract and process referer
+               if (($referer = $request->headers->get('referer'))) {
+                       //Create referer request instance
+                       $req = Request::create($referer);
+
+                       //Get referer path
+                       $path = $req->getPathInfo();
+
+                       //Get referer query string
+                       $query = $req->getQueryString();
+
+                       //Remove script name
+                       $path = str_replace($request->getScriptName(), '', $path);
+
+                       //Try with referer path
+                       try {
+                               //Save old context
+                               $oldContext = $this->router->getContext();
+
+                               //Force clean context
+                               //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST
+                               //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42
+                               $this->router->setContext(new RequestContext());
+
+                               //Retrieve route matching path
+                               $route = $this->router->match($path);
+
+                               //Reset context
+                               $this->router->setContext($oldContext);
+
+                               //Clear old context
+                               unset($oldContext);
+
+                               //Without logout route name
+                               if (($name = $route['_route']) != $logout) {
+                                       //Remove route and controller from route defaults
+                                       unset($route['_route'], $route['_controller'], $route['_canonical_route']);
+
+                                       //Generate url
+                                       $url = $this->router->generate($name, $route);
+
+                                       //Set event response
+                                       $event->setResponse(new RedirectResponse($url, 302));
+
+                                       //Return
+                                       return;
+                               //With logout route name
+                               } else {
+                                       //Unset referer and route
+                                       unset($referer, $route);
+                               }
+                       //No route matched
+                       } catch (ResourceNotFoundException $e) {
+                               //Unset referer and route
+                               unset($referer, $route);
+                       }
+               }
+
+               //With index route from config
+               if (!empty($name = $this->config['route']['index']['name']) && is_array($context = $this->config['route']['index']['context'])) {
+                       //Without logout route name
+                       if ($name != $logout) {
+                               //Try index route
+                               try {
+                                       //Generate url
+                                       $url = $this->router->generate($name, $context);
+
+                                       //Set event response
+                                       $event->setResponse(new RedirectResponse($url, 302));
+
+                                       //Return
+                                       return;
+                               //No route matched
+                               } catch (ResourceNotFoundException $e) {
+                                       //Unset name and context
+                                       unset($name, $context);
+                               }
+                       //With logout route name
+                       } else {
+                               //Unset name and context
+                               unset($name, $context);
+                       }
+               }
+
+               //Try target url
+               try {
+                       //Save old context
+                       $oldContext = $this->router->getContext();
+
+                       //Force clean context
+                       //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST
+                       //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42
+                       $this->router->setContext(new RequestContext());
+
+                       //With logout target path
+                       if ($this->targetUrl[0] == '/') {
+                               //Retrieve route matching target url
+                               $route = $this->router->match($this->targetUrl);
+
+                               //Reset context
+                               $this->router->setContext($oldContext);
+
+                               //Clear old context
+                               unset($oldContext);
+
+                               //Without logout route name
+                               if (($name = $route['_route']) != $logout) {
+                                       //Remove route and controller from route defaults
+                                       unset($route['_route'], $route['_controller'], $route['_canonical_route']);
+
+                                       //Generate url
+                                       $url = $this->router->generate($name, $route);
+
+                                       //Set event response
+                                       $event->setResponse(new RedirectResponse($url, 302));
+
+                                       //Return
+                                       return;
+                               //With logout route name
+                               } else {
+                                       //Unset name and route
+                                       unset($name, $route);
+                               }
+                       //With route name
+                       } else {
+                               //Retrieve route matching path
+                               $url = $this->router->generate($this->targetUrl);
+
+                               //Set event response
+                               $event->setResponse(new RedirectResponse($url, 302));
+
+                               //Return
+                               return;
+                       }
+               //Get first route from route collection if / path was not matched
+               } catch (ResourceNotFoundException|RouteNotFoundException|MissingMandatoryParametersException|InvalidParameterException $e) {
+                       //Unset name and route
+                       unset($name, $route);
+               }
+
+               //Set event response
+               $event->setResponse(new RedirectResponse('/', 302));
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public static function getSubscribedEvents(): array {
+               return [
+                       LogoutEvent::class => ['onLogout', 64],
+               ];
+       }
+}