From 1f8db731a0dd6ba464f04c0cde2752a8f4be4102 Mon Sep 17 00:00:00 2001 From: Amrouche Hamza Date: Wed, 15 Nov 2017 16:25:33 +0100 Subject: [PATCH 01/39] [Console] Fix global console flag when used in chain --- src/Symfony/Component/Console/Input/ArgvInput.php | 8 ++++++++ .../Component/Console/Tests/Input/ArgvInputTest.php | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/Symfony/Component/Console/Input/ArgvInput.php b/src/Symfony/Component/Console/Input/ArgvInput.php index c2f533eb36798..4493a5bafc3b9 100644 --- a/src/Symfony/Component/Console/Input/ArgvInput.php +++ b/src/Symfony/Component/Console/Input/ArgvInput.php @@ -282,6 +282,14 @@ public function hasParameterOption($values) if ($token === $value || 0 === strpos($token, $value.'=')) { return true; } + + if (0 === strpos($token, '-') && 0 !== strpos($token, '--')) { + $searchableToken = str_replace('-', '', $token); + $searchableValue = str_replace('-', '', $value); + if ('' !== $searchableToken && '' !== $searchableValue && false !== strpos($searchableToken, $searchableValue)) { + return true; + } + } } } diff --git a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php index 1fe21d0f6c1a8..85fb533715b62 100644 --- a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php +++ b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php @@ -296,6 +296,9 @@ public function testHasParameterOption() $input = new ArgvInput(array('cli.php', '-f', 'foo')); $this->assertTrue($input->hasParameterOption('-f'), '->hasParameterOption() returns true if the given short option is in the raw input'); + $input = new ArgvInput(array('cli.php', '-fh')); + $this->assertTrue($input->hasParameterOption('-fh'), '->hasParameterOption() returns true if the given short option is in the raw input'); + $input = new ArgvInput(array('cli.php', '--foo', 'foo')); $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input'); From 675a3fe7f616ceda079f90c96c8da692bf698fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Strzelecki?= Date: Thu, 24 Jul 2014 10:48:45 +0200 Subject: [PATCH 02/39] added ability for substitute aliases when mapping in YAML is on single line --- src/Symfony/Component/Yaml/Parser.php | 55 ++++++++++++++++++- .../Yaml/Tests/Fixtures/sfMergeKey.yml | 8 ++- .../Component/Yaml/Tests/ParserTest.php | 12 ++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 8ffa8ccf64940..93b2fca4d71b9 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -491,6 +491,59 @@ private function moveToPreviousLine() return true; } + /** + * Substitute from alias where is used a << + * + * @param mixed $values Parsed YAML in which aliases are not substituted + * + * @return mixed YAML with substituted aliases + * + * @throws Exception\ParseException When indentation problem are detected + */ + private function substituteAliases($values) + { + if (is_array($values)) { + $keys = array(); + foreach ($values as $key => $value) { + if ($key ==='<<' && preg_grep('/^\*.+/', (array) $value) === array_values((array) $value)) { + $values[$key] = array(); + foreach ((array) $value as $ref) { + $refName = substr($ref, 1); + if (!array_key_exists($refName, $this->refs)) { + throw new ParseException( + sprintf('Reference "%s" does not exist.', $refName), + $this->getRealCurrentLineNb() + 1, + $this->currentLine + ); + } + + $keys = array_merge( + $keys, + array_diff(array_keys($this->refs[$refName]), $keys) + ); + $values[$key] = array_replace($this->refs[$refName], $values[$key]); + } + } elseif (!isset($result[$key]) || is_array($result[$key])) { + $keys[] = $key; + $values[$key] = $this->substituteAliases($value); + } + } + + if (isset($values['<<'])) { + $values = array_replace($values['<<'], $values); + unset($values['<<']); + uksort( + $values, + function ($a, $b) use ($keys) { + return array_search($a, $keys, true) - array_search($b, $keys, true); + } + ); + } + } + + return $values; + } + /** * Parses a YAML value. * @@ -526,7 +579,7 @@ private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $ob } try { - return Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); + return $this->substituteAliases(Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs)); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml index 59f612514170a..a37eca2e3cd73 100644 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml +++ b/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml @@ -22,6 +22,7 @@ yaml: | foo: bar foo: ignore bar: foo + bar_inline: {a: before, d: other, <<: *foo, b: new, x: Oren, c: { foo: bar, foo: ignore, bar: foo}} duplicate: foo: bar foo: ignore @@ -46,15 +47,20 @@ yaml: | p: 12345 z: <<: *nestedref + head_inline: &head_inline { <<: [ *foo , *dong , *foo2 ] } + recursive_inline: { <<: *head_inline, c: { <<: *foo2 } } php: | array( 'foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull'), 'bar' => array('a' => 'before', 'd' => 'other', 'e' => null, 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'), + 'bar_inline' => array('a' => 'before', 'd' => 'other', 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'), 'duplicate' => array('foo' => 'bar'), 'foo2' => array('a' => 'Ballmer'), 'ding' => array('fi', 'fei', 'fo', 'fam'), 'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'), 'head' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam'), 'taz' => array('a' => 'Steve', 'w' => array('p' => 1234)), - 'nested' => array('a' => 'Steve', 'w' => array('p' => 12345), 'd' => 'Doug', 'z' => array('p' => 12345)) + 'nested' => array('a' => 'Steve', 'w' => array('p' => 12345), 'd' => 'Doug', 'z' => array('p' => 12345)), + 'head_inline' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam'), + 'recursive_inline' => array('a' => 'Steve', 'b' => 'Clark', 'c' => array('a' => 'Ballmer'), 'fi', 'fei', 'fo', 'fam') ) diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 9be278f67ec92..97ce0cac0f770 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1207,6 +1207,18 @@ public function testParseReferencesOnMergeKeys() $this->assertSame($expected, $this->parser->parse($yaml)); } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Reference "foo" does not exist + */ + public function testEvalRefException() + { + $yaml = <<parser->parse($yaml); + } } class B From dd26c80aa95c32af0ddbf195b2bab1cd29c74b7b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 20 Nov 2017 08:47:12 +0100 Subject: [PATCH 03/39] substitute aliases in inline mappings --- src/Symfony/Component/Yaml/Inline.php | 22 +++++++- src/Symfony/Component/Yaml/Parser.php | 55 +------------------ .../Yaml/Tests/Fixtures/sfMergeKey.yml | 6 +- 3 files changed, 23 insertions(+), 60 deletions(-) diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 9ff9b89c1c123..31a0514fd4972 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -365,6 +365,7 @@ private static function parseMapping($mapping, &$i = 0, $references = array()) $output = array(); $len = strlen($mapping); ++$i; + $allowOverwrite = false; // {foo: bar, bar:foo, ...} while ($i < $len) { @@ -384,6 +385,10 @@ private static function parseMapping($mapping, &$i = 0, $references = array()) // key $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false); + if ('<<' === $key) { + $allowOverwrite = true; + } + // value $done = false; @@ -395,7 +400,12 @@ private static function parseMapping($mapping, &$i = 0, $references = array()) // Spec: Keys MUST be unique; first one wins. // Parser cannot abort this mapping earlier, since lines // are processed sequentially. - if (!isset($output[$key])) { + // But overwriting is allowed when a merge node is used in current block. + if ('<<' === $key) { + foreach ($value as $parsedValue) { + $output += $parsedValue; + } + } elseif ($allowOverwrite || !isset($output[$key])) { $output[$key] = $value; } $done = true; @@ -406,7 +416,10 @@ private static function parseMapping($mapping, &$i = 0, $references = array()) // Spec: Keys MUST be unique; first one wins. // Parser cannot abort this mapping earlier, since lines // are processed sequentially. - if (!isset($output[$key])) { + // But overwriting is allowed when a merge node is used in current block. + if ('<<' === $key) { + $output += $value; + } elseif ($allowOverwrite || !isset($output[$key])) { $output[$key] = $value; } $done = true; @@ -419,7 +432,10 @@ private static function parseMapping($mapping, &$i = 0, $references = array()) // Spec: Keys MUST be unique; first one wins. // Parser cannot abort this mapping earlier, since lines // are processed sequentially. - if (!isset($output[$key])) { + // But overwriting is allowed when a merge node is used in current block. + if ('<<' === $key) { + $output += $value; + } elseif ($allowOverwrite || !isset($output[$key])) { $output[$key] = $value; } $done = true; diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 93b2fca4d71b9..8ffa8ccf64940 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -491,59 +491,6 @@ private function moveToPreviousLine() return true; } - /** - * Substitute from alias where is used a << - * - * @param mixed $values Parsed YAML in which aliases are not substituted - * - * @return mixed YAML with substituted aliases - * - * @throws Exception\ParseException When indentation problem are detected - */ - private function substituteAliases($values) - { - if (is_array($values)) { - $keys = array(); - foreach ($values as $key => $value) { - if ($key ==='<<' && preg_grep('/^\*.+/', (array) $value) === array_values((array) $value)) { - $values[$key] = array(); - foreach ((array) $value as $ref) { - $refName = substr($ref, 1); - if (!array_key_exists($refName, $this->refs)) { - throw new ParseException( - sprintf('Reference "%s" does not exist.', $refName), - $this->getRealCurrentLineNb() + 1, - $this->currentLine - ); - } - - $keys = array_merge( - $keys, - array_diff(array_keys($this->refs[$refName]), $keys) - ); - $values[$key] = array_replace($this->refs[$refName], $values[$key]); - } - } elseif (!isset($result[$key]) || is_array($result[$key])) { - $keys[] = $key; - $values[$key] = $this->substituteAliases($value); - } - } - - if (isset($values['<<'])) { - $values = array_replace($values['<<'], $values); - unset($values['<<']); - uksort( - $values, - function ($a, $b) use ($keys) { - return array_search($a, $keys, true) - array_search($b, $keys, true); - } - ); - } - } - - return $values; - } - /** * Parses a YAML value. * @@ -579,7 +526,7 @@ private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $ob } try { - return $this->substituteAliases(Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs)); + return Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml index a37eca2e3cd73..499446c10cb76 100644 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml +++ b/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml @@ -53,7 +53,7 @@ php: | array( 'foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull'), 'bar' => array('a' => 'before', 'd' => 'other', 'e' => null, 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'), - 'bar_inline' => array('a' => 'before', 'd' => 'other', 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'), + 'bar_inline' => array('a' => 'before', 'd' => 'other', 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'e' => 'notnull', 'x' => 'Oren'), 'duplicate' => array('foo' => 'bar'), 'foo2' => array('a' => 'Ballmer'), 'ding' => array('fi', 'fei', 'fo', 'fam'), @@ -61,6 +61,6 @@ php: | 'head' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam'), 'taz' => array('a' => 'Steve', 'w' => array('p' => 1234)), 'nested' => array('a' => 'Steve', 'w' => array('p' => 12345), 'd' => 'Doug', 'z' => array('p' => 12345)), - 'head_inline' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam'), - 'recursive_inline' => array('a' => 'Steve', 'b' => 'Clark', 'c' => array('a' => 'Ballmer'), 'fi', 'fei', 'fo', 'fam') + 'head_inline' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam'), + 'recursive_inline' => array('a' => 'Steve', 'b' => 'Clark', 'c' => array('a' => 'Ballmer'), 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam'), ) From b746e8a0172857486db33b8f0d5b91de35b6c797 Mon Sep 17 00:00:00 2001 From: Amrouche Hamza Date: Sun, 19 Nov 2017 13:41:04 +0200 Subject: [PATCH 04/39] [HttpKernel] add a test for FilterControllerEvents --- .../FilterControllerArgumentsEventTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/Symfony/Component/HttpKernel/Tests/Event/FilterControllerArgumentsEventTest.php diff --git a/src/Symfony/Component/HttpKernel/Tests/Event/FilterControllerArgumentsEventTest.php b/src/Symfony/Component/HttpKernel/Tests/Event/FilterControllerArgumentsEventTest.php new file mode 100644 index 0000000000000..9165d31f24a15 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Event/FilterControllerArgumentsEventTest.php @@ -0,0 +1,17 @@ +assertEquals($filterController->getArguments(), array('test')); + } +} From 37baa1de512da4390a953bfcc0d59f36e73ddc0e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 23 Nov 2017 12:13:33 +0100 Subject: [PATCH 05/39] Fix merge --- src/Symfony/Component/DependencyInjection/ContainerBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 84863a2fb8245..15da037be7869 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -1151,7 +1151,7 @@ private function callMethod($service, $call, \SplObjectStorage $inlinedDefinitio */ private function shareService(Definition $definition, $service, $id, \SplObjectStorage $inlinedDefinitions) { - if (!$definition->isShared() || self::SCOPE_PROTOTYPE === $scope = $definition->getScope()) { + if (!$definition->isShared() || self::SCOPE_PROTOTYPE === $scope = $definition->getScope(false)) { return; } if (null === $id) { From ab6abd5293aa292b6d14c56142acc4674b60f658 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 24 Nov 2017 06:48:33 -0800 Subject: [PATCH 06/39] updated CHANGELOG for 3.4.0-RC2 --- CHANGELOG-3.4.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG-3.4.md b/CHANGELOG-3.4.md index ff448f9565412..2e91b3fd9dd7f 100644 --- a/CHANGELOG-3.4.md +++ b/CHANGELOG-3.4.md @@ -7,6 +7,22 @@ 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-RC2 (2017-11-24) + + * bug #25146 [DI] Dont resolve envs in service ids (nicolas-grekas) + * bug #25113 [Routing] Fix "config-file-relative" annotation loader resources (nicolas-grekas, sroze) + * bug #25065 [FrameworkBundle] Update translation commands to work with default paths (yceruto) + * bug #25109 Make debug:container search command case-insensitive (jzawadzki) + * bug #25121 [FrameworkBundle] Fix AssetsInstallCommand (nicolas-grekas) + * bug #25102 [Form] Fixed ContextErrorException in FileType (chihiro-adachi) + * bug #25130 [DI] Fix handling of inlined definitions by ContainerBuilder (nicolas-grekas) + * bug #25119 [DI] Fix infinite loop when analyzing references (nicolas-grekas) + * bug #25094 [FrameworkBundle][DX] Display a nice error message if an enabled component is missing (derrabus) + * bug #25100 [SecurityBundle] providerIds is undefined error when firewall provider is not specified (karser) + * bug #25100 [SecurityBundle] providerIds is undefined error when firewall provider is not specified (karser) + * bug #25100 [SecurityBundle] providerIds is undefined error when firewall provider is not specified (karser) + * bug #25097 [Bridge\PhpUnit] Turn "preserveGlobalState" to false by default, revert "Blacklist" removal (nicolas-grekas) + * 3.4.0-RC1 (2017-11-21) * bug #25077 [Bridge/Twig] Let getFlashes starts the session (MatTheCat) From 75761741158669e4aa386f516cf53ed66b502dad Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 24 Nov 2017 06:48:42 -0800 Subject: [PATCH 07/39] updated VERSION for 3.4.0-RC2 --- 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 a30d5916f9ae6..f714859845c2c 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-RC2'; const VERSION_ID = 30400; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = 'RC2'; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021'; From 3502020834a401416c2c5d35e6ac6026d39fae72 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 23 Nov 2017 14:17:09 -0500 Subject: [PATCH 08/39] adding checks for the expression language --- .../Component/DependencyInjection/Loader/XmlFileLoader.php | 4 ++++ .../Component/DependencyInjection/Loader/YamlFileLoader.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index d839ab267d318..598cf36f859a6 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -506,6 +506,10 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $lowercase = $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior); break; case 'expression': + if (!class_exists(Expression::class)) { + throw new \LogicException(sprintf('The type="expression" attribute cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".')); + } + $arguments[$key] = new Expression($arg->nodeValue); break; case 'collection': diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 3af504d9e7959..81d3ccb881558 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -775,6 +775,10 @@ private function resolveServices($value, $file, $isParameter = false) $value[$k] = $this->resolveServices($v, $file, $isParameter); } } elseif (is_string($value) && 0 === strpos($value, '@=')) { + if (!class_exists(Expression::class)) { + throw new \LogicException(sprintf('The "@=" expression syntax cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".')); + } + return new Expression(substr($value, 2)); } elseif (is_string($value) && 0 === strpos($value, '@')) { if (0 === strpos($value, '@@')) { From e52825e2533af77361ed7a424fb0952e9161ff2d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 24 Nov 2017 07:20:16 -0800 Subject: [PATCH 09/39] bumped Symfony version to 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 f714859845c2c..a30d5916f9ae6 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-RC2'; + const VERSION = '3.4.0-DEV'; const VERSION_ID = 30400; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'RC2'; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021'; From fd4340693c86317ba0e301f72c0e76fd8859fe24 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Fri, 24 Nov 2017 15:22:02 +0000 Subject: [PATCH 10/39] Automatically enable the CSRF protection if CSRF manager exists --- .../FrameworkBundle/DependencyInjection/Configuration.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index d5030fc9fe7cf..c9dbd6af77a2a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -21,6 +21,7 @@ use Symfony\Component\Form\Form; use Symfony\Component\Lock\Lock; use Symfony\Component\Lock\Store\SemaphoreStore; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Translation\Translator; use Symfony\Component\Validator\Validation; @@ -142,7 +143,7 @@ private function addCsrfSection(ArrayNodeDefinition $rootNode) $rootNode ->children() ->arrayNode('csrf_protection') - ->canBeEnabled() + ->{!class_exists(FullStack::class) && class_exists(CsrfTokenManagerInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->end() ->end() ; From e251993ce2fc22c311a85d8de3ceb0127d96ce8b Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 24 Nov 2017 07:38:29 -0800 Subject: [PATCH 11/39] bumped Symfony version to 4.0.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 b0cd20167fe81..175626c456bfc 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-RC2'; + const VERSION = '4.0.0-DEV'; const VERSION_ID = 40000; const MAJOR_VERSION = 4; const MINOR_VERSION = 0; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'RC2'; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '07/2018'; const END_OF_LIFE = '01/2019'; From decaf234ddda595d310dacd13304bbce1d8b2eec Mon Sep 17 00:00:00 2001 From: Amrouche Hamza Date: Thu, 23 Nov 2017 19:40:42 +0100 Subject: [PATCH 12/39] [DependencyInjection] Add more information to the message when passing miss matching class. --- .../Compiler/AutowirePass.php | 3 ++- .../Fixtures/includes/autowiring_classes.php | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index afda55c94f21b..1113667503966 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -336,13 +336,14 @@ private function createTypeAlternatives(TypedReference $reference) if ($message = $this->getAliasesSuggestionForType($type = $reference->getType())) { return ' '.$message; } - if (isset($this->ambiguousServiceTypes[$type])) { $message = sprintf('one of these existing services: "%s"', implode('", "', $this->ambiguousServiceTypes[$type])); } elseif (isset($this->types[$type])) { $message = sprintf('the existing "%s" service', $this->types[$type]); } elseif ($reference->getRequiringClass() && !$reference->canBeAutoregistered()) { return ' It cannot be auto-registered because it is from a different root namespace.'; + } elseif (is_array($this->types) && (null !== $key = array_search(strtolower($type), array_map('strtolower', $this->types)))) { + return sprintf(' Maybe you mean %s instead of %s ?', $key, $type); } else { return; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php index bf99eff6a2838..ffa0196298f73 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php @@ -6,6 +6,13 @@ class Foo { } +class FooFoo +{ + public function __construct(Foo $foo) + { + } +} + class Bar { public function __construct(Foo $foo) @@ -293,6 +300,17 @@ public function setChildMethodWithoutDocBlock(A $a) } } +class ARealClass { + +} + +class ClassMisMatch { + + public function __construct(arealclass $n = null) + { + } +} + class NotWireable { public function setNotAutowireable(NotARealClass $n) From ced0857560bc8314a13055d03f368c630d61c6ac Mon Sep 17 00:00:00 2001 From: Gawain Lynch Date: Sun, 26 Nov 2017 11:31:13 +0100 Subject: [PATCH 13/39] Remove unreachable code --- .../Compiler/RegisterServiceSubscribersPass.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php index 8c81452b315a6..f8dba86a0b547 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php @@ -74,9 +74,6 @@ protected function processValue($value, $isRoot = false) if ($optionalBehavior = '?' === $type[0]) { $type = substr($type, 1); $optionalBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; - } elseif ($optionalBehavior = '!' === $type[0]) { - $type = substr($type, 1); - $optionalBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE; } if (is_int($key)) { $key = $type; From a264238e8a398029266350ef14d9b1c088cd4161 Mon Sep 17 00:00:00 2001 From: "Issei.M" Date: Sat, 25 Nov 2017 00:42:53 +0900 Subject: [PATCH 14/39] [Form] Don't rely on if http-foundation isn't in FileType --- .../Component/Form/Extension/Core/Type/FileType.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php index ddd5cae0bc3c3..7d84512d9b3c7 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -92,9 +92,12 @@ public function finishView(FormView $view, FormInterface $form, array $options) */ public function configureOptions(OptionsResolver $resolver) { - $dataClass = function (Options $options) { - return $options['multiple'] ? null : 'Symfony\Component\HttpFoundation\File\File'; - }; + $dataClass = null; + if (class_exists('Symfony\Component\HttpFoundation\File\File')) { + $dataClass = function (Options $options) { + return $options['multiple'] ? null : 'Symfony\Component\HttpFoundation\File\File'; + }; + } $emptyData = function (Options $options) { return $options['multiple'] ? array() : null; From 7de1af4d53e974a1c728c3ba3c61082907e59b2e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 26 Nov 2017 18:11:23 +0100 Subject: [PATCH 15/39] [HttpKernel] Read $_ENV when checking SHELL_VERBOSITY --- src/Symfony/Component/HttpKernel/Log/Logger.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Log/Logger.php b/src/Symfony/Component/HttpKernel/Log/Logger.php index 8facb03c5a29d..617efcf13e5a4 100644 --- a/src/Symfony/Component/HttpKernel/Log/Logger.php +++ b/src/Symfony/Component/HttpKernel/Log/Logger.php @@ -42,8 +42,8 @@ public function __construct($minLevel = null, $output = 'php://stderr', callable if (null === $minLevel) { $minLevel = LogLevel::WARNING; - if (isset($_SERVER['SHELL_VERBOSITY'])) { - switch ((int) $_SERVER['SHELL_VERBOSITY']) { + if (isset($_ENV['SHELL_VERBOSITY']) || isset($_SERVER['SHELL_VERBOSITY'])) { + switch ((int) (isset($_ENV['SHELL_VERBOSITY']) ? $_ENV['SHELL_VERBOSITY'] : $_SERVER['SHELL_VERBOSITY'])) { case -1: $minLevel = LogLevel::ERROR; break; case 1: $minLevel = LogLevel::NOTICE; break; case 2: $minLevel = LogLevel::INFO; break; From 73f1ac5ea2bc153f6a8ba9b9ad03a02996691117 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 26 Nov 2017 18:52:52 +0100 Subject: [PATCH 16/39] [DI] Fix tracking of env vars in exceptions --- .../MergeExtensionConfigurationPass.php | 28 +++++++++------ .../MergeExtensionConfigurationPassTest.php | 34 +++++++++++++++++++ 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php index c3b4d78dd622d..491e2c48589d2 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php @@ -52,18 +52,26 @@ public function process(ContainerBuilder $container) } $config = $resolvingBag->resolveValue($config); - $tmpContainer = new ContainerBuilder($resolvingBag); - $tmpContainer->setResourceTracking($container->isTrackingResources()); - $tmpContainer->addObjectResource($extension); - if ($extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension->getConfiguration($config, $tmpContainer)) { - $tmpContainer->addObjectResource($configuration); - } + try { + $tmpContainer = new ContainerBuilder($resolvingBag); + $tmpContainer->setResourceTracking($container->isTrackingResources()); + $tmpContainer->addObjectResource($extension); + if ($extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension->getConfiguration($config, $tmpContainer)) { + $tmpContainer->addObjectResource($configuration); + } - foreach ($exprLangProviders as $provider) { - $tmpContainer->addExpressionLanguageProvider($provider); - } + foreach ($exprLangProviders as $provider) { + $tmpContainer->addExpressionLanguageProvider($provider); + } - $extension->load($config, $tmpContainer); + $extension->load($config, $tmpContainer); + } catch (\Exception $e) { + if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) { + $container->getParameterBag()->mergeEnvPlaceholders($resolvingBag); + } + + throw $e; + } if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) { // don't keep track of env vars that are *overridden* when configs are merged diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php index 44d1933a80fa8..7f4583a16c95d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php @@ -84,6 +84,22 @@ public function testOverriddenEnvsAreMerged() $this->assertSame(array('BAZ', 'FOO'), array_keys($container->getParameterBag()->getEnvPlaceholders())); $this->assertSame(array('BAZ' => 1, 'FOO' => 0), $container->getEnvCounters()); } + + public function testThrowingExtensionsGetMergedBag() + { + $container = new ContainerBuilder(); + $container->registerExtension(new ThrowingExtension()); + $container->prependExtensionConfig('throwing', array('bar' => '%env(FOO)%')); + + try { + $pass = new MergeExtensionConfigurationPass(); + $pass->process($container); + $this->fail('An exception should have been thrown.'); + } catch (\Exception $e) { + } + + $this->assertSame(array('FOO'), array_keys($container->getParameterBag()->getEnvPlaceholders())); + } } class FooConfiguration implements ConfigurationInterface @@ -125,3 +141,21 @@ public function load(array $configs, ContainerBuilder $container) } } } + +class ThrowingExtension extends Extension +{ + public function getAlias() + { + return 'throwing'; + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new FooConfiguration(); + } + + public function load(array $configs, ContainerBuilder $container) + { + throw new \Exception(); + } +} From df4d6d6e16031501e896d1b1074e9ddd8330576e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 26 Nov 2017 21:27:09 +0100 Subject: [PATCH 17/39] don't override existing verbosity env var --- src/Symfony/Component/HttpKernel/Kernel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index a30d5916f9ae6..2f0453031f2dd 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -120,7 +120,7 @@ public function boot() return; } - if ($this->debug && !isset($_SERVER['SHELL_VERBOSITY'])) { + if ($this->debug && !isset($_ENV['SHELL_VERBOSITY']) && !isset($_SERVER['SHELL_VERBOSITY'])) { putenv('SHELL_VERBOSITY=3'); $_ENV['SHELL_VERBOSITY'] = 3; $_SERVER['SHELL_VERBOSITY'] = 3; From 01edbf78023894c65128019ec62a24785bcdaed4 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 27 Nov 2017 10:12:25 +0100 Subject: [PATCH 18/39] [FrameworkBundle] Make MicroKernelTraitTest green --- .../FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php index 2cb1ba8f7d67b..539306fcea2b9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php @@ -18,7 +18,7 @@ class MicroKernelTraitTest extends TestCase { public function test() { - $kernel = new ConcreteMicroKernel('test', true); + $kernel = new ConcreteMicroKernel('test', false); $kernel->boot(); $request = Request::create('/'); @@ -31,7 +31,7 @@ public function test() public function testAsEventSubscriber() { - $kernel = new ConcreteMicroKernel('test', true); + $kernel = new ConcreteMicroKernel('test', false); $kernel->boot(); $request = Request::create('/danger'); From 6e622c668b40366ed35be456c7e7c5476b1ca3c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 26 Nov 2017 15:56:42 +0100 Subject: [PATCH 19/39] [DI] Prevent a ReflectionException during cache:clear when the parent class doesn't exist --- .../DependencyInjection/Dumper/PhpDumper.php | 2 +- .../Tests/Fixtures/ParentNotExists.php | 7 +++++++ .../containers/container_inline_requires.php | 2 ++ .../Tests/Fixtures/php/container_inline_requires.php | 12 ++++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/ParentNotExists.php diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 0a33fb3083a16..ca9f26309407c 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -378,7 +378,7 @@ private function collectLineage($class, array &$lineage) if (isset($lineage[$class])) { return; } - if (!$r = $this->container->getReflectionClass($class)) { + if (!$r = $this->container->getReflectionClass($class, false)) { return; } if ($this->container instanceof $class) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ParentNotExists.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ParentNotExists.php new file mode 100644 index 0000000000000..ae637f917fef0 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ParentNotExists.php @@ -0,0 +1,7 @@ +register(HotPath\C1::class)->addTag('container.hot_path')->setPublic(true); $container->register(HotPath\C2::class)->addArgument(new Reference(HotPath\C3::class))->setPublic(true); $container->register(HotPath\C3::class); +$container->register(ParentNotExists::class)->setPublic(true); return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_inline_requires.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_inline_requires.php index d3d94290fd6a9..c888dc997d604 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_inline_requires.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_inline_requires.php @@ -32,8 +32,10 @@ public function __construct() 'symfony\\component\\dependencyinjection\\tests\\fixtures\\includes\\hotpath\\c1' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C1', 'symfony\\component\\dependencyinjection\\tests\\fixtures\\includes\\hotpath\\c2' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C2', 'symfony\\component\\dependencyinjection\\tests\\fixtures\\includes\\hotpath\\c3' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C3', + 'symfony\\component\\dependencyinjection\\tests\\fixtures\\parentnotexists' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\ParentNotExists', ); $this->methodMap = array( + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\ParentNotExists' => 'getParentNotExistsService', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C1' => 'getC1Service', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C2' => 'getC2Service', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C3' => 'getC3Service', @@ -75,6 +77,16 @@ public function isFrozen() return true; } + /** + * Gets the public 'Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists + */ + protected function getParentNotExistsService() + { + return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists(); + } + /** * Gets the public 'Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C1' shared service. * From eab90ef1cf61b8921a50d1c0da99c9e6900ffb20 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 27 Nov 2017 15:08:03 +0100 Subject: [PATCH 20/39] modify definitions only if the do exist If the `TranslatorPass` is used an application without the Console component, the commands will not be registered. Thus, their service definitions must not be modified. --- .../Translation/DependencyInjection/TranslatorPass.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php b/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php index a5e4097843c07..db2a2a1ecc46b 100644 --- a/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php +++ b/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php @@ -80,8 +80,15 @@ public function process(ContainerBuilder $container) ->replaceArgument(3, $loaders) ; - if ($container->hasParameter('twig.default_path')) { + if (!$container->hasParameter('twig.default_path')) { + return; + } + + if ($container->hasDefinition($this->debugCommandServiceId)) { $container->getDefinition($this->debugCommandServiceId)->replaceArgument(4, $container->getParameter('twig.default_path')); + } + + if ($container->hasDefinition($this->updateCommandServiceId)) { $container->getDefinition($this->updateCommandServiceId)->replaceArgument(5, $container->getParameter('twig.default_path')); } } From 3a873f887762d1ca7df8693276c2b7ebe583dff3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 27 Nov 2017 16:08:42 +0100 Subject: [PATCH 21/39] [HttpKernel] Better handling of legacy cache --- src/Symfony/Component/HttpKernel/Kernel.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 2f0453031f2dd..97d23b2096b89 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -581,8 +581,11 @@ protected function initializeContainer() $class = $this->getContainerClass(); $cacheDir = $this->warmupDir ?: $this->getCacheDir(); $cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug); - $fresh = true; - if (!$cache->isFresh()) { + if ($fresh = $cache->isFresh()) { + $this->container = require $cache->getPath(); + $fresh = \is_object($this->container); + } + if (!$fresh) { if ($this->debug) { $collectedLogs = array(); $previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) { @@ -632,11 +635,9 @@ protected function initializeContainer() $oldContainer = file_exists($cache->getPath()) && is_object($oldContainer = @include $cache->getPath()) ? new \ReflectionClass($oldContainer) : false; $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); - - $fresh = false; + $this->container = require $cache->getPath(); } - $this->container = require $cache->getPath(); $this->container->set('kernel', $this); if ($fresh) { From a44f8a5b1ae18a8899fa2ea0369f87404d439ab8 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Mon, 27 Nov 2017 18:35:30 +0100 Subject: [PATCH 22/39] [FrameworkBundle][Serializer] Remove YamlEncoder definition if Yaml component isn't installed --- .../DependencyInjection/FrameworkExtension.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index cb0a53d5d6072..8b21f6c644a5a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -56,6 +56,7 @@ use Symfony\Component\Validator\ObjectInitializerInterface; use Symfony\Component\WebLink\HttpHeaderSerializer; use Symfony\Component\Workflow; +use Symfony\Component\Yaml\Yaml; /** * FrameworkExtension. @@ -1172,6 +1173,10 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->removeDefinition('serializer.normalizer.object'); } + if (!class_exists(Yaml::class)) { + $container->removeDefinition('serializer.encoder.yaml'); + } + $serializerLoaders = array(); if (isset($config['enable_annotations']) && $config['enable_annotations']) { if (!$this->annotationsConfigEnabled) { From 7fe3fe824547101a9c0d66a558ff1d4873dfe5f0 Mon Sep 17 00:00:00 2001 From: Amrouche Hamza Date: Tue, 28 Nov 2017 06:31:17 +0100 Subject: [PATCH 23/39] [HttpFoundation] AutExpireFlashBag should not clear new flashes --- .../HttpFoundation/Session/Flash/AutoExpireFlashBag.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php b/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php index 59ba309005b38..0ed6600974371 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php +++ b/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php @@ -106,7 +106,7 @@ public function get($type, array $default = array()) public function all() { $return = $this->flashes['display']; - $this->flashes = array('new' => array(), 'display' => array()); + $this->flashes['display'] = array(); return $return; } From 706201efe46580e89d3f24d6fa097e065c43a2c8 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 28 Nov 2017 08:49:55 +0000 Subject: [PATCH 24/39] Test that it do not remove the new flashes when displaying the existing ones --- .../Tests/Session/Flash/AutoExpireFlashBagTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/AutoExpireFlashBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/AutoExpireFlashBagTest.php index 18b71a5021fbb..0e0724a3249a4 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/AutoExpireFlashBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/AutoExpireFlashBagTest.php @@ -150,4 +150,12 @@ public function testClear() { $this->assertEquals(array('notice' => array('A previous flash message')), $this->bag->clear()); } + + public function testDoNotRemoveTheNewFlashesWhenDisplayingTheExistingOnes() + { + $this->bag->add('success', 'Something'); + $this->bag->all(); + + $this->assertEquals(array('new' => array('success' => array('Something')), 'display' => array()), $this->array); + } } From 6e87382bfaf2ca5fa75f307abf328495961f6a3a Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Tue, 28 Nov 2017 09:21:56 +0000 Subject: [PATCH 25/39] Do not cache cache attributes if `attributes` is in the context --- .../Normalizer/AbstractObjectNormalizer.php | 4 +++ .../Tests/Normalizer/ObjectNormalizerTest.php | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 68e86da1f21e2..29eab6f1ca8b4 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -126,6 +126,10 @@ protected function getAttributes($object, $format = null, array $context) return $allowedAttributes; } + if (isset($context['attributes'])) { + return $this->extractAttributes($object, $format, $context); + } + if (isset($this->attributesCache[$class])) { return $this->attributesCache[$class]; } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index b2d7b872f4d94..9407fdf52d7b0 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -713,6 +713,37 @@ public function testAttributesContextDenormalizeConstructor() 'inner' => array('foo' => 'foo', 'bar' => 'bar'), ), DummyWithConstructorObjectAndDefaultValue::class, null, $context)); } + + public function testNormalizeSameObjectWithDifferentAttributes() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $this->normalizer = new ObjectNormalizer($classMetadataFactory); + $serializer = new Serializer(array($this->normalizer)); + $this->normalizer->setSerializer($serializer); + + $dummy = new ObjectOuter(); + $dummy->foo = new ObjectInner(); + $dummy->foo->foo = 'foo.foo'; + $dummy->foo->bar = 'foo.bar'; + + $dummy->bar = new ObjectInner(); + $dummy->bar->foo = 'bar.foo'; + $dummy->bar->bar = 'bar.bar'; + + $this->assertEquals(array( + 'foo' => array( + 'bar' => 'foo.bar', + ), + 'bar' => array( + 'foo' => 'bar.foo', + ), + ), $this->normalizer->normalize($dummy, 'json', array( + 'attributes' => array( + 'foo' => array('bar'), + 'bar' => array('foo'), + ), + ))); + } } class ObjectDummy From ee3b6fe642170b49949c8f0a4686e4b2fe0b75d6 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 28 Nov 2017 14:19:16 +0100 Subject: [PATCH 26/39] [HttpKernel] Keep legacy container files for concurrent requests --- src/Symfony/Component/HttpKernel/Kernel.php | 15 +++++++++++++-- .../Component/HttpKernel/Tests/KernelTest.php | 3 ++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 2f0453031f2dd..9d9ad366d8058 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -643,8 +643,19 @@ protected function initializeContainer() return; } - if ($oldContainer && get_class($this->container) !== $oldContainer->name) { - (new Filesystem())->remove(dirname($oldContainer->getFileName())); + if ($oldContainer) { + // Because concurrent requests might still be using them, + // 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 (@unlink($legacyContainer)) { + (new Filesystem())->remove(substr($legacyContainer, 0, -16)); + } + } + if (get_class($this->container) !== $oldContainer->name) { + touch($oldContainerDir.'.legacyContainer'); + } } 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 21342f026e4d7..6dee293ea9de2 100644 --- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -832,7 +832,8 @@ public function testKernelReset() $kernel->boot(); $this->assertTrue(get_class($kernel->getContainer()) !== $containerClass); - $this->assertFileNotExists($containerFile); + $this->assertFileExists($containerFile); + $this->assertFileExists(dirname($containerFile).'.legacyContainer'); } public function testKernelPass() From b988aa7c59f457ab4d4f03d45ed53d04df4cea56 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 29 Nov 2017 09:00:03 +0100 Subject: [PATCH 27/39] [DI] Fix infinite loop in InlineServiceDefinitionsPass --- .../Compiler/InlineServiceDefinitionsPass.php | 5 +---- .../InlineServiceDefinitionsPassTest.php | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index f2ef363c837cc..ff9ac315b0af8 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -61,10 +61,7 @@ protected function processValue($value, $isRoot = false) $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); $this->inlinedServiceIds[$id][] = $this->currentId; - if ($definition->isShared()) { - return $definition; - } - $value = clone $definition; + return $definition->isShared() ? $definition : clone $definition; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php index 26b24fa713242..09e4913664784 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php @@ -92,6 +92,25 @@ public function testProcessDoesInlineNonSharedService() $this->assertNotSame($container->getDefinition('bar'), $arguments[2]); } + public function testProcessInlinesMixedServicesLoop() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->addArgument(new Reference('bar')) + ->setShared(false) + ; + $container + ->register('bar') + ->setPublic(false) + ->addMethodCall('setFoo', array(new Reference('foo'))) + ; + + $this->process($container); + + $this->assertEquals($container->getDefinition('foo')->getArgument(0), $container->getDefinition('bar')); + } + public function testProcessInlinesIfMultipleReferencesButAllFromTheSameDefinition() { $container = new ContainerBuilder(); From 2744b41c83d9810eaeaf984f7a40f101d8fcb92e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 27 Nov 2017 21:00:44 +0100 Subject: [PATCH 28/39] [DI] Clear service reference graph --- .../Component/DependencyInjection/Compiler/Compiler.php | 2 ++ .../Compiler/ServiceReferenceGraph.php | 3 +++ .../Compiler/ServiceReferenceGraphNode.php | 8 ++++++++ .../Component/DependencyInjection/Dumper/PhpDumper.php | 1 + 4 files changed, 14 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php index a8499a02bc5f1..e39c9598549d8 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php @@ -159,6 +159,8 @@ public function compile(ContainerBuilder $container) } throw $e; + } finally { + $this->getServiceReferenceGraph()->clear(); } } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php index fb04c9a0b6e87..d040e66c4dac7 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php @@ -75,6 +75,9 @@ public function getNodes() */ public function clear() { + foreach ($this->nodes as $node) { + $node->clear(); + } $this->nodes = array(); } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php index 3219d06e96989..1052d2c6ae59e 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php @@ -107,4 +107,12 @@ public function getValue() { return $this->value; } + + /** + * Clears all edges. + */ + public function clear() + { + $this->inEdges = $this->outEdges = array(); + } } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index ca9f26309407c..3d20c42279547 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -142,6 +142,7 @@ public function dump(array $options = array()) $currentPath = array($id => $id); $this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath); } + $this->container->getCompiler()->getServiceReferenceGraph()->clear(); $this->docStar = $options['debug'] ? '*' : ''; From 9d553f533f3c4d3ea00145f785b79c86305d3186 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 29 Nov 2017 10:06:19 +0100 Subject: [PATCH 29/39] [HttpKernel] Fix race condition when clearing old containers --- src/Symfony/Component/HttpKernel/Kernel.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 62f71d622281f..789eb268cf5a8 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -644,19 +644,18 @@ protected function initializeContainer() return; } - if ($oldContainer) { + if ($oldContainer && get_class($this->container) !== $oldContainer->name) { // Because concurrent requests might still be using them, // 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 (@unlink($legacyContainer)) { + if ($oldContainerDir.'.legacyContainer' !== $legacyContainer && @unlink($legacyContainer)) { (new Filesystem())->remove(substr($legacyContainer, 0, -16)); } } - if (get_class($this->container) !== $oldContainer->name) { - touch($oldContainerDir.'.legacyContainer'); - } + + touch($oldContainerDir.'.legacyContainer'); } if ($this->container->has('cache_warmer')) { From d84b47fe9de42e7f9f1f4722c4490d95719eb9c8 Mon Sep 17 00:00:00 2001 From: Amrouche Hamza Date: Wed, 29 Nov 2017 07:49:34 +0100 Subject: [PATCH 30/39] [HttpKernel] Arrays with scalar values passed to ESI fragment renderer throw deprecation notice --- .../Fragment/AbstractSurrogateFragmentRenderer.php | 4 ++-- .../Tests/Fragment/EsiFragmentRendererTest.php | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php index e955cd0d43c6f..9526908a37858 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php @@ -98,8 +98,8 @@ private function generateSignedFragmentUri($uri, Request $request) private function containsNonScalars(array $values) { foreach ($values as $value) { - if (is_array($value) && $this->containsNonScalars($value)) { - return true; + if (is_array($value)) { + return $this->containsNonScalars($value); } elseif (!is_scalar($value) && null !== $value) { return true; } diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php index bfe922e22c76b..7099b4d7e8bf6 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php @@ -34,7 +34,15 @@ public function testRenderFallbackWithObjectAttributesIsDeprecated() { $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true), new UriSigner('foo')); $request = Request::create('/'); - $reference = new ControllerReference('main_controller', array('foo' => array('a' => array(), 'b' => new \stdClass())), array()); + $reference = new ControllerReference('main_controller', array('foo' => new \stdClass()), array()); + $strategy->render($reference, $request); + } + + public function testRenderFallbackWithScalarIsNotDeprecated() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true), new UriSigner('foo')); + $request = Request::create('/'); + $reference = new ControllerReference('main_controller', array('foo' => array(true)), array()); $strategy->render($reference, $request); } From 9897da4c020b9188965132330ffc5ff8ff20480e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 29 Nov 2017 14:05:31 +0100 Subject: [PATCH 31/39] remove upgrade instructions for kernel.root_dir --- UPGRADE-3.3.md | 6 ------ UPGRADE-4.0.md | 6 ------ 2 files changed, 12 deletions(-) diff --git a/UPGRADE-3.3.md b/UPGRADE-3.3.md index 1b61c352238fe..2efc4df740d83 100644 --- a/UPGRADE-3.3.md +++ b/UPGRADE-3.3.md @@ -254,12 +254,6 @@ HttpFoundation HttpKernel ----------- - * Deprecated the `kernel.root_dir` parameter. Use the new `kernel.project_dir` - parameter instead. - - * Deprecated the `Kernel::getRootDir()` method. Use the new `Kernel::getProjectDir()` - method instead. - * The `Extension::addClassesToCompile()` and `Extension::getClassesToCompile()` methods have been deprecated and will be removed in 4.0. * The `Psr6CacheClearer::addPool()` method has been deprecated. Pass an array diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index b1bfc214837a4..5adaa8b794292 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -361,12 +361,6 @@ HttpFoundation HttpKernel ---------- - * Removed the `kernel.root_dir` parameter. Use the `kernel.project_dir` parameter - instead. - - * Removed the `Kernel::getRootDir()` method. Use the `Kernel::getProjectDir()` - method instead. - * The `Extension::addClassesToCompile()` and `Extension::getClassesToCompile()` methods have been removed. * Possibility to pass non-scalar values as URI attributes to the ESI and SSI From de5eecc5fa220c145cc8dcc4ac1120aa6dc6e80a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 27 Nov 2017 21:00:44 +0100 Subject: [PATCH 32/39] [DI] Fix circular reference when using setters --- .../DependencyInjection/ContainerBuilder.php | 62 ++-- .../DependencyInjection/Dumper/PhpDumper.php | 335 ++++++++---------- .../Tests/ContainerBuilderTest.php | 22 +- .../Tests/Dumper/PhpDumperTest.php | 56 +-- .../containers/container_almost_circular.php | 30 ++ .../Tests/Fixtures/php/services9_as_files.txt | 8 +- .../Tests/Fixtures/php/services9_compiled.php | 8 +- ...p => services_almost_circular_private.php} | 68 ++++ ...hp => services_almost_circular_public.php} | 95 +++++ ...uires.php => services_inline_requires.php} | 0 .../Tests/Fixtures/php/services_locator.php | 4 +- 11 files changed, 404 insertions(+), 284 deletions(-) rename src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/{container_almost_circular_private.php => services_almost_circular_private.php} (53%) rename src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/{container_almost_circular_public.php => services_almost_circular_public.php} (54%) rename src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/{container_inline_requires.php => services_inline_requires.php} (100%) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index b7381de7e5811..b96426d487092 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -570,10 +570,13 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV return $this->doGet($id, $invalidBehavior); } - private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = array()) { $id = $this->normalizeId($id); + if (isset($inlineServices[$id])) { + return $inlineServices[$id]; + } if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) { return parent::get($id, $invalidBehavior); } @@ -582,7 +585,7 @@ private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_ } if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) { - return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior); + return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices); } try { @@ -599,7 +602,7 @@ private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_ $this->{$loading}[$id] = true; try { - $service = $this->createService($definition, new \SplObjectStorage(), $id); + $service = $this->createService($definition, $inlineServices, $id); } finally { unset($this->{$loading}[$id]); } @@ -1054,10 +1057,10 @@ public function findDefinition($id) * @throws RuntimeException When the service is a synthetic service * @throws InvalidArgumentException When configure callable is not callable */ - private function createService(Definition $definition, \SplObjectStorage $inlinedDefinitions, $id = null, $tryProxy = true) + private function createService(Definition $definition, array &$inlineServices, $id = null, $tryProxy = true) { - if (null === $id && isset($inlinedDefinitions[$definition])) { - return $inlinedDefinitions[$definition]; + if (null === $id && isset($inlineServices[$h = spl_object_hash($definition)])) { + return $inlineServices[$h]; } if ($definition instanceof ChildDefinition) { @@ -1078,11 +1081,11 @@ private function createService(Definition $definition, \SplObjectStorage $inline ->instantiateProxy( $this, $definition, - $id, function () use ($definition, $inlinedDefinitions, $id) { - return $this->createService($definition, $inlinedDefinitions, $id, false); + $id, function () use ($definition, &$inlineServices, $id) { + return $this->createService($definition, $inlineServices, $id, false); } ); - $this->shareService($definition, $proxy, $id, $inlinedDefinitions); + $this->shareService($definition, $proxy, $id, $inlineServices); return $proxy; } @@ -1093,7 +1096,7 @@ private function createService(Definition $definition, \SplObjectStorage $inline require_once $parameterBag->resolveValue($definition->getFile()); } - $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlinedDefinitions); + $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlineServices); if (null !== $id && $definition->isShared() && isset($this->services[$id]) && ($tryProxy || !$definition->isLazy())) { return $this->services[$id]; @@ -1101,7 +1104,7 @@ private function createService(Definition $definition, \SplObjectStorage $inline if (null !== $factory = $definition->getFactory()) { if (is_array($factory)) { - $factory = array($this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlinedDefinitions), $factory[1]); + $factory = array($this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices), $factory[1]); } elseif (!is_string($factory)) { throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id)); } @@ -1130,16 +1133,16 @@ private function createService(Definition $definition, \SplObjectStorage $inline if ($tryProxy || !$definition->isLazy()) { // share only if proxying failed, or if not a proxy - $this->shareService($definition, $service, $id, $inlinedDefinitions); + $this->shareService($definition, $service, $id, $inlineServices); } - $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlinedDefinitions); + $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlineServices); foreach ($properties as $name => $value) { $service->$name = $value; } foreach ($definition->getMethodCalls() as $call) { - $this->callMethod($service, $call, $inlinedDefinitions); + $this->callMethod($service, $call, $inlineServices); } if ($callable = $definition->getConfigurator()) { @@ -1147,9 +1150,9 @@ private function createService(Definition $definition, \SplObjectStorage $inline $callable[0] = $parameterBag->resolveValue($callable[0]); if ($callable[0] instanceof Reference) { - $callable[0] = $this->doGet((string) $callable[0], $callable[0]->getInvalidBehavior()); + $callable[0] = $this->doGet((string) $callable[0], $callable[0]->getInvalidBehavior(), $inlineServices); } elseif ($callable[0] instanceof Definition) { - $callable[0] = $this->createService($callable[0], $inlinedDefinitions); + $callable[0] = $this->createService($callable[0], $inlineServices); } } @@ -1173,14 +1176,14 @@ private function createService(Definition $definition, \SplObjectStorage $inline */ public function resolveServices($value) { - return $this->doResolveServices($value, new \SplObjectStorage()); + return $this->doResolveServices($value); } - private function doResolveServices($value, \SplObjectStorage $inlinedDefinitions) + private function doResolveServices($value, array &$inlineServices = array()) { if (is_array($value)) { foreach ($value as $k => $v) { - $value[$k] = $this->doResolveServices($v, $inlinedDefinitions); + $value[$k] = $this->doResolveServices($v, $inlineServices); } } elseif ($value instanceof ServiceClosureArgument) { $reference = $value->getValues()[0]; @@ -1223,9 +1226,9 @@ private function doResolveServices($value, \SplObjectStorage $inlinedDefinitions return $count; }); } elseif ($value instanceof Reference) { - $value = $this->doGet((string) $value, $value->getInvalidBehavior()); + $value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices); } elseif ($value instanceof Definition) { - $value = $this->createService($value, $inlinedDefinitions); + $value = $this->createService($value, $inlineServices); } elseif ($value instanceof Expression) { $value = $this->getExpressionLanguage()->evaluate($value, array('container' => $this)); } @@ -1540,7 +1543,7 @@ private function getProxyInstantiator() return $this->proxyInstantiator; } - private function callMethod($service, $call, \SplObjectStorage $inlinedDefinitions) + private function callMethod($service, $call, array &$inlineServices) { foreach (self::getServiceConditionals($call[1]) as $s) { if (!$this->has($s)) { @@ -1548,12 +1551,12 @@ private function callMethod($service, $call, \SplObjectStorage $inlinedDefinitio } } foreach (self::getInitializedConditionals($call[1]) as $s) { - if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) { + if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE, $inlineServices)) { return; } } - call_user_func_array(array($service, $call[0]), $this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlinedDefinitions)); + call_user_func_array(array($service, $call[0]), $this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlineServices)); } /** @@ -1563,14 +1566,11 @@ private function callMethod($service, $call, \SplObjectStorage $inlinedDefinitio * @param object $service * @param string|null $id */ - private function shareService(Definition $definition, $service, $id, \SplObjectStorage $inlinedDefinitions) + private function shareService(Definition $definition, $service, $id, array &$inlineServices) { - if (!$definition->isShared()) { - return; - } - if (null === $id) { - $inlinedDefinitions[$definition] = $service; - } else { + $inlineServices[null !== $id ? $id : spl_object_hash($definition)] = $service; + + if (null !== $id && $definition->isShared()) { $this->services[$id] = $service; unset($this->loading[$id], $this->alreadyLoading[$id]); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index ca9f26309407c..13f163b4ded4b 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -51,7 +51,6 @@ class PhpDumper extends Dumper */ const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'; - private $inlinedDefinitions; private $definitionVariables; private $referenceVariables; private $variableCount; @@ -84,8 +83,6 @@ public function __construct(ContainerBuilder $container) } parent::__construct($container); - - $this->inlinedDefinitions = new \SplObjectStorage(); } /** @@ -270,60 +267,48 @@ private function getProxyDumper() /** * Generates Service local temp variables. * - * @param string $cId - * @param Definition $definition - * @param array $inlinedDefinitions - * * @return string */ - private function addServiceLocalTempVariables($cId, Definition $definition, array $inlinedDefinitions) + private function addServiceLocalTempVariables($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions, \SplObjectStorage $allInlinedDefinitions) { - static $template = " \$%s = %s;\n"; + $allCalls = $calls = $behavior = array(); - array_unshift($inlinedDefinitions, $definition); + foreach ($allInlinedDefinitions as $def) { + $arguments = array($def->getArguments(), $def->getFactory(), $def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()); + $this->getServiceCallsFromArguments($arguments, $allCalls, false, $cId, $behavior, $allInlinedDefinitions[$def]); + } - $collectLineage = $this->inlineRequires && !$this->isHotPath($definition); - $isNonLazyShared = isset($this->circularReferences[$cId]) && !$this->getProxyDumper()->isProxyCandidate($definition) && $definition->isShared(); - $lineage = $calls = $behavior = array(); - foreach ($inlinedDefinitions as $iDefinition) { - if ($collectLineage && !$iDefinition->isDeprecated() && $class = is_array($factory = $iDefinition->getFactory()) && is_string($factory[0]) ? $factory[0] : $iDefinition->getClass()) { - $this->collectLineage($class, $lineage); + $isPreInstance = isset($inlinedDefinitions[$definition]) && isset($this->circularReferences[$cId]) && !$this->getProxyDumper()->isProxyCandidate($definition) && $definition->isShared(); + foreach ($inlinedDefinitions as $def) { + $this->getServiceCallsFromArguments(array($def->getArguments(), $def->getFactory()), $calls, $isPreInstance, $cId); + if ($def !== $definition) { + $arguments = array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()); + $this->getServiceCallsFromArguments($arguments, $calls, $isPreInstance && !$this->hasReference($cId, $arguments, true), $cId); } - $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior, $isNonLazyShared, $cId); - $isPreInstantiation = $isNonLazyShared && $iDefinition !== $definition && !$this->hasReference($cId, $iDefinition->getMethodCalls(), true) && !$this->hasReference($cId, $iDefinition->getProperties(), true); - $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior, $isPreInstantiation, $cId); - $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior, $isPreInstantiation, $cId); - $this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior, $isPreInstantiation, $cId); - $this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior, $isNonLazyShared, $cId); + } + if (!isset($inlinedDefinitions[$definition])) { + $arguments = array($definition->getProperties(), $definition->getMethodCalls(), $definition->getConfigurator()); + $this->getServiceCallsFromArguments($arguments, $calls, false, $cId); } $code = ''; foreach ($calls as $id => $callCount) { - if ('service_container' === $id || $id === $cId) { + if ('service_container' === $id || $id === $cId || isset($this->referenceVariables[$id])) { continue; } - - if ($collectLineage && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior[$id] && $this->container->has($id) - && $this->isTrivialInstance($iDefinition = $this->container->findDefinition($id)) - && $class = is_array($factory = $iDefinition->getFactory()) && is_string($factory[0]) ? $factory[0] : $iDefinition->getClass() - ) { - $this->collectLineage($class, $lineage); + if ($callCount <= 1 && $allCalls[$id] <= 1) { + continue; } - if ($callCount > 1) { - $name = $this->getNextVariableName(); - $this->referenceVariables[$id] = new Variable($name); + $name = $this->getNextVariableName(); + $this->referenceVariables[$id] = new Variable($name); - if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) { - $code .= sprintf($template, $name, $this->getServiceCall($id)); - } else { - $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, $behavior[$id]))); - } - } + $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id] ? new Reference($id, $behavior[$id]) : null; + $code .= sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($id, $reference)); } if ('' !== $code) { - if ($isNonLazyShared) { + if ($isPreInstance) { $code .= <<services['$cId'])) { @@ -336,14 +321,6 @@ private function addServiceLocalTempVariables($cId, Definition $definition, arra $code .= "\n"; } - if ($lineage && $lineage = array_diff_key(array_flip($lineage), $this->inlinedRequires)) { - $code = "\n".$code; - - foreach (array_reverse($lineage) as $file => $class) { - $code = sprintf(" require_once %s;\n", $file).$code; - } - } - return $code; } @@ -353,11 +330,6 @@ private function analyzeCircularReferences(array $edges, &$checkedNodes, &$curre $node = $edge->getDestNode(); $id = $node->getId(); - if (isset($checkedNodes[$id])) { - continue; - } - $checkedNodes[$id] = true; - if ($node->getValue() && ($edge->isLazy() || $edge->isWeak())) { // no-op } elseif (isset($currentPath[$id])) { @@ -365,10 +337,11 @@ private function analyzeCircularReferences(array $edges, &$checkedNodes, &$curre $this->circularReferences[$parentId][$id] = $id; $id = $parentId; } - } else { + } elseif (!isset($checkedNodes[$id])) { + $checkedNodes[$id] = true; $currentPath[$id] = $id; $this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath); - array_pop($currentPath); + unset($currentPath[$id]); } } } @@ -428,18 +401,39 @@ private function generateProxyClasses() * * @return string */ - private function addServiceInclude(Definition $definition, array $inlinedDefinitions) + private function addServiceInclude($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions) { - $template = " require_once %s;\n"; $code = ''; - if (null !== $file = $definition->getFile()) { - $code .= sprintf($template, $this->dumpValue($file)); + if ($this->inlineRequires && !$this->isHotPath($definition)) { + $lineage = $calls = $behavior = array(); + foreach ($inlinedDefinitions as $def) { + if (!$def->isDeprecated() && $class = is_array($factory = $def->getFactory()) && is_string($factory[0]) ? $factory[0] : $def->getClass()) { + $this->collectLineage($class, $lineage); + } + $arguments = array($def->getArguments(), $def->getFactory(), $def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()); + $this->getServiceCallsFromArguments($arguments, $calls, false, $cId, $behavior, $inlinedDefinitions[$def]); + } + + foreach ($calls as $id => $callCount) { + if ('service_container' !== $id && $id !== $cId + && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior[$id] + && $this->container->has($id) + && $this->isTrivialInstance($def = $this->container->findDefinition($id)) + && $class = is_array($factory = $def->getFactory()) && is_string($factory[0]) ? $factory[0] : $def->getClass() + ) { + $this->collectLineage($class, $lineage); + } + } + + foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { + $code .= sprintf(" require_once %s;\n", $file); + } } - foreach ($inlinedDefinitions as $definition) { - if (null !== $file = $definition->getFile()) { - $code .= sprintf($template, $this->dumpValue($file)); + foreach ($inlinedDefinitions as $def) { + if ($file = $def->getFile()) { + $code .= sprintf(" require_once %s;\n", $this->dumpValue($file)); } } @@ -453,62 +447,51 @@ private function addServiceInclude(Definition $definition, array $inlinedDefinit /** * Generates the inline definition of a service. * - * @param string $id - * @param array $inlinedDefinitions - * * @return string * * @throws RuntimeException When the factory definition is incomplete * @throws ServiceCircularReferenceException When a circular reference is detected */ - private function addServiceInlinedDefinitions($id, array $inlinedDefinitions) + private function addServiceInlinedDefinitions($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, &$isSimpleInstance) { $code = ''; - $variableMap = $this->definitionVariables; - $nbOccurrences = new \SplObjectStorage(); - $processed = new \SplObjectStorage(); - foreach ($inlinedDefinitions as $definition) { - if (false === $nbOccurrences->contains($definition)) { - $nbOccurrences->offsetSet($definition, 1); - } else { - $i = $nbOccurrences->offsetGet($definition); - $nbOccurrences->offsetSet($definition, $i + 1); + foreach ($inlinedDefinitions as $def) { + if ($definition === $def) { + continue; } - } - - foreach ($inlinedDefinitions as $sDefinition) { - if ($processed->contains($sDefinition)) { + if ($inlinedDefinitions[$def] <= 1 && !$def->getMethodCalls() && !$def->getProperties() && !$def->getConfigurator() && false === strpos($this->dumpValue($def->getClass()), '$')) { continue; } - $processed->offsetSet($sDefinition); - - $class = $this->dumpValue($sDefinition->getClass()); - if ($nbOccurrences->offsetGet($sDefinition) > 1 || $sDefinition->getMethodCalls() || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) { + if (isset($this->definitionVariables[$def])) { + $name = $this->definitionVariables[$def]; + } else { $name = $this->getNextVariableName(); - $variableMap->offsetSet($sDefinition, new Variable($name)); - - // a construct like: - // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a); - // this is an indication for a wrong implementation, you can circumvent this problem - // by setting up your service structure like this: - // $b = new ServiceB(); - // $a = new ServiceA(ServiceB $b); - // $b->setServiceA(ServiceA $a); - if ($this->hasReference($id, $sDefinition->getArguments())) { - throw new ServiceCircularReferenceException($id, array($id)); - } + $this->definitionVariables[$def] = new Variable($name); + } - $code .= $this->addNewInstance($sDefinition, '$'.$name, ' = ', $id); + // a construct like: + // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a); + // this is an indication for a wrong implementation, you can circumvent this problem + // by setting up your service structure like this: + // $b = new ServiceB(); + // $a = new ServiceA(ServiceB $b); + // $b->setServiceA(ServiceA $a); + if ($this->hasReference($id, array($def->getArguments(), $def->getFactory()))) { + throw new ServiceCircularReferenceException($id, array($id)); + } - if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) { - $code .= $this->addServiceProperties($sDefinition, $name); - $code .= $this->addServiceMethodCalls($sDefinition, $name); - $code .= $this->addServiceConfigurator($sDefinition, $name); - } + $code .= $this->addNewInstance($def, '$'.$name, ' = ', $id); - $code .= "\n"; + if (!$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true)) { + $code .= $this->addServiceProperties($def, $name); + $code .= $this->addServiceMethodCalls($def, $name); + $code .= $this->addServiceConfigurator($def, $name); + } else { + $isSimpleInstance = false; } + + $code .= "\n"; } return $code; @@ -559,29 +542,6 @@ private function addServiceInstance($id, Definition $definition, $isSimpleInstan return $code; } - /** - * Checks if the definition is a simple instance. - * - * @param string $id - * @param Definition $definition - * - * @return bool - */ - private function isSimpleInstance($id, Definition $definition, array $inlinedDefinitions) - { - foreach (array_merge(array($definition), $inlinedDefinitions) as $sDefinition) { - if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) { - continue; - } - - if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) { - return false; - } - } - - return true; - } - /** * Checks if the definition is a trivial instance. * @@ -667,27 +627,17 @@ private function addServiceProperties(Definition $definition, $variableName = 'i /** * Generates the inline definition setup. * - * @param string $id - * @param array $inlinedDefinitions - * @param bool $isSimpleInstance - * * @return string * * @throws ServiceCircularReferenceException when the container contains a circular reference */ - private function addServiceInlinedDefinitionsSetup($id, array $inlinedDefinitions, $isSimpleInstance) + private function addServiceInlinedDefinitionsSetup($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, $isSimpleInstance) { $this->referenceVariables[$id] = new Variable('instance'); $code = ''; - $processed = new \SplObjectStorage(); - foreach ($inlinedDefinitions as $iDefinition) { - if ($processed->contains($iDefinition)) { - continue; - } - $processed->offsetSet($iDefinition); - - if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) { + foreach ($inlinedDefinitions as $def) { + if ($definition === $def || !$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true)) { continue; } @@ -697,13 +647,13 @@ private function addServiceInlinedDefinitionsSetup($id, array $inlinedDefinition throw new ServiceCircularReferenceException($id, array($id)); } - $name = (string) $this->definitionVariables->offsetGet($iDefinition); - $code .= $this->addServiceProperties($iDefinition, $name); - $code .= $this->addServiceMethodCalls($iDefinition, $name); - $code .= $this->addServiceConfigurator($iDefinition, $name); + $name = (string) $this->definitionVariables[$def]; + $code .= $this->addServiceProperties($def, $name); + $code .= $this->addServiceMethodCalls($def, $name); + $code .= $this->addServiceConfigurator($def, $name); } - if ('' !== $code) { + if ('' !== $code && ($definition->getProperties() || $definition->getMethodCalls() || $definition->getConfigurator())) { $code .= "\n"; } @@ -828,15 +778,28 @@ protected function {$methodName}($lazyInitialization) $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id))); } - $inlinedDefinitions = $this->getInlinedDefinitions($definition); - $isSimpleInstance = $this->isSimpleInstance($id, $definition, $inlinedDefinitions); + $inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition)); + $constructorDefinitions = $this->getDefinitionsFromArguments(array($definition->getArguments(), $definition->getFactory())); + $otherDefinitions = new \SplObjectStorage(); + + foreach ($inlinedDefinitions as $def) { + if ($def === $definition || isset($constructorDefinitions[$def])) { + $constructorDefinitions[$def] = $inlinedDefinitions[$def]; + } else { + $otherDefinitions[$def] = $inlinedDefinitions[$def]; + } + } + + $isSimpleInstance = !$definition->getProperties() && !$definition->getMethodCalls() && !$definition->getConfigurator(); $code .= - $this->addServiceInclude($definition, $inlinedDefinitions). - $this->addServiceLocalTempVariables($id, $definition, $inlinedDefinitions). - $this->addServiceInlinedDefinitions($id, $inlinedDefinitions). + $this->addServiceInclude($id, $definition, $inlinedDefinitions). + $this->addServiceLocalTempVariables($id, $definition, $constructorDefinitions, $inlinedDefinitions). + $this->addServiceInlinedDefinitions($id, $definition, $constructorDefinitions, $isSimpleInstance). $this->addServiceInstance($id, $definition, $isSimpleInstance). - $this->addServiceInlinedDefinitionsSetup($id, $inlinedDefinitions, $isSimpleInstance). + $this->addServiceLocalTempVariables($id, $definition, $otherDefinitions, $inlinedDefinitions). + $this->addServiceInlinedDefinitions($id, $definition, $otherDefinitions, $isSimpleInstance). + $this->addServiceInlinedDefinitionsSetup($id, $definition, $inlinedDefinitions, $isSimpleInstance). $this->addServiceProperties($definition). $this->addServiceMethodCalls($definition). $this->addServiceConfigurator($definition). @@ -1255,11 +1218,10 @@ private function addInlineRequires() foreach ($this->container->findTaggedServiceIds($this->hotPathTag) as $id => $tags) { $definition = $this->container->getDefinition($id); - $inlinedDefinitions = $this->getInlinedDefinitions($definition); - array_unshift($inlinedDefinitions, $definition); + $inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition)); - foreach ($inlinedDefinitions as $iDefinition) { - if ($class = is_array($factory = $iDefinition->getFactory()) && is_string($factory[0]) ? $factory[0] : $iDefinition->getClass()) { + foreach ($inlinedDefinitions as $def) { + if ($class = is_array($factory = $def->getFactory()) && is_string($factory[0]) ? $factory[0] : $def->getClass()) { $this->collectLineage($class, $lineage); } } @@ -1539,16 +1501,16 @@ private function getServiceConditionals($value) /** * Builds service calls from arguments. */ - private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior, $isPreInstantiation, $callerId) + private function getServiceCallsFromArguments(array $arguments, array &$calls, $isPreInstance, $callerId, array &$behavior = array(), $step = 1) { foreach ($arguments as $argument) { if (is_array($argument)) { - $this->getServiceCallsFromArguments($argument, $calls, $behavior, $isPreInstantiation, $callerId); + $this->getServiceCallsFromArguments($argument, $calls, $isPreInstance, $callerId, $behavior, $step); } elseif ($argument instanceof Reference) { $id = (string) $argument; if (!isset($calls[$id])) { - $calls[$id] = (int) ($isPreInstantiation && isset($this->circularReferences[$callerId][$id])); + $calls[$id] = (int) ($isPreInstance && isset($this->circularReferences[$callerId][$id])); } if (!isset($behavior[$id])) { $behavior[$id] = $argument->getInvalidBehavior(); @@ -1556,52 +1518,35 @@ private function getServiceCallsFromArguments(array $arguments, array &$calls, a $behavior[$id] = min($behavior[$id], $argument->getInvalidBehavior()); } - ++$calls[$id]; + $calls[$id] += $step; } } } - /** - * Returns the inline definition. - * - * @return array - */ - private function getInlinedDefinitions(Definition $definition) + private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null) { - if (false === $this->inlinedDefinitions->contains($definition)) { - $definitions = array_merge( - $this->getDefinitionsFromArguments($definition->getArguments()), - $this->getDefinitionsFromArguments($definition->getMethodCalls()), - $this->getDefinitionsFromArguments($definition->getProperties()), - $this->getDefinitionsFromArguments(array($definition->getConfigurator())), - $this->getDefinitionsFromArguments(array($definition->getFactory())) - ); - - $this->inlinedDefinitions->offsetSet($definition, $definitions); - - return $definitions; + if (null === $definitions) { + $definitions = new \SplObjectStorage(); } - return $this->inlinedDefinitions->offsetGet($definition); - } - - /** - * Gets the definition from arguments. - * - * @return array - */ - private function getDefinitionsFromArguments(array $arguments) - { - $definitions = array(); foreach ($arguments as $argument) { if (is_array($argument)) { - $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument)); - } elseif ($argument instanceof Definition) { - $definitions = array_merge( - $definitions, - $this->getInlinedDefinitions($argument), - array($argument) - ); + $this->getDefinitionsFromArguments($argument, $definitions); + } elseif (!$argument instanceof Definition) { + // no-op + } elseif (isset($definitions[$argument])) { + $definitions[$argument] = 1 + $definitions[$argument]; + } else { + $definitions[$argument] = 1; + $this->getDefinitionsFromArguments($argument->getArguments(), $definitions); + $this->getDefinitionsFromArguments(array($argument->getFactory()), $definitions); + $this->getDefinitionsFromArguments($argument->getProperties(), $definitions); + $this->getDefinitionsFromArguments($argument->getMethodCalls(), $definitions); + $this->getDefinitionsFromArguments(array($argument->getConfigurator()), $definitions); + // move current definition last in the list + $nbOccurences = $definitions[$argument]; + unset($definitions[$argument]); + $definitions[$argument] = $nbOccurences; } } @@ -1656,9 +1601,7 @@ private function hasReference($id, array $arguments, $deep = false, array &$visi continue; } - $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties()); - - if ($this->hasReference($id, $arguments, $deep, $visited)) { + if ($this->hasReference($id, array($service->getArguments(), $service->getFactory(), $service->getProperties(), $service->getMethodCalls(), $service->getConfigurator()), $deep, $visited)) { return true; } } @@ -1740,7 +1683,7 @@ private function dumpValue($value, $interpolate = true) } } elseif ($value instanceof Definition) { if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) { - return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate); + return $this->dumpValue($this->definitionVariables[$value], $interpolate); } if ($value->getMethodCalls()) { throw new RuntimeException('Cannot dump definitions which have method calls.'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 0685b9238fbd6..bbc2c01ef075a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1249,24 +1249,26 @@ public function testUninitializedReference() $this->assertEquals(array('foo1' => new \stdClass(), 'foo3' => new \stdClass()), iterator_to_array($bar->iter)); } - public function testAlmostCircularPrivate() + /** + * @dataProvider provideAlmostCircular + */ + public function testAlmostCircular($visibility) { - $public = false; $container = include __DIR__.'/Fixtures/containers/container_almost_circular.php'; $foo = $container->get('foo'); - $this->assertSame($foo, $foo->bar->foobar->foo); - } - public function testAlmostCircularPublic() - { - $public = true; - $container = include __DIR__.'/Fixtures/containers/container_almost_circular.php'; + $foo2 = $container->get('foo2'); + $this->assertSame($foo2, $foo2->bar->foobar->foo); - $foo = $container->get('foo'); + $this->assertSame(array(), (array) $container->get('foobar4')); + } - $this->assertSame($foo, $foo->bar->foobar->foo); + public function provideAlmostCircular() + { + yield array('public'); + yield array('private'); } public function testRegisterForAutoconfiguration() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 4e8da769f4276..b70dc7b172b28 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -302,21 +302,6 @@ public function testOverrideServiceWhenUsingADumpedContainer() $this->assertSame($decorator, $container->get('decorator_service'), '->set() overrides an already defined service'); } - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException - */ - public function testCircularReference() - { - $container = new ContainerBuilder(); - $container->register('foo', 'stdClass')->addArgument(new Reference('bar'))->setPublic(true); - $container->register('bar', 'stdClass')->setPublic(false)->addMethodCall('setA', array(new Reference('baz'))); - $container->register('baz', 'stdClass')->addMethodCall('setA', array(new Reference('foo')))->setPublic(true); - $container->compile(); - - $dumper = new PhpDumper($container); - $dumper->dump(); - } - public function testDumpAutowireData() { $container = include self::$fixturesPath.'/containers/container24.php'; @@ -774,38 +759,35 @@ public function testUninitializedReference() $this->assertEquals(array('foo1' => new \stdClass(), 'foo3' => new \stdClass()), iterator_to_array($bar->iter)); } - public function testAlmostCircularPrivate() + /** + * @dataProvider provideAlmostCircular + */ + public function testAlmostCircular($visibility) { - $public = false; $container = include self::$fixturesPath.'/containers/container_almost_circular.php'; $container->compile(); $dumper = new PhpDumper($container); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/container_almost_circular_private.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Almost_Circular_Private'))); + $container = 'Symfony_DI_PhpDumper_Test_Almost_Circular_'.ucfirst($visibility); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_almost_circular_'.$visibility.'.php', $dumper->dump(array('class' => $container))); - require self::$fixturesPath.'/php/container_almost_circular_private.php'; + require self::$fixturesPath.'/php/services_almost_circular_'.$visibility.'.php'; - $container = new \Symfony_DI_PhpDumper_Test_Almost_Circular_Private(); - $foo = $container->get('foo'); + $container = new $container(); + $foo = $container->get('foo'); $this->assertSame($foo, $foo->bar->foobar->foo); - } - public function testAlmostCircularPublic() - { - $public = true; - $container = include self::$fixturesPath.'/containers/container_almost_circular.php'; - $container->compile(); - $dumper = new PhpDumper($container); + $foo2 = $container->get('foo2'); + $this->assertSame($foo2, $foo2->bar->foobar->foo); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/container_almost_circular_public.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Almost_Circular_Public'))); - - require self::$fixturesPath.'/php/container_almost_circular_public.php'; - - $container = new \Symfony_DI_PhpDumper_Test_Almost_Circular_Public(); - $foo = $container->get('foo'); + $this->assertSame(array(), (array) $container->get('foobar4')); + } - $this->assertSame($foo, $foo->bar->foobar->foo); + public function provideAlmostCircular() + { + yield array('public'); + yield array('private'); } public function testHotPathOptimizations() @@ -815,12 +797,12 @@ public function testHotPathOptimizations() $container->compile(); $dumper = new PhpDumper($container); - $dump = $dumper->dump(array('hot_path_tag' => 'container.hot_path', 'inline_class_loader_parameter' => 'inline_requires', 'file' => self::$fixturesPath.'/php/container_inline_requires.php')); + $dump = $dumper->dump(array('hot_path_tag' => 'container.hot_path', 'inline_class_loader_parameter' => 'inline_requires', 'file' => self::$fixturesPath.'/php/services_inline_requires.php')); if ('\\' === DIRECTORY_SEPARATOR) { $dump = str_replace("'\\\\includes\\\\HotPath\\\\", "'/includes/HotPath/", $dump); } - $this->assertStringEqualsFile(self::$fixturesPath.'/php/container_inline_requires.php', $dump); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_inline_requires.php', $dump); } public function testDumpHandlesLiteralClassWithRootNamespace() 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 eaef674a3ec5b..6d73b3ec6c774 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 @@ -5,8 +5,11 @@ use Symfony\Component\DependencyInjection\Dumper\PhpDumper; use Symfony\Component\DependencyInjection\Reference; +$public = 'public' === $visibility; $container = new ContainerBuilder(); +// same visibility for deps + $container->register('foo', FooCircular::class)->setPublic(true) ->addArgument(new Reference('bar')); @@ -16,4 +19,31 @@ $container->register('foobar', FoobarCircular::class)->setPublic($public) ->addArgument(new Reference('foo')); +// mixed visibility for deps + +$container->register('foo2', FooCircular::class)->setPublic(true) + ->addArgument(new Reference('bar2')); + +$container->register('bar2', BarCircular::class)->setPublic(!$public) + ->addMethodCall('addFoobar', array(new Reference('foobar2'))); + +$container->register('foobar2', FoobarCircular::class)->setPublic($public) + ->addArgument(new Reference('foo2')); + +// simple inline setter with internal reference + +$container->register('bar3', BarCircular::class)->setPublic(true) + ->addMethodCall('addFoobar', array(new Reference('foobar3'), new Reference('foobar3'))); + +$container->register('foobar3', FoobarCircular::class)->setPublic($public); + +// loop with non-shared dep + +$container->register('foo4', 'stdClass')->setPublic($public) + ->setShared(false) + ->setProperty('foobar', new Reference('foobar4')); + +$container->register('foobar4', 'stdClass')->setPublic(true) + ->addArgument(new Reference('foo4')); + return $container; 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 8c76d4c7636f3..6724846f8dd8e 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 @@ -33,11 +33,11 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. // Returns the public 'configured_service' shared service. +$this->services['configured_service'] = $instance = new \stdClass(); + $a = new \ConfClass(); $a->setFoo(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->load(__DIR__.'/getBazService.php')) && false ?: '_'}); -$this->services['configured_service'] = $instance = new \stdClass(); - $a->configureStdClass($instance); return $instance; @@ -153,10 +153,10 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. // Returns the public 'foo_with_inline' shared service. -$a = new \Bar(); - $this->services['foo_with_inline'] = $instance = new \Foo(); +$a = new \Bar(); + $a->pub = 'pub'; $a->setBaz(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->load(__DIR__.'/getBazService.php')) && false ?: '_'}); 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 403eb12954fe1..ec0db041f4381 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -129,11 +129,11 @@ protected function getBazService() */ protected function getConfiguredServiceService() { + $this->services['configured_service'] = $instance = new \stdClass(); + $a = new \ConfClass(); $a->setFoo(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->getBazService()) && false ?: '_'}); - $this->services['configured_service'] = $instance = new \stdClass(); - $a->configureStdClass($instance); return $instance; @@ -259,10 +259,10 @@ protected function getFooBarService() */ protected function getFooWithInlineService() { - $a = new \Bar(); - $this->services['foo_with_inline'] = $instance = new \Foo(); + $a = new \Bar(); + $a->pub = 'pub'; $a->setBaz(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->getBazService()) && false ?: '_'}); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_almost_circular_private.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php similarity index 53% rename from src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_almost_circular_private.php rename to src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php index 055f409a5318c..1707f8912781f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_almost_circular_private.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php @@ -23,7 +23,11 @@ public function __construct() { $this->services = array(); $this->methodMap = array( + 'bar2' => 'getBar2Service', + 'bar3' => 'getBar3Service', 'foo' => 'getFooService', + 'foo2' => 'getFoo2Service', + 'foobar4' => 'getFoobar4Service', ); $this->aliases = array(); @@ -35,7 +39,10 @@ public function getRemovedIds() 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, 'bar' => true, + 'foo4' => true, 'foobar' => true, + 'foobar2' => true, + 'foobar3' => true, ); } @@ -56,6 +63,36 @@ public function isFrozen() return true; } + /** + * Gets the public 'bar2' shared service. + * + * @return \BarCircular + */ + protected function getBar2Service() + { + $this->services['bar2'] = $instance = new \BarCircular(); + + $instance->addFoobar(new \FoobarCircular(${($_ = isset($this->services['foo2']) ? $this->services['foo2'] : $this->getFoo2Service()) && false ?: '_'})); + + return $instance; + } + + /** + * Gets the public 'bar3' shared service. + * + * @return \BarCircular + */ + protected function getBar3Service() + { + $this->services['bar3'] = $instance = new \BarCircular(); + + $a = new \FoobarCircular(); + + $instance->addFoobar($a, $a); + + return $instance; + } + /** * Gets the public 'foo' shared service. * @@ -69,6 +106,37 @@ protected function getFooService() $a->addFoobar(new \FoobarCircular($instance)); + return $instance; + } + + /** + * Gets the public 'foo2' shared service. + * + * @return \FooCircular + */ + protected function getFoo2Service() + { + $a = ${($_ = isset($this->services['bar2']) ? $this->services['bar2'] : $this->getBar2Service()) && false ?: '_'}; + + if (isset($this->services['foo2'])) { + return $this->services['foo2']; + } + + return $this->services['foo2'] = new \FooCircular($a); + } + + /** + * Gets the public 'foobar4' shared service. + * + * @return \stdClass + */ + protected function getFoobar4Service() + { + $a = new \stdClass(); + + $this->services['foobar4'] = $instance = new \stdClass($a); + + $a->foobar = $instance; return $instance; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_almost_circular_public.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php similarity index 54% rename from src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_almost_circular_public.php rename to src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php index 5c4057d7fa1af..5d4d9b0f29cf9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_almost_circular_public.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php @@ -24,8 +24,14 @@ public function __construct() $this->services = array(); $this->methodMap = array( 'bar' => 'getBarService', + 'bar3' => 'getBar3Service', 'foo' => 'getFooService', + 'foo2' => 'getFoo2Service', + 'foo4' => 'getFoo4Service', 'foobar' => 'getFoobarService', + 'foobar2' => 'getFoobar2Service', + 'foobar3' => 'getFoobar3Service', + 'foobar4' => 'getFoobar4Service', ); $this->aliases = array(); @@ -36,6 +42,7 @@ public function getRemovedIds() return array( 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'bar2' => true, ); } @@ -70,6 +77,22 @@ protected function getBarService() return $instance; } + /** + * Gets the public 'bar3' shared service. + * + * @return \BarCircular + */ + protected function getBar3Service() + { + $this->services['bar3'] = $instance = new \BarCircular(); + + $a = ${($_ = isset($this->services['foobar3']) ? $this->services['foobar3'] : $this->services['foobar3'] = new \FoobarCircular()) && false ?: '_'}; + + $instance->addFoobar($a, $a); + + return $instance; + } + /** * Gets the public 'foo' shared service. * @@ -86,6 +109,36 @@ protected function getFooService() return $this->services['foo'] = new \FooCircular($a); } + /** + * Gets the public 'foo2' shared service. + * + * @return \FooCircular + */ + protected function getFoo2Service() + { + $a = new \BarCircular(); + + $this->services['foo2'] = $instance = new \FooCircular($a); + + $a->addFoobar(${($_ = isset($this->services['foobar2']) ? $this->services['foobar2'] : $this->getFoobar2Service()) && false ?: '_'}); + + return $instance; + } + + /** + * Gets the public 'foo4' service. + * + * @return \stdClass + */ + protected function getFoo4Service() + { + $instance = new \stdClass(); + + $instance->foobar = ${($_ = isset($this->services['foobar4']) ? $this->services['foobar4'] : $this->getFoobar4Service()) && false ?: '_'}; + + return $instance; + } + /** * Gets the public 'foobar' shared service. * @@ -101,4 +154,46 @@ protected function getFoobarService() return $this->services['foobar'] = new \FoobarCircular($a); } + + /** + * Gets the public 'foobar2' shared service. + * + * @return \FoobarCircular + */ + protected function getFoobar2Service() + { + $a = ${($_ = isset($this->services['foo2']) ? $this->services['foo2'] : $this->getFoo2Service()) && false ?: '_'}; + + if (isset($this->services['foobar2'])) { + return $this->services['foobar2']; + } + + return $this->services['foobar2'] = new \FoobarCircular($a); + } + + /** + * Gets the public 'foobar3' shared service. + * + * @return \FoobarCircular + */ + protected function getFoobar3Service() + { + return $this->services['foobar3'] = new \FoobarCircular(); + } + + /** + * Gets the public 'foobar4' shared service. + * + * @return \stdClass + */ + protected function getFoobar4Service() + { + $a = new \stdClass(); + + $this->services['foobar4'] = $instance = new \stdClass($a); + + $a->foobar = $instance; + + return $instance; + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_inline_requires.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php similarity index 100% rename from src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_inline_requires.php rename to src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php 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 f7e751113cfd3..cd8456be3afcf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php @@ -159,12 +159,12 @@ protected function getTranslator2Service() */ protected function getTranslator3Service() { - $a = ${($_ = isset($this->services['translator.loader_3']) ? $this->services['translator.loader_3'] : $this->services['translator.loader_3'] = new \stdClass()) && false ?: '_'}; - $this->services['translator_3'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator(new \Symfony\Component\DependencyInjection\ServiceLocator(array('translator.loader_3' => function () { return ${($_ = isset($this->services['translator.loader_3']) ? $this->services['translator.loader_3'] : $this->services['translator.loader_3'] = new \stdClass()) && false ?: '_'}; }))); + $a = ${($_ = isset($this->services['translator.loader_3']) ? $this->services['translator.loader_3'] : $this->services['translator.loader_3'] = new \stdClass()) && false ?: '_'}; + $instance->addResource('db', $a, 'nl'); $instance->addResource('db', $a, 'en'); From b0d297b962a2b115d01051d14485f19f43dc9c87 Mon Sep 17 00:00:00 2001 From: Derek Bonner Date: Wed, 29 Nov 2017 20:11:54 -0800 Subject: [PATCH 33/39] [Dotenv] Changed preg_match flags from null to 0 --- src/Symfony/Component/Dotenv/Dotenv.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php index 910a1622f303b..dde92f46af5b6 100644 --- a/src/Symfony/Component/Dotenv/Dotenv.php +++ b/src/Symfony/Component/Dotenv/Dotenv.php @@ -173,7 +173,7 @@ private function lexVarname() private function lexValue() { - if (preg_match('/[ \t]*+(?:#.*)?$/Am', $this->data, $matches, null, $this->cursor)) { + if (preg_match('/[ \t]*+(?:#.*)?$/Am', $this->data, $matches, 0, $this->cursor)) { $this->moveCursor($matches[0]); $this->skipEmptyLines(); @@ -295,7 +295,7 @@ private function lexNestedExpression() private function skipEmptyLines() { - if (preg_match('/(?:\s*+(?:#[^\n]*+)?+)++/A', $this->data, $match, null, $this->cursor)) { + if (preg_match('/(?:\s*+(?:#[^\n]*+)?+)++/A', $this->data, $match, 0, $this->cursor)) { $this->moveCursor($match[0]); } } From 1b141732db0ac76a0a7330c1df8a6e29047b5e20 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 29 Nov 2017 16:33:25 +0100 Subject: [PATCH 34/39] [VarDumper] Dont use empty(), it chokes on eg GMP objects --- src/Symfony/Component/VarDumper/Caster/Caster.php | 4 ++-- src/Symfony/Component/VarDumper/Cloner/VarCloner.php | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Caster/Caster.php b/src/Symfony/Component/VarDumper/Caster/Caster.php index 29f002b8cc1a6..5fdae442dcda8 100644 --- a/src/Symfony/Component/VarDumper/Caster/Caster.php +++ b/src/Symfony/Component/VarDumper/Caster/Caster.php @@ -118,8 +118,8 @@ public static function filter(array $a, $filter, array $listedProperties = array if (null === $v) { $type |= self::EXCLUDE_NULL & $filter; - } - if (empty($v)) { + $type |= self::EXCLUDE_EMPTY & $filter; + } elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || array() === $v) { $type |= self::EXCLUDE_EMPTY & $filter; } if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !in_array($k, $listedProperties, true)) { diff --git a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php index 5399c66010a89..1f588db82cae5 100644 --- a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php @@ -94,13 +94,16 @@ protected function doClone($var) // Create $stub when the original value $v can not be used directly // If $v is a nested structure, put that structure in array $a switch (true) { - case empty($v): - case true === $v: + case null === $v: + case \is_bool($v): case \is_int($v): case \is_float($v): continue 2; case \is_string($v): + if ('' === $v) { + continue 2; + } if (!\preg_match('//u', $v)) { $stub = new Stub(); $stub->type = Stub::TYPE_STRING; @@ -124,6 +127,9 @@ protected function doClone($var) break; case \is_array($v): + if (!$v) { + continue 2; + } $stub = $arrayStub; $stub->class = Stub::ARRAY_INDEXED; From 407f132e43b339b7c1108959cc18c5c1b119c904 Mon Sep 17 00:00:00 2001 From: Samuel ROZE Date: Sat, 25 Nov 2017 14:18:13 +0000 Subject: [PATCH 35/39] Test the suggestion of already registered services --- .../Compiler/AutowirePass.php | 8 ++++--- .../Tests/Compiler/AutowirePassTest.php | 18 +++++++++++++++ .../Fixtures/includes/autowiring_classes.php | 22 ++++--------------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 1113667503966..638669aa57646 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -336,14 +336,16 @@ private function createTypeAlternatives(TypedReference $reference) if ($message = $this->getAliasesSuggestionForType($type = $reference->getType())) { return ' '.$message; } - if (isset($this->ambiguousServiceTypes[$type])) { + + $servicesAndAliases = $this->container->getServiceIds(); + if (!$this->container->has($type) && false !== $key = array_search(strtolower($type), array_map('strtolower', $servicesAndAliases))) { + return sprintf(' Did you mean "%s"?', $servicesAndAliases[$key]); + } elseif (isset($this->ambiguousServiceTypes[$type])) { $message = sprintf('one of these existing services: "%s"', implode('", "', $this->ambiguousServiceTypes[$type])); } elseif (isset($this->types[$type])) { $message = sprintf('the existing "%s" service', $this->types[$type]); } elseif ($reference->getRequiringClass() && !$reference->canBeAutoregistered()) { return ' It cannot be auto-registered because it is from a different root namespace.'; - } elseif (is_array($this->types) && (null !== $key = array_search(strtolower($type), array_map('strtolower', $this->types)))) { - return sprintf(' Maybe you mean %s instead of %s ?', $key, $type); } else { return; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 250965b898e83..906341034f726 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -685,6 +685,24 @@ public function provideNotWireableCalls() ); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "foo": argument "$sam" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setNotAutowireableBecauseOfATypo()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\lesTilleuls" but no such service exists. Did you mean "Symfony\Component\DependencyInjection\Tests\Compiler\LesTilleuls"? + */ + public function testSuggestRegisteredServicesWithSimilarCase() + { + $container = new ContainerBuilder(); + + $container->register(LesTilleuls::class, LesTilleuls::class); + $container->register('foo', NotWireable::class)->setAutowired(true) + ->addMethodCall('setNotAutowireableBecauseOfATypo', array()) + ; + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + (new AutowirePass())->process($container); + } + /** * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException * @expectedExceptionMessage Cannot autowire service "j": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\I" but no such service exists. Try changing the type-hint to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead. diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php index ffa0196298f73..ae1e92eadbafd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php @@ -6,13 +6,6 @@ class Foo { } -class FooFoo -{ - public function __construct(Foo $foo) - { - } -} - class Bar { public function __construct(Foo $foo) @@ -300,23 +293,16 @@ public function setChildMethodWithoutDocBlock(A $a) } } -class ARealClass { - -} - -class ClassMisMatch { - - public function __construct(arealclass $n = null) - { - } -} - class NotWireable { public function setNotAutowireable(NotARealClass $n) { } + public function setNotAutowireableBecauseOfATypo(lesTilleuls $sam) + { + } + public function setBar() { } From b1173f3ea7bff36fe18ea47a61aac6889d4edf78 Mon Sep 17 00:00:00 2001 From: Amrouche Hamza Date: Wed, 29 Nov 2017 08:28:02 +0100 Subject: [PATCH 36/39] [HttpKernel] Add a better error messages when passing a private or non-tagged controller --- .../ContainerControllerResolver.php | 14 ++++- .../ContainerControllerResolverTest.php | 55 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php index fbcecad25e18a..a15091a446125 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php @@ -13,6 +13,7 @@ use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpFoundation\Request; /** @@ -86,6 +87,17 @@ protected function instantiateController($class) return $this->container->get($class); } - return parent::instantiateController($class); + try { + return parent::instantiateController($class); + } catch (\ArgumentCountError $e) { + } catch (\ErrorException $e) { + } catch (\TypeError $e) { + } + + if ($this->container instanceof Container && in_array($class, $this->container->getRemovedIds(), true)) { + 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); + } + + throw $e; } } diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php index b3deb03c9138a..4f77818f12f99 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php @@ -13,6 +13,8 @@ use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ContainerControllerResolver; @@ -106,6 +108,48 @@ public function testNonInstantiableController() $this->assertSame(array(NonInstantiableController::class, 'action'), $controller); } + /** + * @expectedException \LogicException + * @expectedExceptionMessage Controller "Symfony\Component\HttpKernel\Tests\Controller\ImpossibleConstructController" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"? + */ + public function testNonConstructController() + { + $container = $this->getMockBuilder(Container::class)->getMock(); + $container->expects($this->at(0)) + ->method('has') + ->with(ImpossibleConstructController::class) + ->will($this->returnValue(true)) + ; + + $container->expects($this->at(1)) + ->method('has') + ->with(ImpossibleConstructController::class) + ->will($this->returnValue(false)) + ; + + $container->expects($this->atLeastOnce()) + ->method('getRemovedIds') + ->with() + ->will($this->returnValue(array(ImpossibleConstructController::class))) + ; + + $resolver = $this->createControllerResolver(null, $container); + $request = Request::create('/'); + $request->attributes->set('_controller', array(ImpossibleConstructController::class, 'action')); + + if (\PHP_VERSION_ID < 70100) { + ErrorHandler::register(); + try { + $resolver->getController($request); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } else { + $resolver->getController($request); + } + } + public function testNonInstantiableControllerWithCorrespondingService() { $service = new \stdClass(); @@ -196,3 +240,14 @@ public static function action() { } } + +class ImpossibleConstructController +{ + public function __construct($toto, $controller) + { + } + + public function action() + { + } +} From 56846ac6c1273a72e7233c9416340b89a6266d97 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 30 Nov 2017 11:09:16 +0100 Subject: [PATCH 37/39] [HttpFoundation] Add Session::isEmpty(), fix MockFileSessionStorage to behave like the native one --- .../Security/Factory/AbstractFactory.php | 2 +- .../Security/Factory/JsonLoginFactory.php | 1 - .../HttpFoundation/Session/Session.php | 25 +++++- .../Session/SessionBagProxy.php | 79 +++++++++++++++++++ .../Storage/MockFileSessionStorage.php | 21 ++++- .../Tests/Session/SessionTest.php | 18 +++++ .../AbstractTestSessionListener.php | 7 +- .../EventListener/TestSessionListenerTest.php | 20 +++++ 8 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 src/Symfony/Component/HttpFoundation/Session/SessionBagProxy.php diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php index d1b7980faa8a6..26fa75572cd9e 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php @@ -29,7 +29,7 @@ abstract class AbstractFactory implements SecurityFactoryInterface protected $options = array( 'check_path' => '/login_check', 'use_forward' => false, - 'require_previous_session' => true, + 'require_previous_session' => false, ); protected $defaultSuccessHandlerOptions = array( diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php index 28a7bf5743078..5a391ffacaeab 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php @@ -28,7 +28,6 @@ public function __construct() $this->addOption('password_path', 'password'); $this->defaultFailureHandlerOptions = array(); $this->defaultSuccessHandlerOptions = array(); - $this->options['require_previous_session'] = false; } /** diff --git a/src/Symfony/Component/HttpFoundation/Session/Session.php b/src/Symfony/Component/HttpFoundation/Session/Session.php index 09caa3442fa49..0c3371fab6c6d 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Session.php +++ b/src/Symfony/Component/HttpFoundation/Session/Session.php @@ -28,6 +28,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable private $flashName; private $attributeName; + private $data = array(); /** * @param SessionStorageInterface $storage A SessionStorageInterface instance @@ -108,7 +109,7 @@ public function remove($name) */ public function clear() { - $this->storage->getBag($this->attributeName)->clear(); + $this->getAttributeBag()->clear(); } /** @@ -139,6 +140,22 @@ public function count() return count($this->getAttributeBag()->all()); } + /** + * @return bool + * + * @internal + */ + public function isEmpty() + { + foreach ($this->data as &$data) { + if (!empty($data)) { + return false; + } + } + + return true; + } + /** * {@inheritdoc} */ @@ -210,7 +227,7 @@ public function getMetadataBag() */ public function registerBag(SessionBagInterface $bag) { - $this->storage->registerBag($bag); + $this->storage->registerBag(new SessionBagProxy($bag, $this->data)); } /** @@ -218,7 +235,7 @@ public function registerBag(SessionBagInterface $bag) */ public function getBag($name) { - return $this->storage->getBag($name); + return $this->storage->getBag($name)->getBag(); } /** @@ -240,6 +257,6 @@ public function getFlashBag() */ private function getAttributeBag() { - return $this->storage->getBag($this->attributeName); + return $this->storage->getBag($this->attributeName)->getBag(); } } diff --git a/src/Symfony/Component/HttpFoundation/Session/SessionBagProxy.php b/src/Symfony/Component/HttpFoundation/Session/SessionBagProxy.php new file mode 100644 index 0000000000000..6c4cab6716456 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Session/SessionBagProxy.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +/** + * @author Nicolas Grekas + * + * @internal + */ +final class SessionBagProxy implements SessionBagInterface +{ + private $bag; + private $data; + + public function __construct(SessionBagInterface $bag, array &$data) + { + $this->bag = $bag; + $this->data = &$data; + } + + /** + * @return SessionBagInterface + */ + public function getBag() + { + return $this->bag; + } + + /** + * @return bool + */ + public function isEmpty() + { + return empty($this->data[$this->bag->getStorageKey()]); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->bag->getName(); + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$array) + { + $this->data[$this->bag->getStorageKey()] = &$array; + + $this->bag->initialize($array); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->bag->getStorageKey(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->bag->clear(); + } +} diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php index 0a580d6027c4f..14f427007ba82 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php @@ -91,7 +91,26 @@ public function save() throw new \RuntimeException('Trying to save a session that was not started yet or was already closed'); } - file_put_contents($this->getFilePath(), serialize($this->data)); + $data = $this->data; + + foreach ($this->bags as $bag) { + if (empty($data[$key = $bag->getStorageKey()])) { + unset($data[$key]); + } + } + if (array($key = $this->metadataBag->getStorageKey()) === array_keys($data)) { + unset($data[$key]); + } + + try { + if ($data) { + file_put_contents($this->getFilePath(), serialize($data)); + } else { + $this->destroy(); + } + } finally { + $this->data = $data; + } // this is needed for Silex, where the session object is re-used across requests // in functional tests. In Symfony, the container is rebooted, so we don't have diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/SessionTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/SessionTest.php index fa93507a41aaf..41720e4b6fc4e 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/SessionTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/SessionTest.php @@ -221,4 +221,22 @@ public function testGetMeta() { $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', $this->session->getMetadataBag()); } + + public function testIsEmpty() + { + $this->assertTrue($this->session->isEmpty()); + + $this->session->set('hello', 'world'); + $this->assertFalse($this->session->isEmpty()); + + $this->session->remove('hello'); + $this->assertTrue($this->session->isEmpty()); + + $flash = $this->session->getFlashBag(); + $flash->set('hello', 'world'); + $this->assertFalse($this->session->isEmpty()); + + $flash->get('hello'); + $this->assertTrue($this->session->isEmpty()); + } } diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php index eb0320f6b91e6..2531db66790d2 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpKernel\EventListener; use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; @@ -60,8 +61,10 @@ public function onKernelResponse(FilterResponseEvent $event) $session = $event->getRequest()->getSession(); if ($session && $session->isStarted()) { $session->save(); - $params = session_get_cookie_params(); - $event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly'])); + if (!$session instanceof Session || !\method_exists($session, 'isEmpty') || !$session->isEmpty()) { + $params = session_get_cookie_params(); + $event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly'])); + } } } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php index 124bd64d848f3..4452f48771b8b 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php @@ -73,6 +73,19 @@ public function testDoesNotDeleteCookieIfUsingSessionLifetime() $this->assertEquals(0, reset($cookies)->getExpiresTime()); } + /** + * @requires function \Symfony\Component\HttpFoundation\Session\Session::isEmpty + */ + public function testEmptySessionDoesNotSendCookie() + { + $this->sessionHasBeenStarted(); + $this->sessionIsEmpty(); + + $response = $this->filterResponse(new Request(), HttpKernelInterface::MASTER_REQUEST); + + $this->assertSame(array(), $response->headers->getCookies()); + } + public function testUnstartedSessionIsNotSave() { $this->sessionHasNotBeenStarted(); @@ -130,6 +143,13 @@ private function sessionHasNotBeenStarted() ->will($this->returnValue(false)); } + private function sessionIsEmpty() + { + $this->session->expects($this->once()) + ->method('isEmpty') + ->will($this->returnValue(true)); + } + private function getSession() { $mock = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session') From 27a52b633b11abe567fc674ede70ab15e87d0c85 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 30 Nov 2017 09:43:34 -0800 Subject: [PATCH 38/39] updated CHANGELOG for 4.0.0 --- CHANGELOG-4.0.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG-4.0.md b/CHANGELOG-4.0.md index fc1f989d485f7..f1c98b1874d2a 100644 --- a/CHANGELOG-4.0.md +++ b/CHANGELOG-4.0.md @@ -7,6 +7,32 @@ 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.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 #25155 [DependencyInjection] Detect case mismatch in autowiring (Simperfit, sroze) + * 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) + * 4.0.0-RC2 (2017-11-24) * bug #25146 [DI] Dont resolve envs in service ids (nicolas-grekas) From ddb377e1fdf79641ed94536ebe3210d524a01e0d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 30 Nov 2017 09:43:40 -0800 Subject: [PATCH 39/39] updated VERSION for 4.0.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 0c567bbfb7fbd..2a6d8a4fd17e8 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-DEV'; + const VERSION = '4.0.0'; const VERSION_ID = 40000; const MAJOR_VERSION = 4; const MINOR_VERSION = 0; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '07/2018'; const END_OF_LIFE = '01/2019';