diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000000000..db1c2a8a6ff44
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,14 @@
+# LDAP
+/src/Symfony/Component/Ldap/* @csarrazi
+# Lock
+/src/Symfony/Component/Lock/* @jderusse
+# Messenger
+/src/Symfony/Bridge/Doctrine/Messenger/* @sroze
+/src/Symfony/Component/Messenger/* @sroze
+# Workflow
+/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php @lyrixx
+/src/Symfony/Bridge/Twig/Tests/Extension/WorkflowExtensionTest.php @lyrixx
+/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php @lyrixx
+/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php @lyrixx
+/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php @lyrixx
+/src/Symfony/Component/Workflow/* @lyrixx
diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000000000..16e2603b76a1d
--- /dev/null
+++ b/.github/CODE_OF_CONDUCT.md
@@ -0,0 +1,8 @@
+# Code of Conduct
+
+This project follows a [Code of Conduct][code_of_conduct] in order to ensure an open and welcoming environment.
+Please read the full text for understanding the accepted and unaccepted behavior.
+Please read also the [reporting guidelines][guidelines], in case you encountered or witnessed any misbehavior.
+
+[code_of_conduct]: https://symfony.com/doc/current/contributing/code_of_conduct/index.html
+[guidelines]: https://symfony.com/doc/current/contributing/code_of_conduct/reporting_guidelines.html
diff --git a/CHANGELOG-2.8.md b/CHANGELOG-2.8.md
index 9b7d325c0e9dc..9f76090b789b7 100644
--- a/CHANGELOG-2.8.md
+++ b/CHANGELOG-2.8.md
@@ -7,6 +7,19 @@ in 2.8 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.8.0...v2.8.1
+* 2.8.40 (2018-05-21)
+
+ * bug #26781 [Form] Fix precision of MoneyToLocalizedStringTransformer's divisions on transform() (syastrebov)
+ * bug #27286 [Translation] Add Occitan plural rule (kylekatarnls)
+ * bug #27246 Disallow invalid characters in session.name (ostrolucky)
+ * bug #24805 [Security] Fix logout (MatTheCat)
+ * bug #27141 [Process] Suppress warnings when open_basedir is non-empty (cbj4074)
+ * bug #27250 [Session] limiting :key for GET_LOCK to 64 chars (oleg-andreyev)
+ * bug #27237 [Debug] Fix populating error_get_last() for handled silent errors (nicolas-grekas)
+ * bug #27236 [Filesystem] Fix usages of error_get_last() (nicolas-grekas)
+ * bug #27152 [HttpFoundation] use brace-style regex delimiters (xabbuh)
+ * feature #24896 Add CODE_OF_CONDUCT.md (egircys)
+
* 2.8.39 (2018-04-30)
* bug #27067 [HttpFoundation] Fix setting session-related ini settings (e-moe)
diff --git a/composer.json b/composer.json
index ac77cba1f10ac..f09d3dfedbf32 100644
--- a/composer.json
+++ b/composer.json
@@ -23,6 +23,7 @@
"psr/log": "~1.0",
"symfony/security-acl": "~2.7|~3.0.0",
"symfony/polyfill-apcu": "~1.1",
+ "symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php54": "~1.0",
diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json
index 9e5d42e326e52..e5b5a39a38269 100644
--- a/src/Symfony/Bridge/Doctrine/composer.json
+++ b/src/Symfony/Bridge/Doctrine/composer.json
@@ -18,6 +18,7 @@
"require": {
"php": ">=5.3.9",
"doctrine/common": "~2.4",
+ "symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
@@ -27,7 +28,7 @@
"symfony/http-kernel": "~2.2|~3.0.0",
"symfony/property-access": "~2.3|~3.0.0",
"symfony/property-info": "~2.8|3.0",
- "symfony/security": "~2.2|~3.0.0",
+ "symfony/security": "^2.8.31|^3.3.13",
"symfony/expression-language": "~2.2|~3.0.0",
"symfony/validator": "~2.7.25|^2.8.18|~3.2.5",
"symfony/translation": "^2.0.5|~3.0.0",
diff --git a/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php
index 413b476f2938d..596fcdd84d2c5 100644
--- a/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php
+++ b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php
@@ -42,7 +42,7 @@ public function isHandlerActivated(array $record)
$isActivated
&& isset($record['context']['exception'])
&& $record['context']['exception'] instanceof HttpException
- && $record['context']['exception']->getStatusCode() == 404
+ && 404 == $record['context']['exception']->getStatusCode()
&& ($request = $this->requestStack->getMasterRequest())
) {
return !preg_match($this->blacklist, $request->getPathInfo());
diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json
index e1937deb671ea..48af8a77b4033 100644
--- a/src/Symfony/Bridge/Twig/composer.json
+++ b/src/Symfony/Bridge/Twig/composer.json
@@ -30,7 +30,7 @@
"symfony/templating": "~2.1|~3.0.0",
"symfony/translation": "~2.7|~3.0.0",
"symfony/yaml": "^2.0.5|~3.0.0",
- "symfony/security": "~2.6|~3.0.0",
+ "symfony/security": "^2.8.31|^3.3.13",
"symfony/security-acl": "~2.6|~3.0.0",
"symfony/stopwatch": "~2.2|~3.0.0",
"symfony/console": "~2.8|~3.0.0",
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index d2cad2ee0df4f..1bda5d65c9aea 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -380,7 +380,16 @@ private function addSessionSection(ArrayNodeDefinition $rootNode)
->children()
->scalarNode('storage_id')->defaultValue('session.storage.native')->end()
->scalarNode('handler_id')->defaultValue('session.handler.native_file')->end()
- ->scalarNode('name')->end()
+ ->scalarNode('name')
+ ->validate()
+ ->ifTrue(function ($v) {
+ parse_str($v, $parsed);
+
+ return implode('&', array_keys($parsed)) !== (string) $v;
+ })
+ ->thenInvalid('Session name %s contains illegal character(s)')
+ ->end()
+ ->end()
->scalarNode('cookie_lifetime')->end()
->scalarNode('cookie_path')->end()
->scalarNode('cookie_domain')->end()
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
index f03d545eb2c58..0d8d4992d5504 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
@@ -41,6 +41,55 @@ public function testDoNoDuplicateDefaultFormResources()
$this->assertEquals(array('FrameworkBundle:Form'), $config['templating']['form']['resources']);
}
+ /**
+ * @dataProvider getTestValidSessionName
+ */
+ public function testValidSessionName($sessionName)
+ {
+ $processor = new Processor();
+ $config = $processor->processConfiguration(
+ new Configuration(true),
+ array(array('session' => array('name' => $sessionName)))
+ );
+
+ $this->assertEquals($sessionName, $config['session']['name']);
+ }
+
+ public function getTestValidSessionName()
+ {
+ return array(
+ array(null),
+ array('PHPSESSID'),
+ array('a&b'),
+ array(',_-!@#$%^*(){}:<>/?'),
+ );
+ }
+
+ /**
+ * @dataProvider getTestInvalidSessionName
+ * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
+ */
+ public function testInvalidSessionName($sessionName)
+ {
+ $processor = new Processor();
+ $processor->processConfiguration(
+ new Configuration(true),
+ array(array('session' => array('name' => $sessionName)))
+ );
+ }
+
+ public function getTestInvalidSessionName()
+ {
+ return array(
+ array('a.b'),
+ array('a['),
+ array('a[]'),
+ array('a[b]'),
+ array('a=b'),
+ array('a+b'),
+ );
+ }
+
/**
* @dataProvider getTestValidTrustedProxiesData
*/
diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json
index 14de4ad38afbf..3e7b75ba127e2 100644
--- a/src/Symfony/Bundle/FrameworkBundle/composer.json
+++ b/src/Symfony/Bundle/FrameworkBundle/composer.json
@@ -30,7 +30,7 @@
"symfony/filesystem": "~2.3|~3.0.0",
"symfony/routing": "^2.8.17",
"symfony/security-core": "~2.6.13|~2.7.9|~2.8|~3.0.0",
- "symfony/security-csrf": "~2.6|~3.0.0",
+ "symfony/security-csrf": "^2.8.31|^3.3.13",
"symfony/stopwatch": "~2.3|~3.0.0",
"symfony/templating": "~2.7|~3.0.0",
"symfony/translation": "~2.8",
@@ -43,7 +43,6 @@
"symfony/css-selector": "^2.0.5|~3.0.0",
"symfony/dom-crawler": "^2.0.5|~3.0.0",
"symfony/polyfill-intl-icu": "~1.0",
- "symfony/security": "~2.6|~3.0.0",
"symfony/form": "^2.8.19",
"symfony/expression-language": "~2.6|~3.0.0",
"symfony/process": "^2.0.5|~3.0.0",
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index 91d2d979c16b1..34276e95e79f2 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -14,7 +14,6 @@
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
-use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
@@ -224,13 +223,14 @@ private function createFirewalls($config, ContainerBuilder $container)
$mapDef = $container->getDefinition('security.firewall.map');
$map = $authenticationProviders = array();
foreach ($firewalls as $name => $firewall) {
- list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds);
+ list($matcher, $listeners, $exceptionListener, $logoutListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds);
$contextId = 'security.firewall.map.context.'.$name;
$context = $container->setDefinition($contextId, new DefinitionDecorator('security.firewall.context'));
$context
->replaceArgument(0, $listeners)
->replaceArgument(1, $exceptionListener)
+ ->replaceArgument(2, $logoutListener)
;
$map[$contextId] = $matcher;
}
@@ -261,7 +261,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);
}
// Provider id (take the first registered provider if none defined)
@@ -288,15 +288,15 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
}
// Logout listener
+ $logoutListenerId = null;
if (isset($firewall['logout'])) {
- $listenerId = 'security.logout_listener.'.$id;
- $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.logout_listener'));
- $listener->replaceArgument(3, array(
+ $logoutListenerId = 'security.logout_listener.'.$id;
+ $logoutListener = $container->setDefinition($logoutListenerId, new DefinitionDecorator('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'])) {
@@ -306,16 +306,16 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
$logoutSuccessHandler = $container->setDefinition($logoutSuccessHandlerId, new DefinitionDecorator('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
@@ -324,12 +324,12 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
$cookieHandler = $container->setDefinition($cookieHandlerId, new DefinitionDecorator('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
@@ -366,7 +366,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
$container->setAlias(new Alias('security.user_checker.'.$id, false), $firewall['user_checker']);
- 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 bbcae99853aa2..029395de9dea0 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
@@ -152,6 +152,7 @@
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php
index 13d096d97e951..e9f8fe66d6395 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,15 +24,17 @@ class FirewallContext
{
private $listeners;
private $exceptionListener;
+ private $logoutListener;
- public function __construct(array $listeners, ExceptionListener $exceptionListener = null)
+ public function __construct(array $listeners, ExceptionListener $exceptionListener = null, LogoutListener $logoutListener = null)
{
$this->listeners = $listeners;
$this->exceptionListener = $exceptionListener;
+ $this->logoutListener = $logoutListener;
}
public function getContext()
{
- return array($this->listeners, $this->exceptionListener);
+ return array($this->listeners, $this->exceptionListener, $this->logoutListener);
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
index dc87681c37511..d45d7b87f04a6 100644
--- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
+++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
@@ -41,6 +41,6 @@ public function getListeners(Request $request)
}
}
- return array(array(), null);
+ return array(array(), null, null);
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
index 5a504dd2eaf01..77f3ab2606943 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
@@ -76,7 +76,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 0000000000000..7eeb7c21171ce
--- /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 0000000000000..d90f774abde2b
--- /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 0000000000000..60e9cb89a229a
--- /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 0000000000000..1dddfca2f8154
--- /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/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json
index 75c6a30dd3ebb..c75bf6ecb9f46 100644
--- a/src/Symfony/Bundle/SecurityBundle/composer.json
+++ b/src/Symfony/Bundle/SecurityBundle/composer.json
@@ -18,7 +18,7 @@
"require": {
"php": ">=5.3.9",
"ext-xml": "*",
- "symfony/security": "^2.8.31|~3.3.13",
+ "symfony/security": "^2.8.40|~3.4.10",
"symfony/security-acl": "~2.7|~3.0.0",
"symfony/http-kernel": "~2.7|~3.0.0",
"symfony/polyfill-php70": "~1.0"
diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json
index 193d4518521f2..74502c88b1d9f 100644
--- a/src/Symfony/Bundle/TwigBundle/composer.json
+++ b/src/Symfony/Bundle/TwigBundle/composer.json
@@ -21,6 +21,7 @@
"symfony/twig-bridge": "~2.7|~3.0.0",
"symfony/http-foundation": "~2.5|~3.0.0",
"symfony/http-kernel": "~2.7.23|^2.8.16",
+ "symfony/polyfill-ctype": "~1.8",
"twig/twig": "~1.34|~2.4"
},
"require-dev": {
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 6860230243106..f813f761715aa 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/Config/composer.json b/src/Symfony/Component/Config/composer.json
index e6d6d3615a8b0..ee1322ccdba16 100644
--- a/src/Symfony/Component/Config/composer.json
+++ b/src/Symfony/Component/Config/composer.json
@@ -17,7 +17,8 @@
],
"require": {
"php": ">=5.3.9",
- "symfony/filesystem": "~2.3|~3.0.0"
+ "symfony/filesystem": "~2.3|~3.0.0",
+ "symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
"symfony/yaml": "~2.7|~3.0.0"
diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php
index 3ed5eab4d3ed2..c8db0cff07f3a 100644
--- a/src/Symfony/Component/Console/Command/Command.php
+++ b/src/Symfony/Component/Console/Command/Command.php
@@ -208,12 +208,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 8a7e81beb5f7d..f7c7c4bd5b6e6 100644
--- a/src/Symfony/Component/Debug/ErrorHandler.php
+++ b/src/Symfony/Component/Debug/ErrorHandler.php
@@ -403,13 +403,15 @@ private function reRegister($prev)
*/
public function handleError($type, $message, $file, $line)
{
- $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
+ $level = error_reporting();
+ $silenced = 0 === ($level & $type);
+ $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;
@@ -549,7 +551,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 0f10bfbf45de9..64ec7b36bdf2d 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/DomCrawler/composer.json b/src/Symfony/Component/DomCrawler/composer.json
index 960220f39d46b..1a0fa47f819ac 100644
--- a/src/Symfony/Component/DomCrawler/composer.json
+++ b/src/Symfony/Component/DomCrawler/composer.json
@@ -17,6 +17,7 @@
],
"require": {
"php": ">=5.3.9",
+ "symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index 98bd6f9b439f0..1355f5cfea5a0 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -21,6 +21,8 @@
*/
class Filesystem
{
+ private static $lastError;
+
/**
* Copies a file.
*
@@ -95,12 +97,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);
}
@@ -169,20 +170,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));
}
}
}
@@ -336,19 +334,16 @@ 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)) {
- $report = error_get_last();
- if (is_array($report)) {
- if ('\\' === DIRECTORY_SEPARATOR && false !== strpos($report['message'], 'error code(1314)')) {
+ if (!self::box('symlink', $originDir, $targetDir)) {
+ if (null !== self::$lastError) {
+ if ('\\' === DIRECTORY_SEPARATOR && false !== strpos(self::$lastError, 'error code(1314)')) {
throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', 0, null, $targetDir);
}
}
@@ -646,4 +641,29 @@ private function getSchemeAndHierarchy($filename)
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/Filesystem/composer.json b/src/Symfony/Component/Filesystem/composer.json
index 8d8b93a569517..3a2db520b6687 100644
--- a/src/Symfony/Component/Filesystem/composer.json
+++ b/src/Symfony/Component/Filesystem/composer.json
@@ -16,7 +16,8 @@
}
],
"require": {
- "php": ">=5.3.9"
+ "php": ">=5.3.9",
+ "symfony/polyfill-ctype": "~1.8"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Filesystem\\": "" },
diff --git a/src/Symfony/Component/Finder/Shell/Command.php b/src/Symfony/Component/Finder/Shell/Command.php
index b66ea339dc8c3..fa385364d4347 100644
--- a/src/Symfony/Component/Finder/Shell/Command.php
+++ b/src/Symfony/Component/Finder/Shell/Command.php
@@ -104,7 +104,7 @@ public function top($bit)
array_unshift($this->bits, $bit);
foreach ($this->labels as $label => $index) {
- $this->labels[$label] += 1;
+ ++$this->labels[$label];
}
return $this;
diff --git a/src/Symfony/Component/Finder/SplFileInfo.php b/src/Symfony/Component/Finder/SplFileInfo.php
index 19f95e26be69a..0f4e025b22bd2 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/Form/FormRegistry.php b/src/Symfony/Component/Form/FormRegistry.php
index 3945a9735b5cb..538063e27d3d5 100644
--- a/src/Symfony/Component/Form/FormRegistry.php
+++ b/src/Symfony/Component/Form/FormRegistry.php
@@ -30,7 +30,7 @@ class FormRegistry implements FormRegistryInterface
private $extensions = array();
/**
- * @var FormTypeInterface[]
+ * @var ResolvedFormTypeInterface[]
*/
private $types = array();
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php
index 1ad3aa1615c98..d9fafdff13a35 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php
@@ -78,6 +78,16 @@ public function testFloatToIntConversionMismatchOnReversTransform()
$transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100);
IntlTestHelper::requireFullIntl($this, false);
\Locale::setDefault('de_AT');
+
$this->assertSame(3655, (int) $transformer->reverseTransform('36,55'));
}
+
+ public function testFloatToIntConversionMismatchOnTransform()
+ {
+ $transformer = new MoneyToLocalizedStringTransformer(null, null, MoneyToLocalizedStringTransformer::ROUND_DOWN, 100);
+ IntlTestHelper::requireFullIntl($this, false);
+ \Locale::setDefault('de_AT');
+
+ $this->assertSame('10,20', $transformer->transform(1020));
+ }
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
index 2c4c15f410428..44f6e45e924cf 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
@@ -2568,7 +2568,7 @@ public function testStripLeadingUnderscoresAndDigitsFromId()
$this->assertEquals('_09name', $view->vars['full_name']);
}
- /**
+ /**
* @dataProvider provideTrimCases
*/
public function testTrimIsDisabled($multiple, $expanded)
diff --git a/src/Symfony/Component/Form/Util/InheritDataAwareIterator.php b/src/Symfony/Component/Form/Util/InheritDataAwareIterator.php
index ba157b7d182f4..a400c16f0b294 100644
--- a/src/Symfony/Component/Form/Util/InheritDataAwareIterator.php
+++ b/src/Symfony/Component/Form/Util/InheritDataAwareIterator.php
@@ -17,7 +17,7 @@
* Contrary to \ArrayIterator, this iterator recognizes changes in the original
* array during iteration.
*
- * You can wrap the iterator into a {@link \RecursiveIterator} in order to
+ * You can wrap the iterator into a {@link \RecursiveIteratorIterator} in order to
* enter any child form that inherits its parent's data and iterate the children
* of that form as well.
*
diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json
index 8360b460c0ab4..90cc9523fb70a 100644
--- a/src/Symfony/Component/Form/composer.json
+++ b/src/Symfony/Component/Form/composer.json
@@ -20,6 +20,7 @@
"symfony/event-dispatcher": "~2.1|~3.0.0",
"symfony/intl": "~2.7.25|^2.8.18|~3.2.5",
"symfony/options-resolver": "~2.6",
+ "symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.0",
"symfony/property-access": "~2.3|~3.0.0"
},
@@ -29,7 +30,7 @@
"symfony/dependency-injection": "~2.7|~3.0.0",
"symfony/http-foundation": "~2.2|~3.0.0",
"symfony/http-kernel": "~2.4|~3.0.0",
- "symfony/security-csrf": "~2.4|~3.0.0",
+ "symfony/security-csrf": "^2.8.31|^3.3.13",
"symfony/translation": "^2.0.5|~3.0.0"
},
"conflict": {
diff --git a/src/Symfony/Component/HttpFoundation/File/File.php b/src/Symfony/Component/HttpFoundation/File/File.php
index e2a67684fcda6..65ece98379019 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 082d8d534e17a..39b29775ccd8e 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/Request.php b/src/Symfony/Component/HttpFoundation/Request.php
index dec3d2fcf6af1..60a7c47ad9cdb 100644
--- a/src/Symfony/Component/HttpFoundation/Request.php
+++ b/src/Symfony/Component/HttpFoundation/Request.php
@@ -590,7 +590,7 @@ public static function getTrustedProxies()
public static function setTrustedHosts(array $hostPatterns)
{
self::$trustedHostPatterns = array_map(function ($hostPattern) {
- return sprintf('#%s#i', $hostPattern);
+ return sprintf('{%s}i', $hostPattern);
}, $hostPatterns);
// we need to reset trusted hosts on trusted host patterns change
self::$trustedHosts = array();
@@ -1301,7 +1301,7 @@ public function getRealMethod()
*
* @param string $format The format
*
- * @return string The associated mime type (null if not found)
+ * @return string|null The associated mime type (null if not found)
*/
public function getMimeType($format)
{
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
index dfd66516062c3..0825ee6ea9899 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
@@ -552,14 +552,16 @@ private function doAdvisoryLock($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/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
index 0de758c1cd6b4..0080cf8ac530a 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
@@ -18,6 +18,11 @@
class RequestTest extends TestCase
{
+ protected function tearDown()
+ {
+ Request::setTrustedHosts(array());
+ }
+
public function testInitialize()
{
$request = new Request();
@@ -1920,9 +1925,15 @@ public function testTrustedHosts()
$request->headers->set('host', 'subdomain.trusted.com');
$this->assertEquals('subdomain.trusted.com', $request->getHost());
+ }
- // reset request for following tests
- Request::setTrustedHosts(array());
+ public function testSetTrustedHostsDoesNotBreakOnSpecialCharacters()
+ {
+ Request::setTrustedHosts(array('localhost(\.local){0,1}#,example.com', 'localhost'));
+
+ $request = Request::create('/');
+ $request->headers->set('host', 'localhost');
+ $this->assertSame('localhost', $request->getHost());
}
public function testFactory()
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 06f80811b05bb..1eee70532ab18 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -59,11 +59,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected $startTime;
protected $loadClassCache;
- const VERSION = '2.8.39';
- const VERSION_ID = 20839;
+ const VERSION = '2.8.40';
+ const VERSION_ID = 20840;
const MAJOR_VERSION = 2;
const MINOR_VERSION = 8;
- const RELEASE_VERSION = 39;
+ const RELEASE_VERSION = 40;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '11/2018';
diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json
index 9a811a6e8a7eb..e082b2a32dc2b 100644
--- a/src/Symfony/Component/HttpKernel/composer.json
+++ b/src/Symfony/Component/HttpKernel/composer.json
@@ -20,6 +20,7 @@
"symfony/event-dispatcher": "^2.6.7|~3.0.0",
"symfony/http-foundation": "~2.7.36|~2.8.29|~3.1.6",
"symfony/debug": "^2.6.2",
+ "symfony/polyfill-ctype": "~1.8",
"psr/log": "~1.0"
},
"require-dev": {
diff --git a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php
index d460aacdda811..a4e36fb46f8e5 100644
--- a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php
+++ b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php
@@ -710,6 +710,7 @@ private function round($value, $precision)
} elseif (isset(self::$customRoundingList[$roundingModeAttribute])) {
$roundingCoef = pow(10, $precision);
$value *= $roundingCoef;
+ $value = (float) (string) $value;
switch ($roundingModeAttribute) {
case self::ROUND_CEILING:
diff --git a/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php b/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php
index 6c1cc569a3297..6d681f32248bc 100644
--- a/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php
+++ b/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php
@@ -428,6 +428,7 @@ public function formatRoundingModeRoundHalfUpProvider()
// array(1.125, '1.13'),
array(1.127, '1.13'),
array(1.129, '1.13'),
+ array(1020 / 100, '10.20'),
);
}
@@ -451,6 +452,7 @@ public function formatRoundingModeRoundHalfDownProvider()
array(1.125, '1.12'),
array(1.127, '1.13'),
array(1.129, '1.13'),
+ array(1020 / 100, '10.20'),
);
}
@@ -474,6 +476,7 @@ public function formatRoundingModeRoundHalfEvenProvider()
array(1.125, '1.12'),
array(1.127, '1.13'),
array(1.129, '1.13'),
+ array(1020 / 100, '10.20'),
);
}
@@ -498,6 +501,7 @@ public function formatRoundingModeRoundCeilingProvider()
array(-1.123, '-1.12'),
array(-1.125, '-1.12'),
array(-1.127, '-1.12'),
+ array(1020 / 100, '10.20'),
);
}
@@ -522,6 +526,7 @@ public function formatRoundingModeRoundFloorProvider()
array(-1.123, '-1.13'),
array(-1.125, '-1.13'),
array(-1.127, '-1.13'),
+ array(1020 / 100, '10.20'),
);
}
@@ -546,6 +551,7 @@ public function formatRoundingModeRoundDownProvider()
array(-1.123, '-1.12'),
array(-1.125, '-1.12'),
array(-1.127, '-1.12'),
+ array(1020 / 100, '10.20'),
);
}
@@ -570,6 +576,7 @@ public function formatRoundingModeRoundUpProvider()
array(-1.123, '-1.13'),
array(-1.125, '-1.13'),
array(-1.127, '-1.13'),
+ array(1020 / 100, '10.20'),
);
}
diff --git a/src/Symfony/Component/Process/ExecutableFinder.php b/src/Symfony/Component/Process/ExecutableFinder.php
index d042a5b13a211..1ec6526d45efd 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 f5c97d6bb9f8f..3e38157247c50 100644
--- a/src/Symfony/Component/Process/PhpExecutableFinder.php
+++ b/src/Symfony/Component/Process/PhpExecutableFinder.php
@@ -49,7 +49,7 @@ public function find($includeArgs = true)
}
if ($php = getenv('PHP_PATH')) {
- if (!is_executable($php)) {
+ if (!@is_executable($php)) {
return false;
}
@@ -57,12 +57,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 9a23d93c98688..97fe728bfd70d 100644
--- a/src/Symfony/Component/Process/Pipes/AbstractPipes.php
+++ b/src/Symfony/Component/Process/Pipes/AbstractPipes.php
@@ -23,6 +23,7 @@ abstract class AbstractPipes implements PipesInterface
private $inputBuffer = '';
private $input;
private $blocked = true;
+ private $lastError;
/**
* @param resource|null $input
@@ -56,10 +57,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');
}
/**
@@ -137,4 +139,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 65f32ecf2735a..935c43209d9da 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/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json
index 59b301e696eac..51acb59195a8f 100644
--- a/src/Symfony/Component/PropertyAccess/composer.json
+++ b/src/Symfony/Component/PropertyAccess/composer.json
@@ -16,7 +16,8 @@
}
],
"require": {
- "php": ">=5.3.9"
+ "php": ">=5.3.9",
+ "symfony/polyfill-ctype": "~1.8"
},
"autoload": {
"psr-4": { "Symfony\\Component\\PropertyAccess\\": "" },
diff --git a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php
index c629ca15255ff..306e19ad91bb9 100644
--- a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php
+++ b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php
@@ -12,6 +12,8 @@
namespace Symfony\Component\Security\Csrf\Tests\TokenStorage;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage;
/**
@@ -22,7 +24,7 @@ class SessionTokenStorageTest extends TestCase
const SESSION_NAMESPACE = 'foobar';
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var Session
*/
private $session;
@@ -33,118 +35,53 @@ class SessionTokenStorageTest extends TestCase
protected function setUp()
{
- $this->session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')
- ->disableOriginalConstructor()
- ->getMock();
+ $this->session = new Session(new MockArraySessionStorage());
$this->storage = new SessionTokenStorage($this->session, self::SESSION_NAMESPACE);
}
- public function testStoreTokenInClosedSession()
+ public function testStoreTokenInNotStartedSessionStartsTheSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(false));
-
- $this->session->expects($this->once())
- ->method('start');
-
- $this->session->expects($this->once())
- ->method('set')
- ->with(self::SESSION_NAMESPACE.'/token_id', 'TOKEN');
-
$this->storage->setToken('token_id', 'TOKEN');
+
+ $this->assertTrue($this->session->isStarted());
}
public function testStoreTokenInActiveSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(true));
-
- $this->session->expects($this->never())
- ->method('start');
-
- $this->session->expects($this->once())
- ->method('set')
- ->with(self::SESSION_NAMESPACE.'/token_id', 'TOKEN');
-
+ $this->session->start();
$this->storage->setToken('token_id', 'TOKEN');
+
+ $this->assertSame('TOKEN', $this->session->get(self::SESSION_NAMESPACE.'/token_id'));
}
public function testCheckTokenInClosedSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(false));
-
- $this->session->expects($this->once())
- ->method('start');
+ $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT');
- $this->session->expects($this->once())
- ->method('has')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue('RESULT'));
-
- $this->assertSame('RESULT', $this->storage->hasToken('token_id'));
+ $this->assertTrue($this->storage->hasToken('token_id'));
+ $this->assertTrue($this->session->isStarted());
}
public function testCheckTokenInActiveSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(true));
-
- $this->session->expects($this->never())
- ->method('start');
+ $this->session->start();
+ $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT');
- $this->session->expects($this->once())
- ->method('has')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue('RESULT'));
-
- $this->assertSame('RESULT', $this->storage->hasToken('token_id'));
+ $this->assertTrue($this->storage->hasToken('token_id'));
}
public function testGetExistingTokenFromClosedSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(false));
-
- $this->session->expects($this->once())
- ->method('start');
-
- $this->session->expects($this->once())
- ->method('has')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue(true));
-
- $this->session->expects($this->once())
- ->method('get')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue('RESULT'));
+ $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT');
$this->assertSame('RESULT', $this->storage->getToken('token_id'));
+ $this->assertTrue($this->session->isStarted());
}
public function testGetExistingTokenFromActiveSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(true));
-
- $this->session->expects($this->never())
- ->method('start');
-
- $this->session->expects($this->once())
- ->method('has')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue(true));
-
- $this->session->expects($this->once())
- ->method('get')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue('RESULT'));
+ $this->session->start();
+ $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT');
$this->assertSame('RESULT', $this->storage->getToken('token_id'));
}
@@ -154,18 +91,6 @@ public function testGetExistingTokenFromActiveSession()
*/
public function testGetNonExistingTokenFromClosedSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(false));
-
- $this->session->expects($this->once())
- ->method('start');
-
- $this->session->expects($this->once())
- ->method('has')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue(false));
-
$this->storage->getToken('token_id');
}
@@ -174,85 +99,33 @@ public function testGetNonExistingTokenFromClosedSession()
*/
public function testGetNonExistingTokenFromActiveSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(true));
-
- $this->session->expects($this->never())
- ->method('start');
-
- $this->session->expects($this->once())
- ->method('has')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue(false));
-
+ $this->session->start();
$this->storage->getToken('token_id');
}
public function testRemoveNonExistingTokenFromClosedSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(false));
-
- $this->session->expects($this->once())
- ->method('start');
-
- $this->session->expects($this->once())
- ->method('remove')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue(null));
-
$this->assertNull($this->storage->removeToken('token_id'));
}
public function testRemoveNonExistingTokenFromActiveSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(true));
-
- $this->session->expects($this->never())
- ->method('start');
-
- $this->session->expects($this->once())
- ->method('remove')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue(null));
+ $this->session->start();
$this->assertNull($this->storage->removeToken('token_id'));
}
public function testRemoveExistingTokenFromClosedSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(false));
-
- $this->session->expects($this->once())
- ->method('start');
-
- $this->session->expects($this->once())
- ->method('remove')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue('TOKEN'));
+ $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'TOKEN');
$this->assertSame('TOKEN', $this->storage->removeToken('token_id'));
}
public function testRemoveExistingTokenFromActiveSession()
{
- $this->session->expects($this->any())
- ->method('isStarted')
- ->will($this->returnValue(true));
-
- $this->session->expects($this->never())
- ->method('start');
-
- $this->session->expects($this->once())
- ->method('remove')
- ->with(self::SESSION_NAMESPACE.'/token_id')
- ->will($this->returnValue('TOKEN'));
+ $this->session->start();
+ $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'TOKEN');
$this->assertSame('TOKEN', $this->storage->removeToken('token_id'));
}
diff --git a/src/Symfony/Component/Security/Guard/composer.json b/src/Symfony/Component/Security/Guard/composer.json
index 320892095cb1d..35c7456638ea8 100644
--- a/src/Symfony/Component/Security/Guard/composer.json
+++ b/src/Symfony/Component/Security/Guard/composer.json
@@ -18,7 +18,7 @@
"require": {
"php": ">=5.3.9",
"symfony/security-core": "~2.8|~3.0.0",
- "symfony/security-http": "~2.7|~3.0.0"
+ "symfony/security-http": "^2.8.31|^3.3.13"
},
"require-dev": {
"psr/log": "~1.0"
diff --git a/src/Symfony/Component/Security/Http/Firewall.php b/src/Symfony/Component/Security/Http/Firewall.php
index 62b0071212e54..376194d3a6c8c 100644
--- a/src/Symfony/Component/Security/Http/Firewall.php
+++ b/src/Symfony/Component/Security/Http/Firewall.php
@@ -47,20 +47,29 @@ 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);
}
// initiate the listener chain
- foreach ($listeners as $listener) {
+ foreach ($authenticationListeners as $listener) {
$listener->handle($event);
if ($event->hasResponse()) {
break;
}
}
+
+ 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 e767d123cb03e..fc97410d4e698 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);
}
}
diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json
index bde88858ae5a3..a604766c0002f 100644
--- a/src/Symfony/Component/Security/Http/composer.json
+++ b/src/Symfony/Component/Security/Http/composer.json
@@ -27,7 +27,7 @@
},
"require-dev": {
"symfony/routing": "~2.2|~3.0.0",
- "symfony/security-csrf": "~2.4|~3.0.0",
+ "symfony/security-csrf": "^2.8.31|^3.3.13",
"psr/log": "~1.0"
},
"suggest": {
diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json
index 70bc5438d1db6..0fdd4c37c1259 100644
--- a/src/Symfony/Component/Serializer/composer.json
+++ b/src/Symfony/Component/Serializer/composer.json
@@ -17,6 +17,7 @@
],
"require": {
"php": ">=5.3.9",
+ "symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-php55": "~1.0"
},
"require-dev": {
diff --git a/src/Symfony/Component/Templating/composer.json b/src/Symfony/Component/Templating/composer.json
index 544483fcf1e24..294eaa6d79940 100644
--- a/src/Symfony/Component/Templating/composer.json
+++ b/src/Symfony/Component/Templating/composer.json
@@ -16,7 +16,8 @@
}
],
"require": {
- "php": ">=5.3.9"
+ "php": ">=5.3.9",
+ "symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
"psr/log": "~1.0"
diff --git a/src/Symfony/Component/Translation/PluralizationRules.php b/src/Symfony/Component/Translation/PluralizationRules.php
index e5ece89620b7f..2b7b118336938 100644
--- a/src/Symfony/Component/Translation/PluralizationRules.php
+++ b/src/Symfony/Component/Translation/PluralizationRules.php
@@ -107,6 +107,7 @@ public static function get($number, $locale)
case 'nl':
case 'nn':
case 'no':
+ case 'oc':
case 'om':
case 'or':
case 'pa':
diff --git a/src/Symfony/Component/Validator/ObjectInitializerInterface.php b/src/Symfony/Component/Validator/ObjectInitializerInterface.php
index dcbc2cd11dbdd..5f9cdad879632 100644
--- a/src/Symfony/Component/Validator/ObjectInitializerInterface.php
+++ b/src/Symfony/Component/Validator/ObjectInitializerInterface.php
@@ -15,7 +15,7 @@
* Prepares an object for validation.
*
* Concrete implementations of this interface are used by {@link ValidationVisitorInterface}
- * to initialize objects just before validating them.
+ * and {@link Validator\ContextualValidatorInterface} to initialize objects just before validating them.
*
* @author Fabien Potencier
* @author Bernhard Schussek
diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json
index c4261f173bbfa..99ccc33023d58 100644
--- a/src/Symfony/Component/Validator/composer.json
+++ b/src/Symfony/Component/Validator/composer.json
@@ -17,6 +17,7 @@
],
"require": {
"php": ">=5.3.9",
+ "symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.0",
"symfony/translation": "~2.4|~3.0.0"
},
diff --git a/src/Symfony/Component/Yaml/composer.json b/src/Symfony/Component/Yaml/composer.json
index af97f80f210a0..d07323608912d 100644
--- a/src/Symfony/Component/Yaml/composer.json
+++ b/src/Symfony/Component/Yaml/composer.json
@@ -16,7 +16,8 @@
}
],
"require": {
- "php": ">=5.3.9"
+ "php": ">=5.3.9",
+ "symfony/polyfill-ctype": "~1.8"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Yaml\\": "" },