From 09d8274fd8337cdb3fae779a67e22b498ab3ae2e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 30 Nov 2017 08:55:59 -0800 Subject: [PATCH 01/49] updated CHANGELOG for 3.4.0 --- CHANGELOG-3.4.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG-3.4.md b/CHANGELOG-3.4.md index 2e91b3fd9dd7f..70a0155445758 100644 --- a/CHANGELOG-3.4.md +++ b/CHANGELOG-3.4.md @@ -7,6 +7,31 @@ in 3.4 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.4.0...v3.4.1 +* 3.4.0 (2017-11-30) + + * bug #25220 [HttpFoundation] Add Session::isEmpty(), fix MockFileSessionStorage to behave like the native one (nicolas-grekas) + * bug #25209 [VarDumper] Dont use empty(), it chokes on eg GMP objects (nicolas-grekas) + * bug #25200 [HttpKernel] Arrays with scalar values passed to ESI fragment renderer throw deprecation notice (Simperfit) + * bug #25201 [HttpKernel] Add a better error messages when passing a private or non-tagged controller (Simperfit) + * bug #25217 [Dotenv] Changed preg_match flags from null to 0 (deekthesqueak) + * bug #25180 [DI] Fix circular reference when using setters (nicolas-grekas) + * bug #25204 [DI] Clear service reference graph (nicolas-grekas) + * bug #25203 [DI] Fix infinite loop in InlineServiceDefinitionsPass (nicolas-grekas) + * bug #25185 [Serializer] Do not cache attributes if `attributes` in context (sroze) + * bug #25190 [HttpKernel] Keep legacy container files for concurrent requests (nicolas-grekas) + * bug #25182 [HttpFoundation] AutExpireFlashBag should not clear new flashes (Simperfit, sroze) + * bug #25174 [Translation] modify definitions only if the do exist (xabbuh) + * bug #25179 [FrameworkBundle][Serializer] Remove YamlEncoder definition if Yaml component isn't installed (ogizanagi) + * bug #25160 [DI] Prevent a ReflectionException during cache:clear when the parent class doesn't exist (dunglas) + * bug #25163 [DI] Fix tracking of env vars in exceptions (nicolas-grekas) + * bug #25162 [HttpKernel] Read $_ENV when checking SHELL_VERBOSITY (nicolas-grekas) + * bug #25158 [DI] Remove unreachable code (GawainLynch) + * bug #25152 [Form] Don't rely on `Symfony\Component\HttpFoundation\File\File` if http-foundation isn't in FileType (issei-m) + * bug #24987 [Console] Fix global console flag when used in chain (Simperfit) + * bug #25137 Adding checks for the expression language (weaverryan) + * bug #25151 [FrameworkBundle] Automatically enable the CSRF protection if CSRF manager exists (sroze) + * bug #25043 [Yaml] added ability for substitute aliases when mapping is on single line (Michał Strzelecki, xabbuh) + * 3.4.0-RC2 (2017-11-24) * bug #25146 [DI] Dont resolve envs in service ids (nicolas-grekas) From 72538ff07f058a6ee8f85d2876dfb4b530f9912d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 30 Nov 2017 08:56:05 -0800 Subject: [PATCH 02/49] updated VERSION for 3.4.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 789eb268cf5a8..bbce191552b7d 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -67,12 +67,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '3.4.0-DEV'; + const VERSION = '3.4.0'; const VERSION_ID = 30400; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021'; From 76d356f36a692dd8ad796de363484c97d6731d1f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 30 Nov 2017 09:39:42 -0800 Subject: [PATCH 03/49] bumped Symfony version to 3.4.1 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index bbce191552b7d..398599c6cf225 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -67,12 +67,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '3.4.0'; - const VERSION_ID = 30400; + const VERSION = '3.4.1-DEV'; + const VERSION_ID = 30401; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; - const RELEASE_VERSION = 0; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 1; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021'; From 186fd5c9cf597bdd23ca4154035945e25ec1d74e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 30 Nov 2017 09:51:21 -0800 Subject: [PATCH 04/49] bumped Symfony version to 4.0.1 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 2a6d8a4fd17e8..1f179525f9b38 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -63,12 +63,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.0.0'; - const VERSION_ID = 40000; + const VERSION = '4.0.1-DEV'; + const VERSION_ID = 40001; const MAJOR_VERSION = 4; const MINOR_VERSION = 0; - const RELEASE_VERSION = 0; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 1; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '07/2018'; const END_OF_LIFE = '01/2019'; From 159fedcef69e236ca83a36cfa3c60f404e5837eb Mon Sep 17 00:00:00 2001 From: Alex Rock Ancelet Date: Thu, 30 Nov 2017 20:02:12 +0100 Subject: [PATCH 05/49] [HttpKernel] Fix issue when resetting DumpDataCollector --- .../Component/HttpKernel/DataCollector/DumpDataCollector.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index f9730b1485045..d0ca671e056b4 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -166,7 +166,9 @@ public function collect(Request $request, Response $response, \Exception $except public function reset() { - $this->stopwatch->reset(); + if ($this->stopwatch) { + $this->stopwatch->reset(); + } $this->data = array(); $this->dataCount = 0; $this->isCollected = false; From 355bbd19fa8d6596578adbfdccb57cfd24c9df29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Thu, 30 Nov 2017 20:40:25 +0100 Subject: [PATCH 06/49] Use a more specific file for detecting the bridge When using bin as a Composer bin-dir, this will break because both composer.json and bin/simple-phpunit will exist relatively to the current directory. This change checks for a non-binary file specific to the bridge that will not have this issue. Fixes #25228 --- src/Symfony/Bridge/PhpUnit/bin/simple-phpunit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit index 562d018124a37..fbac4e1fd32c8 100755 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit @@ -27,7 +27,7 @@ if (PHP_VERSION_ID >= 70200) { } $root = __DIR__; -while (!file_exists($root.'/composer.json') || file_exists($root.'/bin/simple-phpunit')) { +while (!file_exists($root.'/composer.json') || file_exists($root.'/DeprecationErrorHandler.php')) { if ($root === dirname($root)) { break; } From 85058949883eee0b0deafbf5b090704adad28750 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Thu, 30 Nov 2017 23:22:28 -0500 Subject: [PATCH 07/49] Fix collision between view properties and form fields --- .../Bridge/Twig/Extension/FormExtension.php | 7 +++++++ .../views/Form/bootstrap_3_layout.html.twig | 4 ++-- .../views/Form/form_div_layout.html.twig | 4 ++-- .../views/Form/form_table_layout.html.twig | 2 +- .../Extension/FormExtensionDivLayoutTest.php | 16 ++++++++++++++++ 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Extension/FormExtension.php b/src/Symfony/Bridge/Twig/Extension/FormExtension.php index 053b1d4a6b336..aaed8221c1fc8 100644 --- a/src/Symfony/Bridge/Twig/Extension/FormExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/FormExtension.php @@ -14,6 +14,7 @@ use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; use Symfony\Bridge\Twig\Form\TwigRendererInterface; use Symfony\Component\Form\Extension\Core\View\ChoiceView; +use Symfony\Component\Form\FormView; use Twig\Environment; use Twig\Extension\AbstractExtension; use Twig\Extension\InitRuntimeInterface; @@ -97,6 +98,7 @@ public function getTests() { return array( new TwigTest('selectedchoice', array($this, 'isSelectedChoice')), + new TwigTest('rootform', array($this, 'isRootForm')), ); } @@ -156,6 +158,11 @@ public function isSelectedChoice(ChoiceView $choice, $selectedValue) return $choice->value === $selectedValue; } + public function isRootForm(FormView $formView) + { + return null === $formView->parent; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index 2efc0c4d4a213..e3c76349648e5 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -238,12 +238,12 @@ {% block form_errors -%} {% if errors|length > 0 -%} - {% if form.parent %}{% else %}
{% endif %} + {% if form is not rootform %}{% else %}
{% endif %}
    {%- for error in errors -%}
  • {{ error.message }}
  • {%- endfor -%}
