diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index b5d08d596e7e..2d165398f44e 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -184,14 +184,15 @@ private function createFirewalls($config, ContainerBuilder $container)
$configId = 'security.firewall.map.config.'.$name;
- list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $configId);
+ list($matcher, $listeners, $exceptionListener, $logoutListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $configId);
$contextId = 'security.firewall.map.context.'.$name;
$context = $container->setDefinition($contextId, new ChildDefinition('security.firewall.context'));
$context
->replaceArgument(0, new IteratorArgument($listeners))
->replaceArgument(1, $exceptionListener)
- ->replaceArgument(2, new Reference($configId))
+ ->replaceArgument(2, $logoutListener)
+ ->replaceArgument(3, new Reference($configId))
;
$contextRefs[$contextId] = new Reference($contextId);
@@ -237,7 +238,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
// Security disabled?
if (false === $firewall['security']) {
- return array($matcher, array(), null);
+ return array($matcher, array(), null, null);
}
$config->replaceArgument(4, $firewall['stateless']);
@@ -276,16 +277,15 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
$config->replaceArgument(6, $contextKey);
// Logout listener
+ $logoutListenerId = null;
if (isset($firewall['logout'])) {
- $listenerKeys[] = 'logout';
- $listenerId = 'security.logout_listener.'.$id;
- $listener = $container->setDefinition($listenerId, new ChildDefinition('security.logout_listener'));
- $listener->replaceArgument(3, array(
+ $logoutListenerId = 'security.logout_listener.'.$id;
+ $logoutListener = $container->setDefinition($logoutListenerId, new ChildDefinition('security.logout_listener'));
+ $logoutListener->replaceArgument(3, array(
'csrf_parameter' => $firewall['logout']['csrf_parameter'],
'csrf_token_id' => $firewall['logout']['csrf_token_id'],
'logout_path' => $firewall['logout']['path'],
));
- $listeners[] = new Reference($listenerId);
// add logout success handler
if (isset($firewall['logout']['success_handler'])) {
@@ -295,16 +295,16 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
$logoutSuccessHandler = $container->setDefinition($logoutSuccessHandlerId, new ChildDefinition('security.logout.success_handler'));
$logoutSuccessHandler->replaceArgument(1, $firewall['logout']['target']);
}
- $listener->replaceArgument(2, new Reference($logoutSuccessHandlerId));
+ $logoutListener->replaceArgument(2, new Reference($logoutSuccessHandlerId));
// add CSRF provider
if (isset($firewall['logout']['csrf_token_generator'])) {
- $listener->addArgument(new Reference($firewall['logout']['csrf_token_generator']));
+ $logoutListener->addArgument(new Reference($firewall['logout']['csrf_token_generator']));
}
// add session logout handler
if (true === $firewall['logout']['invalidate_session'] && false === $firewall['stateless']) {
- $listener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session')));
+ $logoutListener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session')));
}
// add cookie logout handler
@@ -313,12 +313,12 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
$cookieHandler = $container->setDefinition($cookieHandlerId, new ChildDefinition('security.logout.handler.cookie_clearing'));
$cookieHandler->addArgument($firewall['logout']['delete_cookies']);
- $listener->addMethodCall('addHandler', array(new Reference($cookieHandlerId)));
+ $logoutListener->addMethodCall('addHandler', array(new Reference($cookieHandlerId)));
}
// add custom handlers
foreach ($firewall['logout']['handlers'] as $handlerId) {
- $listener->addMethodCall('addHandler', array(new Reference($handlerId)));
+ $logoutListener->addMethodCall('addHandler', array(new Reference($handlerId)));
}
// register with LogoutUrlGenerator
@@ -378,7 +378,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
$config->replaceArgument(10, $listenerKeys);
$config->replaceArgument(11, isset($firewall['switch_user']) ? $firewall['switch_user'] : null);
- return array($matcher, $listeners, $exceptionListener);
+ return array($matcher, $listeners, $exceptionListener, null !== $logoutListenerId ? new Reference($logoutListenerId) : null);
}
private function createContextListener($container, $contextKey)
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
index 6a4c109af450..515f56db6e8c 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
@@ -138,6 +138,7 @@
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php
index 2375e6b0e3ab..a3b7f1540691 100644
--- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php
+++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php
@@ -12,6 +12,7 @@
namespace Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
+use Symfony\Component\Security\Http\Firewall\LogoutListener;
/**
* This is a wrapper around the actual firewall configuration which allows us
@@ -23,13 +24,24 @@ class FirewallContext
{
private $listeners;
private $exceptionListener;
+ private $logoutListener;
private $config;
- public function __construct(iterable $listeners, ExceptionListener $exceptionListener = null, FirewallConfig $config = null)
+ /**
+ * @param LogoutListener|null $logoutListener
+ */
+ public function __construct(iterable $listeners, ExceptionListener $exceptionListener = null, $logoutListener = null, FirewallConfig $config = null)
{
$this->listeners = $listeners;
$this->exceptionListener = $exceptionListener;
- $this->config = $config;
+ if ($logoutListener instanceof FirewallConfig) {
+ $this->config = $logoutListener;
+ } elseif (null === $logoutListener || $logoutListener instanceof LogoutListener) {
+ $this->logoutListener = $logoutListener;
+ $this->config = $config;
+ } else {
+ throw new \InvalidArgumentException(sprintf('Argument 3 passed to %s() must be instance of %s or null, %s given.', __METHOD__, LogoutListener::class, is_object($logoutListener) ? get_class($logoutListener) : gettype($logoutListener)));
+ }
}
public function getConfig()
@@ -46,4 +58,9 @@ public function getExceptionListener()
{
return $this->exceptionListener;
}
+
+ public function getLogoutListener()
+ {
+ return $this->logoutListener;
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
index 3b98b0e4cadd..e11d5dc0f586 100644
--- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
+++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
@@ -40,10 +40,10 @@ public function getListeners(Request $request)
$context = $this->getFirewallContext($request);
if (null === $context) {
- return array(array(), null);
+ return array(array(), null, null);
}
- return array($context->getListeners(), $context->getExceptionListener());
+ return array($context->getListeners(), $context->getExceptionListener(), $context->getLogoutListener());
}
/**
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
index c1791beaf5fb..17561d6fac93 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
@@ -71,7 +71,7 @@ public function testFirewalls()
$arguments = $contextDef->getArguments();
$listeners[] = array_map('strval', $arguments['index_0']->getValues());
- $configDef = $container->getDefinition((string) $arguments['index_2']);
+ $configDef = $container->getDefinition((string) $arguments['index_3']);
$configs[] = array_values($configDef->getArguments());
}
@@ -99,7 +99,6 @@ public function testFirewalls()
null,
null,
array(
- 'logout',
'switch_user',
'x509',
'remote_user',
@@ -171,7 +170,6 @@ public function testFirewalls()
array(),
array(
'security.channel_listener',
- 'security.logout_listener.secure',
'security.authentication.listener.x509.secure',
'security.authentication.listener.remote_user.secure',
'security.authentication.listener.form.secure',
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php
new file mode 100644
index 000000000000..7eeb7c21171c
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php
@@ -0,0 +1,34 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
+
+class LogoutTest extends WebTestCase
+{
+ public function testSessionLessRememberMeLogout()
+ {
+ $client = $this->createClient(array('test_case' => 'RememberMeLogout', 'root_config' => 'config.yml'));
+
+ $client->request('POST', '/login', array(
+ '_username' => 'johannes',
+ '_password' => 'test',
+ ));
+
+ $cookieJar = $client->getCookieJar();
+ $cookieJar->expire(session_name());
+
+ $this->assertNotNull($cookieJar->get('REMEMBERME'));
+
+ $client->request('GET', '/logout');
+
+ $this->assertNull($cookieJar->get('REMEMBERME'));
+ }
+}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php
new file mode 100644
index 000000000000..d90f774abde2
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Bundle\SecurityBundle\SecurityBundle;
+use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
+
+return array(
+ new FrameworkBundle(),
+ new SecurityBundle(),
+);
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml
new file mode 100644
index 000000000000..60e9cb89a229
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml
@@ -0,0 +1,25 @@
+imports:
+ - { resource: ./../config/framework.yml }
+
+security:
+ encoders:
+ Symfony\Component\Security\Core\User\User: plaintext
+
+ providers:
+ in_memory:
+ memory:
+ users:
+ johannes: { password: test, roles: [ROLE_USER] }
+
+ firewalls:
+ default:
+ form_login:
+ check_path: login
+ remember_me: true
+ require_previous_session: false
+ remember_me:
+ always_remember_me: true
+ secret: key
+ logout: ~
+ anonymous: ~
+ stateless: true
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/routing.yml
new file mode 100644
index 000000000000..1dddfca2f815
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/routing.yml
@@ -0,0 +1,5 @@
+login:
+ path: /login
+
+logout:
+ path: /logout
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php
index 983e8288214a..520a129716f4 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php
@@ -16,6 +16,7 @@
use Symfony\Bundle\SecurityBundle\Security\FirewallContext;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
+use Symfony\Component\Security\Http\Firewall\LogoutListener;
class FirewallContextTest extends TestCase
{
@@ -23,6 +24,7 @@ public function testGetters()
{
$config = new FirewallConfig('main', 'user_checker', 'request_matcher');
$exceptionListener = $this->getExceptionListenerMock();
+ $logoutListener = $this->getLogoutListenerMock();
$listeners = array(
$this
->getMockBuilder(ListenerInterface::class)
@@ -30,10 +32,11 @@ public function testGetters()
->getMock(),
);
- $context = new FirewallContext($listeners, $exceptionListener, $config);
+ $context = new FirewallContext($listeners, $exceptionListener, $logoutListener, $config);
$this->assertEquals($listeners, $context->getListeners());
$this->assertEquals($exceptionListener, $context->getExceptionListener());
+ $this->assertEquals($logoutListener, $context->getLogoutListener());
$this->assertEquals($config, $context->getConfig());
}
@@ -44,4 +47,12 @@ private function getExceptionListenerMock()
->disableOriginalConstructor()
->getMock();
}
+
+ private function getLogoutListenerMock()
+ {
+ return $this
+ ->getMockBuilder(LogoutListener::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php
index 3e6f0a1ac74c..42162369cb15 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php
@@ -20,6 +20,7 @@
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
+use Symfony\Component\Security\Http\Firewall\LogoutListener;
class FirewallMapTest extends TestCase
{
@@ -35,7 +36,7 @@ public function testGetListenersWithEmptyMap()
$firewallMap = new FirewallMap($container, $map);
- $this->assertEquals(array(array(), null), $firewallMap->getListeners($request));
+ $this->assertEquals(array(array(), null, null), $firewallMap->getListeners($request));
$this->assertNull($firewallMap->getFirewallConfig($request));
$this->assertFalse($request->attributes->has(self::ATTRIBUTE_FIREWALL_CONTEXT));
}
@@ -51,7 +52,7 @@ public function testGetListenersWithInvalidParameter()
$firewallMap = new FirewallMap($container, $map);
- $this->assertEquals(array(array(), null), $firewallMap->getListeners($request));
+ $this->assertEquals(array(array(), null, null), $firewallMap->getListeners($request));
$this->assertNull($firewallMap->getFirewallConfig($request));
$this->assertFalse($request->attributes->has(self::ATTRIBUTE_FIREWALL_CONTEXT));
}
@@ -71,6 +72,9 @@ public function testGetListeners()
$exceptionListener = $this->getMockBuilder(ExceptionListener::class)->disableOriginalConstructor()->getMock();
$firewallContext->expects($this->once())->method('getExceptionListener')->willReturn($exceptionListener);
+ $logoutListener = $this->getMockBuilder(LogoutListener::class)->disableOriginalConstructor()->getMock();
+ $firewallContext->expects($this->once())->method('getLogoutListener')->willReturn($logoutListener);
+
$matcher = $this->getMockBuilder(RequestMatcherInterface::class)->getMock();
$matcher->expects($this->once())
->method('matches')
@@ -82,7 +86,7 @@ public function testGetListeners()
$firewallMap = new FirewallMap($container, array('security.firewall.map.context.foo' => $matcher));
- $this->assertEquals(array(array($listener), $exceptionListener), $firewallMap->getListeners($request));
+ $this->assertEquals(array(array($listener), $exceptionListener, $logoutListener), $firewallMap->getListeners($request));
$this->assertEquals($firewallConfig, $firewallMap->getFirewallConfig($request));
$this->assertEquals('security.firewall.map.context.foo', $request->attributes->get(self::ATTRIBUTE_FIREWALL_CONTEXT));
}
diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json
index cd10d1dfb628..8f6c2295c5d2 100644
--- a/src/Symfony/Bundle/SecurityBundle/composer.json
+++ b/src/Symfony/Bundle/SecurityBundle/composer.json
@@ -18,7 +18,7 @@
"require": {
"php": "^7.1.3",
"ext-xml": "*",
- "symfony/security": "~3.4|~4.0",
+ "symfony/security": "~3.4.10|^4.0.10",
"symfony/dependency-injection": "^3.4.3|^4.0.3",
"symfony/http-kernel": "~3.4|~4.0"
},
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig
index be77f891034c..88a5829905ee 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig
@@ -7,10 +7,8 @@
'default': '#999',
'section': '#444',
'event_listener': '#00B8F5',
- 'event_listener_loading': '#00B8F5',
'template': '#66CC00',
'doctrine': '#FF6633',
- 'propel': '#FF6633',
} %}
{% endif %}
diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php
index b8e05d9e417c..9fc6177c6bd7 100644
--- a/src/Symfony/Component/Cache/Traits/RedisTrait.php
+++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php
@@ -126,9 +126,12 @@ public static function createConnection($dsn, array $options = array())
throw new InvalidArgumentException(sprintf('Redis connection failed (%s): %s', $e->getMessage(), $dsn));
}
- if (@!$redis->isConnected()) {
- $e = ($e = error_get_last()) && preg_match('/^Redis::p?connect\(\): (.*)/', $e['message'], $e) ? sprintf(' (%s)', $e[1]) : '';
- throw new InvalidArgumentException(sprintf('Redis connection failed%s: %s', $e, $dsn));
+ set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
+ $isConnected = $redis->isConnected();
+ restore_error_handler();
+ if (!$isConnected) {
+ $error = preg_match('/^Redis::p?connect\(\): (.*)/', $error, $error) ? sprintf(' (%s)', $error[1]) : '';
+ throw new InvalidArgumentException(sprintf('Redis connection failed%s: %s', $error, $dsn));
}
if ((null !== $auth && !$redis->auth($auth))
diff --git a/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php b/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php
index 9d61c9cd8328..10b4a7a9b4dc 100644
--- a/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php
+++ b/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php
@@ -55,7 +55,7 @@ public function testLoadFile()
XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate'));
$this->fail();
} catch (\InvalidArgumentException $e) {
- $this->assertRegExp('/The XML file "[\w:\/\\\.~+-]+" is not valid\./', $e->getMessage());
+ $this->assertRegExp('/The XML file ".+" is not valid\./', $e->getMessage());
}
$this->assertInstanceOf('DOMDocument', XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate')));
diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php
index f42ee945f511..b64d9af361e5 100644
--- a/src/Symfony/Component/Console/Command/Command.php
+++ b/src/Symfony/Component/Console/Command/Command.php
@@ -218,12 +218,11 @@ public function run(InputInterface $input, OutputInterface $output)
if (null !== $this->processTitle) {
if (function_exists('cli_set_process_title')) {
- if (false === @cli_set_process_title($this->processTitle)) {
+ if (!@cli_set_process_title($this->processTitle)) {
if ('Darwin' === PHP_OS) {
$output->writeln('Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.');
} else {
- $error = error_get_last();
- trigger_error($error['message'], E_USER_WARNING);
+ cli_set_process_title($this->processTitle);
}
}
} elseif (function_exists('setproctitle')) {
diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php
index 5dbdd6933898..2702b4157f6d 100644
--- a/src/Symfony/Component/Debug/ErrorHandler.php
+++ b/src/Symfony/Component/Debug/ErrorHandler.php
@@ -380,14 +380,16 @@ private function reRegister($prev)
public function handleError($type, $message, $file, $line)
{
// Level is the current error reporting level to manage silent error.
+ $level = error_reporting();
+ $silenced = 0 === ($level & $type);
// Strong errors are not authorized to be silenced.
- $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
+ $level |= E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
$log = $this->loggedErrors & $type;
$throw = $this->thrownErrors & $type & $level;
$type &= $level | $this->screamedErrors;
if (!$type || (!$log && !$throw)) {
- return $type && $log;
+ return !$silenced && $type && $log;
}
$scope = $this->scopedErrors & $type;
@@ -493,7 +495,7 @@ public function handleError($type, $message, $file, $line)
}
}
- return $type && $log;
+ return !$silenced && $type && $log;
}
/**
diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
index afe16ea4f759..eee8e9dc5753 100644
--- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
+++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
@@ -65,6 +65,30 @@ public function testRegister()
}
}
+ public function testErrorGetLast()
+ {
+ $handler = ErrorHandler::register();
+ $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+ $handler->setDefaultLogger($logger);
+ $handler->screamAt(E_ALL);
+
+ try {
+ @trigger_error('Hello', E_USER_WARNING);
+ $expected = array(
+ 'type' => E_USER_WARNING,
+ 'message' => 'Hello',
+ 'file' => __FILE__,
+ 'line' => __LINE__ - 5,
+ );
+ $this->assertSame($expected, error_get_last());
+ } catch (\Exception $e) {
+ restore_error_handler();
+ restore_exception_handler();
+
+ throw $e;
+ }
+ }
+
public function testNotice()
{
ErrorHandler::register();
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
index 5d2d4429e4a9..901dc06ffaee 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
@@ -118,8 +118,12 @@ protected function getConstructor(Definition $definition, $required)
$class = $definition->getClass();
- if (!$r = $this->container->getReflectionClass($class)) {
- throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class));
+ try {
+ if (!$r = $this->container->getReflectionClass($class)) {
+ throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class));
+ }
+ } catch (\ReflectionException $e) {
+ throw new RuntimeException(sprintf('Invalid service "%s": %s.', $this->currentId, lcfirst(rtrim($e->getMessage(), '.'))));
}
if (!$r = $r->getConstructor()) {
if ($required) {
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
index 00a1e1f99fce..4567ae13c72f 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
@@ -120,6 +120,6 @@ private function isInlineableDefinition($id, Definition $definition, ServiceRefe
return false;
}
- return true;
+ return $this->container->getDefinition($ids[0])->isShared();
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
index c82a97436067..0051fb5ac3df 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php
@@ -27,6 +27,7 @@ class ResolveBindingsPass extends AbstractRecursivePass
{
private $usedBindings = array();
private $unusedBindings = array();
+ private $errorMessages = array();
/**
* {@inheritdoc}
@@ -37,11 +38,19 @@ public function process(ContainerBuilder $container)
parent::process($container);
foreach ($this->unusedBindings as list($key, $serviceId)) {
- throw new InvalidArgumentException(sprintf('Unused binding "%s" in service "%s".', $key, $serviceId));
+ $message = sprintf('Unused binding "%s" in service "%s".', $key, $serviceId);
+ if ($this->errorMessages) {
+ $message .= sprintf("\nCould be related to%s:", 1 < \count($this->errorMessages) ? ' one of' : '');
+ }
+ foreach ($this->errorMessages as $m) {
+ $message .= "\n - ".$m;
+ }
+ throw new InvalidArgumentException($message);
}
} finally {
$this->usedBindings = array();
$this->unusedBindings = array();
+ $this->errorMessages = array();
}
}
@@ -94,6 +103,7 @@ protected function processValue($value, $isRoot = false)
$calls[] = array($constructor, $value->getArguments());
}
} catch (RuntimeException $e) {
+ $this->errorMessages[] = $e->getMessage();
$this->container->getDefinition($this->currentId)->addError($e->getMessage());
return parent::processValue($value, $isRoot);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php
index bb60af4d19e6..78556b2ed21c 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php
@@ -92,7 +92,7 @@ public function testProcessDoesInlineNonSharedService()
$this->assertNotSame($container->getDefinition('bar'), $arguments[2]);
}
- public function testProcessInlinesMixedServicesLoop()
+ public function testProcessDoesNotInlineMixedServicesLoop()
{
$container = new ContainerBuilder();
$container
@@ -108,7 +108,7 @@ public function testProcessInlinesMixedServicesLoop()
$this->process($container);
- $this->assertEquals($container->getDefinition('foo')->getArgument(0), $container->getDefinition('bar'));
+ $this->assertEquals(new Reference('bar'), $container->getDefinition('foo')->getArgument(0));
}
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php
index 16e486afafe2..65f5ceb80fd0 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php
@@ -18,6 +18,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
use Symfony\Component\DependencyInjection\TypedReference;
@@ -61,6 +62,21 @@ public function testUnusedBinding()
$pass->process($container);
}
+ /**
+ * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
+ * @expectedExceptionMessageRegexp Unused binding "$quz" in service [\s\S]+ Invalid service ".*\\ParentNotExists": class NotExists not found\.
+ */
+ public function testMissingParent()
+ {
+ $container = new ContainerBuilder();
+
+ $definition = $container->register(ParentNotExists::class, ParentNotExists::class);
+ $definition->setBindings(array('$quz' => '123'));
+
+ $pass = new ResolveBindingsPass();
+ $pass->process($container);
+ }
+
public function testTypedReferenceSupport()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index db5df5cbeb47..046dbb940e39 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -22,6 +22,8 @@
*/
class Filesystem
{
+ private static $lastError;
+
/**
* Copies a file.
*
@@ -96,12 +98,11 @@ public function mkdir($dirs, $mode = 0777)
continue;
}
- if (true !== @mkdir($dir, $mode, true)) {
- $error = error_get_last();
+ if (!self::box('mkdir', $dir, $mode, true)) {
if (!is_dir($dir)) {
// The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one
- if ($error) {
- throw new IOException(sprintf('Failed to create "%s": %s.', $dir, $error['message']), 0, null, $dir);
+ if (self::$lastError) {
+ throw new IOException(sprintf('Failed to create "%s": %s.', $dir, self::$lastError), 0, null, $dir);
}
throw new IOException(sprintf('Failed to create "%s"', $dir), 0, null, $dir);
}
@@ -170,20 +171,17 @@ public function remove($files)
foreach ($files as $file) {
if (is_link($file)) {
// See https://bugs.php.net/52176
- if (!@(unlink($file) || '\\' !== DIRECTORY_SEPARATOR || rmdir($file)) && file_exists($file)) {
- $error = error_get_last();
- throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message']));
+ if (!(self::box('unlink', $file) || '\\' !== DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) {
+ throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, self::$lastError));
}
} elseif (is_dir($file)) {
$this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS));
- if (!@rmdir($file) && file_exists($file)) {
- $error = error_get_last();
- throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, $error['message']));
+ if (!self::box('rmdir', $file) && file_exists($file)) {
+ throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, self::$lastError));
}
- } elseif (!@unlink($file) && file_exists($file)) {
- $error = error_get_last();
- throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, $error['message']));
+ } elseif (!self::box('unlink', $file) && file_exists($file)) {
+ throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, self::$lastError));
}
}
}
@@ -337,16 +335,14 @@ public function symlink($originDir, $targetDir, $copyOnWindows = false)
$this->mkdir(dirname($targetDir));
- $ok = false;
if (is_link($targetDir)) {
- if (readlink($targetDir) != $originDir) {
- $this->remove($targetDir);
- } else {
- $ok = true;
+ if (readlink($targetDir) === $originDir) {
+ return;
}
+ $this->remove($targetDir);
}
- if (!$ok && true !== @symlink($originDir, $targetDir)) {
+ if (!self::box('symlink', $originDir, $targetDir)) {
$this->linkException($originDir, $targetDir, 'symbolic');
}
}
@@ -378,7 +374,7 @@ public function hardlink($originFile, $targetFiles)
$this->remove($targetFile);
}
- if (true !== @link($originFile, $targetFile)) {
+ if (!self::box('link', $originFile, $targetFile)) {
$this->linkException($originFile, $targetFile, 'hard');
}
}
@@ -391,9 +387,8 @@ public function hardlink($originFile, $targetFiles)
*/
private function linkException($origin, $target, $linkType)
{
- $report = error_get_last();
- if (is_array($report)) {
- if ('\\' === DIRECTORY_SEPARATOR && false !== strpos($report['message'], 'error code(1314)')) {
+ if (self::$lastError) {
+ if ('\\' === DIRECTORY_SEPARATOR && false !== strpos(self::$lastError, 'error code(1314)')) {
throw new IOException(sprintf('Unable to create %s link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target);
}
}
@@ -743,4 +738,29 @@ private function getSchemeAndHierarchy(string $filename): array
return 2 === count($components) ? array($components[0], $components[1]) : array(null, $components[0]);
}
+
+ private static function box($func)
+ {
+ self::$lastError = null;
+ \set_error_handler(__CLASS__.'::handleError');
+ try {
+ $result = \call_user_func_array($func, \array_slice(\func_get_args(), 1));
+ \restore_error_handler();
+
+ return $result;
+ } catch (\Throwable $e) {
+ } catch (\Exception $e) {
+ }
+ \restore_error_handler();
+
+ throw $e;
+ }
+
+ /**
+ * @internal
+ */
+ public static function handleError($type, $msg)
+ {
+ self::$lastError = $msg;
+ }
}
diff --git a/src/Symfony/Component/Finder/SplFileInfo.php b/src/Symfony/Component/Finder/SplFileInfo.php
index 6516113a3ece..b8143ed79916 100644
--- a/src/Symfony/Component/Finder/SplFileInfo.php
+++ b/src/Symfony/Component/Finder/SplFileInfo.php
@@ -66,12 +66,11 @@ public function getRelativePathname()
*/
public function getContents()
{
- $level = error_reporting(0);
+ set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
$content = file_get_contents($this->getPathname());
- error_reporting($level);
+ restore_error_handler();
if (false === $content) {
- $error = error_get_last();
- throw new \RuntimeException($error['message']);
+ throw new \RuntimeException($error);
}
return $content;
diff --git a/src/Symfony/Component/HttpFoundation/File/File.php b/src/Symfony/Component/HttpFoundation/File/File.php
index 4e6887a52ad4..44ac3925114a 100644
--- a/src/Symfony/Component/HttpFoundation/File/File.php
+++ b/src/Symfony/Component/HttpFoundation/File/File.php
@@ -93,9 +93,11 @@ public function move($directory, $name = null)
{
$target = $this->getTargetFile($directory, $name);
- if (!@rename($this->getPathname(), $target)) {
- $error = error_get_last();
- throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message'])));
+ set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
+ $renamed = rename($this->getPathname(), $target);
+ restore_error_handler();
+ if (!$renamed) {
+ throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
}
@chmod($target, 0666 & ~umask());
diff --git a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php
index 678c4f0afc6e..e234537cb22d 100644
--- a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php
+++ b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php
@@ -192,9 +192,11 @@ public function move($directory, $name = null)
$target = $this->getTargetFile($directory, $name);
- if (!@move_uploaded_file($this->getPathname(), $target)) {
- $error = error_get_last();
- throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message'])));
+ set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
+ $moved = move_uploaded_file($this->getPathname(), $target);
+ restore_error_handler();
+ if (!$moved) {
+ throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
}
@chmod($target, 0666 & ~umask());
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
index 7b30aed38105..43f07f25de1d 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
@@ -674,14 +674,16 @@ private function doAdvisoryLock(string $sessionId)
{
switch ($this->driver) {
case 'mysql':
+ // MySQL 5.7.5 and later enforces a maximum length on lock names of 64 characters. Previously, no limit was enforced.
+ $lockId = \substr($sessionId, 0, 64);
// should we handle the return value? 0 on timeout, null on error
// we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout
$stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)');
- $stmt->bindValue(':key', $sessionId, \PDO::PARAM_STR);
+ $stmt->bindValue(':key', $lockId, \PDO::PARAM_STR);
$stmt->execute();
$releaseStmt = $this->pdo->prepare('DO RELEASE_LOCK(:key)');
- $releaseStmt->bindValue(':key', $sessionId, \PDO::PARAM_STR);
+ $releaseStmt->bindValue(':key', $lockId, \PDO::PARAM_STR);
return $releaseStmt;
case 'pgsql':
diff --git a/src/Symfony/Component/HttpKernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php b/src/Symfony/Component/HttpKernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php
index 760366c6943a..05d8d787aa5c 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php
@@ -6,21 +6,6 @@
class UnprocessableEntityHttpExceptionTest extends HttpExceptionTest
{
- /**
- * Test that setting the headers using the setter function
- * is working as expected.
- *
- * @param array $headers The headers to set
- *
- * @dataProvider headerDataProvider
- */
- public function testHeadersSetter($headers)
- {
- $exception = new UnprocessableEntityHttpException(10);
- $exception->setHeaders($headers);
- $this->assertSame($headers, $exception->getHeaders());
- }
-
protected function createException()
{
return new UnprocessableEntityHttpException();
diff --git a/src/Symfony/Component/HttpKernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php b/src/Symfony/Component/HttpKernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php
index d47287a1fbc6..4dae039c11fc 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php
@@ -6,17 +6,7 @@
class UnsupportedMediaTypeHttpExceptionTest extends HttpExceptionTest
{
- /**
- * @dataProvider headerDataProvider
- */
- public function testHeadersSetter($headers)
- {
- $exception = new UnsupportedMediaTypeHttpException(10);
- $exception->setHeaders($headers);
- $this->assertSame($headers, $exception->getHeaders());
- }
-
- protected function createException($headers = array())
+ protected function createException()
{
return new UnsupportedMediaTypeHttpException();
}
diff --git a/src/Symfony/Component/Lock/Store/FlockStore.php b/src/Symfony/Component/Lock/Store/FlockStore.php
index 64438fd461b2..bf72eaefe2e3 100644
--- a/src/Symfony/Component/Lock/Store/FlockStore.php
+++ b/src/Symfony/Component/Lock/Store/FlockStore.php
@@ -78,8 +78,7 @@ private function lock(Key $key, $blocking)
);
// Silence error reporting
- set_error_handler(function () {
- });
+ set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
if (!$handle = fopen($fileName, 'r')) {
if ($handle = fopen($fileName, 'x')) {
chmod($fileName, 0444);
@@ -91,8 +90,7 @@ private function lock(Key $key, $blocking)
restore_error_handler();
if (!$handle) {
- $error = error_get_last();
- throw new LockStorageException($error['message'], 0, null);
+ throw new LockStorageException($error, 0, null);
}
// On Windows, even if PHP doc says the contrary, LOCK_NB works, see
diff --git a/src/Symfony/Component/Process/ExecutableFinder.php b/src/Symfony/Component/Process/ExecutableFinder.php
index d042a5b13a21..1ec6526d45ef 100644
--- a/src/Symfony/Component/Process/ExecutableFinder.php
+++ b/src/Symfony/Component/Process/ExecutableFinder.php
@@ -77,7 +77,7 @@ public function find($name, $default = null, array $extraDirs = array())
}
foreach ($suffixes as $suffix) {
foreach ($dirs as $dir) {
- if (@is_file($file = $dir.DIRECTORY_SEPARATOR.$name.$suffix) && ('\\' === DIRECTORY_SEPARATOR || is_executable($file))) {
+ if (@is_file($file = $dir.DIRECTORY_SEPARATOR.$name.$suffix) && ('\\' === DIRECTORY_SEPARATOR || @is_executable($file))) {
return $file;
}
}
diff --git a/src/Symfony/Component/Process/PhpExecutableFinder.php b/src/Symfony/Component/Process/PhpExecutableFinder.php
index d2b6d5e321dc..f632a89b4acb 100644
--- a/src/Symfony/Component/Process/PhpExecutableFinder.php
+++ b/src/Symfony/Component/Process/PhpExecutableFinder.php
@@ -44,7 +44,7 @@ public function find($includeArgs = true)
}
if ($php = getenv('PHP_PATH')) {
- if (!is_executable($php)) {
+ if (!@is_executable($php)) {
return false;
}
@@ -52,12 +52,12 @@ public function find($includeArgs = true)
}
if ($php = getenv('PHP_PEAR_PHP_BIN')) {
- if (is_executable($php)) {
+ if (@is_executable($php)) {
return $php;
}
}
- if (is_executable($php = PHP_BINDIR.('\\' === DIRECTORY_SEPARATOR ? '\\php.exe' : '/php'))) {
+ if (@is_executable($php = PHP_BINDIR.('\\' === DIRECTORY_SEPARATOR ? '\\php.exe' : '/php'))) {
return $php;
}
diff --git a/src/Symfony/Component/Process/Pipes/AbstractPipes.php b/src/Symfony/Component/Process/Pipes/AbstractPipes.php
index 2bd1fe75b7ff..9a7d88be896f 100644
--- a/src/Symfony/Component/Process/Pipes/AbstractPipes.php
+++ b/src/Symfony/Component/Process/Pipes/AbstractPipes.php
@@ -25,6 +25,7 @@ abstract class AbstractPipes implements PipesInterface
private $inputBuffer = '';
private $input;
private $blocked = true;
+ private $lastError;
/**
* @param resource|string|int|float|bool|\Iterator|null $input
@@ -58,10 +59,11 @@ public function close()
*/
protected function hasSystemCallBeenInterrupted()
{
- $lastError = error_get_last();
+ $lastError = $this->lastError;
+ $this->lastError = null;
// stream_select returns false when the `select` system call is interrupted by an incoming signal
- return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call');
+ return null !== $lastError && false !== stripos($lastError, 'interrupted system call');
}
/**
@@ -165,4 +167,12 @@ protected function write()
return array($this->pipes[0]);
}
}
+
+ /**
+ * @internal
+ */
+ public function handleError($type, $msg)
+ {
+ $this->lastError = $msg;
+ }
}
diff --git a/src/Symfony/Component/Process/Pipes/UnixPipes.php b/src/Symfony/Component/Process/Pipes/UnixPipes.php
index badaf34d4201..23f873471d87 100644
--- a/src/Symfony/Component/Process/Pipes/UnixPipes.php
+++ b/src/Symfony/Component/Process/Pipes/UnixPipes.php
@@ -99,7 +99,9 @@ public function readAndWrite($blocking, $close = false)
unset($r[0]);
// let's have a look if something changed in streams
- if (($r || $w) && false === @stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) {
+ set_error_handler(array($this, 'handleError'));
+ if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) {
+ restore_error_handler();
// if a system call has been interrupted, forget about it, let's try again
// otherwise, an error occurred, let's reset pipes
if (!$this->hasSystemCallBeenInterrupted()) {
@@ -108,6 +110,7 @@ public function readAndWrite($blocking, $close = false)
return $read;
}
+ restore_error_handler();
foreach ($r as $pipe) {
// prior PHP 5.4 the array passed to stream_select is modified and
diff --git a/src/Symfony/Component/Security/Http/Firewall.php b/src/Symfony/Component/Security/Http/Firewall.php
index 28b56d5791bd..9bee596759bd 100644
--- a/src/Symfony/Component/Security/Http/Firewall.php
+++ b/src/Symfony/Component/Security/Http/Firewall.php
@@ -47,13 +47,22 @@ public function onKernelRequest(GetResponseEvent $event)
}
// register listeners for this firewall
- list($listeners, $exceptionListener) = $this->map->getListeners($event->getRequest());
+ $listeners = $this->map->getListeners($event->getRequest());
+
+ $authenticationListeners = $listeners[0];
+ $exceptionListener = $listeners[1];
+ $logoutListener = isset($listeners[2]) ? $listeners[2] : null;
+
if (null !== $exceptionListener) {
$this->exceptionListeners[$event->getRequest()] = $exceptionListener;
$exceptionListener->register($this->dispatcher);
}
- return $this->handleRequest($event, $listeners);
+ $this->handleRequest($event, $authenticationListeners);
+
+ if (null !== $logoutListener) {
+ $logoutListener->handle($event);
+ }
}
public function onKernelFinishRequest(FinishRequestEvent $event)
diff --git a/src/Symfony/Component/Security/Http/FirewallMap.php b/src/Symfony/Component/Security/Http/FirewallMap.php
index e767d123cb03..fc97410d4e69 100644
--- a/src/Symfony/Component/Security/Http/FirewallMap.php
+++ b/src/Symfony/Component/Security/Http/FirewallMap.php
@@ -14,6 +14,7 @@
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
+use Symfony\Component\Security\Http\Firewall\LogoutListener;
/**
* FirewallMap allows configuration of different firewalls for specific parts
@@ -25,9 +26,9 @@ class FirewallMap implements FirewallMapInterface
{
private $map = array();
- public function add(RequestMatcherInterface $requestMatcher = null, array $listeners = array(), ExceptionListener $exceptionListener = null)
+ public function add(RequestMatcherInterface $requestMatcher = null, array $listeners = array(), ExceptionListener $exceptionListener = null, LogoutListener $logoutListener = null)
{
- $this->map[] = array($requestMatcher, $listeners, $exceptionListener);
+ $this->map[] = array($requestMatcher, $listeners, $exceptionListener, $logoutListener);
}
/**
@@ -37,10 +38,10 @@ public function getListeners(Request $request)
{
foreach ($this->map as $elements) {
if (null === $elements[0] || $elements[0]->matches($request)) {
- return array($elements[1], $elements[2]);
+ return array($elements[1], $elements[2], $elements[3]);
}
}
- return array(array(), null);
+ return array(array(), null, null);
}
}