<?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/Resources/config/security_listeners.php +79
*/
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],
];
}
}