<?php 
 
/* 
 * This file is part of the Symfony package. 
 * 
 * (c) Fabien Potencier <fabien@symfony.com> 
 * 
 * For the full copyright and license information, please view the LICENSE 
 * file that was distributed with this source code. 
 */ 
 
namespace Symfony\Component\Security\Http; 
 
use Symfony\Component\EventDispatcher\EventDispatcherInterface; 
use Symfony\Component\EventDispatcher\EventSubscriberInterface; 
use Symfony\Component\HttpKernel\Event\FinishRequestEvent; 
use Symfony\Component\HttpKernel\Event\GetResponseEvent; 
use Symfony\Component\HttpKernel\Event\RequestEvent; 
use Symfony\Component\HttpKernel\KernelEvents; 
use Symfony\Component\Security\Http\Firewall\AbstractListener; 
use Symfony\Component\Security\Http\Firewall\AccessListener; 
use Symfony\Component\Security\Http\Firewall\LogoutListener; 
 
/** 
 * Firewall uses a FirewallMap to register security listeners for the given 
 * request. 
 * 
 * It allows for different security strategies within the same application 
 * (a Basic authentication for the /api, and a web based authentication for 
 * everything else for instance). 
 * 
 * @author Fabien Potencier <fabien@symfony.com> 
 */ 
class Firewall implements EventSubscriberInterface 
{ 
    private $map; 
    private $dispatcher; 
    private $exceptionListeners; 
 
    public function __construct(FirewallMapInterface $map, EventDispatcherInterface $dispatcher) 
    { 
        // the type-hint will be updated to the "EventDispatcherInterface" from symfony/contracts in 5.0 
 
        $this->map = $map; 
        $this->dispatcher = $dispatcher; 
        $this->exceptionListeners = new \SplObjectStorage(); 
    } 
 
    /** 
     * @internal since Symfony 4.3 
     */ 
    public function onKernelRequest(GetResponseEvent $event) 
    { 
        if (!$event->isMasterRequest()) { 
            return; 
        } 
 
        // register listeners for this firewall 
        $listeners = $this->map->getListeners($event->getRequest()); 
 
        if (3 !== \count($listeners)) { 
            @trigger_error(sprintf('Not returning an array of 3 elements from %s::getListeners() is deprecated since Symfony 4.2, the 3rd element must be an instance of %s or null.', FirewallMapInterface::class, LogoutListener::class), \E_USER_DEPRECATED); 
            $listeners[2] = null; 
        } 
 
        $authenticationListeners = $listeners[0]; 
        $exceptionListener = $listeners[1]; 
        $logoutListener = $listeners[2]; 
 
        if (null !== $exceptionListener) { 
            $this->exceptionListeners[$event->getRequest()] = $exceptionListener; 
            $exceptionListener->register($this->dispatcher); 
        } 
 
        $authenticationListeners = function () use ($authenticationListeners, $logoutListener) { 
            $accessListener = null; 
 
            foreach ($authenticationListeners as $listener) { 
                if ($listener instanceof AccessListener) { 
                    $accessListener = $listener; 
 
                    continue; 
                } 
 
                yield $listener; 
            } 
 
            if (null !== $logoutListener) { 
                yield $logoutListener; 
            } 
 
            if (null !== $accessListener) { 
                yield $accessListener; 
            } 
        }; 
 
        if ($event instanceof RequestEvent) { 
            $this->callListeners($event, $authenticationListeners()); 
        } else { 
            $this->handleRequest($event, $authenticationListeners()); 
        } 
    } 
 
    /** 
     * @internal since Symfony 4.3 
     */ 
    public function onKernelFinishRequest(FinishRequestEvent $event) 
    { 
        $request = $event->getRequest(); 
 
        if (isset($this->exceptionListeners[$request])) { 
            $this->exceptionListeners[$request]->unregister($this->dispatcher); 
            unset($this->exceptionListeners[$request]); 
        } 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public static function getSubscribedEvents() 
    { 
        return [ 
            KernelEvents::REQUEST => ['onKernelRequest', 8], 
            KernelEvents::FINISH_REQUEST => 'onKernelFinishRequest', 
        ]; 
    } 
 
    protected function callListeners(RequestEvent $event, iterable $listeners) 
    { 
        $this->handleRequest($event, $listeners); 
    } 
 
    /** 
     * @deprecated since Symfony 4.3, use callListeners instead 
     */ 
    protected function handleRequest(GetResponseEvent $event, $listeners) 
    { 
        foreach ($listeners as $listener) { 
            if (\is_callable($listener)) { 
                $listener($event); 
            } else { 
                @trigger_error(sprintf('Calling the "%s::handle()" method from the firewall is deprecated since Symfony 4.3, extend "%s" instead.', \get_class($listener), AbstractListener::class), \E_USER_DEPRECATED); 
                $listener->handle($event); 
            } 
 
            if ($event->hasResponse()) { 
                break; 
            } 
        } 
    } 
}