- {% if form.parent %}{% else %}
{% endif %} + {% if form is not rootform %}
{% else %}
{% endif %} {%- endif %} {%- endblock form_errors %} 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 a27c81dd495ae..ad00a502a0f71 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 @@ -15,7 +15,7 @@ {%- block form_widget_compound -%}
- {%- if form.parent is empty -%} + {%- if form is rootform -%} {{ form_errors(form) }} {%- endif -%} {{- block('form_rows') -}} @@ -303,7 +303,7 @@ {% endif %} {%- endfor %} - {% if not form.methodRendered and form.parent is null %} + {% if not form.methodRendered and form is rootform %} {%- do form.setMethodRendered() -%} {% set method = method|upper %} {%- if method in ["GET", "POST"] -%} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig index c7b3a4365b51b..39274c6c8d058 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig @@ -31,7 +31,7 @@ {%- block form_widget_compound -%} - {%- if form.parent is empty and errors|length > 0 -%} + {%- if form is rootform and errors|length > 0 -%}
{{- form_errors(form) -}} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php index e2fbb48ce0339..e11daf1831349 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php @@ -146,6 +146,22 @@ public function testIsChoiceSelected($expected, $choice, $value) $this->assertSame($expected, $this->extension->isSelectedChoice($choice, $value)); } + public function isRootFormProvider() + { + return array( + array(true, new FormView()), + array(false, new FormView(new FormView())), + ); + } + + /** + * @dataProvider isRootFormProvider + */ + public function testIsRootForm($expected, FormView $formView) + { + $this->assertSame($expected, $this->extension->isRootForm($formView)); + } + protected function renderForm(FormView $view, array $vars = array()) { return (string) $this->extension->renderer->renderBlock($view, 'form', $vars); From b1ab7d0050be4f98bf1a5dc7cf386975b25dcb4a Mon Sep 17 00:00:00 2001 From: Tim Strehle Date: Thu, 30 Nov 2017 21:28:52 +0100 Subject: [PATCH 08/49] [WebProfilerBundle], [TwigBundle] Fix Profiler breaking XHTML pages (Content-Type: application/xhtml+xml) --- src/Symfony/Bundle/TwigBundle/Resources/views/base_js.html.twig | 2 +- .../Resources/views/Profiler/base_js.html.twig | 2 +- .../Resources/views/Profiler/toolbar_js.html.twig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/base_js.html.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/base_js.html.twig index ccabdc968fb2e..0ac832ee0610d 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/base_js.html.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/base_js.html.twig @@ -1,6 +1,6 @@ {# This file is based on WebProfilerBundle/Resources/views/Profiler/base_js.html.twig. If you make any change in this file, verify the same change is needed in the other file. #} -/*/*/*/* {{ include('@WebProfiler/Profiler/toolbar.css.twig', { 'position': position, 'floatable': true }) }} -/*/* Date: Fri, 1 Dec 2017 11:47:04 +0100 Subject: [PATCH 09/49] do not eagerly filter comment lines Trying to be clever by filtering commented lines inside `getNextEmbedBlock()` does not work as expected. The `#` may as well be part of a multi-line quoted string where it must not be treated as the beginning of a comment. Thus, we only must ensure that a comment-like line does not skip the process of getting the next line of the embed block. --- src/Symfony/Component/Yaml/Parser.php | 15 ++------------- .../Component/Yaml/Tests/ParserTest.php | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 5093d7cd1297f..702df16f0aec0 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -601,21 +601,10 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false) continue; } - // we ignore "comment" lines only when we are not inside a scalar block - if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) { - // remember ignored comment lines (they are used later in nested - // parser calls to determine real line numbers) - // - // CAUTION: beware to not populate the global property here as it - // will otherwise influence the getRealCurrentLineNb() call here - // for consecutive comment lines and subsequent embedded blocks - $this->locallySkippedLineNumbers[] = $this->getRealCurrentLineNb(); - - continue; - } - if ($indent >= $newIndent) { $data[] = substr($this->currentLine, $newIndent); + } elseif ($this->isCurrentLineComment()) { + $data[] = $this->currentLine; } elseif (0 == $indent) { $this->moveToPreviousLine(); diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index f98ac7f51c97b..7a3c405714328 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1554,6 +1554,24 @@ public function testMultiLineQuotedStringWithTrailingBackslash() $this->assertSame(array('foobar' => 'foobar'), $this->parser->parse($yaml)); } + public function testCommentCharactersInMultiLineQuotedStrings() + { + $yaml = << array( + 'foobar' => 'foo #bar', + 'bar' => 'baz', + ), + ); + + $this->assertSame($expected, $this->parser->parse($yaml)); + } + public function testParseMultiLineUnquotedString() { $yaml = << Date: Fri, 1 Dec 2017 14:14:10 +0100 Subject: [PATCH 10/49] [DI] Fix false-positive circular exception --- .../DependencyInjection/Dumper/PhpDumper.php | 2 +- .../Tests/ContainerBuilderTest.php | 3 ++ .../Tests/Dumper/PhpDumperTest.php | 3 ++ .../containers/container_almost_circular.php | 9 +++++ .../php/services_almost_circular_private.php | 20 +++++++++++ .../php/services_almost_circular_public.php | 36 +++++++++++++++++++ 6 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index fb933ce757f5a..2dbdd0acd6ce7 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -478,7 +478,7 @@ private function addServiceInlinedDefinitions($id, Definition $definition, \SplO // $b = new ServiceB(); // $a = new ServiceA(ServiceB $b); // $b->setServiceA(ServiceA $a); - if ($this->hasReference($id, array($def->getArguments(), $def->getFactory()))) { + if (isset($inlinedDefinition[$definition]) && $this->hasReference($id, array($def->getArguments(), $def->getFactory()))) { throw new ServiceCircularReferenceException($id, array($id)); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index bbc2c01ef075a..4fc89eb0b472a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1263,6 +1263,9 @@ public function testAlmostCircular($visibility) $this->assertSame($foo2, $foo2->bar->foobar->foo); $this->assertSame(array(), (array) $container->get('foobar4')); + + $foo5 = $container->get('foo5'); + $this->assertSame($foo5, $foo5->bar->foo); } public function provideAlmostCircular() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index b70dc7b172b28..7455dd6cde681 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -782,6 +782,9 @@ public function testAlmostCircular($visibility) $this->assertSame($foo2, $foo2->bar->foobar->foo); $this->assertSame(array(), (array) $container->get('foobar4')); + + $foo5 = $container->get('foo5'); + $this->assertSame($foo5, $foo5->bar->foo); } public function provideAlmostCircular() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php index 6d73b3ec6c774..dff937ccdbb7f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php @@ -46,4 +46,13 @@ $container->register('foobar4', 'stdClass')->setPublic(true) ->addArgument(new Reference('foo4')); +// loop on the constructor of a setter-injected dep with property + +$container->register('foo5', 'stdClass')->setPublic(true) + ->setProperty('bar', new Reference('bar5')); + +$container->register('bar5', 'stdClass')->setPublic($public) + ->addArgument(new Reference('foo5')) + ->setProperty('foo', new Reference('foo5')); + return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php index 1707f8912781f..ea0e155f30735 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php @@ -27,6 +27,7 @@ public function __construct() 'bar3' => 'getBar3Service', 'foo' => 'getFooService', 'foo2' => 'getFoo2Service', + 'foo5' => 'getFoo5Service', 'foobar4' => 'getFoobar4Service', ); @@ -39,6 +40,7 @@ public function getRemovedIds() 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, 'bar' => true, + 'bar5' => true, 'foo4' => true, 'foobar' => true, 'foobar2' => true, @@ -125,6 +127,24 @@ protected function getFoo2Service() return $this->services['foo2'] = new \FooCircular($a); } + /** + * Gets the public 'foo5' shared service. + * + * @return \stdClass + */ + protected function getFoo5Service() + { + $this->services['foo5'] = $instance = new \stdClass(); + + $a = new \stdClass(${($_ = isset($this->services['foo5']) ? $this->services['foo5'] : $this->getFoo5Service()) && false ?: '_'}); + + $a->foo = $instance; + + $instance->bar = $a; + + return $instance; + } + /** * Gets the public 'foobar4' shared service. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php index 5d4d9b0f29cf9..36961d1c853d5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php @@ -25,9 +25,11 @@ public function __construct() $this->methodMap = array( 'bar' => 'getBarService', 'bar3' => 'getBar3Service', + 'bar5' => 'getBar5Service', 'foo' => 'getFooService', 'foo2' => 'getFoo2Service', 'foo4' => 'getFoo4Service', + 'foo5' => 'getFoo5Service', 'foobar' => 'getFoobarService', 'foobar2' => 'getFoobar2Service', 'foobar3' => 'getFoobar3Service', @@ -93,6 +95,26 @@ protected function getBar3Service() return $instance; } + /** + * Gets the public 'bar5' shared service. + * + * @return \stdClass + */ + protected function getBar5Service() + { + $a = ${($_ = isset($this->services['foo5']) ? $this->services['foo5'] : $this->getFoo5Service()) && false ?: '_'}; + + if (isset($this->services['bar5'])) { + return $this->services['bar5']; + } + + $this->services['bar5'] = $instance = new \stdClass($a); + + $instance->foo = $a; + + return $instance; + } + /** * Gets the public 'foo' shared service. * @@ -139,6 +161,20 @@ protected function getFoo4Service() return $instance; } + /** + * Gets the public 'foo5' shared service. + * + * @return \stdClass + */ + protected function getFoo5Service() + { + $this->services['foo5'] = $instance = new \stdClass(); + + $instance->bar = ${($_ = isset($this->services['bar5']) ? $this->services['bar5'] : $this->getBar5Service()) && false ?: '_'}; + + return $instance; + } + /** * Gets the public 'foobar' shared service. * From de137d286ebe08d87fe15125a5767b3dd9cb0047 Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Fri, 1 Dec 2017 15:32:30 +0100 Subject: [PATCH 11/49] [FrameworkBundle] Rename getDotEnvVars to getDotenvVars --- src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php index 3c5ba3e93d3cf..35e9d73cd74a6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php @@ -89,7 +89,7 @@ protected function execute(InputInterface $input, OutputInterface $output) array('Xdebug', extension_loaded('xdebug') ? 'true' : 'false'), ); - if ($dotenv = self::getDotEnvVars()) { + if ($dotenv = self::getDotenvVars()) { $rows = array_merge($rows, array( new TableSeparator(), array('Environment (.env)'), @@ -128,7 +128,7 @@ private static function isExpired($date) return false !== $date && new \DateTime() > $date->modify('last day of this month 23:59:59'); } - private static function getDotEnvVars() + private static function getDotenvVars() { $vars = array(); foreach (explode(',', getenv('SYMFONY_DOTENV_VARS')) as $name) { From 93c0b3801c630a179b9db53dacb68ef71733c2bf Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 1 Dec 2017 12:51:22 +0100 Subject: [PATCH 12/49] [DI] Add missing deprecation when fetching private services from ContainerBuilder --- .../DependencyInjection/TwigExtensionTest.php | 3 ++- .../DependencyInjection/ContainerBuilder.php | 17 ++++++++--------- .../Tests/ContainerBuilderTest.php | 1 + .../Tests/Fixtures/php/services9_as_files.txt | 2 ++ .../Tests/Fixtures/php/services9_compiled.php | 2 ++ .../Tests/Fixtures/php/services_env_in_id.php | 1 + .../Fixtures/php/services_inline_requires.php | 1 + .../Fixtures/php/services_legacy_privates.php | 8 ++++++++ .../Tests/Fixtures/php/services_locator.php | 1 + .../Fixtures/php/services_private_frozen.php | 1 + .../php/services_private_in_expression.php | 1 + .../Tests/Fixtures/php/services_subscriber.php | 1 + .../Fixtures/php/services_uninitialized_ref.php | 1 + .../Controller/ContainerControllerResolver.php | 2 +- .../ContainerControllerResolverTest.php | 2 +- 15 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php index 2faaaa38fcb13..0039dc789ed75 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php @@ -232,9 +232,10 @@ public function testStopwatchExtensionAvailability($debug, $stopwatchEnabled, $e } $container->registerExtension(new TwigExtension()); $container->loadFromExtension('twig', array()); + $container->setAlias('test.twig.extension.debug.stopwatch', 'twig.extension.debug.stopwatch')->setPublic(true); $this->compileContainer($container); - $tokenParsers = $container->get('twig.extension.debug.stopwatch')->getTokenParsers(); + $tokenParsers = $container->get('test.twig.extension.debug.stopwatch')->getTokenParsers(); $stopwatchIsAvailable = new \ReflectionProperty($tokenParsers[0], 'stopwatchIsAvailable'); $stopwatchIsAvailable->setAccessible(true); diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index b96426d487092..086bfdafe2c18 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -556,15 +556,8 @@ public function has($id) */ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { - if ($this->isCompiled()) { - $id = $this->normalizeId($id); - - if (isset($this->definitions[$id]) && $this->definitions[$id]->isPrivate()) { - @trigger_error(sprintf('Fetching the "%s" private service is deprecated and will fail in Symfony 4.0. Make the service public instead.', $id), E_USER_DEPRECATED); - } - if (isset($this->aliasDefinitions[$id]) && $this->aliasDefinitions[$id]->isPrivate()) { - @trigger_error(sprintf('Fetching the "%s" private alias is deprecated and will fail in Symfony 4.0. Make the alias public instead.', $id), E_USER_DEPRECATED); - } + if ($this->isCompiled() && isset($this->removedIds[$id = $this->normalizeId($id)])) { + @trigger_error(sprintf('Fetching the "%s" private service or alias is deprecated since Symfony 3.4 and will fail in 4.0. Make it public instead.', $id), E_USER_DEPRECATED); } return $this->doGet($id, $invalidBehavior); @@ -776,6 +769,12 @@ public function compile(/*$resolveEnvPlaceholders = false*/) } parent::compile(); + + foreach ($this->definitions + $this->aliasDefinitions as $id => $definition) { + if (!$definition->isPublic() || $definition->isPrivate()) { + $this->removedIds[$id] = true; + } + } } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index bbc2c01ef075a..3af3444c28bd0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -733,6 +733,7 @@ public function testEnvInId() PsrContainerInterface::class => true, ContainerInterface::class => true, 'baz_%env(BAR)%' => true, + 'bar_%env(BAR)%' => true, ); $this->assertSame($expected, $container->getRemovedIds()); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt index 6724846f8dd8e..7af3d39ac3b5a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt @@ -9,8 +9,10 @@ return array( 'configurator_service_simple' => true, 'decorated.pif-pouf' => true, 'decorator_service.inner' => true, + 'factory_simple' => true, 'inlined' => true, 'new_factory' => true, + 'tagged_iterator_foo' => true, ); [Container%s/getBazService.php] => true, 'decorated.pif-pouf' => true, 'decorator_service.inner' => true, + 'factory_simple' => true, 'inlined' => true, 'new_factory' => true, + 'tagged_iterator_foo' => true, ); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php index e122069a40990..7649ccd4572e0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php @@ -44,6 +44,7 @@ public function getRemovedIds() return array( 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'bar_%env(BAR)%' => true, 'baz_%env(BAR)%' => true, ); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php index c888dc997d604..d8d600ed24b23 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php @@ -57,6 +57,7 @@ public function getRemovedIds() return array( 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C3' => true, ); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_legacy_privates.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_legacy_privates.php index f8c33ae59966a..03947c425df79 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_legacy_privates.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_legacy_privates.php @@ -60,9 +60,17 @@ public function getRemovedIds() return array( 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'decorated_private' => true, + 'decorated_private_alias' => true, 'foo' => true, + 'private' => true, + 'private_alias' => true, 'private_alias_decorator.inner' => true, + 'private_child' => true, 'private_decorator.inner' => true, + 'private_not_inlined' => true, + 'private_not_removed' => true, + 'private_parent' => true, ); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php index cd8456be3afcf..2ea323954fa5b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php @@ -45,6 +45,7 @@ public function getRemovedIds() return array( 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'baz_service' => true, 'translator.loader_1_locator' => true, 'translator.loader_2_locator' => true, 'translator.loader_3_locator' => true, diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php index 59857ecac3ef8..7d1c429826a98 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php @@ -39,6 +39,7 @@ public function getRemovedIds() return array( 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'baz_service' => true, ); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php index 70af6ce7e3661..abf7271f48a52 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php @@ -39,6 +39,7 @@ public function getRemovedIds() 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, 'private_bar' => true, + 'private_foo' => true, ); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php index 76d87489a98b6..967d8012680bc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php @@ -43,6 +43,7 @@ public function getRemovedIds() return array( 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true, 'service_locator.jmktfsv' => true, ); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php index df5a40c839749..832530bd7d29d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php @@ -41,6 +41,7 @@ public function getRemovedIds() 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, 'foo2' => true, + 'foo3' => true, ); } diff --git a/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php index a15091a446125..75355084d2608 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php @@ -94,7 +94,7 @@ protected function instantiateController($class) } catch (\TypeError $e) { } - if ($this->container instanceof Container && in_array($class, $this->container->getRemovedIds(), true)) { + if ($this->container instanceof Container && isset($this->container->getRemovedIds()[$class])) { throw new \LogicException(sprintf('Controller "%s" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"?', $class), 0, $e); } diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php index 4f77818f12f99..83217aef558fc 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php @@ -130,7 +130,7 @@ public function testNonConstructController() $container->expects($this->atLeastOnce()) ->method('getRemovedIds') ->with() - ->will($this->returnValue(array(ImpossibleConstructController::class))) + ->will($this->returnValue(array(ImpossibleConstructController::class => true))) ; $resolver = $this->createControllerResolver(null, $container); From 7dac528a8609251f7765413ca3d655a5f7a5001b Mon Sep 17 00:00:00 2001 From: Amrouche Hamza Date: Mon, 30 Oct 2017 06:23:08 +0100 Subject: [PATCH 13/49] [Validator] ExpressionValidator should use OBJECT_TO_STRING to allow value in message --- .../Constraints/ExpressionValidator.php | 4 +-- .../Constraints/ExpressionValidatorTest.php | 34 +++++++++++++++++++ .../Validator/Tests/Fixtures/ToString.php | 22 ++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/Validator/Tests/Fixtures/ToString.php diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php index 360ec5a8d9e8d..96761f51f2c2a 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php @@ -78,11 +78,11 @@ public function validate($value, Constraint $constraint) if (!$this->getExpressionLanguage()->evaluate($constraint->expression, $variables)) { if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ value }}', $this->formatValue($value, self::OBJECT_TO_STRING)) ->addViolation(); } else { $this->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ value }}', $this->formatValue($value, self::OBJECT_TO_STRING)) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php index 3b6de4d412582..be15967dcbd88 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Validator\Constraints\Expression; use Symfony\Component\Validator\Constraints\ExpressionValidator; use Symfony\Component\Validator\Tests\Fixtures\Entity; +use Symfony\Component\Validator\Tests\Fixtures\ToString; use Symfony\Component\Validator\Validation; class ExpressionValidatorTest extends AbstractConstraintValidatorTest @@ -90,6 +91,39 @@ public function testFailingExpressionAtObjectLevel() ->assertRaised(); } + public function testSucceedingExpressionAtObjectLevelWithToString() + { + $constraint = new Expression('this.data == 1'); + + $object = new ToString(); + $object->data = '1'; + + $this->setObject($object); + + $this->validator->validate($object, $constraint); + + $this->assertNoViolation(); + } + + public function testFailingExpressionAtObjectLevelWithToString() + { + $constraint = new Expression(array( + 'expression' => 'this.data == 1', + 'message' => 'myMessage', + )); + + $object = new ToString(); + $object->data = '2'; + + $this->setObject($object); + + $this->validator->validate($object, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', 'toString') + ->assertRaised(); + } + public function testSucceedingExpressionAtPropertyLevel() { $constraint = new Expression('value == this.data'); diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/ToString.php b/src/Symfony/Component/Validator/Tests/Fixtures/ToString.php new file mode 100644 index 0000000000000..714fdb9e98f5f --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/ToString.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +class ToString +{ + public $data; + + public function __toString() + { + return 'toString'; + } +} From 3e7780cb908b34903f2ea82b5070c532a8e71c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 1 Dec 2017 20:16:54 +0100 Subject: [PATCH 14/49] [link] Prevent warnings when running link with 2.7 --- link | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/link b/link index f4070998e72b5..3bbc97f041a20 100755 --- a/link +++ b/link @@ -35,11 +35,14 @@ if (!is_dir("$argv[1]/vendor/symfony")) { } $sfPackages = array('symfony/symfony' => __DIR__); + +$filesystem = new Filesystem(); foreach (glob(__DIR__.'/src/Symfony/{Bundle,Bridge,Component,Component/Security}/*', GLOB_BRACE | GLOB_ONLYDIR | GLOB_NOSORT) as $dir) { - $sfPackages[json_decode(file_get_contents("$dir/composer.json"))->name] = $dir; + if ($filesystem->exists($composer = "$dir/composer.json")) { + $sfPackages[json_decode(file_get_contents($composer))->name] = $dir; + } } -$filesystem = new Filesystem(); foreach (glob("$argv[1]/vendor/symfony/*", GLOB_ONLYDIR | GLOB_NOSORT) as $dir) { $package = 'symfony/'.basename($dir); if (is_link($dir)) { From 5377e2846312d6c44ef7d1ff6f01e2df569fc932 Mon Sep 17 00:00:00 2001 From: Michael Gwynne Date: Fri, 1 Dec 2017 20:03:44 +0000 Subject: [PATCH 15/49] [Intl] Correct Typehint --- src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php index 888f68a72b635..874a7e290b776 100644 --- a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php +++ b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php @@ -257,7 +257,7 @@ class NumberFormatter * @throws MethodArgumentValueNotImplementedException When the $style is not supported * @throws MethodArgumentNotImplementedException When the pattern value is different than null */ - public function __construct(?string $locale = 'en', string $style = null, $pattern = null) + public function __construct(?string $locale = 'en', int $style = null, $pattern = null) { if ('en' !== $locale && null !== $locale) { throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); From 28175767d2bbbec9fba509afd08a45cca9d63b0c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 2 Dec 2017 00:46:52 +0100 Subject: [PATCH 16/49] [DI] Trigger deprecation when setting a to-be-private synthetic service --- src/Symfony/Component/DependencyInjection/Container.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 7e53edd9ddd8d..681ddc7024d3a 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -174,7 +174,7 @@ public function set($id, $service) } if (isset($this->privates[$id]) || !(isset($this->fileMap[$id]) || isset($this->methodMap[$id]))) { - if (isset($this->syntheticIds[$id]) || (!isset($this->privates[$id]) && !isset($this->getRemovedIds()[$id]))) { + if (!isset($this->privates[$id]) && !isset($this->getRemovedIds()[$id])) { // no-op } elseif (null === $service) { @trigger_error(sprintf('The "%s" service is private, unsetting it is deprecated since Symfony 3.2 and will fail in 4.0.', $id), E_USER_DEPRECATED); From 05c3c81a200aabf7d16875d9019f3b89a929a247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 1 Dec 2017 20:12:52 +0100 Subject: [PATCH 17/49] [link] clear the cache after linking --- link | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/link b/link index f4070998e72b5..adba4a5a1ad7e 100755 --- a/link +++ b/link @@ -57,3 +57,7 @@ foreach (glob("$argv[1]/vendor/symfony/*", GLOB_ONLYDIR | GLOB_NOSORT) as $dir) $filesystem->symlink($sfDir, $dir); echo "\"$package\" has been linked to \"$sfPackages[$package]\".".PHP_EOL; } + +foreach (glob("$argv[1]/var/cache/*") as $cacheDir) { + $filesystem->remove($cacheDir); +} From 23b575819849f523d6c86c57dc08d8034b523583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sat, 2 Dec 2017 16:57:48 +0100 Subject: [PATCH 18/49] [FrameworkBundle] Fix visibility of a test helper --- .../FrameworkBundle/Tests/Controller/RedirectControllerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php index 14b6e4428e550..c4d6d837c6a7a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php @@ -290,7 +290,7 @@ private function createRedirectController($httpPort = null, $httpsPort = null) return $controller; } - public function assertRedirectUrl(Response $returnResponse, $expectedUrl) + private function assertRedirectUrl(Response $returnResponse, $expectedUrl) { $this->assertTrue($returnResponse->isRedirect($expectedUrl), "Expected: $expectedUrl\nGot: ".$returnResponse->headers->get('Location')); } From 4a5a3f52ab4b5b01794bb2386cd2721f7b4333e0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 1 Dec 2017 18:03:34 +0100 Subject: [PATCH 19/49] [Console][DI] Fail gracefully --- src/Symfony/Component/Console/Application.php | 36 ++++++++++++++----- .../DependencyInjection/Dumper/PhpDumper.php | 18 ++++++---- .../Tests/Fixtures/php/services9.php | 2 +- .../Tests/Fixtures/php/services9_as_files.txt | 14 +++++--- .../Tests/Fixtures/php/services9_compiled.php | 2 +- .../Fixtures/php/services_inline_requires.php | 14 ++++---- src/Symfony/Component/HttpKernel/Kernel.php | 24 +++++++++---- .../Component/HttpKernel/Tests/KernelTest.php | 2 +- 8 files changed, 74 insertions(+), 38 deletions(-) diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index b98365c059332..f3bc0d59641a5 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -40,6 +40,7 @@ use Symfony\Component\Console\Event\ConsoleTerminateEvent; use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\Exception\FatalThrowableError; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -118,6 +119,25 @@ public function run(InputInterface $input = null, OutputInterface $output = null $output = new ConsoleOutput(); } + $renderException = function ($e) use ($output) { + if (!$e instanceof \Exception) { + $e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } + if ($output instanceof ConsoleOutputInterface) { + $this->renderException($e, $output->getErrorOutput()); + } else { + $this->renderException($e, $output); + } + }; + if ($phpHandler = set_exception_handler($renderException)) { + restore_exception_handler(); + if (!is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) { + $debugHandler = true; + } elseif ($debugHandler = $phpHandler[0]->setExceptionHandler($renderException)) { + $phpHandler[0]->setExceptionHandler($debugHandler); + } + } + if (null !== $this->dispatcher && $this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) { @trigger_error(sprintf('The "ConsoleEvents::EXCEPTION" event is deprecated since Symfony 3.3 and will be removed in 4.0. Listen to the "ConsoleEvents::ERROR" event instead.'), E_USER_DEPRECATED); } @@ -125,21 +145,13 @@ public function run(InputInterface $input = null, OutputInterface $output = null $this->configureIO($input, $output); try { - $e = null; $exitCode = $this->doRun($input, $output); } catch (\Exception $e) { - } - - if (null !== $e) { if (!$this->catchExceptions) { throw $e; } - if ($output instanceof ConsoleOutputInterface) { - $this->renderException($e, $output->getErrorOutput()); - } else { - $this->renderException($e, $output); - } + $renderException($e); $exitCode = $e->getCode(); if (is_numeric($exitCode)) { @@ -150,6 +162,12 @@ public function run(InputInterface $input = null, OutputInterface $output = null } else { $exitCode = 1; } + } finally { + if (!$phpHandler) { + restore_exception_handler(); + } elseif (!$debugHandler) { + $phpHandler[0]->setExceptionHandler(null); + } } if ($this->autoExit) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index fb933ce757f5a..200c329ed853c 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -217,12 +217,16 @@ public function dump(array $options = array()) {$namespaceLine} // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. -if (!class_exists(\\Container{$hash}\\{$options['class']}::class, false)) { - require __DIR__.'/Container{$hash}/{$options['class']}.php'; +if (\\class_exists(\\Container{$hash}\\{$options['class']}::class, false)) { + // no-op +} elseif (!include __DIR__.'/Container{$hash}/{$options['class']}.php') { + touch(__DIR__.'/Container{$hash}.legacy'); + + return; } -if (!class_exists({$options['class']}::class, false)) { - class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false); +if (!\\class_exists({$options['class']}::class, false)) { + \\class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false); } return new \\Container{$hash}\\{$options['class']}(); @@ -428,13 +432,13 @@ private function addServiceInclude($cId, Definition $definition, \SplObjectStora } foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { - $code .= sprintf(" require_once %s;\n", $file); + $code .= sprintf(" include_once %s;\n", $file); } } foreach ($inlinedDefinitions as $def) { if ($file = $def->getFile()) { - $code .= sprintf(" require_once %s;\n", $this->dumpValue($file)); + $code .= sprintf(" include_once %s;\n", $this->dumpValue($file)); } } @@ -1233,7 +1237,7 @@ private function addInlineRequires() foreach ($lineage as $file) { if (!isset($this->inlinedRequires[$file])) { $this->inlinedRequires[$file] = true; - $code .= sprintf(" require_once %s;\n", $file); + $code .= sprintf(" include_once %s;\n", $file); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index 30b94d456ef6f..467733f467d6c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -297,7 +297,7 @@ protected function getLazyContextIgnoreInvalidRefService() */ protected function getMethodCall1Service() { - require_once '%path%foo.php'; + include_once '%path%foo.php'; $this->services['method_call1'] = $instance = new \Bar\FooClass(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt index 6724846f8dd8e..c25630d1da537 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt @@ -198,7 +198,7 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. // Returns the public 'method_call1' shared service. -require_once ($this->targetDirs[0].'/Fixtures/includes/foo.php'); +include_once ($this->targetDirs[0].'/Fixtures/includes/foo.php'); $this->services['method_call1'] = $instance = new \Bar\FooClass(); @@ -471,12 +471,16 @@ class ProjectServiceContainer extends Container // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. -if (!class_exists(\Container%s\ProjectServiceContainer::class, false)) { - require __DIR__.'/Container%s/ProjectServiceContainer.php'; +if (\class_exists(\Container%s\ProjectServiceContainer::class, false)) { + // no-op +} elseif (!include __DIR__.'/Container%s/ProjectServiceContainer.php') { + touch(__DIR__.'/Container%s.legacy'); + + return; } -if (!class_exists(ProjectServiceContainer::class, false)) { - class_alias(\Container%s\ProjectServiceContainer::class, ProjectServiceContainer::class, false); +if (!\class_exists(ProjectServiceContainer::class, false)) { + \class_alias(\Container%s\ProjectServiceContainer::class, ProjectServiceContainer::class, false); } return new \Container%s\ProjectServiceContainer(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index ec0db041f4381..1b039c5190f89 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -307,7 +307,7 @@ protected function getLazyContextIgnoreInvalidRefService() */ protected function getMethodCall1Service() { - require_once '%path%foo.php'; + include_once '%path%foo.php'; $this->services['method_call1'] = $instance = new \Bar\FooClass(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php index c888dc997d604..9969f18cc2ec1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php @@ -46,10 +46,10 @@ public function __construct() $this->aliases = array(); - require_once $this->targetDirs[1].'/includes/HotPath/I1.php'; - require_once $this->targetDirs[1].'/includes/HotPath/P1.php'; - require_once $this->targetDirs[1].'/includes/HotPath/T1.php'; - require_once $this->targetDirs[1].'/includes/HotPath/C1.php'; + include_once $this->targetDirs[1].'/includes/HotPath/I1.php'; + include_once $this->targetDirs[1].'/includes/HotPath/P1.php'; + include_once $this->targetDirs[1].'/includes/HotPath/T1.php'; + include_once $this->targetDirs[1].'/includes/HotPath/C1.php'; } public function getRemovedIds() @@ -104,8 +104,8 @@ protected function getC1Service() */ protected function getC2Service() { - require_once $this->targetDirs[1].'/includes/HotPath/C2.php'; - require_once $this->targetDirs[1].'/includes/HotPath/C3.php'; + include_once $this->targetDirs[1].'/includes/HotPath/C2.php'; + include_once $this->targetDirs[1].'/includes/HotPath/C3.php'; return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3()) && false ?: '_'}); } @@ -117,7 +117,7 @@ protected function getC2Service() */ protected function getC3Service() { - require_once $this->targetDirs[1].'/includes/HotPath/C3.php'; + include_once $this->targetDirs[1].'/includes/HotPath/C3.php'; return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3(); } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 398599c6cf225..426fe8929cfc2 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -582,7 +582,13 @@ protected function initializeContainer() $cacheDir = $this->warmupDir ?: $this->getCacheDir(); $cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug); if ($fresh = $cache->isFresh()) { - $this->container = require $cache->getPath(); + // Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors + $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); + try { + $this->container = include $cache->getPath(); + } finally { + error_reporting($errorLevel); + } $fresh = \is_object($this->container); } if (!$fresh) { @@ -590,7 +596,7 @@ protected function initializeContainer() $collectedLogs = array(); $previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) { if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) { - return $previousHandler ? $previousHandler($type, $message, $file, $line) : false; + return $previousHandler ? $previousHandler($type & ~E_WARNING, $message, $file, $line) : E_WARNING === $type; } if (isset($collectedLogs[$message])) { @@ -617,23 +623,27 @@ protected function initializeContainer() 'count' => 1, ); }); + } else { + $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); } try { $container = null; $container = $this->buildContainer(); $container->compile(); + + $oldContainer = file_exists($cache->getPath()) && is_object($oldContainer = include $cache->getPath()) ? new \ReflectionClass($oldContainer) : false; } finally { if ($this->debug) { restore_error_handler(); file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs))); file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : ''); + } else { + error_reporting($errorLevel); } } - $oldContainer = file_exists($cache->getPath()) && is_object($oldContainer = @include $cache->getPath()) ? new \ReflectionClass($oldContainer) : false; - $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); $this->container = require $cache->getPath(); } @@ -649,13 +659,13 @@ protected function initializeContainer() // old container files are not removed immediately, // but on a next dump of the container. $oldContainerDir = dirname($oldContainer->getFileName()); - foreach (glob(dirname($oldContainerDir).'/*.legacyContainer') as $legacyContainer) { - if ($oldContainerDir.'.legacyContainer' !== $legacyContainer && @unlink($legacyContainer)) { + foreach (glob(dirname($oldContainerDir).'/*.legacy') as $legacyContainer) { + if ($oldContainerDir.'.legacy' !== $legacyContainer && @unlink($legacyContainer)) { (new Filesystem())->remove(substr($legacyContainer, 0, -16)); } } - touch($oldContainerDir.'.legacyContainer'); + touch($oldContainerDir.'.legacy'); } if ($this->container->has('cache_warmer')) { diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php index 6dee293ea9de2..243529ed20a2e 100644 --- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -833,7 +833,7 @@ public function testKernelReset() $this->assertTrue(get_class($kernel->getContainer()) !== $containerClass); $this->assertFileExists($containerFile); - $this->assertFileExists(dirname($containerFile).'.legacyContainer'); + $this->assertFileExists(dirname($containerFile).'.legacy'); } public function testKernelPass() From 3bdeda048b7a9b56ca18551a1de14547d3e3f135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 2 Dec 2017 15:59:45 +0100 Subject: [PATCH 20/49] Fail as early and noisily as possible Today, I tried using SYMFONY_PHPUNIT_VERSION=6 because I don't really care about the minor version. I got lots of warnings, followed by hard-to-understand error messages. This will silence the first warning and will throw an exception instead. --- src/Symfony/Bridge/PhpUnit/bin/simple-phpunit | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit index b30eaa14538dc..110e57d321389 100755 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit @@ -51,7 +51,12 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__ passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? '(del /S /F /Q %s & rmdir %1$s) >nul': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION")); } if (extension_loaded('openssl') && ini_get('allow_url_fopen') && !isset($_SERVER['http_proxy']) && !isset($_SERVER['https_proxy'])) { - stream_copy_to_stream(fopen("https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip", 'rb'), fopen("$PHPUNIT_VERSION.zip", 'wb')); + $remoteZip = "https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip"; + $remoteZipStream = @fopen($remoteZip, 'rb'); + if (!$remoteZipStream) { + throw new \RuntimeException("Could not find $remoteZip"); + } + stream_copy_to_stream($remoteZipStream, fopen("$PHPUNIT_VERSION.zip", 'wb')); } else { @unlink("$PHPUNIT_VERSION.zip"); passthru("wget https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip"); From 483dd134f467f73131f5dee436ba52b703cdb246 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 3 Dec 2017 12:09:35 +0100 Subject: [PATCH 21/49] [DI] Cast ids to string, as done on 3.4 --- .../DependencyInjection/ContainerBuilder.php | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 8386a7bc00e1a..42e03f4785b33 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -467,6 +467,8 @@ public function getCompiler() */ public function set($id, $service) { + $id = (string) $id; + if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) { // setting a synthetic service on a compiled container is alright throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id)); @@ -484,7 +486,7 @@ public function set($id, $service) */ public function removeDefinition($id) { - if (isset($this->definitions[$id])) { + if (isset($this->definitions[$id = (string) $id])) { unset($this->definitions[$id]); $this->removedIds[$id] = true; } @@ -499,6 +501,8 @@ public function removeDefinition($id) */ public function has($id) { + $id = (string) $id; + return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id); } @@ -519,7 +523,7 @@ public function has($id) */ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { - if ($this->isCompiled() && isset($this->removedIds[$id]) && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { + if ($this->isCompiled() && isset($this->removedIds[$id = (string) $id]) && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { return parent::get($id); } @@ -779,6 +783,8 @@ public function setAliases(array $aliases) */ public function setAlias($alias, $id) { + $alias = (string) $alias; + if (is_string($id)) { $id = new Alias($id); } elseif (!$id instanceof Alias) { @@ -801,7 +807,7 @@ public function setAlias($alias, $id) */ public function removeAlias($alias) { - if (isset($this->aliasDefinitions[$alias])) { + if (isset($this->aliasDefinitions[$alias = (string) $alias])) { unset($this->aliasDefinitions[$alias]); $this->removedIds[$alias] = true; } @@ -816,7 +822,7 @@ public function removeAlias($alias) */ public function hasAlias($id) { - return isset($this->aliasDefinitions[$id]); + return isset($this->aliasDefinitions[$id = (string) $id]); } /** @@ -840,6 +846,8 @@ public function getAliases() */ public function getAlias($id) { + $id = (string) $id; + if (!isset($this->aliasDefinitions[$id])) { throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id)); } @@ -928,6 +936,8 @@ public function setDefinition($id, Definition $definition) throw new BadMethodCallException('Adding definition to a compiled container is not allowed'); } + $id = (string) $id; + unset($this->aliasDefinitions[$id], $this->removedIds[$id]); return $this->definitions[$id] = $definition; @@ -942,7 +952,7 @@ public function setDefinition($id, Definition $definition) */ public function hasDefinition($id) { - return isset($this->definitions[$id]); + return isset($this->definitions[(string) $id]); } /** @@ -956,6 +966,8 @@ public function hasDefinition($id) */ public function getDefinition($id) { + $id = (string) $id; + if (!isset($this->definitions[$id])) { throw new ServiceNotFoundException($id); } @@ -976,6 +988,8 @@ public function getDefinition($id) */ public function findDefinition($id) { + $id = (string) $id; + while (isset($this->aliasDefinitions[$id])) { $id = (string) $this->aliasDefinitions[$id]; } From 75b21e9c869ce21dfa74b0fc1bb709b737127a45 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Sun, 3 Dec 2017 13:07:37 +0000 Subject: [PATCH 22/49] Throw an exception is expression language is not installed --- .../Compiler/AnalyzeServiceReferencesPass.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php index 217c7c2034073..a9b812e61d10f 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php @@ -14,6 +14,7 @@ use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\ExpressionLanguage; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -155,6 +156,10 @@ private function getDefinitionId($id) private function getExpressionLanguage() { if (null === $this->expressionLanguage) { + if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { + throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + } + $providers = $this->container->getExpressionLanguageProviders(); $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) { if ('""' === substr_replace($arg, '', 1, -1)) { From c98d967b0ca6220b5aa690a27dffc10096fb9150 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 2 Dec 2017 11:31:36 +0100 Subject: [PATCH 23/49] [DI] turn $private to protected in dumped container, to make cache:clear BC --- .../Component/DependencyInjection/Dumper/PhpDumper.php | 8 ++++++-- .../Tests/Fixtures/php/services1-1.php | 6 +++++- .../DependencyInjection/Tests/Fixtures/php/services1.php | 6 +++++- .../DependencyInjection/Tests/Fixtures/php/services10.php | 6 +++++- .../DependencyInjection/Tests/Fixtures/php/services12.php | 6 +++++- .../DependencyInjection/Tests/Fixtures/php/services13.php | 6 +++++- .../DependencyInjection/Tests/Fixtures/php/services19.php | 6 +++++- .../DependencyInjection/Tests/Fixtures/php/services24.php | 6 +++++- .../DependencyInjection/Tests/Fixtures/php/services26.php | 6 +++++- .../DependencyInjection/Tests/Fixtures/php/services33.php | 6 +++++- .../DependencyInjection/Tests/Fixtures/php/services8.php | 6 +++++- .../Tests/Fixtures/php/services9_as_files.txt | 6 +++++- .../Tests/Fixtures/php/services9_compiled.php | 6 +++++- .../Fixtures/php/services_almost_circular_private.php | 6 +++++- .../Fixtures/php/services_almost_circular_public.php | 6 +++++- .../Tests/Fixtures/php/services_array_params.php | 6 +++++- .../Tests/Fixtures/php/services_base64_env.php | 6 +++++- .../Tests/Fixtures/php/services_env_in_id.php | 6 +++++- .../Tests/Fixtures/php/services_inline_requires.php | 6 +++++- .../Tests/Fixtures/php/services_locator.php | 6 +++++- .../Tests/Fixtures/php/services_private_frozen.php | 6 +++++- .../Tests/Fixtures/php/services_private_in_expression.php | 6 +++++- .../Tests/Fixtures/php/services_rot13_env.php | 6 +++++- .../Tests/Fixtures/php/services_subscriber.php | 6 +++++- .../Tests/Fixtures/php/services_uninitialized_ref.php | 6 +++++- 25 files changed, 126 insertions(+), 26 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index df7c0835d3658..fa33fe8a8a2ec 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -874,7 +874,11 @@ class $class extends $baseClass { private \$parameters; private \$targetDirs = array(); - private \$privates = array(); + + /*{$this->docStar} + * @internal but protected for BC on cache:clear + */ + protected \$privates = array(); public function __construct() { @@ -1058,7 +1062,7 @@ private function addAliases(): string return $code." );\n"; } - private function addInlineRequires() :string + private function addInlineRequires(): string { if (!$this->hotPathTag || !$this->inlineRequires) { return ''; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php index cbc17d74df3b7..a04d80affcec1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php @@ -20,7 +20,11 @@ class Container extends \Symfony\Component\DependencyInjection\Dump\AbstractCont { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php index bb312436dc837..f25c59b81596f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php index ef88c481583af..31c4475ec7dab 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php index 4a1fbb901c313..9e193f5c9b0ee 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php index d17073ae0b2ed..8c90280d272a2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php index 80635ad02dbb0..673c9d54bbeca 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php index baa8a5eeb207e..090a77dd3c2c3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php index 700275f1e6b96..2089bfe6386c3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_EnvParameters extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php index 4ca6299d7435d..1c70b0ee8d06b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php index 0d60c3699cba6..dc7da1c274e5c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt index 579bf285ad643..d3b0724d80fbc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt @@ -309,7 +309,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index 72e06b3416f69..0772d93ebe4d0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php index e8fdeb0d8a253..4de6bfc233193 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php index 91b05f361a81b..79a0c11cc15c3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php index cd7bc61ceb015..0e4e9ea239ee6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php index 83aee3007b6fe..8af802f70dab3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_Base64Parameters extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php index b6afb3cf15580..4100dcdd2b914 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php index 05bae361195d4..bc9a7a8d98def 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php index b8aba31b1ea8b..59bbce3a995c4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php index 404944a3369b5..86315e2ebaffc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php index efaa0fb1d9839..5caf9104dd34d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php index 61b0b7294799f..012a36023b0f8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_Rot13Parameters extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php index 934cad86b5feb..1c223c3e65b96 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php index 446d2ae482e77..0f5090c80bebe 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_Uninitialized_Reference extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { From 11c6b386c6da2713c8d59044138e3125c4d9dd6d Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Sun, 3 Dec 2017 13:36:40 +0000 Subject: [PATCH 24/49] Ensure services & aliases can be referred to with `__toString`able objects --- .../Tests/ContainerBuilderTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 2f89b9ddcbe83..4942121e188e3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1310,6 +1310,24 @@ public function testArgumentsHaveHigherPriorityThanBindings() $this->assertSame('via-argument', $container->get('foo')->class1->identifier); $this->assertSame('via-bindings', $container->get('foo')->class2->identifier); } + + public function testIdCanBeAnObjectAsLongAsItCanBeCastToString() + { + $id = new Reference('another_service'); + $aliasId = new Reference('alias_id'); + + $container = new ContainerBuilder(); + $container->set($id, new \stdClass()); + $container->setAlias($aliasId, 'another_service'); + + $this->assertTrue($container->has('another_service')); + $this->assertTrue($container->has($id)); + $this->assertTrue($container->hasAlias('alias_id')); + $this->assertTrue($container->hasAlias($aliasId)); + + $container->removeAlias($aliasId); + $container->removeDefinition($id); + } } class FooClass From 9fb6a885803c64139cc341c86e779c7b57715963 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 3 Dec 2017 21:54:18 +0100 Subject: [PATCH 25/49] Remove rc/beta suffix from composer.json files --- src/Symfony/Bridge/Twig/composer.json | 2 +- src/Symfony/Bundle/FrameworkBundle/composer.json | 2 +- src/Symfony/Bundle/SecurityBundle/composer.json | 8 ++++---- src/Symfony/Component/Form/composer.json | 2 +- src/Symfony/Component/Security/Csrf/composer.json | 4 ++-- src/Symfony/Component/Security/composer.json | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 0b16573768da5..33ac7d5849c7c 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -24,7 +24,7 @@ "symfony/asset": "~2.8|~3.0|~4.0", "symfony/dependency-injection": "~2.8|~3.0|~4.0", "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/form": "~3.4-beta4|~4.0-beta4", + "symfony/form": "~3.4|~4.0", "symfony/http-foundation": "^3.3.11|~4.0", "symfony/http-kernel": "~3.2|~4.0", "symfony/polyfill-intl-icu": "~1.0", diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index abbc1dbef368a..e2382363bebc5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -22,7 +22,7 @@ "symfony/class-loader": "~3.2", "symfony/dependency-injection": "~3.4|~4.0", "symfony/config": "~3.4|~4.0", - "symfony/event-dispatcher": "^3.4-beta4|~4.0-beta4", + "symfony/event-dispatcher": "~3.4|~4.0", "symfony/http-foundation": "^3.3.11|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/polyfill-mbstring": "~1.0", diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 4dc3f78ad0f14..d1bf164f0a1f8 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -18,7 +18,7 @@ "require": { "php": "^5.5.9|>=7.0.8", "ext-xml": "*", - "symfony/security": "~3.4-rc1|~4.0-rc1", + "symfony/security": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/http-kernel": "~3.3|~4.0", "symfony/polyfill-php70": "~1.0" @@ -29,9 +29,9 @@ "symfony/console": "~3.4|~4.0", "symfony/css-selector": "~2.8|~3.0|~4.0", "symfony/dom-crawler": "~2.8|~3.0|~4.0", - "symfony/event-dispatcher": "^3.3.1|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", "symfony/form": "^3.4|~4.0", - "symfony/framework-bundle": "^3.4-rc1|~4.0-rc1", + "symfony/framework-bundle": "~3.4|~4.0", "symfony/http-foundation": "~3.3|~4.0", "symfony/security-acl": "~2.8|~3.0", "symfony/translation": "~3.4|~4.0", @@ -47,7 +47,7 @@ }, "conflict": { "symfony/var-dumper": "<3.3", - "symfony/event-dispatcher": "<3.3.1", + "symfony/event-dispatcher": "<3.4", "symfony/framework-bundle": "<3.4", "symfony/console": "<3.4" }, diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 7afb274b4d27e..f9836b8ac066e 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -32,7 +32,7 @@ "symfony/http-kernel": "^3.3.5|~4.0", "symfony/security-csrf": "~2.8|~3.0|~4.0", "symfony/translation": "~2.8|~3.0|~4.0", - "symfony/var-dumper": "~3.3.11|~3.4-beta3|~4.0-beta3", + "symfony/var-dumper": "~3.3.11|~3.4|~4.0", "symfony/console": "~3.4|~4.0" }, "conflict": { diff --git a/src/Symfony/Component/Security/Csrf/composer.json b/src/Symfony/Component/Security/Csrf/composer.json index 72bef3805e2f6..5b27fee3a4591 100644 --- a/src/Symfony/Component/Security/Csrf/composer.json +++ b/src/Symfony/Component/Security/Csrf/composer.json @@ -22,10 +22,10 @@ "symfony/security-core": "~2.8|~3.0|~4.0" }, "require-dev": { - "symfony/http-foundation": "^2.8.31|~3.3.13|~3.4-beta5|~4.0-beta5" + "symfony/http-foundation": "^2.8.31|~3.3.13|~3.4|~4.0" }, "conflict": { - "symfony/http-foundation": "<2.8.31|~3.3,<3.3.13|~3.4,<3.4-beta5|~4.0,<4.0-beta5" + "symfony/http-foundation": "<2.8.31|~3.3,<3.3.13" }, "suggest": { "symfony/http-foundation": "For using the class SessionTokenStorage." diff --git a/src/Symfony/Component/Security/composer.json b/src/Symfony/Component/Security/composer.json index 7fee5322a24b9..679208ca096b8 100644 --- a/src/Symfony/Component/Security/composer.json +++ b/src/Symfony/Component/Security/composer.json @@ -18,7 +18,7 @@ "require": { "php": "^5.5.9|>=7.0.8", "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/http-foundation": "^2.8.31|~3.3.13|~3.4-beta5|~4.0-beta5", + "symfony/http-foundation": "^2.8.31|~3.3.13|~3.4|~4.0", "symfony/http-kernel": "~3.3|~4.0", "symfony/polyfill-php56": "~1.0", "symfony/polyfill-php70": "~1.0", From 56f24d08c981f6f7ddcc021c7f1941038f43c6e8 Mon Sep 17 00:00:00 2001 From: Valentin Date: Mon, 4 Dec 2017 02:10:25 +0300 Subject: [PATCH 26/49] Fixed the null value exception case. --- .../Validator/Constraints/ValidValidator.php | 4 ++++ .../Tests/Constraints/ValidValidatorTest.php | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/Symfony/Component/Validator/Constraints/ValidValidator.php b/src/Symfony/Component/Validator/Constraints/ValidValidator.php index b2f1f1c5a06b9..be5fbc12660ba 100644 --- a/src/Symfony/Component/Validator/Constraints/ValidValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ValidValidator.php @@ -26,6 +26,10 @@ public function validate($value, Constraint $constraint) throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Valid'); } + if (null === $value) { + return; + } + $this->context ->getValidator() ->inContext($this->context) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ValidValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ValidValidatorTest.php index f95650d359b3b..c4ccf1551f2a0 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ValidValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ValidValidatorTest.php @@ -20,6 +20,18 @@ public function testPropertyPathsArePassedToNestedContexts() $this->assertSame('fooBar.fooBarBaz.foo', $violations->get(0)->getPropertyPath()); } + public function testNullValues() + { + $validatorBuilder = new ValidatorBuilder(); + $validator = $validatorBuilder->enableAnnotationMapping()->getValidator(); + + $foo = new Foo(); + $foo->fooBar = null; + $violations = $validator->validate($foo, null, array('nested')); + + $this->assertCount(0, $violations); + } + protected function createValidator() { return new ValidValidator(); From 93441c1a850aa1d29352122d443287adad06900e Mon Sep 17 00:00:00 2001 From: Alessandro Lai Date: Mon, 4 Dec 2017 10:18:44 +0100 Subject: [PATCH 27/49] Add test case for #25264 --- .../DependencyInjection/Tests/ContainerTest.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php index 8035de6230882..84271ab02667b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php @@ -538,6 +538,16 @@ public function testReplacingAPreDefinedServiceIsDeprecated() $this->assertSame($bar, $c->get('bar'), '->set() replaces a pre-defined service'); } + + /** + * @group legacy + * @expectedDeprecation The "synthetic" service is private, replacing it is deprecated since Symfony 3.2 and will fail in 4.0. + */ + public function testSetWithPrivateSyntheticServiceThrowsDeprecation() + { + $c = new ProjectServiceContainer(); + $c->set('synthetic', new \stdClass()); + } } class ProjectServiceContainer extends Container @@ -565,8 +575,12 @@ public function __construct() $this->__foo_bar = new \stdClass(); $this->__foo_baz = new \stdClass(); $this->__internal = new \stdClass(); - $this->privates = array('internal' => true); + $this->privates = array( + 'internal' => true, + 'synthetic' => true, + ); $this->aliases = array('alias' => 'bar'); + $this->syntheticIds['synthetic'] = true; } protected function getInternalService() From 369075a282b0f29eeed511708651d2f9f6e67d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Wer=C5=82os?= Date: Mon, 4 Dec 2017 13:08:37 +0100 Subject: [PATCH 28/49] Fix for missing whitespace control modifier in form layout --- .../Twig/Resources/views/Form/form_div_layout.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 a27c81dd495ae..eb0f6d057dcea 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 @@ -301,7 +301,7 @@ {% if not child.rendered %} {{- form_row(child) -}} {% endif %} - {%- endfor %} + {%- endfor -%} {% if not form.methodRendered and form.parent is null %} {%- do form.setMethodRendered() -%} @@ -315,7 +315,7 @@ {%- if form_method != method -%} {%- endif -%} - {% endif %} + {% endif -%} {% endblock form_rest %} {# Support #} From 4d39a2d8dc3d52b19c5dc0a776bd470a4b7fbc2a Mon Sep 17 00:00:00 2001 From: Vladimir Reznichenko Date: Mon, 13 Nov 2017 21:26:24 +0100 Subject: [PATCH 29/49] SCA with Php Inspections (EA Extended) --- src/Symfony/Component/HttpKernel/Kernel.php | 2 +- src/Symfony/Component/Workflow/Definition.php | 2 +- src/Symfony/Component/Workflow/DefinitionBuilder.php | 2 +- src/Symfony/Component/Workflow/Transition.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 3b465dc7d5b2c..684529e78a981 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -423,7 +423,7 @@ public function setClassCache(array $classes) */ public function setAnnotatedClassCache(array $annotatedClasses) { - file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/annotations.map', sprintf('warmupDir ?: $this->getCacheDir()).'/annotations.map', sprintf(' Date: Mon, 4 Dec 2017 13:50:51 +0100 Subject: [PATCH 30/49] [SecurityBundle] Fix compat with HttpFoundation >=3.4 --- .../Security/Factory/AbstractFactory.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php index d1b7980faa8a6..39c73dc0c3d95 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler; /** * AbstractFactory is the base class for all classes inheriting from @@ -29,7 +30,7 @@ abstract class AbstractFactory implements SecurityFactoryInterface protected $options = array( 'check_path' => '/login_check', 'use_forward' => false, - 'require_previous_session' => true, + 'require_previous_session' => null, ); protected $defaultSuccessHandlerOptions = array( @@ -80,6 +81,10 @@ public function addConfiguration(NodeDefinition $node) ->scalarNode('failure_handler')->end() ; + if (array_key_exists('require_previous_session', $this->options) && null === $this->options['require_previous_session']) { + $this->options['require_previous_session'] = !class_exists(AbstractSessionHandler::class); + } + foreach (array_merge($this->options, $this->defaultSuccessHandlerOptions, $this->defaultFailureHandlerOptions) as $name => $default) { if (is_bool($default)) { $builder->booleanNode($name)->defaultValue($default); From 6a2f518e74daab1f66653299f247293b390f2fc0 Mon Sep 17 00:00:00 2001 From: Niels Keurentjes Date: Mon, 4 Dec 2017 12:36:56 +0100 Subject: [PATCH 31/49] Disallow viewing dot-files in Profiler The file viewer in the profiler should not open files that were meant to be hidden, like specifically .env files, but similarly files like .htaccess that might expose server configuration knowledge. --- .../Controller/ProfilerController.php | 2 +- .../Controller/ProfilerControllerTest.php | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php index 4aca4cc354e44..0920a1aa5e18a 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php @@ -385,7 +385,7 @@ public function openAction(Request $request) $filename = $this->baseDir.DIRECTORY_SEPARATOR.$file; - if (preg_match("'(^|[/\\\\])\.\.?([/\\\\]|$)'", $file) || !is_readable($filename)) { + if (preg_match("'(^|[/\\\\])\.'", $file) || !is_readable($filename)) { throw new NotFoundHttpException(sprintf('The file "%s" cannot be opened.', $file)); } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php index 839c9f21d9fe9..670f71dfc176d 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController; use Symfony\Bundle\WebProfilerBundle\Csp\ContentSecurityPolicyHandler; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Profiler\Profile; use Symfony\Component\HttpFoundation\Request; @@ -46,6 +47,42 @@ public function getEmptyTokenCases() ); } + /** + * @dataProvider getOpenFileCases + */ + public function testOpeningDisallowedPaths($path, $isAllowed) + { + $urlGenerator = $this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->getMock(); + $twig = $this->getMockBuilder('Twig\Environment')->disableOriginalConstructor()->getMock(); + $profiler = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profiler') + ->disableOriginalConstructor() + ->getMock(); + + $controller = new ProfilerController($urlGenerator, $profiler, $twig, array(), 'bottom', null, __DIR__.'/../..'); + + try { + $response = $controller->openAction(Request::create('/_wdt/open', Request::METHOD_GET, array('file' => $path))); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertTrue($isAllowed); + } catch (NotFoundHttpException $e) { + $this->assertFalse($isAllowed); + } + } + + public function getOpenFileCases() + { + return array( + array('README.md', true), + array('composer.json', true), + array('Controller/ProfilerController.php', true), + array('.gitignore', false), + array('../TwigBundle/README.md', false), + array('Controller/../README.md', false), + array('Controller/./ProfilerController.php', false), + ); + } + /** * @dataProvider provideCspVariants */ From 888b48a89c7847d04a0a911ffc7e23f2e9b94131 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Mon, 4 Dec 2017 08:54:58 -0500 Subject: [PATCH 32/49] Fix collision between view properties and form fields --- .../Twig/Resources/views/Form/foundation_5_layout.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig index dc7bec9fb6ccd..af9e658923923 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig @@ -318,11 +318,11 @@ {% block form_errors -%} {% if errors|length > 0 -%} - {% if form.parent %}{% else %}
{% endif %} + {% if form is not rootform %}{% else %}
{% endif %} {%- for error in errors -%} {{ error.message }} {% if not loop.last %}, {% endif %} {%- endfor -%} - {% if form.parent %}{% else %}
{% endif %} + {% if form is not rootform %}
{% else %}
{% endif %} {%- endif %} {%- endblock form_errors %} From c330965cfbe6ef223330de6134668d3c1ad31cc9 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Mon, 4 Dec 2017 09:06:01 -0500 Subject: [PATCH 33/49] Fix collision between view properties and form fields --- .../Twig/Resources/views/Form/bootstrap_4_layout.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig index 96ca4be4d68a7..4e4e9facc3a22 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig @@ -173,7 +173,7 @@ {% block form_errors -%} {%- if errors|length > 0 -%} -
+
    {%- for error in errors -%}
  • {{ error.message }}
  • From b23b957ae9cc77eda71357b3f07dbaaf13691e01 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 4 Dec 2017 14:06:32 +0100 Subject: [PATCH 34/49] parse newlines in quoted multiline strings --- src/Symfony/Component/Yaml/Parser.php | 17 ++++++++++++++++- src/Symfony/Component/Yaml/Tests/ParserTest.php | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 702df16f0aec0..a6e39007dfe0f 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -700,6 +700,8 @@ private function parseValue($value, $flags, $context) return Inline::parse($value, $flags, $this->refs); } + $lines = array(); + while ($this->moveToNextLine()) { // unquoted strings end before the first unindented line if (null === $quotation && 0 === $this->getCurrentLineIndentation()) { @@ -708,7 +710,7 @@ private function parseValue($value, $flags, $context) break; } - $value .= ' '.trim($this->currentLine); + $lines[] = trim($this->currentLine); // quoted string values end with a line that is terminated with the quotation character if ('' !== $this->currentLine && substr($this->currentLine, -1) === $quotation) { @@ -716,6 +718,19 @@ private function parseValue($value, $flags, $context) } } + for ($i = 0, $linesCount = count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) { + if ('' === $lines[$i]) { + $value .= "\n"; + $previousLineBlank = true; + } elseif ($previousLineBlank) { + $value .= $lines[$i]; + $previousLineBlank = false; + } else { + $value .= ' '.$lines[$i]; + $previousLineBlank = false; + } + } + Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); $parsedValue = Inline::parse($value, $flags, $this->refs); diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 7a3c405714328..1d35ecc1bd819 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1572,6 +1572,20 @@ public function testCommentCharactersInMultiLineQuotedStrings() $this->assertSame($expected, $this->parser->parse($yaml)); } + public function testBlankLinesInQuotedMultiLineString() + { + $yaml = << "foo\nbar", + ); + + $this->assertSame($expected, $this->parser->parse($yaml)); + } + public function testParseMultiLineUnquotedString() { $yaml = << Date: Mon, 4 Dec 2017 16:10:11 +0100 Subject: [PATCH 35/49] [DI] Fix deep-inlining of non-shared refs --- .../Compiler/InlineServiceDefinitionsPass.php | 44 ++++++++++++--- .../InlineServiceDefinitionsPassTest.php | 54 +++++++++++++++++++ 2 files changed, 90 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index ff9ac315b0af8..8eb6e94d24696 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -13,6 +13,7 @@ use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Reference; /** @@ -23,6 +24,7 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface { private $repeatedPass; + private $cloningIds = array(); private $inlinedServiceIds = array(); /** @@ -54,18 +56,44 @@ protected function processValue($value, $isRoot = false) // Reference found in ArgumentInterface::getValues() are not inlineable return $value; } - if ($value instanceof Reference && $this->container->hasDefinition($id = (string) $value)) { - $definition = $this->container->getDefinition($id); - if ($this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) { - $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); - $this->inlinedServiceIds[$id][] = $this->currentId; - - return $definition->isShared() ? $definition : clone $definition; + if ($value instanceof Definition && $this->cloningIds) { + if ($value->isShared()) { + return $value; } + $value = clone $value; + } + + if (!$value instanceof Reference || !$this->container->hasDefinition($id = (string) $value)) { + return parent::processValue($value, $isRoot); + } + + $definition = $this->container->getDefinition($id); + + if (!$this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) { + return $value; } - return parent::processValue($value, $isRoot); + $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); + $this->inlinedServiceIds[$id][] = $this->currentId; + + if ($definition->isShared()) { + return $definition; + } + + if (isset($this->cloningIds[$id])) { + $ids = array_keys($this->cloningIds); + $ids[] = $id; + + throw new ServiceCircularReferenceException($id, array_slice($ids, array_search($id, $ids))); + } + + $this->cloningIds[$id] = true; + try { + return $this->processValue($definition); + } finally { + unset($this->cloningIds[$id]); + } } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php index 09e4913664784..365c16c2d2c95 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php @@ -111,6 +111,60 @@ public function testProcessInlinesMixedServicesLoop() $this->assertEquals($container->getDefinition('foo')->getArgument(0), $container->getDefinition('bar')); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + * @expectedExceptionMessage Circular reference detected for service "bar", path: "bar -> foo -> bar". + */ + public function testProcessThrowsOnNonSharedLoops() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->addArgument(new Reference('bar')) + ->setShared(false) + ; + $container + ->register('bar') + ->setShared(false) + ->addMethodCall('setFoo', array(new Reference('foo'))) + ; + + $this->process($container); + } + + public function testProcessNestedNonSharedServices() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->addArgument(new Reference('bar1')) + ->addArgument(new Reference('bar2')) + ; + $container + ->register('bar1') + ->setShared(false) + ->addArgument(new Reference('baz')) + ; + $container + ->register('bar2') + ->setShared(false) + ->addArgument(new Reference('baz')) + ; + $container + ->register('baz') + ->setShared(false) + ; + + $this->process($container); + + $baz1 = $container->getDefinition('foo')->getArgument(0)->getArgument(0); + $baz2 = $container->getDefinition('foo')->getArgument(1)->getArgument(0); + + $this->assertEquals($container->getDefinition('baz'), $baz1); + $this->assertEquals($container->getDefinition('baz'), $baz2); + $this->assertNotSame($baz1, $baz2); + } + public function testProcessInlinesIfMultipleReferencesButAllFromTheSameDefinition() { $container = new ContainerBuilder(); From 17d84f6a873aa50c460bb7844dff97b630f42a6a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 4 Dec 2017 17:04:41 +0100 Subject: [PATCH 36/49] [DI] Fix missing unset leading to false-positive circular ref --- src/Symfony/Component/DependencyInjection/Container.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 681ddc7024d3a..14af37ef78ac4 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -298,6 +298,7 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE } elseif (isset($this->methodMap[$id])) { return self::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior ? null : $this->{$this->methodMap[$id]}(); } elseif (--$i && $id !== $normalizedId = $this->normalizeId($id)) { + unset($this->loading[$id]); $id = $normalizedId; continue; } elseif (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) { From 890edf7c38398b6981e0fdcdeb7786fee7f6bace Mon Sep 17 00:00:00 2001 From: Amrouche Hamza Date: Mon, 4 Dec 2017 15:32:05 +0100 Subject: [PATCH 37/49] [FrameworkBundle] Fix a bug where a color tag will be shown when passing an antislash --- .../FrameworkBundle/Console/Descriptor/TextDescriptor.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 6977d4df9afa5..70012e3dc7c1b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; +use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\DependencyInjection\Alias; @@ -226,7 +227,8 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $rawOutput = isset($options['raw_text']) && $options['raw_text']; foreach ($this->sortServiceIds($serviceIds) as $serviceId) { $definition = $this->resolveServiceDefinition($builder, $serviceId); - $styledServiceId = $rawOutput ? $serviceId : sprintf('%s', $serviceId); + + $styledServiceId = $rawOutput ? $serviceId : sprintf('%s', OutputFormatter::escape($serviceId)); if ($definition instanceof Definition) { if ($showTag) { foreach ($definition->getTag($showTag) as $key => $tag) { From 0f8ff1583abffdf3b4eab026ea6e6052b30c1f11 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 4 Dec 2017 10:21:12 -0800 Subject: [PATCH 38/49] marked method as being internal --- src/Symfony/Bridge/Twig/Extension/FormExtension.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Symfony/Bridge/Twig/Extension/FormExtension.php b/src/Symfony/Bridge/Twig/Extension/FormExtension.php index aaed8221c1fc8..e101704efaa9d 100644 --- a/src/Symfony/Bridge/Twig/Extension/FormExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/FormExtension.php @@ -158,6 +158,9 @@ public function isSelectedChoice(ChoiceView $choice, $selectedValue) return $choice->value === $selectedValue; } + /** + * @internal + */ public function isRootForm(FormView $formView) { return null === $formView->parent; From 93e136b306f585b6f37fc869416b2f5c09b30fd6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 4 Dec 2017 10:22:59 -0800 Subject: [PATCH 39/49] moved method to function --- .../Bridge/Twig/Extension/FormExtension.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Extension/FormExtension.php b/src/Symfony/Bridge/Twig/Extension/FormExtension.php index 6e6274dcc4935..beed1c95b3a12 100644 --- a/src/Symfony/Bridge/Twig/Extension/FormExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/FormExtension.php @@ -106,7 +106,7 @@ public function getTests() { return array( new TwigTest('selectedchoice', 'Symfony\Bridge\Twig\Extension\twig_is_selected_choice'), - new TwigTest('rootform', array($this, 'isRootForm')), + new TwigTest('rootform', 'Symfony\Bridge\Twig\Extension\twig_is_root_form'), ); } @@ -166,14 +166,6 @@ public function __unset($name) unset($this->$name); } - /** - * @internal - */ - public function isRootForm(FormView $formView) - { - return null === $formView->parent; - } - /** * {@inheritdoc} */ @@ -202,3 +194,11 @@ function twig_is_selected_choice(ChoiceView $choice, $selectedValue) return $choice->value === $selectedValue; } + +/** + * @internal + */ +function twig_is_root_form(FormView $formView) +{ + return null === $formView->parent; +} From 11105f37177558826336bd12042c4eda0bf7ea37 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 4 Dec 2017 10:36:52 -0800 Subject: [PATCH 40/49] fixed tests --- .../Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php index 9fa0a22ea31c6..6ac1846a5d414 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php @@ -159,7 +159,7 @@ public function isRootFormProvider() */ public function testIsRootForm($expected, FormView $formView) { - $this->assertSame($expected, $this->extension->isRootForm($formView)); + $this->assertSame($expected, twig_is_root_form($formView)); } protected function renderForm(FormView $view, array $vars = array()) From 844c402171e87c538edd250be44b2c92bdc8e7c4 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 2 Dec 2017 09:27:26 -0800 Subject: [PATCH 41/49] [Security] Adding a GuardAuthenticatorHandler alias --- src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml index ce6021823ba74..8e6133528c4bd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml @@ -13,6 +13,8 @@ + + Date: Sun, 3 Dec 2017 10:59:27 +0100 Subject: [PATCH 42/49] [DI] Register singly-implemented interfaces when doing PSR-4 discovery --- .../DependencyInjection/Loader/FileLoader.php | 21 ++++++-- .../Tests/Fixtures/Prototype/Foo.php | 4 +- .../Tests/Fixtures/Prototype/FooInterface.php | 7 +++ .../Tests/Fixtures/Prototype/Sub/Bar.php | 2 +- .../Fixtures/Prototype/Sub/BarInterface.php | 7 +++ .../Tests/Loader/FileLoaderTest.php | 50 +++++++++++++++++++ 6 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/FooInterface.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Sub/BarInterface.php diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index ade932add1406..c61acdf065183 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -56,10 +56,25 @@ public function registerClasses(Definition $prototype, $namespace, $resource, $e $classes = $this->findClasses($namespace, $resource, $exclude); // prepare for deep cloning - $prototype = serialize($prototype); + $serializedPrototype = serialize($prototype); + $interfaces = array(); + $singlyImplemented = array(); foreach ($classes as $class) { - $this->setDefinition($class, unserialize($prototype)); + if (interface_exists($class, false)) { + $interfaces[] = $class; + } else { + $this->setDefinition($class, unserialize($serializedPrototype)); + foreach (class_implements($class, false) as $interface) { + $singlyImplemented[$interface] = isset($singlyImplemented[$interface]) ? false : $class; + } + } + } + foreach ($interfaces as $interface) { + if (!empty($singlyImplemented[$interface])) { + $this->container->setAlias($interface, $singlyImplemented[$interface]) + ->setPublic(false); + } } } @@ -129,7 +144,7 @@ private function findClasses($namespace, $pattern, $excludePattern) throw new InvalidArgumentException(sprintf('Expected to find class "%s" in file "%s" while importing services from resource "%s", but it was not found! Check the namespace prefix used with the resource.', $class, $path, $pattern)); } - if ($r->isInstantiable()) { + if ($r->isInstantiable() || $r->isInterface()) { $classes[] = $class; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php index ee533fecd9019..b6690a8d2680a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php @@ -2,13 +2,13 @@ namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype; -class Foo +class Foo implements FooInterface, Sub\BarInterface { public function __construct($bar = null) { } - function setFoo(self $foo) + public function setFoo(self $foo) { } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/FooInterface.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/FooInterface.php new file mode 100644 index 0000000000000..1855dcfc59e0e --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/FooInterface.php @@ -0,0 +1,7 @@ +getDefinitions()) ); + $this->assertEquals( + array( + PsrContainerInterface::class, + ContainerInterface::class, + BarInterface::class, + ), + array_keys($container->getAliases()) + ); } public function testRegisterClassesWithExclude() @@ -111,6 +124,43 @@ public function testRegisterClassesWithExclude() $this->assertTrue($container->has(Baz::class)); $this->assertFalse($container->has(Foo::class)); $this->assertFalse($container->has(DeeperBaz::class)); + + $this->assertEquals( + array( + PsrContainerInterface::class, + ContainerInterface::class, + BarInterface::class, + ), + array_keys($container->getAliases()) + ); + } + + public function testNestedRegisterClasses() + { + $container = new ContainerBuilder(); + $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures')); + + $prototype = new Definition(); + $prototype->setPublic(true)->setPrivate(true); + $loader->registerClasses($prototype, 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', 'Prototype/*'); + + $this->assertTrue($container->has(Bar::class)); + $this->assertTrue($container->has(Baz::class)); + $this->assertTrue($container->has(Foo::class)); + + $this->assertEquals( + array( + PsrContainerInterface::class, + ContainerInterface::class, + FooInterface::class, + ), + array_keys($container->getAliases()) + ); + + $alias = $container->getAlias(FooInterface::class); + $this->assertSame(Foo::class, (string) $alias); + $this->assertFalse($alias->isPublic()); + $this->assertFalse($alias->isPrivate()); } /** From 0f0a5764ca7445ead215bbad3e69cf9623df4fe3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 4 Dec 2017 11:01:57 -0800 Subject: [PATCH 43/49] fixed typo --- .../Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php index 6ac1846a5d414..2d1d03c5e6d26 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php @@ -159,7 +159,7 @@ public function isRootFormProvider() */ public function testIsRootForm($expected, FormView $formView) { - $this->assertSame($expected, twig_is_root_form($formView)); + $this->assertSame($expected, \Symfony\Bridge\Twig\Extension\twig_is_root_form($formView)); } protected function renderForm(FormView $view, array $vars = array()) From bf017a93937a1c351945d4ef41aa43f8225246dc Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 4 Dec 2017 11:38:58 -0800 Subject: [PATCH 44/49] fixed tests --- .../Tests/Controller/ProfilerControllerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php index c2efc24f6fe10..6b02ec292c051 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php @@ -59,7 +59,7 @@ public function testOpeningDisallowedPaths($path, $isAllowed) ->disableOriginalConstructor() ->getMock(); - $controller = new ProfilerController($urlGenerator, $profiler, $twig, array(), 'bottom', null, __DIR__.'/../..'); + $controller = new ProfilerController($urlGenerator, $profiler, $twig, array(), null, __DIR__.'/../..'); try { $response = $controller->openAction(Request::create('/_wdt/open', Request::METHOD_GET, array('file' => $path))); From dec77f19831f4b4c6204513298f41e15569424c0 Mon Sep 17 00:00:00 2001 From: David Maicher Date: Mon, 4 Dec 2017 20:48:56 +0100 Subject: [PATCH 45/49] [SecurityBundle] add note to info text of no-op config option logout_on_user_change --- .../SecurityBundle/DependencyInjection/MainConfiguration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index 0ca7e7f4b7f51..7c990dce0d9d3 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -199,7 +199,7 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto ->scalarNode('context')->cannotBeEmpty()->end() ->booleanNode('logout_on_user_change') ->defaultTrue() - ->info('When true, it will trigger a logout for the user if something has changed.') + ->info('When true, it will trigger a logout for the user if something has changed. Note: No-Op option since 4.0. Will always be true.') ->end() ->arrayNode('logout') ->treatTrueLike(array()) From 4eff1466d655c98e8930ffafa324efd96f4259a9 Mon Sep 17 00:00:00 2001 From: David Maicher Date: Sat, 2 Dec 2017 17:46:48 +0100 Subject: [PATCH 46/49] [SecurityBundle] fix setLogoutOnUserChange calls for context listeners --- .../DependencyInjection/MainConfiguration.php | 11 ----- .../DependencyInjection/SecurityExtension.php | 21 ++++++---- .../SecurityExtensionTest.php | 41 ++++++++++++++++++- 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index fb66cf78e706e..ad076f05e71e1 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -340,17 +340,6 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto return $firewall; }) ->end() - ->validate() - ->ifTrue(function ($v) { - return (isset($v['stateless']) && true === $v['stateless']) || (isset($v['security']) && false === $v['security']); - }) - ->then(function ($v) { - // this option doesn't change behavior when true when stateless, so prevent deprecations - $v['logout_on_user_change'] = true; - - return $v; - }) - ->end() ; } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 835794f15fdd5..1bc06b1f47900 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -43,6 +43,7 @@ class SecurityExtension extends Extension private $factories = array(); private $userProviderFactories = array(); private $expressionLanguage; + private $logoutOnUserChangeByContextKey = array(); public function __construct() { @@ -276,12 +277,6 @@ private function createFirewalls($config, ContainerBuilder $container) $customUserChecker = true; } - if (!isset($firewall['logout_on_user_change']) || !$firewall['logout_on_user_change']) { - @trigger_error(sprintf('Not setting "logout_on_user_change" to true on firewall "%s" is deprecated as of 3.4, it will always be true in 4.0.', $name), E_USER_DEPRECATED); - } - - $contextListenerDefinition->addMethodCall('setLogoutOnUserChange', array($firewall['logout_on_user_change'])); - $configId = 'security.firewall.map.config.'.$name; list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $configId); @@ -370,7 +365,16 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $contextKey = $firewall['context']; } - $listeners[] = new Reference($this->createContextListener($container, $contextKey)); + if (!$logoutOnUserChange = $firewall['logout_on_user_change']) { + @trigger_error(sprintf('Not setting "logout_on_user_change" to true on firewall "%s" is deprecated as of 3.4, it will always be true in 4.0.', $id), E_USER_DEPRECATED); + } + + if (isset($this->logoutOnUserChangeByContextKey[$contextKey]) && $this->logoutOnUserChangeByContextKey[$contextKey][1] !== $logoutOnUserChange) { + throw new InvalidConfigurationException(sprintf('Firewalls "%s" and "%s" need to have the same value for option "logout_on_user_change" as they are sharing the context "%s"', $this->logoutOnUserChangeByContextKey[$contextKey][0], $id, $contextKey)); + } + + $this->logoutOnUserChangeByContextKey[$contextKey] = array($id, $logoutOnUserChange); + $listeners[] = new Reference($this->createContextListener($container, $contextKey, $logoutOnUserChange)); } $config->replaceArgument(6, $contextKey); @@ -481,7 +485,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a return array($matcher, $listeners, $exceptionListener); } - private function createContextListener($container, $contextKey) + private function createContextListener($container, $contextKey, $logoutUserOnChange) { if (isset($this->contextListeners[$contextKey])) { return $this->contextListeners[$contextKey]; @@ -490,6 +494,7 @@ private function createContextListener($container, $contextKey) $listenerId = 'security.context_listener.'.count($this->contextListeners); $listener = $container->setDefinition($listenerId, new ChildDefinition('security.context_listener')); $listener->replaceArgument(2, $contextKey); + $listener->addMethodCall('setLogoutOnUserChange', array($logoutUserOnChange)); return $this->contextListeners[$contextKey] = $listenerId; } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index 0b339188e7590..289c5af20f20d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -127,7 +127,7 @@ public function testDisableRoleHierarchyVoter() * @group legacy * @expectedDeprecation Not setting "logout_on_user_change" to true on firewall "some_firewall" is deprecated as of 3.4, it will always be true in 4.0. */ - public function testDeprecationForUserLogout() + public function testConfiguresLogoutOnUserChangeForContextListenersCorrectly() { $container = $this->getRawContainer(); @@ -135,13 +135,52 @@ public function testDeprecationForUserLogout() 'providers' => array( 'default' => array('id' => 'foo'), ), + 'firewalls' => array( + 'some_firewall' => array( + 'pattern' => '/.*', + 'http_basic' => null, + 'logout_on_user_change' => false, + ), + 'some_other_firewall' => array( + 'pattern' => '/.*', + 'http_basic' => null, + 'logout_on_user_change' => true, + ), + ), + )); + + $container->compile(); + + $this->assertEquals(array(array('setLogoutOnUserChange', array(false))), $container->getDefinition('security.context_listener.0')->getMethodCalls()); + $this->assertEquals(array(array('setLogoutOnUserChange', array(true))), $container->getDefinition('security.context_listener.1')->getMethodCalls()); + } + /** + * @group legacy + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage Firewalls "some_firewall" and "some_other_firewall" need to have the same value for option "logout_on_user_change" as they are sharing the context "my_context" + */ + public function testThrowsIfLogoutOnUserChangeDifferentForSharedContext() + { + $container = $this->getRawContainer(); + + $container->loadFromExtension('security', array( + 'providers' => array( + 'default' => array('id' => 'foo'), + ), 'firewalls' => array( 'some_firewall' => array( 'pattern' => '/.*', 'http_basic' => null, + 'context' => 'my_context', 'logout_on_user_change' => false, ), + 'some_other_firewall' => array( + 'pattern' => '/.*', + 'http_basic' => null, + 'context' => 'my_context', + 'logout_on_user_change' => true, + ), ), )); From 1ff22e6accde46ad693ecbb8b234c65e4c53e717 Mon Sep 17 00:00:00 2001 From: Ricardo de Vries Date: Mon, 4 Dec 2017 12:06:27 +0100 Subject: [PATCH 47/49] [Bridge/PhpUnit] Prefer ['argv'] over --- src/Symfony/Bridge/PhpUnit/bin/simple-phpunit | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit index 110e57d321389..b3f302cd340bf 100755 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit @@ -108,6 +108,9 @@ EOPHP } +global $argv, $argc; +$argv = isset($_SERVER['argv']) ? $_SERVER['argv'] : array(); +$argc = isset($_SERVER['argc']) ? $_SERVER['argc'] : 0; $components = array(); $cmd = array_map('escapeshellarg', $argv); $exit = 0; From 34f90255e6d16f1a15ae5b997bb99112ee353b5c Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 4 Dec 2017 16:18:14 -0800 Subject: [PATCH 48/49] updated CHANGELOG for 4.0.1 --- CHANGELOG-4.0.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CHANGELOG-4.0.md b/CHANGELOG-4.0.md index f1c98b1874d2a..2a4c1db371283 100644 --- a/CHANGELOG-4.0.md +++ b/CHANGELOG-4.0.md @@ -7,6 +7,35 @@ in 4.0 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/v4.0.0...v4.0.1 +* 4.0.1 (2017-12-05) + + * bug #25304 [Bridge/PhpUnit] Prefer $_SERVER['argv'] over $argv (ricknox) + * bug #25272 [SecurityBundle] fix setLogoutOnUserChange calls for context listeners (dmaicher) + * bug #25282 [DI] Register singly-implemented interfaces when doing PSR-4 discovery (nicolas-grekas) + * bug #25274 [Security] Adding a GuardAuthenticatorHandler alias (weaverryan) + * bug #25308 [FrameworkBundle] Fix a bug where a color tag will be shown when passing an antislash (Simperfit) + * bug #25278 Fix for missing whitespace control modifier in form layout (kubawerlos) + * bug #25306 [Form][TwigBridge] Fix collision between view properties and form fields (yceruto) + * bug #25305 [Form][TwigBridge] Fix collision between view properties and form fields (yceruto) + * bug #25236 [Form][TwigBridge] Fix collision between view properties and form fields (yceruto) + * bug #25312 [DI] Fix deep-inlining of non-shared refs (nicolas-grekas) + * bug #25309 [Yaml] parse newlines in quoted multiline strings (xabbuh) + * bug #25313 [DI] Fix missing unset leading to false-positive circular ref (nicolas-grekas) + * bug #25268 [DI] turn $private to protected in dumped container, to make cache:clear BC (nicolas-grekas) + * bug #25285 [DI] Throw an exception if Expression Language is not installed (sroze) + * bug #25241 [Yaml] do not eagerly filter comment lines (xabbuh) + * bug #25284 [DI] Cast ids to string, as done on 3.4 (nicolas-grekas, sroze) + * bug #25297 [Validator] Fixed the @Valid(groups={"group"}) against null exception case (vudaltsov) + * bug #25255 [Console][DI] Fail gracefully (nicolas-grekas) + * bug #25264 [DI] Trigger deprecation when setting a to-be-private synthetic service (nicolas-grekas) + * bug #25258 [link] Prevent warnings when running link with 2.7 (dunglas) + * bug #25244 [DI] Add missing deprecation when fetching private services from ContainerBuilder (nicolas-grekas) + * bug #24750 [Validator] ExpressionValidator should use OBJECT_TO_STRING (Simperfit) + * bug #25247 [DI] Fix false-positive circular exception (nicolas-grekas) + * bug #25226 [HttpKernel] Fix issue when resetting DumpDataCollector (Pierstoval) + * bug #25230 Use a more specific file for detecting the bridge (greg0ire) + * bug #25232 [WebProfilerBundle] [TwigBundle] Fix Profiler breaking XHTML pages (tistre) + * 4.0.0 (2017-11-30) * bug #25220 [HttpFoundation] Add Session::isEmpty(), fix MockFileSessionStorage to behave like the native one (nicolas-grekas) From da67aa7d67860bcafdc90e4c768fe572eb7c4b60 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 4 Dec 2017 16:18:20 -0800 Subject: [PATCH 49/49] updated VERSION for 4.0.1 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index c9d881512e927..39efec369d7f8 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -63,12 +63,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.0.1-DEV'; + const VERSION = '4.0.1'; const VERSION_ID = 40001; const MAJOR_VERSION = 4; const MINOR_VERSION = 0; const RELEASE_VERSION = 1; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '07/2018'; const END_OF_LIFE = '01/2019';