diff --git a/CHANGELOG-3.3.md b/CHANGELOG-3.3.md
index 6c3cb3540ff0c..1817eff60299e 100644
--- a/CHANGELOG-3.3.md
+++ b/CHANGELOG-3.3.md
@@ -7,6 +7,72 @@ in 3.3 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/v3.3.0...v3.3.1
+* 3.3.3 (2017-07-04)
+
+ * bug #23366 [FrameworkBundle] Don't get() private services from debug:router (chalasr)
+ * bug #23239 [FrameworkBundle] call setContainer() for autowired controllers (xabbuh)
+ * bug #23351 [Dotenv] parse concatenated variable values (xabbuh)
+ * bug #23341 [DoctrineBridge][Security][Validator] do not validate empty values (xabbuh)
+ * bug #23274 Display a better error design when the toolbar cannot be displayed (yceruto)
+ * bug #23342 [Dotenv] parse escaped quotes in unquoted env var values (xabbuh)
+ * bug #23291 [Security] Fix Firewall ExceptionListener priority (chalasr)
+ * bug #23296 [WebProfilerBundle] Fix css trick used for offsetting html anchor from fixed header (ogizanagi)
+ * bug #23333 [PropertyAccess] Fix TypeError discard (dunglas)
+ * bug #23326 [Cache] fix cleanup of expired items for PdoAdapter (dmaicher)
+ * bug #23345 [Console] fix description of INF default values (xabbuh)
+ * bug #23328 [FrameworkBundle] Display a proper warning on cache:clear without the --no-warmup option (ogizanagi)
+ * bug #23299 [Workflow] Added more events to the announce function (Nyholm)
+ * bug #23279 Don't call count on non countable object (pierredup)
+ * bug #23283 [TwigBundle] add back exception check (xabbuh)
+ * bug #23268 Show exception is checked twice in ExceptionController of twig (gmponos)
+ * bug #23266 Display a better error message when the toolbar cannot be displayed (javiereguiluz)
+ * bug #23271 [FrameworkBundle] allow SSI fragments configuration in XML files (xabbuh)
+ * bug #23254 [Form][TwigBridge] render hidden _method field in form_rest() (xabbuh)
+ * bug #23250 [Translation] return fallback locales whenever possible (xabbuh)
+ * bug #23237 [Cache] Fix Predis client cluster with pipeline (flolivaud)
+ * bug #23240 [Console] Fix catching exception type in QuestionHelper (voronkovich)
+ * bug #23218 [DI] Dedup tags when using instanceof/autoconfigure (ogizanagi)
+ * bug #23231 Improved the exception page when there is no message (javiereguiluz)
+ * bug #23229 [WebProfilerBundle] Eliminate line wrap on count column (routing) (e-moe)
+ * bug #22732 [Security] fix switch user _exit without having current token (dmaicher)
+ * bug #23226 [Validator] replace hardcoded service id (xabbuh)
+ * bug #22730 [FrameworkBundle] Sessions: configurable "use_strict_mode" option for NativeSessionStorage (MacDada)
+ * bug #23195 [FrameworkBundle] [Command] Clean bundle directory, fixes #23177 (NicolasPion)
+ * bug #23213 Fixed composer resources between web/cli (iltar)
+ * bug #23160 [WebProfilerBundle] Fix the icon for the Cache panel (javiereguiluz)
+ * bug #23052 [TwigBundle] Add Content-Type header for exception response (rchoquet)
+ * bug #23173 [WebServerBundle] Fix router script option BC (1ed)
+ * bug #23199 Reset redirectCount when throwing exception (hvanoch)
+ * bug #23180 [FrameworkBundle] Expose the AbstractController's container to its subclasses (BPScott)
+ * bug #23186 [TwigBundle] Move template.xml loading to a compiler pass (ogizanagi)
+ * bug #23130 Keep s-maxage when expiry and validation are used in combination (mpdude)
+ * bug #23129 Fix two edge cases in ResponseCacheStrategy (mpdude)
+ * feature #22636 [Routing] Expose request in route conditions, if needed and possible (ro0NL)
+ * bug #22636 [Routing] Expose request in route conditions, if needed and possible (ro0NL)
+ * bug #22943 [SecurityBundle] Move cache of the firewall context into the request parameters (GromNaN)
+ * bug #23088 [FrameworkBundle] Dont set pre-defined esi/ssi services (ro0NL)
+ * bug #23057 [Translation][FrameworkBundle] Fix resource loading order inconsistency reported in #23034 (mpdude)
+ * bug #23092 [Filesystem] added workaround in Filesystem::rename for PHP bug (VolCh)
+ * bug #23074 [HttpFoundation] add back support for legacy constant values (xabbuh)
+ * bug #23128 [HttpFoundation] fix for Support for new 7.1 session options (vincentaubert)
+ * bug #23176 [VarDumper] fixes (nicolas-grekas)
+ * bug #23100 [PropertyAccess] Do not silence TypeErrors from client code. (tsufeki)
+ * bug #23156 [PropertyAccess] Fix Usage with anonymous classes (mablae)
+ * bug #23168 [Config] Fix ** GlobResource on Windows (nicolas-grekas)
+ * bug #23171 [Yaml] Fix linting yaml with constants as keys (chalasr)
+ * bug #23121 [Routing] Revert the change in [#b42018] with respect to Routing/Route.php (Dan Wilga)
+ * bug #23141 [DI] Fix keys resolution in ResolveParameterPlaceHoldersPass (nicolas-grekas)
+ * bug #23145 Fix the conditional definition of the SymfonyTestsListener (stof)
+ * bug #23091 [Cache] ApcuAdapter::isSupported() should return true when apc.enable_cli=Off (nicolas-grekas)
+ * bug #22953 #22839 - changed debug toolbar dump section to relative and use full window width (mkurzeja)
+ * bug #23086 [FrameworkBundle] Fix perf issue in CacheClearCommand::warmup() (nicolas-grekas)
+ * bug #23090 [SecurityBundle] Made 2 service aliases private (nicolas-grekas)
+ * bug #23108 [Yaml] Remove line number in deprecation notices (nicolas-grekas)
+ * bug #23098 Cache ipCheck (2.7) (gonzalovilaseca)
+ * bug #23082 [MonologBridge] Do not silence errors in ServerLogHandler::formatRecord (lyrixx)
+ * bug #23007 [HttpKernel][Debug] Fix missing trace on deprecations collected during bootstrapping & silenced errors (ogizanagi)
+ * bug #23069 [SecurityBundle] Show unique Inherited roles in profile panel (yceruto)
+
* 3.3.2 (2017-06-06)
* bug #23073 [TwigBridge] Fix namespaced classes (ogizanagi)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 6816e9794a0a2..2ea3ea63b87fd 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -20,13 +20,13 @@ Symfony is the result of the work of many people who made the code better
- Javier Eguiluz (javier.eguiluz)
- Hugo Hamon (hhamon)
- Abdellatif Ait boudad (aitboudad)
+ - Romain Neutron (romain)
- Pascal Borreli (pborreli)
- Wouter De Jong (wouterj)
- - Romain Neutron (romain)
- - Grégoire Pineau (lyrixx)
- Robin Chalas (chalas_r)
- - Joseph Bielawski (stloyd)
- Maxime Steinhausser (ogizanagi)
+ - Grégoire Pineau (lyrixx)
+ - Joseph Bielawski (stloyd)
- Karma Dordrak (drak)
- Lukas Kahwe Smith (lsmith)
- Martin Hasoň (hason)
@@ -72,8 +72,8 @@ Symfony is the result of the work of many people who made the code better
- Dariusz Górecki (canni)
- Titouan Galopin (tgalopin)
- Douglas Greenshields (shieldo)
- - Konstantin Myakshin (koc)
- Jáchym Toušek (enumag)
+ - Konstantin Myakshin (koc)
- Lee McDermott
- Brandon Turner
- Luis Cordova (cordoval)
@@ -112,10 +112,10 @@ Symfony is the result of the work of many people who made the code better
- Jacob Dreesen (jdreesen)
- Tobias Nyholm (tobias)
- Tomáš Votruba (tomas_votruba)
+ - Yonel Ceruto González (yonelceruto)
- Fabien Pennequin (fabienpennequin)
- Gordon Franke (gimler)
- Eric GELOEN (gelo)
- - Yonel Ceruto González (yonelceruto)
- Daniel Wehner (dawehner)
- Tugdual Saunier (tucksaun)
- Théo FIDRY (theofidry)
@@ -130,6 +130,7 @@ Symfony is the result of the work of many people who made the code better
- Daniel Gomes (danielcsgomes)
- Hidenori Goto (hidenorigoto)
- Guilherme Blanco (guilhermeblanco)
+ - Vincent AUBERT (vincent)
- Pablo Godel (pgodel)
- Jérémie Augustin (jaugustin)
- Andréia Bohner (andreia)
@@ -140,10 +141,10 @@ Symfony is the result of the work of many people who made the code better
- Joel Wurtz (brouznouf)
- Philipp Wahala (hifi)
- Vyacheslav Pavlov
+ - Richard van Laak (rvanlaak)
- Javier Spagnoletti (phansys)
- Richard Shank (iampersistent)
- Thomas Rabaix (rande)
- - Vincent AUBERT (vincent)
- Rouven Weßling (realityking)
- Teoh Han Hui (teohhanhui)
- Jérôme Vasseur (jvasseur)
@@ -151,7 +152,6 @@ Symfony is the result of the work of many people who made the code better
- Helmer Aaviksoo
- Grégoire Paris (greg0ire)
- Hiromi Hishida (77web)
- - Richard van Laak (rvanlaak)
- Matthieu Ouellette-Vachon (maoueh)
- Michał Pipa (michal.pipa)
- Amal Raghav (kertz)
@@ -244,6 +244,7 @@ Symfony is the result of the work of many people who made the code better
- Uwe Jäger (uwej711)
- Eugene Leonovich (rybakit)
- Filippo Tessarotto
+ - Oleg Voronkovich
- Joseph Rouff (rouffj)
- Félix Labrecque (woodspire)
- GordonsLondon
@@ -279,7 +280,6 @@ Symfony is the result of the work of many people who made the code better
- Jordan Samouh (jordansamouh)
- Chris Smith (cs278)
- Florian Klein (docteurklein)
- - Oleg Voronkovich
- Manuel Kiessling (manuelkiessling)
- Atsuhiro KUBO (iteman)
- Andrew Moore (finewolf)
@@ -389,6 +389,7 @@ Symfony is the result of the work of many people who made the code better
- Emanuele Gaspari (inmarelibero)
- Sébastien Santoro (dereckson)
- Brian King
+ - Frank de Jonge (frenkynet)
- Michel Salib (michelsalib)
- geoffrey
- Steffen Roßkamp
@@ -525,7 +526,6 @@ Symfony is the result of the work of many people who made the code better
- Romain Pierre (romain-pierre)
- Jan Behrens
- Mantas Var (mvar)
- - Frank de Jonge (frenkynet)
- Sebastian Krebs
- Jean-Christophe Cuvelier [Artack]
- Christopher Davis (chrisguitarguy)
@@ -1280,6 +1280,7 @@ Symfony is the result of the work of many people who made the code better
- ged15
- Daan van Renterghem
- Nicole Cordes
+ - Martin Kirilov
- Bram Van der Sype (brammm)
- Christopher Hertel (chertel)
- Guile (guile)
diff --git a/composer.json b/composer.json
index f91e8fc1a86b2..3b096baff3c55 100644
--- a/composer.json
+++ b/composer.json
@@ -115,7 +115,6 @@
"Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/",
"Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/",
"Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/",
- "Symfony\\Bridge\\Swiftmailer\\": "src/Symfony/Bridge/Swiftmailer/",
"Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/",
"Symfony\\Bundle\\": "src/Symfony/Bundle/",
"Symfony\\Component\\": "src/Symfony/Component/"
diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php
index c2fcb520e10fe..78d82b322568b 100644
--- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php
+++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php
@@ -63,6 +63,10 @@ public function validate($entity, Constraint $constraint)
throw new ConstraintDefinitionException('At least one field has to be specified.');
}
+ if (null === $entity) {
+ return;
+ }
+
if ($constraint->em) {
$em = $this->registry->getManager($constraint->em);
diff --git a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php
index fb258808f3836..d1d4968df4cda 100644
--- a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php
+++ b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php
@@ -51,9 +51,15 @@ public function handle(array $record)
if (!$this->socket = $this->socket ?: $this->createSocket()) {
return false === $this->bubble;
}
+ } finally {
+ restore_error_handler();
+ }
- $recordFormatted = $this->formatRecord($record);
+ $recordFormatted = $this->formatRecord($record);
+ set_error_handler(self::class.'::nullErrorHandler');
+
+ try {
if (-1 === stream_socket_sendto($this->socket, $recordFormatted)) {
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
diff --git a/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php b/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php
index 2dad6ef0095bc..c11fde9526eab 100644
--- a/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php
+++ b/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php
@@ -18,53 +18,53 @@
if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) {
class_alias('Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListener', 'Symfony\Bridge\PhpUnit\SymfonyTestsListener');
-
- return;
-}
-
-/**
- * Collects and replays skipped tests.
- *
- * @author Nicolas Grekas
- *
- * @final
- */
-class SymfonyTestsListener extends BaseTestListener
-{
- private $trait;
-
- public function __construct(array $mockedNamespaces = array())
+// Using an early return instead of a else does not work when using the PHPUnit phar due to some weird PHP behavior (the class
+// gets defined without executing the code before it and so the definition is not properly conditional)
+} else {
+ /**
+ * Collects and replays skipped tests.
+ *
+ * @author Nicolas Grekas
+ *
+ * @final
+ */
+ class SymfonyTestsListener extends BaseTestListener
{
- $this->trait = new Legacy\SymfonyTestsListenerTrait($mockedNamespaces);
- }
+ private $trait;
- public function globalListenerDisabled()
- {
- $this->trait->globalListenerDisabled();
- }
+ public function __construct(array $mockedNamespaces = array())
+ {
+ $this->trait = new Legacy\SymfonyTestsListenerTrait($mockedNamespaces);
+ }
- public function startTestSuite(TestSuite $suite)
- {
- return $this->trait->startTestSuite($suite);
- }
+ public function globalListenerDisabled()
+ {
+ $this->trait->globalListenerDisabled();
+ }
- public function addSkippedTest(Test $test, \Exception $e, $time)
- {
- return $this->trait->addSkippedTest($test, $e, $time);
- }
+ public function startTestSuite(TestSuite $suite)
+ {
+ return $this->trait->startTestSuite($suite);
+ }
- public function startTest(Test $test)
- {
- return $this->trait->startTest($test);
- }
+ public function addSkippedTest(Test $test, \Exception $e, $time)
+ {
+ return $this->trait->addSkippedTest($test, $e, $time);
+ }
- public function addWarning(Test $test, Warning $e, $time)
- {
- return $this->trait->addWarning($test, $e, $time);
- }
+ public function startTest(Test $test)
+ {
+ return $this->trait->startTest($test);
+ }
- public function endTest(Test $test, $time)
- {
- return $this->trait->endTest($test, $time);
+ public function addWarning(Test $test, Warning $e, $time)
+ {
+ return $this->trait->addWarning($test, $e, $time);
+ }
+
+ public function endTest(Test $test, $time)
+ {
+ return $this->trait->endTest($test, $time);
+ }
}
}
diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
index 51e13a843033f..f70ca33809a78 100644
--- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
@@ -302,6 +302,7 @@
{%- endblock form -%}
{%- block form_start -%}
+ {%- do form.setMethodRendered() -%}
{% set method = method|upper %}
{%- if method in ["GET", "POST"] -%}
{% set form_method = method %}
@@ -337,6 +338,20 @@
{{- form_row(child) -}}
{% endif %}
{%- endfor %}
+
+ {% if not form.methodRendered %}
+ {%- do form.setMethodRendered() -%}
+ {% set method = method|upper %}
+ {%- if method in ["GET", "POST"] -%}
+ {% set form_method = method %}
+ {%- else -%}
+ {% set form_method = "POST" %}
+ {%- endif -%}
+
+ {%- if form_method != method -%}
+
+ {%- endif -%}
+ {% endif %}
{% endblock form_rest %}
{# Support #}
diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json
index a7f1e23747536..8fe41945b72aa 100644
--- a/src/Symfony/Bridge/Twig/composer.json
+++ b/src/Symfony/Bridge/Twig/composer.json
@@ -23,7 +23,7 @@
"fig/link-util": "^1.0",
"symfony/asset": "~2.8|~3.0",
"symfony/finder": "~2.8|~3.0",
- "symfony/form": "^3.2.7",
+ "symfony/form": "^3.2.10|^3.3.3",
"symfony/http-kernel": "~3.2",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/routing": "~2.8|~3.0",
@@ -38,6 +38,9 @@
"symfony/expression-language": "~2.8|~3.0",
"symfony/web-link": "~3.3"
},
+ "conflict": {
+ "symfony/form": "<3.2.10|~3.3,<3.3.3"
+ },
"suggest": {
"symfony/finder": "",
"symfony/asset": "For using the AssetExtension",
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php
index 136b1c21fae98..dff0e245f741b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php
@@ -114,6 +114,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
$rows = array();
$copyUsed = false;
$exitCode = 0;
+ $validAssetDirs = array();
/** @var BundleInterface $bundle */
foreach ($this->getContainer()->get('kernel')->getBundles() as $bundle) {
if (!is_dir($originDir = $bundle->getPath().'/Resources/public')) {
@@ -152,6 +153,13 @@ protected function execute(InputInterface $input, OutputInterface $output)
$exitCode = 1;
$rows[] = array(sprintf('%s>', '\\' === DIRECTORY_SEPARATOR ? 'ERROR' : "\xE2\x9C\x98" /* HEAVY BALLOT X (U+2718) */), $message, $e->getMessage());
}
+ $validAssetDirs[] = $targetDir;
+ }
+ // remove the assets of the bundles that no longer exist
+ foreach (new \FilesystemIterator($bundlesDir) as $dir) {
+ if (!in_array($dir, $validAssetDirs)) {
+ $this->filesystem->remove($dir);
+ }
}
$io->table(array('', 'Bundle', 'Method / Error'), $rows);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
index e13d5b33c7d8e..2331afecb4e84 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
@@ -77,7 +77,11 @@ protected function execute(InputInterface $input, OutputInterface $output)
if ($input->getOption('no-warmup')) {
$filesystem->rename($realCacheDir, $oldCacheDir);
} else {
- @trigger_error('Calling cache:clear without the --no-warmup option is deprecated since version 3.3. Cache warmup should be done with the cache:warmup command instead.', E_USER_DEPRECATED);
+ $warning = 'Calling cache:clear without the --no-warmup option is deprecated since version 3.3. Cache warmup should be done with the cache:warmup command instead.';
+
+ @trigger_error($warning, E_USER_DEPRECATED);
+
+ $io->warning($warning);
$this->warmupCache($input, $output, $realCacheDir, $oldCacheDir);
}
@@ -158,7 +162,7 @@ protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = tr
$safeTempKernel = str_replace('\\', '\\\\', get_class($tempKernel));
$realKernelFQN = get_class($realKernel);
- foreach (Finder::create()->files()->name('*.meta')->in($warmupDir) as $file) {
+ foreach (Finder::create()->files()->depth('<3')->name('*.meta')->in($warmupDir) as $file) {
file_put_contents($file, preg_replace(
'/(C\:\d+\:)"'.$safeTempKernel.'"/',
sprintf('$1"%s"', $realKernelFQN),
@@ -170,14 +174,16 @@ protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = tr
$search = array($warmupDir, str_replace('\\', '\\\\', $warmupDir));
$replace = str_replace('\\', '/', $realCacheDir);
foreach (Finder::create()->files()->in($warmupDir) as $file) {
- $content = str_replace($search, $replace, file_get_contents($file));
- file_put_contents($file, $content);
+ $content = str_replace($search, $replace, file_get_contents($file), $count);
+ if ($count) {
+ file_put_contents($file, $content);
+ }
}
// fix references to container's class
$tempContainerClass = get_class($tempKernel->getContainer());
$realContainerClass = get_class($realKernel->getContainer());
- foreach (Finder::create()->files()->name($tempContainerClass.'*')->in($warmupDir) as $file) {
+ foreach (Finder::create()->files()->depth('<2')->name($tempContainerClass.'*')->in($warmupDir) as $file) {
$content = str_replace($tempContainerClass, $realContainerClass, file_get_contents($file));
file_put_contents($file, $content);
rename($file, str_replace(DIRECTORY_SEPARATOR.$tempContainerClass, DIRECTORY_SEPARATOR.$realContainerClass, $file));
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php
index 22f0bc6795d5b..e7d6d80a57ac7 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php
@@ -12,6 +12,7 @@
namespace Symfony\Bundle\FrameworkBundle\Command;
use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper;
+use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -112,7 +113,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
private function convertController(Route $route)
{
if ($route->hasDefault('_controller')) {
- $nameParser = $this->getContainer()->get('controller_name_converter');
+ $nameParser = new ControllerNameParser($this->getApplication()->getKernel());
try {
$route->setDefault('_controller', $nameParser->build($route->getDefault('_controller')));
} catch (\InvalidArgumentException $e) {
diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
index 6d0cee247da77..3ba3b8471edc4 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
@@ -16,7 +16,6 @@
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\RequestStack;
-use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Routing\RouterInterface;
@@ -36,7 +35,10 @@ abstract class AbstractController implements ServiceSubscriberInterface
{
use ControllerTrait;
- private $container;
+ /**
+ * @var ContainerInterface
+ */
+ protected $container;
/**
* @internal
diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php
index 355f526fdc950..3de157bd4ff3e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php
@@ -49,7 +49,19 @@ protected function createController($controller)
$controller = $this->parser->parse($controller);
}
- return parent::createController($controller);
+ $resolvedController = parent::createController($controller);
+
+ if (1 === substr_count($controller, ':') && is_array($resolvedController)) {
+ if ($resolvedController[0] instanceof ContainerAwareInterface) {
+ $resolvedController[0]->setContainer($this->container);
+ }
+
+ if ($resolvedController[0] instanceof AbstractController && null !== $previousContainer = $resolvedController[0]->setContainer($this->container)) {
+ $resolvedController[0]->setContainer($previousContainer);
+ }
+ }
+
+ return $resolvedController;
}
/**
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index 611bfad97db82..c386ebeb33709 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -435,6 +435,7 @@ private function addSessionSection(ArrayNodeDefinition $rootNode)
->scalarNode('gc_divisor')->end()
->scalarNode('gc_probability')->defaultValue(1)->end()
->scalarNode('gc_maxlifetime')->end()
+ ->booleanNode('use_strict_mode')->end()
->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end()
->integerNode('metadata_update_threshold')
->defaultValue('0')
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index b607cdd2bf563..131db0ae86343 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -702,7 +702,7 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c
// session storage
$container->setAlias('session.storage', $config['storage_id']);
$options = array();
- foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor') as $key) {
+ foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor', 'use_strict_mode') as $key) {
if (isset($config[$key])) {
$options[$key] = $config[$key];
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php
index fad4adc04434d..06aa922210569 100644
--- a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php
+++ b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php
@@ -54,8 +54,7 @@ public function __construct(HttpKernelInterface $kernel, $cacheDir = null)
protected function forward(Request $request, $raw = false, Response $entry = null)
{
$this->getKernel()->boot();
- $this->getKernel()->getContainer()->set('cache', $this);
- $this->getKernel()->getContainer()->set($this->getSurrogate()->getName(), $this->getSurrogate());
+ $this->getKernel()->getContainer()->set('cache', $this); // to be removed in 4.0?
return parent::forward($request, $raw, $entry);
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
index 29d07c5758eed..d6138133cf2ea 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
@@ -13,6 +13,7 @@
+
@@ -58,6 +59,10 @@
+
+
+
+
@@ -110,6 +115,7 @@
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php
index 6d32a37fd0ec9..597082485a0b6 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php
@@ -12,9 +12,10 @@
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
use PHPUnit\Framework\TestCase;
-use Symfony\Component\Console\Application;
+use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand;
+use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
@@ -51,16 +52,15 @@ public function testDebugInvalidRoute()
*/
private function createCommandTester()
{
- $application = new Application();
+ $application = new Application($this->getKernel());
$command = new RouterDebugCommand();
- $command->setContainer($this->getContainer());
$application->add($command);
return new CommandTester($application->find('debug:router'));
}
- private function getContainer()
+ private function getKernel()
{
$routeCollection = new RouteCollection();
$routeCollection->add('foo', new Route('foo'));
@@ -82,14 +82,25 @@ private function getContainer()
->with('router')
->will($this->returnValue(true))
;
-
$container
+ ->expects($this->any())
->method('get')
- ->will($this->returnValueMap(array(
- array('router', 1, $router),
- array('controller_name_converter', 1, $loader),
- )));
+ ->with('router')
+ ->willReturn($router)
+ ;
+
+ $kernel = $this->getMockBuilder(KernelInterface::class)->getMock();
+ $kernel
+ ->expects($this->any())
+ ->method('getContainer')
+ ->willReturn($container)
+ ;
+ $kernel
+ ->expects($this->once())
+ ->method('getBundles')
+ ->willReturn(array())
+ ;
- return $container;
+ return $kernel;
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php
index cd15b8c5e82fc..384bd7ca53079 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php
@@ -12,10 +12,11 @@
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
use PHPUnit\Framework\TestCase;
-use Symfony\Component\Console\Application;
+use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand;
use Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand;
+use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RequestContext;
@@ -45,20 +46,14 @@ public function testWithNotMatchPath()
*/
private function createCommandTester()
{
- $application = new Application();
-
- $command = new RouterMatchCommand();
- $command->setContainer($this->getContainer());
- $application->add($command);
-
- $command = new RouterDebugCommand();
- $command->setContainer($this->getContainer());
- $application->add($command);
+ $application = new Application($this->getKernel());
+ $application->add(new RouterMatchCommand());
+ $application->add(new RouterDebugCommand());
return new CommandTester($application->find('router:match'));
}
- private function getContainer()
+ private function getKernel()
{
$routeCollection = new RouteCollection();
$routeCollection->add('foo', new Route('foo'));
@@ -81,16 +76,27 @@ private function getContainer()
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock();
$container
- ->expects($this->once())
+ ->expects($this->atLeastOnce())
->method('has')
->with('router')
->will($this->returnValue(true));
- $container->method('get')
- ->will($this->returnValueMap(array(
- array('router', 1, $router),
- array('controller_name_converter', 1, $loader),
- )));
+ $container
+ ->expects($this->any())
+ ->method('get')
+ ->willReturn($router);
+
+ $kernel = $this->getMockBuilder(KernelInterface::class)->getMock();
+ $kernel
+ ->expects($this->any())
+ ->method('getContainer')
+ ->willReturn($container)
+ ;
+ $kernel
+ ->expects($this->once())
+ ->method('getBundles')
+ ->willReturn(array())
+ ;
- return $container;
+ return $kernel;
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php
index 937cbfc7286d0..6783ec25c5ab5 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php
@@ -13,7 +13,6 @@
use Psr\Container\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
-use Symfony\Component\HttpFoundation\File\File;
class AbstractControllerTest extends ControllerTraitTest
{
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php
index 7946a96e8d2c0..5880ee0186a18 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php
@@ -13,6 +13,7 @@
use Psr\Container\ContainerInterface as Psr11ContainerInterface;
use Psr\Log\LoggerInterface;
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver;
use Symfony\Component\DependencyInjection\Container;
@@ -68,6 +69,24 @@ public function testGetControllerWithBundleNotation()
$this->assertSame('testAction', $controller[1]);
}
+ public function testContainerAwareControllerGetsContainerWhenNotSet()
+ {
+ class_exists(AbstractControllerTest::class);
+
+ $controller = new ContainerAwareController();
+
+ $container = new Container();
+ $container->set(TestAbstractController::class, $controller);
+
+ $resolver = $this->createControllerResolver(null, $container);
+
+ $request = Request::create('/');
+ $request->attributes->set('_controller', TestAbstractController::class.':testAction');
+
+ $this->assertSame(array($controller, 'testAction'), $resolver->getController($request));
+ $this->assertSame($container, $controller->getContainer());
+ }
+
public function testAbstractControllerGetsContainerWhenNotSet()
{
class_exists(AbstractControllerTest::class);
@@ -86,6 +105,24 @@ class_exists(AbstractControllerTest::class);
$this->assertSame($container, $controller->setContainer($container));
}
+ public function testAbstractControllerServiceWithFcqnIdGetsContainerWhenNotSet()
+ {
+ class_exists(AbstractControllerTest::class);
+
+ $controller = new DummyController();
+
+ $container = new Container();
+ $container->set(DummyController::class, $controller);
+
+ $resolver = $this->createControllerResolver(null, $container);
+
+ $request = Request::create('/');
+ $request->attributes->set('_controller', DummyController::class.':fooAction');
+
+ $this->assertSame(array($controller, 'fooAction'), $resolver->getController($request));
+ $this->assertSame($container, $controller->getContainer());
+ }
+
public function testAbstractControllerGetsNoContainerWhenSet()
{
class_exists(AbstractControllerTest::class);
@@ -106,6 +143,26 @@ class_exists(AbstractControllerTest::class);
$this->assertSame($controllerContainer, $controller->setContainer($container));
}
+ public function testAbstractControllerServiceWithFcqnIdGetsNoContainerWhenSet()
+ {
+ class_exists(AbstractControllerTest::class);
+
+ $controller = new DummyController();
+ $controllerContainer = new Container();
+ $controller->setContainer($controllerContainer);
+
+ $container = new Container();
+ $container->set(DummyController::class, $controller);
+
+ $resolver = $this->createControllerResolver(null, $container);
+
+ $request = Request::create('/');
+ $request->attributes->set('_controller', DummyController::class.':fooAction');
+
+ $this->assertSame(array($controller, 'fooAction'), $resolver->getController($request));
+ $this->assertSame($controllerContainer, $controller->getContainer());
+ }
+
protected function createControllerResolver(LoggerInterface $logger = null, Psr11ContainerInterface $container = null, ControllerNameParser $parser = null)
{
if (!$parser) {
@@ -152,3 +209,15 @@ public function __invoke()
{
}
}
+
+class DummyController extends AbstractController
+{
+ public function getContainer()
+ {
+ return $this->container;
+ }
+
+ public function fooAction()
+ {
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php
index 58a4340e6979f..2b2b3f45f0a8d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php
@@ -13,6 +13,9 @@
'esi' => array(
'enabled' => true,
),
+ 'ssi' => array(
+ 'enabled' => true,
+ ),
'profiler' => array(
'only_exceptions' => true,
'enabled' => false,
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml
index 5c6a221c18489..81f7823eecb87 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml
@@ -12,6 +12,7 @@
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml
index f471372097c15..fed4fa7d3e65f 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml
@@ -8,6 +8,8 @@ framework:
http_method_override: false
esi:
enabled: true
+ ssi:
+ enabled: true
profiler:
only_exceptions: true
enabled: false
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index 4f86c85d4f8fb..eee6169ae15da 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -134,6 +134,13 @@ public function testEsi()
$this->assertTrue($container->hasDefinition('esi'), '->registerEsiConfiguration() loads esi.xml');
}
+ public function testSsi()
+ {
+ $container = $this->createContainerFromFile('full');
+
+ $this->assertTrue($container->hasDefinition('ssi'), '->registerSsiConfiguration() loads ssi.xml');
+ }
+
public function testEnabledProfiler()
{
$container = $this->createContainerFromFile('profiler');
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php
index 2861297fe0256..a1c50e59fdb90 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php
@@ -68,6 +68,6 @@ protected static function createKernel(array $options = array())
protected static function getVarDir()
{
- return substr(strrchr(get_called_class(), '\\'), 1);
+ return 'FB'.substr(strrchr(get_called_class(), '\\'), 1);
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php
index ef6fa9330b0d8..6d710a76e8819 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php
@@ -293,6 +293,51 @@ public function testInvalidOptions()
(new Translator($container, new MessageSelector(), 'en', array(), array('foo' => 'bar')));
}
+ /** @dataProvider getDebugModeAndCacheDirCombinations */
+ public function testResourceFilesOptionLoadsBeforeOtherAddedResources($debug, $enableCache)
+ {
+ $someCatalogue = $this->getCatalogue('some_locale', array());
+
+ $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
+
+ $loader->expects($this->at(0))
+ ->method('load')
+ /* The "messages.some_locale.loader" is passed via the resource_file option and shall be loaded first */
+ ->with('messages.some_locale.loader', 'some_locale', 'messages')
+ ->willReturn($someCatalogue);
+
+ $loader->expects($this->at(1))
+ ->method('load')
+ /* This resource is added by an addResource() call and shall be loaded after the resource_files */
+ ->with('second_resource.some_locale.loader', 'some_locale', 'messages')
+ ->willReturn($someCatalogue);
+
+ $options = array(
+ 'resource_files' => array('some_locale' => array('messages.some_locale.loader')),
+ 'debug' => $debug,
+ );
+
+ if ($enableCache) {
+ $options['cache_dir'] = $this->tmpDir;
+ }
+
+ /** @var Translator $translator */
+ $translator = $this->createTranslator($loader, $options);
+ $translator->addResource('loader', 'second_resource.some_locale.loader', 'some_locale', 'messages');
+
+ $translator->trans('some_message', array(), null, 'some_locale');
+ }
+
+ public function getDebugModeAndCacheDirCombinations()
+ {
+ return array(
+ array(false, false),
+ array(true, false),
+ array(false, true),
+ array(true, true),
+ );
+ }
+
protected function getCatalogue($locale, $messages, $resources = array())
{
$catalogue = new MessageCatalogue($locale);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
index 2b83e14b10e66..d18a3b81f04ef 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
@@ -39,6 +39,14 @@ class Translator extends BaseTranslator implements WarmableInterface
*/
private $resourceLocales;
+ /**
+ * Holds parameters from addResource() calls so we can defer the actual
+ * parent::addResource() calls until initialize() is executed.
+ *
+ * @var array
+ */
+ private $resources = array();
+
/**
* Constructor.
*
@@ -80,9 +88,7 @@ public function __construct(ContainerInterface $container, MessageSelector $sele
$this->options = array_merge($this->options, $options);
$this->resourceLocales = array_keys($this->options['resource_files']);
- if (null !== $this->options['cache_dir'] && $this->options['debug']) {
- $this->loadResources();
- }
+ $this->addResourceFiles($this->options['resource_files']);
parent::__construct($defaultLocale, $selector, $this->options['cache_dir'], $this->options['debug']);
}
@@ -108,6 +114,11 @@ public function warmUp($cacheDir)
}
}
+ public function addResource($format, $resource, $locale, $domain = null)
+ {
+ $this->resources[] = array($format, $resource, $locale, $domain);
+ }
+
/**
* {@inheritdoc}
*/
@@ -119,7 +130,12 @@ protected function initializeCatalogue($locale)
protected function initialize()
{
- $this->loadResources();
+ foreach ($this->resources as $key => $params) {
+ list($format, $resource, $locale, $domain) = $params;
+ parent::addResource($format, $resource, $locale, $domain);
+ }
+ $this->resources = array();
+
foreach ($this->loaderIds as $id => $aliases) {
foreach ($aliases as $alias) {
$this->addLoader($alias, $this->container->get($id));
@@ -127,14 +143,13 @@ protected function initialize()
}
}
- private function loadResources()
+ private function addResourceFiles($filesByLocale)
{
- foreach ($this->options['resource_files'] as $locale => $files) {
+ foreach ($filesByLocale as $locale => $files) {
foreach ($files as $key => $file) {
// filename is domain.locale.format
list($domain, $locale, $format) = explode('.', basename($file), 3);
$this->addResource($format, $file, $locale, $domain);
- unset($this->options['resource_files'][$locale][$key]);
}
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php
index 6831e94d047af..9c0de7d2cda1a 100644
--- a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php
+++ b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php
@@ -118,7 +118,7 @@ public function collect(Request $request, Response $response, \Exception $except
'logout_url' => $logoutUrl,
'user' => $token->getUsername(),
'roles' => array_map(function (RoleInterface $role) { return $role->getRole(); }, $assignedRoles),
- 'inherited_roles' => array_map(function (RoleInterface $role) { return $role->getRole(); }, $inheritedRoles),
+ 'inherited_roles' => array_unique(array_map(function (RoleInterface $role) { return $role->getRole(); }, $inheritedRoles)),
'supports_role_hierarchy' => null !== $this->roleHierarchy,
);
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
index 4b7dba5e45be8..79435ff5d4619 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
@@ -19,7 +19,7 @@
%security.access.always_authenticate_before_granting%
-
+
@@ -59,7 +59,7 @@
-
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
index 073d0d869d8f0..9cd6ec4d78db2 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
@@ -222,7 +222,7 @@
{% for voter in collector.voters %}
{{ loop.index }}
-
{{ voter }}
+
{{ profiler_dump(voter) }}
{% endfor %}
diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
index 55272ec3043bd..af31a0fb60026 100644
--- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
+++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
@@ -12,6 +12,7 @@
namespace Symfony\Bundle\SecurityBundle\Security;
use Psr\Container\ContainerInterface;
+use Symfony\Bundle\SecurityBundle\Security\FirewallContext;
use Symfony\Component\Security\Http\FirewallMapInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -141,15 +142,27 @@ public function getFirewallConfig(Request $request)
return $context->getConfig();
}
+ /**
+ * @return FirewallContext
+ */
private function getFirewallContext(Request $request)
{
- if ($this->contexts->contains($request)) {
- return $this->contexts[$request];
+ if ($request->attributes->has('_firewall_context')) {
+ $storedContextId = $request->attributes->get('_firewall_context');
+ foreach ($this->map as $contextId => $requestMatcher) {
+ if ($contextId === $storedContextId) {
+ return $this->container->get($contextId);
+ }
+ }
+
+ $request->attributes->remove('_firewall_context');
}
foreach ($this->map as $contextId => $requestMatcher) {
if (null === $requestMatcher || $requestMatcher->matches($request)) {
- return $this->contexts[$request] = $this->container->get($contextId);
+ $request->attributes->set('_firewall_context', $contextId);
+
+ return $this->container->get($contextId);
}
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php
index f155f901c9c3c..de30be687426b 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php
@@ -164,6 +164,11 @@ public function provideRoles()
array('ROLE_ADMIN'),
array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'),
),
+ array(
+ array('ROLE_ADMIN', 'ROLE_OPERATOR'),
+ array('ROLE_ADMIN', 'ROLE_OPERATOR'),
+ array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'),
+ ),
);
}
@@ -171,6 +176,7 @@ private function getRoleHierarchy()
{
return new RoleHierarchy(array(
'ROLE_ADMIN' => array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'),
+ 'ROLE_OPERATOR' => array('ROLE_USER'),
));
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php
index 8bace799a37a8..bb98a2a065ca0 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php
@@ -68,6 +68,6 @@ protected static function createKernel(array $options = array())
protected static function getVarDir()
{
- return substr(strrchr(get_called_class(), '\\'), 1);
+ return 'SB'.substr(strrchr(get_called_class(), '\\'), 1);
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php
new file mode 100644
index 0000000000000..f9047cfdd233e
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallMapTest.php
@@ -0,0 +1,90 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\SecurityBundle\Tests\Security;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Bundle\SecurityBundle\Security\FirewallConfig;
+use Symfony\Bundle\SecurityBundle\Security\FirewallContext;
+use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
+use Symfony\Component\DependencyInjection\Container;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestMatcherInterface;
+use Symfony\Component\Security\Core\User\UserCheckerInterface;
+use Symfony\Component\Security\Http\Firewall\ExceptionListener;
+use Symfony\Component\Security\Http\Firewall\ListenerInterface;
+
+class FirewallMapTest extends TestCase
+{
+ const ATTRIBUTE_FIREWALL_CONTEXT = '_firewall_context';
+
+ public function testGetListenersWithEmptyMap()
+ {
+ $request = new Request();
+
+ $map = array();
+ $container = $this->getMockBuilder(Container::class)->getMock();
+ $container->expects($this->never())->method('get');
+
+ $firewallMap = new FirewallMap($container, $map);
+
+ $this->assertEquals(array(array(), null), $firewallMap->getListeners($request));
+ $this->assertNull($firewallMap->getFirewallConfig($request));
+ $this->assertFalse($request->attributes->has(self::ATTRIBUTE_FIREWALL_CONTEXT));
+ }
+
+ public function testGetListenersWithInvalidParameter()
+ {
+ $request = new Request();
+ $request->attributes->set(self::ATTRIBUTE_FIREWALL_CONTEXT, 'foo');
+
+ $map = array();
+ $container = $this->getMockBuilder(Container::class)->getMock();
+ $container->expects($this->never())->method('get');
+
+ $firewallMap = new FirewallMap($container, $map);
+
+ $this->assertEquals(array(array(), null), $firewallMap->getListeners($request));
+ $this->assertNull($firewallMap->getFirewallConfig($request));
+ $this->assertFalse($request->attributes->has(self::ATTRIBUTE_FIREWALL_CONTEXT));
+ }
+
+ public function testGetListeners()
+ {
+ $request = new Request();
+
+ $firewallContext = $this->getMockBuilder(FirewallContext::class)->disableOriginalConstructor()->getMock();
+
+ $firewallConfig = new FirewallConfig('main', $this->getMockBuilder(UserCheckerInterface::class)->getMock());
+ $firewallContext->expects($this->once())->method('getConfig')->willReturn($firewallConfig);
+
+ $listener = $this->getMockBuilder(ListenerInterface::class)->getMock();
+ $firewallContext->expects($this->once())->method('getListeners')->willReturn(array($listener));
+
+ $exceptionListener = $this->getMockBuilder(ExceptionListener::class)->disableOriginalConstructor()->getMock();
+ $firewallContext->expects($this->once())->method('getExceptionListener')->willReturn($exceptionListener);
+
+ $matcher = $this->getMockBuilder(RequestMatcherInterface::class)->getMock();
+ $matcher->expects($this->once())
+ ->method('matches')
+ ->with($request)
+ ->willReturn(true);
+
+ $container = $this->getMockBuilder(Container::class)->getMock();
+ $container->expects($this->exactly(2))->method('get')->willReturn($firewallContext);
+
+ $firewallMap = new FirewallMap($container, array('security.firewall.map.context.foo' => $matcher));
+
+ $this->assertEquals(array(array($listener), $exceptionListener), $firewallMap->getListeners($request));
+ $this->assertEquals($firewallConfig, $firewallMap->getFirewallConfig($request));
+ $this->assertEquals('security.firewall.map.context.foo', $request->attributes->get(self::ATTRIBUTE_FIREWALL_CONTEXT));
+ }
+}
diff --git a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php
index d648d63dd5688..f2db82b0995b0 100644
--- a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php
+++ b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php
@@ -72,7 +72,7 @@ public function showAction(Request $request, FlattenException $exception, DebugL
'logger' => $logger,
'currentContent' => $currentContent,
)
- ));
+ ), 200, array('Content-Type' => $request->getMimeType($request->getRequestFormat()) ?: 'text/html'));
}
/**
diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php
index 0bd1f9049f67f..31d1b758eff56 100644
--- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php
+++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php
@@ -61,10 +61,7 @@ public function process(ContainerBuilder $container)
$container->getDefinition('twig.extension.httpkernel')->addTag('twig.extension');
// inject Twig in the hinclude service if Twig is the only registered templating engine
- if (
- !$container->hasParameter('templating.engines')
- || array('twig') == $container->getParameter('templating.engines')
- ) {
+ if ((!$container->hasParameter('templating.engines') || array('twig') == $container->getParameter('templating.engines')) && $container->hasDefinition('fragment.renderer.hinclude')) {
$container->getDefinition('fragment.renderer.hinclude')
->addTag('kernel.fragment_renderer', array('alias' => 'hinclude'))
->replaceArgument(0, new Reference('twig'))
@@ -89,6 +86,7 @@ public function process(ContainerBuilder $container)
$twigLoader->clearTag('twig.loader');
} else {
$container->setAlias('twig.loader.filesystem', new Alias('twig.loader.native_filesystem', false));
+ $container->removeDefinition('templating.engine.twig');
}
if ($container->has('assets.packages')) {
diff --git a/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php b/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php
index 2b9b2502ae83d..a93a9afc58ca3 100644
--- a/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php
+++ b/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php
@@ -76,7 +76,6 @@ protected function findTemplate($template, $throw = true)
}
$file = null;
- $previous = null;
try {
$file = parent::findTemplate($logicalName);
} catch (LoaderError $e) {
diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig
index 4b35e4fad77db..8ed5226ad5ffa 100644
--- a/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig
+++ b/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig
@@ -13,6 +13,7 @@
+ {% if exception.message is not empty %}