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.7.md b/CHANGELOG-2.7.md
index 2f6ece9b92809..6903fb4ed455d 100644
--- a/CHANGELOG-2.7.md
+++ b/CHANGELOG-2.7.md
@@ -7,6 +7,20 @@ in 2.7 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.7.0...v2.7.1
+* 2.7.47 (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)
+ * bug #27067 [HttpFoundation] Fix setting session-related ini settings (e-moe)
+
* 2.7.46 (2018-04-27)
* bug #26831 [Bridge/Doctrine] count(): Parameter must be an array or an object that implements Countable (gpenverne)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index bc4ebf6f78139..672246e7f9da8 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -17,9 +17,9 @@ Symfony is the result of the work of many people who made the code better
- Johannes S (johannes)
- Jakub Zalas (jakubzalas)
- Kris Wallsmith (kriswallsmith)
+ - Maxime Steinhausser (ogizanagi)
- Ryan Weaver (weaverryan)
- Javier Eguiluz (javier.eguiluz)
- - Maxime Steinhausser (ogizanagi)
- Grégoire Pineau (lyrixx)
- Hugo Hamon (hhamon)
- Abdellatif Ait boudad (aitboudad)
@@ -35,17 +35,17 @@ Symfony is the result of the work of many people who made the code better
- Jean-François Simon (jfsimon)
- Benjamin Eberlei (beberlei)
- Igor Wiedler (igorw)
+ - Samuel ROZE (sroze)
- Jules Pietri (heah)
- Eriksen Costa (eriksencosta)
- Guilhem Niot (energetick)
- Sarah Khalil (saro0h)
- - Samuel ROZE (sroze)
+ - Yonel Ceruto (yonelceruto)
- Jonathan Wage (jwage)
- Hamza Amrouche (simperfit)
- Diego Saint Esteben (dosten)
- - Yonel Ceruto (yonelceruto)
- - Alexandre Salomé (alexandresalome)
- Iltar van der Berg (kjarli)
+ - Alexandre Salomé (alexandresalome)
- William Durand (couac)
- ornicar
- Francis Besset (francisbesset)
@@ -59,9 +59,9 @@ Symfony is the result of the work of many people who made the code better
- Henrik Bjørnskov (henrikbjorn)
- Dany Maillard (maidmaid)
- Miha Vrhovnik
+ - Kevin Bond (kbond)
- Tobias Nyholm (tobias)
- Diego Saint Esteben (dii3g0)
- - Kevin Bond (kbond)
- Konstantin Kudryashov (everzet)
- Alexander M. Turek (derrabus)
- Bilal Amarni (bamarni)
@@ -83,12 +83,12 @@ Symfony is the result of the work of many people who made the code better
- Dariusz Górecki (canni)
- Issei Murasawa (issei_m)
- Douglas Greenshields (shieldo)
+ - David Maicher (dmaicher)
- Lee McDermott
- Brandon Turner
- Luis Cordova (cordoval)
- Graham Campbell (graham)
- Daniel Holmes (dholmes)
- - David Maicher (dmaicher)
- Dariusz Ruminski
- Toni Uebernickel (havvg)
- Bart van den Burg (burgov)
@@ -103,9 +103,9 @@ Symfony is the result of the work of many people who made the code better
- Maxime STEINHAUSSER
- Michal Piotrowski (eventhorizon)
- Tim Nagel (merk)
+ - Grégoire Paris (greg0ire)
- Brice BERNARD (brikou)
- Baptiste Clavié (talus)
- - Grégoire Paris (greg0ire)
- marc.weistroff
- lenar
- Alexander Schwenn (xelaris)
@@ -139,21 +139,21 @@ Symfony is the result of the work of many people who made the code better
- Sebastian Hörl (blogsh)
- Daniel Gomes (danielcsgomes)
- Hidenori Goto (hidenorigoto)
+ - Jérôme Vasseur (jvasseur)
+ - Valentin Udaltsov (vudaltsov)
+ - gadelat (gadelat)
- Guilherme Blanco (guilhermeblanco)
- Pablo Godel (pgodel)
- - Jérôme Vasseur (jvasseur)
- Jérémie Augustin (jaugustin)
- Andréia Bohner (andreia)
- Philipp Wahala (hifi)
- Julien Falque (julienfalque)
- Rafael Dohms (rdohms)
- Arnaud Kleinpeter (nanocom)
- - gadelat (gadelat)
- jwdeitch
- Teoh Han Hui (teohhanhui)
- Mikael Pajunen
- Joel Wurtz (brouznouf)
- - Valentin Udaltsov (vudaltsov)
- Chris Wilkinson (thewilkybarkid)
- Oleg Voronkovich
- Vyacheslav Pavlov
@@ -226,6 +226,7 @@ Symfony is the result of the work of many people who made the code better
- Julien Brochet (mewt)
- Leo Feyer
- Tristan Darricau (nicofuma)
+ - Nikolay Labinskiy (e-moe)
- Michaël Perrin (michael.perrin)
- Marcel Beerta (mazen)
- Loïc Faugeron
@@ -260,6 +261,7 @@ Symfony is the result of the work of many people who made the code better
- Kristen Gilden (kgilden)
- Pierre-Yves LEBECQ (pylebecq)
- Jordan Samouh (jordansamouh)
+ - Baptiste Lafontaine (magnetik)
- Jakub Kucharovic (jkucharovic)
- Uwe Jäger (uwej711)
- Eugene Leonovich (rybakit)
@@ -270,7 +272,6 @@ Symfony is the result of the work of many people who made the code better
- Jan Sorgalla (jsor)
- Ray
- Tyson Andre
- - Nikolay Labinskiy (e-moe)
- Chekote
- Thomas Adam
- Albert Casademont (acasademont)
@@ -286,6 +287,7 @@ Symfony is the result of the work of many people who made the code better
- Oskar Stark (oskarstark)
- Thomas Lallement (raziel057)
- Giorgio Premi
+ - Christian Schmidt
- Beau Simensen (simensen)
- Michael Hirschler (mvhirsch)
- Robert Kiss (kepten)
@@ -317,7 +319,6 @@ Symfony is the result of the work of many people who made the code better
- Jerzy Zawadzki (jzawadzki)
- Wouter J
- Ismael Ambrosi (iambrosi)
- - Baptiste Lafontaine
- François Pluchino (francoispluchino)
- Aurelijus Valeiša (aurelijus)
- Jan Decavele (jandc)
@@ -361,6 +362,7 @@ Symfony is the result of the work of many people who made the code better
- Yaroslav Kiliba
- Terje Bråten
- Mathieu Lechat
+ - MatTheCat
- Robbert Klarenbeek (robbertkl)
- JhonnyL
- David Badura (davidbadura)
@@ -424,7 +426,6 @@ Symfony is the result of the work of many people who made the code better
- Jeanmonod David (jeanmonod)
- Christopher Davis (chrisguitarguy)
- Jan Schumann
- - Christian Schmidt
- Niklas Fiekas
- Markus Bachmann (baachi)
- lancergr
@@ -437,6 +438,7 @@ Symfony is the result of the work of many people who made the code better
- Josip Kruslin
- Asmir Mustafic (goetas)
- vagrant
+ - Aurimas Niekis (gcds)
- EdgarPE
- Florian Pfitzer (marmelatze)
- Asier Illarramendi (doup)
@@ -514,6 +516,7 @@ Symfony is the result of the work of many people who made the code better
- De Cock Xavier (xdecock)
- Almog Baku (almogbaku)
- Scott Arciszewski
+ - Xavier HAUSHERR
- Norbert Orzechowicz (norzechowicz)
- Denis Charrier (brucewouaigne)
- Matthijs van den Bos (matthijs)
@@ -529,7 +532,6 @@ Symfony is the result of the work of many people who made the code better
- Dawid Pakuła (zulusx)
- Florian Rey (nervo)
- Rodrigo Borrego Bernabé (rodrigobb)
- - MatTheCat
- Denis Gorbachev (starfall)
- Peter van Dommelen
- Tim van Densen
@@ -563,6 +565,7 @@ Symfony is the result of the work of many people who made the code better
- Mantas Var (mvar)
- Sebastian Krebs
- Jean-Christophe Cuvelier [Artack]
+ - Simon DELICATA
- alcaeus
- Fred Cox
- vitaliytv
@@ -580,6 +583,7 @@ Symfony is the result of the work of many people who made the code better
- James Johnston
- Sinan Eldem
- Alexandre Dupuy (satchette)
+ - Malte Blättermann
- Andre Rømcke (andrerom)
- Nahuel Cuesta (ncuesta)
- Chris Boden (cboden)
@@ -604,7 +608,6 @@ Symfony is the result of the work of many people who made the code better
- Michal Trojanowski
- David Fuhr
- Kamil Kokot (pamil)
- - Aurimas Niekis (gcds)
- Max Grigorian (maxakawizard)
- mcfedr (mcfedr)
- Rostyslav Kinash
@@ -721,6 +724,7 @@ Symfony is the result of the work of many people who made the code better
- Adam Szaraniec (mimol)
- Yosmany Garcia (yosmanyga)
- Wouter de Wild
+ - Antoine M (amakdessi)
- Degory Valentine
- izzyp
- Benoit Lévêque (benoit_leveque)
@@ -730,6 +734,7 @@ Symfony is the result of the work of many people who made the code better
- Xavier Lacot (xavier)
- possum
- Denis Zunke (donalberto)
+ - Philipp Cordes
- Ahmed TAILOULOUTE (ahmedtai)
- Olivier Maisonneuve (olineuve)
- Masterklavi
@@ -749,7 +754,6 @@ Symfony is the result of the work of many people who made the code better
- Adrien Lucas (adrienlucas)
- Zhuravlev Alexander (scif)
- James Michael DuPont
- - Xavier HAUSHERR
- Tom Klingenberg
- Christopher Hall (mythmakr)
- Patrick Dawkins (pjcdawkins)
@@ -802,6 +806,7 @@ Symfony is the result of the work of many people who made the code better
- corphi
- grizlik
- Derek ROTH
+ - Ben Johnson
- Dmytro Boiko (eagle)
- Shin Ohno (ganchiku)
- Geert De Deckere (geertdd)
@@ -882,6 +887,7 @@ Symfony is the result of the work of many people who made the code better
- Michael Tibben
- Billie Thompson
- Sander Marechal
+ - Icode4Food (icode4food)
- Radosław Benkel
- jean pasqualini (darkilliant)
- Ross Motley (rossmotley)
@@ -993,6 +999,7 @@ Symfony is the result of the work of many people who made the code better
- DerManoMann
- Olaf Klischat
- orlovv
+ - Jonathan Hedstrom
- Peter Smeets (darkspartan)
- Jhonny Lidfors (jhonny)
- Julien Bianchi (jubianchi)
@@ -1005,7 +1012,6 @@ Symfony is the result of the work of many people who made the code better
- Andrew Tch
- Alexander Cheprasov
- Rodrigo Díez Villamuera (rodrigodiez)
- - Malte Blättermann
- e-ivanov
- Jochen Bayer (jocl)
- Alex Bowers
@@ -1092,9 +1098,11 @@ Symfony is the result of the work of many people who made the code better
- Tobias Stöckler
- Mario Young
- Ilia (aliance)
+ - Chris McCafferty (cilefen)
- Grégoire Penverne (gpenverne)
- Mo Di (modi)
- Pablo Schläpfer
+ - Gert de Pagter
- Jelte Steijaert (jelte)
- Quique Porta (quiqueporta)
- stoccc
@@ -1177,9 +1185,9 @@ Symfony is the result of the work of many people who made the code better
- Andreas Frömer
- Philip Frank
- Lance McNearney
- - Antoine M (amakdessi)
- Gonzalo Vilaseca (gonzalovilaseca)
- Giorgio Premi
+ - ncou
- Ian Carroll
- caponica
- Matt Daum (daum)
@@ -1197,7 +1205,6 @@ Symfony is the result of the work of many people who made the code better
- Tadcka
- Beth Binkovitz
- Gonzalo Míguez
- - Philipp Cordes
- Pierre Rineau
- Romain Geissler
- Adrien Moiruad
@@ -1352,6 +1359,7 @@ Symfony is the result of the work of many people who made the code better
- Pablo Maria Martelletti (pmartelletti)
- Yassine Guedidi (yguedidi)
- Waqas Ahmed
+ - Bert Hekman
- Luis Muñoz
- Matthew Donadio
- Houziaux mike
@@ -1450,6 +1458,7 @@ Symfony is the result of the work of many people who made the code better
- Yannick Warnier (ywarnier)
- Kevin Decherf
- Jason Woods
+ - Oleg Andreyev
- klemens
- dened
- Dmitry Korotovsky
@@ -1508,6 +1517,7 @@ Symfony is the result of the work of many people who made the code better
- Pierre Rineau
- Maxim Lovchikov
- adenkejawen
+ - Florent SEVESTRE (aniki-taicho)
- Ari Pringle (apringle)
- Dan Ordille (dordille)
- Jan Eichhorn (exeu)
@@ -1759,7 +1769,6 @@ Symfony is the result of the work of many people who made the code better
- Matt Janssen
- Ben Miller
- Peter Gribanov
- - Ben Johnson
- kwiateusz
- David Soria Parra
- Sergiy Sokolenko
@@ -1889,6 +1898,7 @@ Symfony is the result of the work of many people who made the code better
- Julien Sanchez (sumbobyboys)
- Guillermo Gisinger (t3chn0r)
- Markus Tacker (tacker)
+ - Tarmo Leppänen (tarlepp)
- Tyler Stroud (tystr)
- Moritz Kraft (userfriendly)
- Víctor Mateo (victormateo)
diff --git a/composer.json b/composer.json
index f263c15d45324..350ea8cce2185 100644
--- a/composer.json
+++ b/composer.json
@@ -21,6 +21,7 @@
"doctrine/common": "~2.4",
"paragonie/random_compat": "~1.0",
"symfony/polyfill-apcu": "~1.1",
+ "symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.1",
"twig/twig": "~1.34|~2.4",
"psr/log": "~1.0"
diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json
index e9dda5f59ae43..cd0794f3e4fbc 100644
--- a/src/Symfony/Bridge/Doctrine/composer.json
+++ b/src/Symfony/Bridge/Doctrine/composer.json
@@ -17,7 +17,8 @@
],
"require": {
"php": ">=5.3.9",
- "doctrine/common": "~2.4"
+ "doctrine/common": "~2.4",
+ "symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
"symfony/stopwatch": "~2.2",
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/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index e834b73c4bca0..3c4ffb2773a76 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -339,7 +339,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 a20a120d0710b..6505d5a034932 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/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index 3ffe2876e88d8..9c526f88daa9d 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -222,13 +222,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;
}
@@ -259,7 +260,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)
@@ -286,15 +287,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'],
'intention' => $firewall['logout']['csrf_token_id'],
'logout_path' => $firewall['logout']['path'],
));
- $listeners[] = new Reference($listenerId);
// add logout success handler
if (isset($firewall['logout']['success_handler'])) {
@@ -304,16 +305,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
@@ -322,12 +323,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
@@ -362,7 +363,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
// Exception listener
$exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $configuredEntryPoint ?: $defaultEntryPoint, $firewall['stateless']));
- 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 b7c1407c1cc56..b044ccba98e74 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
@@ -150,6 +150,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 855bed9b6899d..371baa704a835 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..48fd4ed6cc3cd
--- /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
+ key: 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 e765b16bef867..90edcf6e38cee 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.7.38|~2.8.31",
+ "symfony/security": "~2.7.47|~2.8.40",
"symfony/security-acl": "~2.7",
"symfony/http-kernel": "~2.7"
},
diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json
index cd2f229e4ff88..d5b85177bcc56 100644
--- a/src/Symfony/Bundle/TwigBundle/composer.json
+++ b/src/Symfony/Bundle/TwigBundle/composer.json
@@ -21,7 +21,8 @@
"symfony/twig-bridge": "~2.7",
"twig/twig": "~1.34|~2.4",
"symfony/http-foundation": "~2.5",
- "symfony/http-kernel": "~2.7.23|^2.8.16"
+ "symfony/http-kernel": "~2.7.23|^2.8.16",
+ "symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
"symfony/stopwatch": "~2.2",
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 3361be296e26b..469aee8f5fe60 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': '#aacd4e',
'section': '#666',
'event_listener': '#3dd',
- 'event_listener_loading': '#add',
'template': '#dd3',
'doctrine': '#d3d',
- 'propel': '#f4d',
'child_sections': '#eed',
} %}
{% endif %}
diff --git a/src/Symfony/Component/Config/composer.json b/src/Symfony/Component/Config/composer.json
index 47f8642ae6dbc..2027e190c8d44 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"
+ "symfony/filesystem": "~2.3",
+ "symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
"symfony/yaml": "~2.7"
diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php
index 4b8eaea64e666..3fe12e867a827 100644
--- a/src/Symfony/Component/Console/Command/Command.php
+++ b/src/Symfony/Component/Console/Command/Command.php
@@ -205,12 +205,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/Console/composer.json b/src/Symfony/Component/Console/composer.json
index c4fb8c192a401..9f63559861811 100644
--- a/src/Symfony/Component/Console/composer.json
+++ b/src/Symfony/Component/Console/composer.json
@@ -27,7 +27,7 @@
"suggest": {
"symfony/event-dispatcher": "",
"symfony/process": "",
- "psr/log": "For using the console logger"
+ "psr/log-implementation": "For using the console logger"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Console\\": "" },
diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php
index e056862b8e675..4671b85469d78 100644
--- a/src/Symfony/Component/Debug/ErrorHandler.php
+++ b/src/Symfony/Component/Debug/ErrorHandler.php
@@ -377,13 +377,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;
@@ -479,7 +481,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 4eb4aef0c0fa9..d8f4a74d86969 100644
--- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
+++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
@@ -64,6 +64,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 5ea57b2657041..068f8390ed922 100644
--- a/src/Symfony/Component/DomCrawler/composer.json
+++ b/src/Symfony/Component/DomCrawler/composer.json
@@ -16,7 +16,8 @@
}
],
"require": {
- "php": ">=5.3.9"
+ "php": ">=5.3.9",
+ "symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
"symfony/css-selector": "~2.3"
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index 5c3b2a2d9cb8e..0f6d0a269e6cb 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);
}
}
@@ -580,4 +575,29 @@ private function toIterator($files)
return $files;
}
+
+ 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 20a13bbd92b0d..e1783a7f10247 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 47f4b422216b9..43114dbcc07db 100644
--- a/src/Symfony/Component/Finder/Shell/Command.php
+++ b/src/Symfony/Component/Finder/Shell/Command.php
@@ -100,7 +100,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 70b0c78e6cfa5..c4bfb0e303c53 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/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 5b3af06994b82..67441c9ec9804 100644
--- a/src/Symfony/Component/Form/composer.json
+++ b/src/Symfony/Component/Form/composer.json
@@ -20,6 +20,7 @@
"symfony/event-dispatcher": "~2.1",
"symfony/intl": "~2.7.25|^2.8.18",
"symfony/options-resolver": "~2.6",
+ "symfony/polyfill-ctype": "~1.8",
"symfony/property-access": "~2.3"
},
"require-dev": {
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 ecdcdbc25acc2..7e8e075a833f3 100644
--- a/src/Symfony/Component/HttpFoundation/Request.php
+++ b/src/Symfony/Component/HttpFoundation/Request.php
@@ -581,7 +581,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();
@@ -1322,7 +1322,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/Session/Storage/NativeSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
index f03cdf343024d..34d94c55bcc9f 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
@@ -340,7 +340,7 @@ public function setOptions(array $options)
}
$validOptions = array_flip(array(
- 'cache_limiter', 'cookie_domain', 'cookie_httponly',
+ 'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
'cookie_lifetime', 'cookie_path', 'cookie_secure',
'entropy_file', 'entropy_length', 'gc_divisor',
'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
@@ -348,13 +348,13 @@ public function setOptions(array $options)
'serialize_handler', 'use_strict_mode', 'use_cookies',
'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
- 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags',
+ 'upload_progress.freq', 'upload_progress.min_freq', 'url_rewriter.tags',
'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
));
foreach ($options as $key => $value) {
if (isset($validOptions[$key])) {
- ini_set('session.'.$key, $value);
+ ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value);
}
}
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
index 0c5451dfd6ccc..688a7c714a1d7 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();
@@ -1871,9 +1876,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/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
index 3501f74784a9e..529583c01aade 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
@@ -183,6 +183,23 @@ public function testCookieOptions()
$this->assertEquals($options, $gco);
}
+ public function testSessionOptions()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('HHVM is not handled in this test case.');
+ }
+
+ $options = array(
+ 'url_rewriter.tags' => 'a=href',
+ 'cache_expire' => '200',
+ );
+
+ $this->getStorage($options);
+
+ $this->assertSame('a=href', ini_get('url_rewriter.tags'));
+ $this->assertSame('200', ini_get('session.cache_expire'));
+ }
+
/**
* @expectedException \InvalidArgumentException
*/
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 43a441fbe25d5..e0d221e2edce8 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -58,11 +58,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected $startTime;
protected $loadClassCache;
- const VERSION = '2.7.46';
- const VERSION_ID = 20746;
+ const VERSION = '2.7.47';
+ const VERSION_ID = 20747;
const MAJOR_VERSION = 2;
const MINOR_VERSION = 7;
- const RELEASE_VERSION = 46;
+ const RELEASE_VERSION = 47;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '05/2018';
diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json
index 1b6b096a8a61f..6986945497ebf 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",
"symfony/http-foundation": "~2.7.36|^2.8.29",
"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 f68a60143a747..4a9acb5be9ac9 100644
--- a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php
+++ b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php
@@ -708,6 +708,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 c02990a198a15..8e881763efcf8 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/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/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json
index bcacfbc7549e9..e1bde54956281 100644
--- a/src/Symfony/Component/Serializer/composer.json
+++ b/src/Symfony/Component/Serializer/composer.json
@@ -16,7 +16,8 @@
}
],
"require": {
- "php": ">=5.3.9"
+ "php": ">=5.3.9",
+ "symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
"symfony/yaml": "^2.0.5",
diff --git a/src/Symfony/Component/Templating/composer.json b/src/Symfony/Component/Templating/composer.json
index 74f4412623328..5ccb27c1028b4 100644
--- a/src/Symfony/Component/Templating/composer.json
+++ b/src/Symfony/Component/Templating/composer.json
@@ -16,13 +16,14 @@
}
],
"require": {
- "php": ">=5.3.9"
+ "php": ">=5.3.9",
+ "symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
"psr/log": "~1.0"
},
"suggest": {
- "psr/log": "For using debug logging in loaders"
+ "psr/log-implementation": "For using debug logging in loaders"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Templating\\": "" },
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/Translation/composer.json b/src/Symfony/Component/Translation/composer.json
index 3ba1a64367895..128f37f0d7f5d 100644
--- a/src/Symfony/Component/Translation/composer.json
+++ b/src/Symfony/Component/Translation/composer.json
@@ -30,7 +30,7 @@
"suggest": {
"symfony/config": "",
"symfony/yaml": "",
- "psr/log": "To use logging capability in translator"
+ "psr/log-implementation": "To use logging capability in translator"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Translation\\": "" },
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 e6db2e6907f0e..7fe92bae77c71 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/translation": "~2.4"
},
"require-dev": {
diff --git a/src/Symfony/Component/Yaml/composer.json b/src/Symfony/Component/Yaml/composer.json
index 03eb421d294fc..eeab73c355d15 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\\": "" },