From 6331687f65f595b800b2795085e6baacb571c250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Tue, 24 Jul 2018 17:28:18 +0200 Subject: [PATCH 01/47] Allow multidimensional collection in property info --- .../Extractor/PhpDocExtractor.php | 24 +++++++++---------- .../Tests/Extractors/PhpDocExtractorTest.php | 2 ++ .../Extractors/ReflectionExtractorTest.php | 2 ++ .../PropertyInfo/Tests/Fixtures/Dummy.php | 10 ++++++++ 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php index 717baddab2b57..7a7eee65dd0cf 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php @@ -319,30 +319,28 @@ private function createType($docType, $nullable) { // Cannot guess if (!$docType || 'mixed' === $docType) { - return; - } - - if ($collection = '[]' === substr($docType, -2)) { - $docType = substr($docType, 0, -2); + return null; } - $docType = $this->normalizeType($docType); - list($phpType, $class) = $this->getPhpTypeAndClass($docType); - - $array = 'array' === $docType; - - if ($collection || $array) { - if ($array || 'mixed' === $docType) { + if ('[]' === substr($docType, -2)) { + if ('mixed[]' === $docType) { $collectionKeyType = null; $collectionValueType = null; } else { $collectionKeyType = new Type(Type::BUILTIN_TYPE_INT); - $collectionValueType = new Type($phpType, $nullable, $class); + $collectionValueType = $this->createType(substr($docType, 0, -2), $nullable); } return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, $collectionKeyType, $collectionValueType); } + $docType = $this->normalizeType($docType); + list($phpType, $class) = $this->getPhpTypeAndClass($docType); + + if ('array' === $docType) { + return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, null, null); + } + return new Type($phpType, $nullable, $class); } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php index 4110c6b8a5bf4..f251ab11360ae 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php @@ -62,6 +62,8 @@ public function typesProvider() array('bal', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')), null, null), array('parent', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), null, null), array('collection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null), + array('mixedCollection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, null, null)), null, null), + array('nestedCollection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING, false)))), null, null), array('a', array(new Type(Type::BUILTIN_TYPE_INT)), 'A.', null), array('b', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), 'B.', null), array('c', array(new Type(Type::BUILTIN_TYPE_BOOL, true)), null, null), diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php index 2828878ce6cf0..1ac2f019077bb 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php @@ -37,6 +37,8 @@ public function testGetProperties() 'bal', 'parent', 'collection', + 'nestedCollection', + 'mixedCollection', 'B', 'Guid', 'g', diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php index d358bae13ad61..e56f840a81639 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php @@ -46,6 +46,16 @@ class Dummy extends ParentDummy */ public $collection; + /** + * @var string[][] + */ + public $nestedCollection; + + /** + * @var mixed[] + */ + public $mixedCollection; + /** * @var ParentDummy */ From ce49036790e91b6be626cf8a6243ccb75e87125d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Fri, 20 Jul 2018 11:22:01 +0200 Subject: [PATCH 02/47] Allow multidimensional collection in property info --- .../Tests/Extractor/PhpDocExtractorTest.php | 6 +++++ .../Extractor/ReflectionExtractorTest.php | 6 +++++ .../PropertyInfo/Tests/Fixtures/Dummy.php | 8 +++++++ .../PropertyInfo/Util/PhpDocTypeHelper.php | 24 +++++++++---------- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php index 080df2893a877..6766b249a77d8 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php @@ -87,6 +87,8 @@ public function typesProvider() array('bal', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')), null, null), array('parent', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), null, null), array('collection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null), + array('nestedCollection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING, false)))), null, null), + array('mixedCollection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, null, null)), null, null), array('a', array(new Type(Type::BUILTIN_TYPE_INT)), 'A.', null), array('b', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), 'B.', null), array('c', array(new Type(Type::BUILTIN_TYPE_BOOL, true)), null, null), @@ -126,6 +128,8 @@ public function typesWithCustomPrefixesProvider() array('bal', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')), null, null), array('parent', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), null, null), array('collection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null), + array('nestedCollection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING, false)))), null, null), + array('mixedCollection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, null, null)), null, null), array('a', null, 'A.', null), array('b', null, 'B.', null), array('c', array(new Type(Type::BUILTIN_TYPE_BOOL, true)), null, null), @@ -164,6 +168,8 @@ public function typesWithNoPrefixesProvider() array('bal', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')), null, null), array('parent', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), null, null), array('collection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null), + array('nestedCollection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING, false)))), null, null), + array('mixedCollection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, null, null)), null, null), array('a', null, 'A.', null), array('b', null, 'B.', null), array('c', null, null, null), diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index e7fddf473f37b..24ef06239be59 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -38,6 +38,8 @@ public function testGetProperties() 'bal', 'parent', 'collection', + 'nestedCollection', + 'mixedCollection', 'B', 'Guid', 'g', @@ -77,6 +79,8 @@ public function testGetPropertiesWithCustomPrefixes() 'bal', 'parent', 'collection', + 'nestedCollection', + 'mixedCollection', 'B', 'Guid', 'g', @@ -108,6 +112,8 @@ public function testGetPropertiesWithNoPrefixes() 'bal', 'parent', 'collection', + 'nestedCollection', + 'mixedCollection', 'B', 'Guid', 'g', diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php index 76c2e1042c604..152de1d8f1840 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php @@ -45,6 +45,14 @@ class Dummy extends ParentDummy * @Groups({"a", "b"}) */ public $collection; + /** + * @var string[][] + */ + public $nestedCollection; + /** + * @var mixed[] + */ + public $mixedCollection; /** * @var ParentDummy diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php index e526c62348797..431544b849d46 100644 --- a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php +++ b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php @@ -89,30 +89,28 @@ private function createType($docType, $nullable) { // Cannot guess if (!$docType || 'mixed' === $docType) { - return; + return null; } - if ($collection = '[]' === substr($docType, -2)) { - $docType = substr($docType, 0, -2); - } - - $docType = $this->normalizeType($docType); - list($phpType, $class) = $this->getPhpTypeAndClass($docType); - - $array = 'array' === $docType; - - if ($collection || $array) { - if ($array || 'mixed' === $docType) { + if ('[]' === substr($docType, -2)) { + if ('mixed[]' === $docType) { $collectionKeyType = null; $collectionValueType = null; } else { $collectionKeyType = new Type(Type::BUILTIN_TYPE_INT); - $collectionValueType = new Type($phpType, $nullable, $class); + $collectionValueType = $this->createType(substr($docType, 0, -2), $nullable); } return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, $collectionKeyType, $collectionValueType); } + $docType = $this->normalizeType($docType); + list($phpType, $class) = $this->getPhpTypeAndClass($docType); + + if ('array' === $docType) { + return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, null, null); + } + return new Type($phpType, $nullable, $class); } From ac0cd15402e8dd46baad8f6ac9ae75c72dc6d5ba Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 27 Jul 2018 17:20:18 +0200 Subject: [PATCH 03/47] Remove the Expires header when calling Response::expire() --- src/Symfony/Component/HttpFoundation/Response.php | 1 + src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 10581146facaa..6b6308e36c2ee 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -675,6 +675,7 @@ public function expire() { if ($this->isFresh()) { $this->headers->set('Age', $this->getMaxAge()); + $this->headers->remove('Expires'); } return $this; diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index 881a68e98b3dd..fa3a40539db9d 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -362,6 +362,11 @@ public function testExpire() $response->headers->set('Expires', -1); $response->expire(); $this->assertNull($response->headers->get('Age'), '->expire() does not set the Age when the response is expired'); + + $response = new Response(); + $response->headers->set('Expires', date(DATE_RFC2822, time() + 600)); + $response->expire(); + $this->assertNull($response->headers->get('Expires'), '->expire() removes the Expires header when the response is fresh'); } public function testGetTtl() From e843bb86c88c9a52a6c9d3a98afb11f796624abe Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 24 Jul 2018 11:19:17 +0200 Subject: [PATCH 04/47] [DI] Fix false-positive circular ref leading to wrong exceptions or infinite loops at runtime --- .../ResolveReferencesToAliasesPass.php | 2 +- .../DependencyInjection/Container.php | 2 +- .../DependencyInjection/ContainerBuilder.php | 94 +++++------ .../DependencyInjection/Dumper/PhpDumper.php | 137 +++++++++------- .../Tests/ContainerBuilderTest.php | 6 + .../Tests/Dumper/PhpDumperTest.php | 53 ++---- .../containers/container_almost_circular.php | 46 ++++++ .../php/services_almost_circular_private.php | 125 +++++++++++++- .../php/services_almost_circular_public.php | 155 ++++++++++++++++++ 9 files changed, 474 insertions(+), 146 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php index 8ffb245c12f2c..839af1ae232d3 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php @@ -66,7 +66,7 @@ private function getDefinitionId($id, ContainerBuilder $container) $seen = array(); while ($container->hasAlias($id)) { if (isset($seen[$id])) { - throw new ServiceCircularReferenceException($id, array_keys($seen)); + throw new ServiceCircularReferenceException($id, array_merge(array_keys($seen), array($id))); } $seen[$id] = true; $id = $container->normalizeId($container->getAlias($id)); diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 5dfa1da59dc50..4d3e3883d428a 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -294,7 +294,7 @@ public function get($id, $invalidBehavior = /* self::EXCEPTION_ON_INVALID_REFERE } if (isset($this->loading[$id])) { - throw new ServiceCircularReferenceException($id, array_keys($this->loading)); + throw new ServiceCircularReferenceException($id, array_merge(array_keys($this->loading), array($id))); } $this->loading[$id] = true; diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index c2b73e70c84dc..7a66382f6058b 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -122,7 +122,6 @@ class ContainerBuilder extends Container implements TaggedContainerInterface private $autoconfiguredInstanceof = array(); private $removedIds = array(); - private $alreadyLoading = array(); private static $internalTypes = array( 'int' => true, @@ -588,22 +587,32 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV return $this->doGet($id, $invalidBehavior); } - private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = array()) + private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = null, $isConstructorArgument = false) { $id = $this->normalizeId($id); if (isset($inlineServices[$id])) { return $inlineServices[$id]; } - if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) { - return parent::get($id, $invalidBehavior); + if (null === $inlineServices) { + $isConstructorArgument = true; + $inlineServices = array(); } - if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) { - return $service; + try { + if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) { + return parent::get($id, $invalidBehavior); + } + if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) { + return $service; + } + } catch (ServiceCircularReferenceException $e) { + if ($isConstructorArgument) { + throw $e; + } } if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) { - return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices); + return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices, $isConstructorArgument); } try { @@ -616,16 +625,17 @@ private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_ throw $e; } - $loading = isset($this->alreadyLoading[$id]) ? 'loading' : 'alreadyLoading'; - $this->{$loading}[$id] = true; + if ($isConstructorArgument) { + $this->loading[$id] = true; + } try { - $service = $this->createService($definition, $inlineServices, $id); + return $this->createService($definition, $inlineServices, $isConstructorArgument, $id); } finally { - unset($this->{$loading}[$id]); + if ($isConstructorArgument) { + unset($this->loading[$id]); + } } - - return $service; } /** @@ -1092,7 +1102,7 @@ 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, array &$inlineServices, $id = null, $tryProxy = true) + private function createService(Definition $definition, array &$inlineServices, $isConstructorArgument = false, $id = null, $tryProxy = true) { if (null === $id && isset($inlineServices[$h = spl_object_hash($definition)])) { return $inlineServices[$h]; @@ -1110,16 +1120,14 @@ private function createService(Definition $definition, array &$inlineServices, $ @trigger_error($definition->getDeprecationMessage($id), E_USER_DEPRECATED); } - if ($tryProxy && $definition->isLazy()) { - $proxy = $this - ->getProxyInstantiator() - ->instantiateProxy( - $this, - $definition, - $id, function () use ($definition, &$inlineServices, $id) { - return $this->createService($definition, $inlineServices, $id, false); - } - ); + if ($tryProxy && $definition->isLazy() && !$tryProxy = !($proxy = $this->proxyInstantiator) || $proxy instanceof RealServiceInstantiator) { + $proxy = $proxy->instantiateProxy( + $this, + $definition, + $id, function () use ($definition, &$inlineServices, $id) { + return $this->createService($definition, $inlineServices, true, $id, false); + } + ); $this->shareService($definition, $proxy, $id, $inlineServices); return $proxy; @@ -1131,19 +1139,21 @@ private function createService(Definition $definition, array &$inlineServices, $ require_once $parameterBag->resolveValue($definition->getFile()); } - $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]; - } + $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlineServices, $isConstructorArgument); if (null !== $factory = $definition->getFactory()) { if (\is_array($factory)) { - $factory = array($this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices), $factory[1]); + $factory = array($this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices, $isConstructorArgument), $factory[1]); } elseif (!\is_string($factory)) { throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id)); } + } + if (null !== $id && $definition->isShared() && isset($this->services[$id]) && ($tryProxy || !$definition->isLazy())) { + return $this->services[$id]; + } + + if (null !== $factory) { $service = \call_user_func_array($factory, $arguments); if (!$definition->isDeprecated() && \is_array($factory) && \is_string($factory[0])) { @@ -1214,11 +1224,11 @@ public function resolveServices($value) return $this->doResolveServices($value); } - private function doResolveServices($value, array &$inlineServices = array()) + private function doResolveServices($value, array &$inlineServices = array(), $isConstructorArgument = false) { if (\is_array($value)) { foreach ($value as $k => $v) { - $value[$k] = $this->doResolveServices($v, $inlineServices); + $value[$k] = $this->doResolveServices($v, $inlineServices, $isConstructorArgument); } } elseif ($value instanceof ServiceClosureArgument) { $reference = $value->getValues()[0]; @@ -1261,9 +1271,9 @@ private function doResolveServices($value, array &$inlineServices = array()) return $count; }); } elseif ($value instanceof Reference) { - $value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices); + $value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices, $isConstructorArgument); } elseif ($value instanceof Definition) { - $value = $this->createService($value, $inlineServices); + $value = $this->createService($value, $inlineServices, $isConstructorArgument); } elseif ($value instanceof Parameter) { $value = $this->getParameter((string) $value); } elseif ($value instanceof Expression) { @@ -1584,20 +1594,6 @@ protected function getEnv($name) } } - /** - * Retrieves the currently set proxy instantiator or instantiates one. - * - * @return InstantiatorInterface - */ - private function getProxyInstantiator() - { - if (!$this->proxyInstantiator) { - $this->proxyInstantiator = new RealServiceInstantiator(); - } - - return $this->proxyInstantiator; - } - private function callMethod($service, $call, array &$inlineServices) { foreach (self::getServiceConditionals($call[1]) as $s) { @@ -1627,7 +1623,7 @@ private function shareService(Definition $definition, $service, $id, array &$inl if (null !== $id && $definition->isShared()) { $this->services[$id] = $service; - unset($this->loading[$id], $this->alreadyLoading[$id]); + unset($this->loading[$id]); } } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 71e776310b270..cf6495b9c66f3 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -138,6 +138,29 @@ public function dump(array $options = array()) $this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass); + if ($this->getProxyDumper() instanceof NullDumper) { + (new AnalyzeServiceReferencesPass(true))->process($this->container); + $this->circularReferences = array(); + $checkedNodes = array(); + foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { + $currentPath = array($id => $id); + $this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath); + } + foreach ($this->circularReferences as $parent => $ids) { + $path = array($parent); + while (!isset($ids[$parent])) { + foreach ($ids as $id) { + $path[] = $id; + $ids = $this->circularReferences[$id]; + break; + } + } + $path[] = $parent.'". Try running "composer require symfony/proxy-manager-bridge'; + + throw new ServiceCircularReferenceException($parent, $path); + } + } + (new AnalyzeServiceReferencesPass())->process($this->container); $this->circularReferences = array(); $checkedNodes = array(); @@ -286,21 +309,18 @@ private function getProxyDumper() * * @return string */ - private function addServiceLocalTempVariables($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions, \SplObjectStorage $allInlinedDefinitions) + private function addServiceLocalTempVariables($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions, array $serviceCalls, $preInstance = false) { - $allCalls = $calls = $behavior = array(); - - 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]); - } + $calls = array(); - $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 ($preInstance && !$inlinedDefinitions[$def][1]) { + continue; + } + $this->getServiceCallsFromArguments(array($def->getArguments(), $def->getFactory()), $calls, $preInstance, $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($arguments, $calls, $preInstance && !$this->hasReference($cId, $arguments, true), $cId); } } if (!isset($inlinedDefinitions[$definition])) { @@ -309,23 +329,23 @@ private function addServiceLocalTempVariables($cId, Definition $definition, \Spl } $code = ''; - foreach ($calls as $id => $callCount) { + foreach ($calls as $id => list($callCount)) { if ('service_container' === $id || $id === $cId || isset($this->referenceVariables[$id])) { continue; } - if ($callCount <= 1 && $allCalls[$id] <= 1) { + if ($callCount <= 1 && $serviceCalls[$id][0] <= 1) { continue; } $name = $this->getNextVariableName(); $this->referenceVariables[$id] = new Variable($name); - $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id] ? new Reference($id, $behavior[$id]) : null; + $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $serviceCalls[$id][1] ? new Reference($id, $serviceCalls[$id][1]) : null; $code .= sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($id, $reference)); } if ('' !== $code) { - if ($isPreInstance) { + if ($preInstance) { $code .= <<services['$cId'])) { @@ -347,7 +367,7 @@ private function analyzeCircularReferences(array $edges, &$checkedNodes, &$curre $node = $edge->getDestNode(); $id = $node->getId(); - if ($node->getValue() && ($edge->isLazy() || $edge->isWeak())) { + if ($node->getValue() && (($edge->isLazy() && !$this->getProxyDumper() instanceof NullDumper) || $edge->isWeak())) { // no-op } elseif (isset($currentPath[$id])) { foreach (array_reverse($currentPath) as $parentId) { @@ -425,23 +445,21 @@ private function generateProxyClasses() * * @return string */ - private function addServiceInclude($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions) + private function addServiceInclude($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions, array $serviceCalls) { $code = ''; if ($this->inlineRequires && !$this->isHotPath($definition)) { - $lineage = $calls = $behavior = array(); + $lineage = array(); foreach ($inlinedDefinitions as $def) { if (!$def->isDeprecated() && \is_string($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) { + foreach ($serviceCalls as $id => list($callCount, $behavior)) { if ('service_container' !== $id && $id !== $cId - && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior[$id] + && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior && $this->container->has($id) && $this->isTrivialInstance($def = $this->container->findDefinition($id)) && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass()) @@ -476,24 +494,24 @@ private function addServiceInclude($cId, Definition $definition, \SplObjectStora * @throws RuntimeException When the factory definition is incomplete * @throws ServiceCircularReferenceException When a circular reference is detected */ - private function addServiceInlinedDefinitions($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, &$isSimpleInstance) + private function addServiceInlinedDefinitions($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, &$isSimpleInstance, $preInstance = false) { $code = ''; foreach ($inlinedDefinitions as $def) { - if ($definition === $def) { + if ($definition === $def || isset($this->definitionVariables[$def])) { continue; } - if ($inlinedDefinitions[$def] <= 1 && !$def->getMethodCalls() && !$def->getProperties() && !$def->getConfigurator() && false === strpos($this->dumpValue($def->getClass()), '$')) { + if ($inlinedDefinitions[$def][0] <= 1 && !$def->getMethodCalls() && !$def->getProperties() && !$def->getConfigurator() && false === strpos($this->dumpValue($def->getClass()), '$')) { continue; } - if (isset($this->definitionVariables[$def])) { - $name = $this->definitionVariables[$def]; - } else { - $name = $this->getNextVariableName(); - $this->definitionVariables[$def] = new Variable($name); + if ($preInstance && !$inlinedDefinitions[$def][1]) { + continue; } + $name = $this->getNextVariableName(); + $this->definitionVariables[$def] = 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 @@ -502,7 +520,7 @@ private function addServiceInlinedDefinitions($id, Definition $definition, \SplO // $a = new ServiceA(ServiceB $b); // $b->setServiceA(ServiceA $a); if (isset($inlinedDefinitions[$definition]) && $this->hasReference($id, array($def->getArguments(), $def->getFactory()))) { - throw new ServiceCircularReferenceException($id, array($id)); + throw new ServiceCircularReferenceException($id, array($id, '...', $id)); } $code .= $this->addNewInstance($def, '$'.$name, ' = ', $id); @@ -558,6 +576,7 @@ private function addServiceInstance($id, Definition $definition, $isSimpleInstan } $code = $this->addNewInstance($definition, $return, $instantiation, $id); + $this->referenceVariables[$id] = new Variable('instance'); if (!$isSimpleInstance) { $code .= "\n"; @@ -657,8 +676,6 @@ private function addServiceProperties(Definition $definition, $variableName = 'i */ private function addServiceInlinedDefinitionsSetup($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, $isSimpleInstance) { - $this->referenceVariables[$id] = new Variable('instance'); - $code = ''; foreach ($inlinedDefinitions as $def) { if ($definition === $def || !$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true)) { @@ -668,7 +685,7 @@ private function addServiceInlinedDefinitionsSetup($id, Definition $definition, // if the instance is simple, the return statement has already been generated // so, the only possible way to get there is because of a circular reference if ($isSimpleInstance) { - throw new ServiceCircularReferenceException($id, array($id)); + throw new ServiceCircularReferenceException($id, array($id, '...', $id)); } $name = (string) $this->definitionVariables[$def]; @@ -805,6 +822,7 @@ protected function {$methodName}($lazyInitialization) $inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition)); $constructorDefinitions = $this->getDefinitionsFromArguments(array($definition->getArguments(), $definition->getFactory())); $otherDefinitions = new \SplObjectStorage(); + $serviceCalls = array(); foreach ($inlinedDefinitions as $def) { if ($def === $definition || isset($constructorDefinitions[$def])) { @@ -812,16 +830,21 @@ protected function {$methodName}($lazyInitialization) } else { $otherDefinitions[$def] = $inlinedDefinitions[$def]; } + $arguments = array($def->getArguments(), $def->getFactory(), $def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()); + $this->getServiceCallsFromArguments($arguments, $serviceCalls, false, $id); } $isSimpleInstance = !$definition->getProperties() && !$definition->getMethodCalls() && !$definition->getConfigurator(); + $preInstance = isset($this->circularReferences[$id]) && !$this->getProxyDumper()->isProxyCandidate($definition) && $definition->isShared(); $code .= - $this->addServiceInclude($id, $definition, $inlinedDefinitions). - $this->addServiceLocalTempVariables($id, $definition, $constructorDefinitions, $inlinedDefinitions). - $this->addServiceInlinedDefinitions($id, $definition, $constructorDefinitions, $isSimpleInstance). + $this->addServiceInclude($id, $definition, $inlinedDefinitions, $serviceCalls). + $this->addServiceLocalTempVariables($id, $definition, $constructorDefinitions, $serviceCalls, $preInstance). + $this->addServiceInlinedDefinitions($id, $definition, $constructorDefinitions, $isSimpleInstance, $preInstance). $this->addServiceInstance($id, $definition, $isSimpleInstance). - $this->addServiceLocalTempVariables($id, $definition, $otherDefinitions, $inlinedDefinitions). + $this->addServiceLocalTempVariables($id, $definition, $constructorDefinitions->offsetUnset($definition) ?: $constructorDefinitions, $serviceCalls). + $this->addServiceLocalTempVariables($id, $definition, $otherDefinitions, $serviceCalls). + $this->addServiceInlinedDefinitions($id, $definition, $constructorDefinitions, $isSimpleInstance). $this->addServiceInlinedDefinitions($id, $definition, $otherDefinitions, $isSimpleInstance). $this->addServiceInlinedDefinitionsSetup($id, $definition, $inlinedDefinitions, $isSimpleInstance). $this->addServiceProperties($definition). @@ -1558,30 +1581,29 @@ private function getServiceConditionals($value) /** * Builds service calls from arguments. + * + * Populates $calls with "referenced id" => ["reference count", "invalid behavior"] pairs. */ - private function getServiceCallsFromArguments(array $arguments, array &$calls, $isPreInstance, $callerId, array &$behavior = array(), $step = 1) + private function getServiceCallsFromArguments(array $arguments, array &$calls, $preInstance, $callerId) { foreach ($arguments as $argument) { if (\is_array($argument)) { - $this->getServiceCallsFromArguments($argument, $calls, $isPreInstance, $callerId, $behavior, $step); + $this->getServiceCallsFromArguments($argument, $calls, $preInstance, $callerId); } elseif ($argument instanceof Reference) { $id = $this->container->normalizeId($argument); if (!isset($calls[$id])) { - $calls[$id] = (int) ($isPreInstance && isset($this->circularReferences[$callerId][$id])); - } - if (!isset($behavior[$id])) { - $behavior[$id] = $argument->getInvalidBehavior(); + $calls[$id] = array((int) ($preInstance && isset($this->circularReferences[$callerId][$id])), $argument->getInvalidBehavior()); } else { - $behavior[$id] = min($behavior[$id], $argument->getInvalidBehavior()); + $calls[$id][1] = min($calls[$id][1], $argument->getInvalidBehavior()); } - $calls[$id] += $step; + ++$calls[$id][0]; } } } - private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null) + private function getDefinitionsFromArguments(array $arguments, $isConstructorArgument = true, \SplObjectStorage $definitions = null) { if (null === $definitions) { $definitions = new \SplObjectStorage(); @@ -1589,22 +1611,23 @@ private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage foreach ($arguments as $argument) { if (\is_array($argument)) { - $this->getDefinitionsFromArguments($argument, $definitions); + $this->getDefinitionsFromArguments($argument, $isConstructorArgument, $definitions); } elseif (!$argument instanceof Definition) { // no-op } elseif (isset($definitions[$argument])) { - $definitions[$argument] = 1 + $definitions[$argument]; + $def = $definitions[$argument]; + $definitions[$argument] = array(1 + $def[0], $isConstructorArgument || $def[1]); } 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); + $definitions[$argument] = array(1, $isConstructorArgument); + $this->getDefinitionsFromArguments($argument->getArguments(), $isConstructorArgument, $definitions); + $this->getDefinitionsFromArguments(array($argument->getFactory()), $isConstructorArgument, $definitions); + $this->getDefinitionsFromArguments($argument->getProperties(), false, $definitions); + $this->getDefinitionsFromArguments($argument->getMethodCalls(), false, $definitions); + $this->getDefinitionsFromArguments(array($argument->getConfigurator()), false, $definitions); // move current definition last in the list - $nbOccurences = $definitions[$argument]; + $def = $definitions[$argument]; unset($definitions[$argument]); - $definitions[$argument] = $nbOccurences; + $definitions[$argument] = $def; } } @@ -1640,7 +1663,7 @@ private function hasReference($id, array $arguments, $deep = false, array &$visi return true; } - if (!$deep || isset($visited[$argumentId]) || !isset($this->circularReferences[$id][$argumentId])) { + if (!$deep || isset($visited[$argumentId]) || !isset($this->circularReferences[$argumentId])) { continue; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 1d2640e7878f1..f3f762c3ad500 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1355,6 +1355,12 @@ public function testAlmostCircular($visibility) $foo5 = $container->get('foo5'); $this->assertSame($foo5, $foo5->bar->foo); + + $manager = $container->get('manager'); + $this->assertEquals(new \stdClass(), $manager); + + $manager = $container->get('manager2'); + $this->assertEquals(new \stdClass(), $manager); } public function provideAlmostCircular() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index e90edd50283b4..b876bf48da95d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -23,6 +23,7 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; use Symfony\Component\DependencyInjection\EnvVarProcessorInterface; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -530,50 +531,22 @@ public function testCircularReferenceAllowanceForLazyServices() $container->compile(); $dumper = new PhpDumper($container); + $dumper->setProxyDumper(new \DummyProxyDumper()); $dumper->dump(); $this->addToAssertionCount(1); - } - - public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServices() - { - /* - * test graph: - * [connection] -> [event_manager] --> [entity_manager](lazy) - * | - * --(call)- addEventListener ("@lazy_service") - * - * [lazy_service](lazy) -> [entity_manager](lazy) - * - */ - - $container = new ContainerBuilder(); - - $eventManagerDefinition = new Definition('stdClass'); - - $connectionDefinition = $container->register('connection', 'stdClass')->setPublic(true); - $connectionDefinition->addArgument($eventManagerDefinition); - - $container->register('entity_manager', 'stdClass') - ->setPublic(true) - ->setLazy(true) - ->addArgument(new Reference('connection')); - - $lazyServiceDefinition = $container->register('lazy_service', 'stdClass'); - $lazyServiceDefinition->setPublic(true); - $lazyServiceDefinition->setLazy(true); - $lazyServiceDefinition->addArgument(new Reference('entity_manager')); - - $eventManagerDefinition->addMethodCall('addEventListener', array(new Reference('lazy_service'))); - - $container->compile(); $dumper = new PhpDumper($container); - $dumper->setProxyDumper(new \DummyProxyDumper()); - $dumper->dump(); + $message = 'Circular reference detected for service "bar", path: "bar -> foo -> bar". Try running "composer require symfony/proxy-manager-bridge".'; + if (method_exists($this, 'expectException')) { + $this->expectException(ServiceCircularReferenceException::class); + $this->expectExceptionMessage($message); + } else { + $this->setExpectedException(ServiceCircularReferenceException::class, $message); + } - $this->addToAssertionCount(1); + $dumper->dump(); } public function testDedupLazyProxy() @@ -851,6 +824,12 @@ public function testAlmostCircular($visibility) $foo5 = $container->get('foo5'); $this->assertSame($foo5, $foo5->bar->foo); + + $manager = $container->get('manager'); + $this->assertEquals(new \stdClass(), $manager); + + $manager = $container->get('manager2'); + $this->assertEquals(new \stdClass(), $manager); } public function provideAlmostCircular() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php index dff937ccdbb7f..2079e136b74b3 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 @@ -55,4 +55,50 @@ ->addArgument(new Reference('foo5')) ->setProperty('foo', new Reference('foo5')); +// doctrine-like event system + some extra + +$container->register('manager', 'stdClass')->setPublic(true) + ->addArgument(new Reference('connection')); + +$container->register('logger', 'stdClass')->setPublic(true) + ->addArgument(new Reference('connection')) + ->setProperty('handler', (new Definition('stdClass'))->addArgument(new Reference('manager'))) +; +$container->register('connection', 'stdClass')->setPublic(true) + ->addArgument(new Reference('dispatcher')) + ->addArgument(new Reference('config')); + +$container->register('config', 'stdClass')->setPublic(false) + ->setProperty('logger', new Reference('logger')); + +$container->register('dispatcher', 'stdClass')->setPublic($public) + ->setLazy($public) + ->setProperty('subscriber', new Reference('subscriber')); + +$container->register('subscriber', 'stdClass')->setPublic(true) + ->addArgument(new Reference('manager')); + +// doctrine-like event system + some extra (bis) + +$container->register('manager2', 'stdClass')->setPublic(true) + ->addArgument(new Reference('connection2')); + +$container->register('logger2', 'stdClass')->setPublic(false) + ->addArgument(new Reference('connection2')) + ->setProperty('handler2', (new Definition('stdClass'))->addArgument(new Reference('manager2'))) +; +$container->register('connection2', 'stdClass')->setPublic(true) + ->addArgument(new Reference('dispatcher2')) + ->addArgument(new Reference('config2')); + +$container->register('config2', 'stdClass')->setPublic(false) + ->setProperty('logger2', new Reference('logger2')); + +$container->register('dispatcher2', 'stdClass')->setPublic($public) + ->setLazy($public) + ->setProperty('subscriber2', new Reference('subscriber2')); + +$container->register('subscriber2', 'stdClass')->setPublic(false) + ->addArgument(new Reference('manager2')); + return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php index 45fb2432c33c9..f0ad8ef31c00a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php @@ -25,10 +25,16 @@ public function __construct() $this->methodMap = array( 'bar2' => 'getBar2Service', 'bar3' => 'getBar3Service', + 'connection' => 'getConnectionService', + 'connection2' => 'getConnection2Service', 'foo' => 'getFooService', 'foo2' => 'getFoo2Service', 'foo5' => 'getFoo5Service', 'foobar4' => 'getFoobar4Service', + 'logger' => 'getLoggerService', + 'manager' => 'getManagerService', + 'manager2' => 'getManager2Service', + 'subscriber' => 'getSubscriberService', ); $this->aliases = array(); @@ -41,10 +47,16 @@ public function getRemovedIds() 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, 'bar' => true, 'bar5' => true, + 'config' => true, + 'config2' => true, + 'dispatcher' => true, + 'dispatcher2' => true, 'foo4' => true, 'foobar' => true, 'foobar2' => true, 'foobar3' => true, + 'logger2' => true, + 'subscriber2' => true, ); } @@ -95,6 +107,49 @@ protected function getBar3Service() return $instance; } + /** + * Gets the public 'connection' shared service. + * + * @return \stdClass + */ + protected function getConnectionService() + { + $a = new \stdClass(); + + $b = new \stdClass(); + + $this->services['connection'] = $instance = new \stdClass($a, $b); + + $a->subscriber = ${($_ = isset($this->services['subscriber']) ? $this->services['subscriber'] : $this->getSubscriberService()) && false ?: '_'}; + $b->logger = ${($_ = isset($this->services['logger']) ? $this->services['logger'] : $this->getLoggerService()) && false ?: '_'}; + + return $instance; + } + + /** + * Gets the public 'connection2' shared service. + * + * @return \stdClass + */ + protected function getConnection2Service() + { + $a = new \stdClass(); + + $b = new \stdClass(); + + $this->services['connection2'] = $instance = new \stdClass($a, $b); + + $c = ${($_ = isset($this->services['manager2']) ? $this->services['manager2'] : $this->getManager2Service()) && false ?: '_'}; + + $d = new \stdClass($instance); + + $a->subscriber2 = new \stdClass($c); + $d->handler2 = new \stdClass($c); + $b->logger2 = $d; + + return $instance; + } + /** * Gets the public 'foo' shared service. * @@ -136,7 +191,7 @@ protected function getFoo5Service() { $this->services['foo5'] = $instance = new \stdClass(); - $a = new \stdClass(${($_ = isset($this->services['foo5']) ? $this->services['foo5'] : $this->getFoo5Service()) && false ?: '_'}); + $a = new \stdClass($instance); $a->foo = $instance; @@ -160,4 +215,72 @@ protected function getFoobar4Service() return $instance; } + + /** + * Gets the public 'logger' shared service. + * + * @return \stdClass + */ + protected function getLoggerService() + { + $a = ${($_ = isset($this->services['connection']) ? $this->services['connection'] : $this->getConnectionService()) && false ?: '_'}; + + if (isset($this->services['logger'])) { + return $this->services['logger']; + } + + $this->services['logger'] = $instance = new \stdClass($a); + + $instance->handler = new \stdClass(${($_ = isset($this->services['manager']) ? $this->services['manager'] : $this->getManagerService()) && false ?: '_'}); + + return $instance; + } + + /** + * Gets the public 'manager' shared service. + * + * @return \stdClass + */ + protected function getManagerService() + { + $a = ${($_ = isset($this->services['connection']) ? $this->services['connection'] : $this->getConnectionService()) && false ?: '_'}; + + if (isset($this->services['manager'])) { + return $this->services['manager']; + } + + return $this->services['manager'] = new \stdClass($a); + } + + /** + * Gets the public 'manager2' shared service. + * + * @return \stdClass + */ + protected function getManager2Service() + { + $a = ${($_ = isset($this->services['connection2']) ? $this->services['connection2'] : $this->getConnection2Service()) && false ?: '_'}; + + if (isset($this->services['manager2'])) { + return $this->services['manager2']; + } + + return $this->services['manager2'] = new \stdClass($a); + } + + /** + * Gets the public 'subscriber' shared service. + * + * @return \stdClass + */ + protected function getSubscriberService() + { + $a = ${($_ = isset($this->services['manager']) ? $this->services['manager'] : $this->getManagerService()) && false ?: '_'}; + + if (isset($this->services['subscriber'])) { + return $this->services['subscriber']; + } + + return $this->services['subscriber'] = new \stdClass($a); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php index be18eb183abd0..784346b9242fa 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php @@ -26,6 +26,10 @@ public function __construct() 'bar' => 'getBarService', 'bar3' => 'getBar3Service', 'bar5' => 'getBar5Service', + 'connection' => 'getConnectionService', + 'connection2' => 'getConnection2Service', + 'dispatcher' => 'getDispatcherService', + 'dispatcher2' => 'getDispatcher2Service', 'foo' => 'getFooService', 'foo2' => 'getFoo2Service', 'foo4' => 'getFoo4Service', @@ -34,6 +38,10 @@ public function __construct() 'foobar2' => 'getFoobar2Service', 'foobar3' => 'getFoobar3Service', 'foobar4' => 'getFoobar4Service', + 'logger' => 'getLoggerService', + 'manager' => 'getManagerService', + 'manager2' => 'getManager2Service', + 'subscriber' => 'getSubscriberService', ); $this->aliases = array(); @@ -45,6 +53,10 @@ public function getRemovedIds() 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, 'bar2' => true, + 'config' => true, + 'config2' => true, + 'logger2' => true, + 'subscriber2' => true, ); } @@ -115,6 +127,81 @@ protected function getBar5Service() return $instance; } + /** + * Gets the public 'connection' shared service. + * + * @return \stdClass + */ + protected function getConnectionService() + { + $a = ${($_ = isset($this->services['dispatcher']) ? $this->services['dispatcher'] : $this->getDispatcherService()) && false ?: '_'}; + + if (isset($this->services['connection'])) { + return $this->services['connection']; + } + + $b = new \stdClass(); + + $this->services['connection'] = $instance = new \stdClass($a, $b); + + $b->logger = ${($_ = isset($this->services['logger']) ? $this->services['logger'] : $this->getLoggerService()) && false ?: '_'}; + + return $instance; + } + + /** + * Gets the public 'connection2' shared service. + * + * @return \stdClass + */ + protected function getConnection2Service() + { + $a = ${($_ = isset($this->services['dispatcher2']) ? $this->services['dispatcher2'] : $this->getDispatcher2Service()) && false ?: '_'}; + + if (isset($this->services['connection2'])) { + return $this->services['connection2']; + } + + $b = new \stdClass(); + + $this->services['connection2'] = $instance = new \stdClass($a, $b); + + $c = new \stdClass($instance); + + $c->handler2 = new \stdClass(${($_ = isset($this->services['manager2']) ? $this->services['manager2'] : $this->getManager2Service()) && false ?: '_'}); + $b->logger2 = $c; + + return $instance; + } + + /** + * Gets the public 'dispatcher' shared service. + * + * @return \stdClass + */ + protected function getDispatcherService($lazyLoad = true) + { + $this->services['dispatcher'] = $instance = new \stdClass(); + + $instance->subscriber = ${($_ = isset($this->services['subscriber']) ? $this->services['subscriber'] : $this->getSubscriberService()) && false ?: '_'}; + + return $instance; + } + + /** + * Gets the public 'dispatcher2' shared service. + * + * @return \stdClass + */ + protected function getDispatcher2Service($lazyLoad = true) + { + $this->services['dispatcher2'] = $instance = new \stdClass(); + + $instance->subscriber2 = new \stdClass(${($_ = isset($this->services['manager2']) ? $this->services['manager2'] : $this->getManager2Service()) && false ?: '_'}); + + return $instance; + } + /** * Gets the public 'foo' shared service. * @@ -232,4 +319,72 @@ protected function getFoobar4Service() return $instance; } + + /** + * Gets the public 'logger' shared service. + * + * @return \stdClass + */ + protected function getLoggerService() + { + $a = ${($_ = isset($this->services['connection']) ? $this->services['connection'] : $this->getConnectionService()) && false ?: '_'}; + + if (isset($this->services['logger'])) { + return $this->services['logger']; + } + + $this->services['logger'] = $instance = new \stdClass($a); + + $instance->handler = new \stdClass(${($_ = isset($this->services['manager']) ? $this->services['manager'] : $this->getManagerService()) && false ?: '_'}); + + return $instance; + } + + /** + * Gets the public 'manager' shared service. + * + * @return \stdClass + */ + protected function getManagerService() + { + $a = ${($_ = isset($this->services['connection']) ? $this->services['connection'] : $this->getConnectionService()) && false ?: '_'}; + + if (isset($this->services['manager'])) { + return $this->services['manager']; + } + + return $this->services['manager'] = new \stdClass($a); + } + + /** + * Gets the public 'manager2' shared service. + * + * @return \stdClass + */ + protected function getManager2Service() + { + $a = ${($_ = isset($this->services['connection2']) ? $this->services['connection2'] : $this->getConnection2Service()) && false ?: '_'}; + + if (isset($this->services['manager2'])) { + return $this->services['manager2']; + } + + return $this->services['manager2'] = new \stdClass($a); + } + + /** + * Gets the public 'subscriber' shared service. + * + * @return \stdClass + */ + protected function getSubscriberService() + { + $a = ${($_ = isset($this->services['manager']) ? $this->services['manager'] : $this->getManagerService()) && false ?: '_'}; + + if (isset($this->services['subscriber'])) { + return $this->services['subscriber']; + } + + return $this->services['subscriber'] = new \stdClass($a); + } } From 15835bc7dfb3bcad3deb9d11f66ba8eff942e3d8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 1 Aug 2018 16:12:10 +0200 Subject: [PATCH 05/47] updated CHANGELOG for 2.8.44 --- CHANGELOG-2.8.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG-2.8.md b/CHANGELOG-2.8.md index 4db4bfd7cb1e7..4760af369f84f 100644 --- a/CHANGELOG-2.8.md +++ b/CHANGELOG-2.8.md @@ -7,6 +7,14 @@ in 2.8 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.8.0...v2.8.1 +* 2.8.44 (2018-08-01) + + * security #cve-2018-14774 [HttpKernel] fix trusted headers management in HttpCache and InlineFragmentRenderer (nicolas-grekas) + * security #cve-2018-14773 [HttpFoundation] Remove support for legacy and risky HTTP headers (nicolas-grekas) + * bug #28003 [HttpKernel] Fixes invalid REMOTE_ADDR in inline subrequest when configuring trusted proxy with subnet (netiul) + * bug #28045 [HttpFoundation] Fix Cookie::isCleared (ro0NL) + * bug #28080 [HttpFoundation] fixed using _method parameter with invalid type (Phobetor) + * 2.8.43 (2018-07-23) * bug #28005 [HttpKernel] Fixed templateExists on parse error of the template name (yceruto) From 282e062226e26ffc43fda998ad55935b49710c39 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 1 Aug 2018 16:12:26 +0200 Subject: [PATCH 06/47] update CONTRIBUTORS for 2.8.44 --- CONTRIBUTORS.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index b6eef0b63198c..bf391b7ee79ce 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -32,10 +32,10 @@ Symfony is the result of the work of many people who made the code better - Lukas Kahwe Smith (lsmith) - Martin Hasoň (hason) - Jeremy Mikola (jmikola) + - Samuel ROZE (sroze) - Jean-François Simon (jfsimon) - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) - - Samuel ROZE (sroze) - Jules Pietri (heah) - Eriksen Costa (eriksencosta) - Guilhem Niot (energetick) @@ -67,8 +67,8 @@ Symfony is the result of the work of many people who made the code better - Bilal Amarni (bamarni) - Jérémy DERUSSÉ (jderusse) - Florin Patan (florinpatan) - - Mathieu Piot (mpiot) - Gábor Egyed (1ed) + - Mathieu Piot (mpiot) - Michel Weimerskirch (mweimerskirch) - Titouan Galopin (tgalopin) - Andrej Hudec (pulzarraider) @@ -101,9 +101,9 @@ Symfony is the result of the work of many people who made the code better - Paráda József (paradajozsef) - Arnaud Le Blanc (arnaud-lb) - Maxime STEINHAUSSER + - Grégoire Paris (greg0ire) - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) - - Grégoire Paris (greg0ire) - Brice BERNARD (brikou) - Valentin Udaltsov (vudaltsov) - gadelat (gadelat) @@ -159,6 +159,7 @@ Symfony is the result of the work of many people who made the code better - Vyacheslav Pavlov - Richard van Laak (rvanlaak) - Richard Shank (iampersistent) + - Jannik Zschiesche (apfelbox) - Thomas Rabaix (rande) - Rouven Weßling (realityking) - Clemens Tolboom @@ -170,7 +171,6 @@ Symfony is the result of the work of many people who made the code better - Amal Raghav (kertz) - Jonathan Ingram (jonathaningram) - Artur Kotyrba - - Jannik Zschiesche (apfelbox) - GDIBass - jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent) - James Halsall (jaitsu) @@ -284,6 +284,7 @@ Symfony is the result of the work of many people who made the code better - Andreas Schempp (aschempp) - jdhoek - Pavel Batanov (scaytrase) + - Massimiliano Arione (garak) - Bob den Otter (bopp) - Nikita Konstantinov - Wodor Wodorski @@ -332,7 +333,6 @@ Symfony is the result of the work of many people who made the code better - Adrian Rudnik (kreischweide) - Francesc Rosàs (frosas) - Romain Pierre (romain-pierre) - - Massimiliano Arione (garak) - Julien Galenski (ruian) - Bongiraud Dominique - janschoenherr @@ -407,6 +407,7 @@ Symfony is the result of the work of many people who made the code better - Karoly Negyesi (chx) - Ivan Kurnosov - Xavier HAUSHERR + - David Prévot - Albert Jessurum (ajessu) - Laszlo Korte - Miha Vrhovnik @@ -438,6 +439,7 @@ Symfony is the result of the work of many people who made the code better - Mihai Stancu - Olivier Dolbeau (odolbeau) - Jan Rosier (rosier) + - Alessandro Lai (jean85) - Arturs Vonda - Josip Kruslin - Asmir Mustafic (goetas) @@ -565,7 +567,6 @@ Symfony is the result of the work of many people who made the code better - Disquedur - Michiel Boeckaert (milio) - Geoffrey Tran (geoff) - - David Prévot - Jan Behrens - Mantas Var (mvar) - Sebastian Krebs @@ -584,7 +585,6 @@ Symfony is the result of the work of many people who made the code better - Max Rath (drak3) - Stéphane Escandell (sescandell) - Konstantin S. M. Möllers (ksmmoellers) - - Alessandro Lai (jean85) - James Johnston - Sinan Eldem - Alexandre Dupuy (satchette) @@ -713,6 +713,7 @@ Symfony is the result of the work of many people who made the code better - Yuen-Chi Lian - Besnik Br - Jose Gonzalez + - Oleksii Zhurbytskyi - Dariusz Ruminski - Joshua Nye - Claudio Zizza @@ -882,6 +883,7 @@ Symfony is the result of the work of many people who made the code better - datibbaw - Erik Saunier (snickers) - Rootie + - Kyle - Raul Fraile (raulfraile) - sensio - Sebastien Morel (plopix) @@ -1382,6 +1384,7 @@ Symfony is the result of the work of many people who made the code better - Matt Wells - Nicolas Appriou - stloyd + - Andreas - Chris Tickner - Andrew Coulton - Jeremy Benoist @@ -1432,7 +1435,6 @@ Symfony is the result of the work of many people who made the code better - Nicolas Bastien (nicolas_bastien) - Denis (yethee) - Andrew Zhilin (zhil) - - Oleksii Zhurbytskyi - Andy Stanberry - Felix Marezki - Normunds @@ -1973,7 +1975,6 @@ Symfony is the result of the work of many people who made the code better - fh-github@fholzhauer.de - AbdElKader Bouadjadja - DSeemiller - - Kyle - Jan Emrich - Mark Topper - Xavier REN @@ -2001,3 +2002,4 @@ Symfony is the result of the work of many people who made the code better - Matej Žilák (teo_sk) - Vladislav Vlastovskiy (vlastv) - RENAUDIN Xavier (xorrox) + - Yannick Vanhaeren (yvh) From 4abd3a8eb02458f5f8ac474e9968af9ec7e7e461 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 1 Aug 2018 16:12:31 +0200 Subject: [PATCH 07/47] updated VERSION for 2.8.44 --- 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 e07787699bcb2..f9b22f7700e19 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.8.44-DEV'; + const VERSION = '2.8.44'; const VERSION_ID = 20844; const MAJOR_VERSION = 2; const MINOR_VERSION = 8; const RELEASE_VERSION = 44; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '11/2018'; const END_OF_LIFE = '11/2019'; From 3ff6e333dd004d3898e9cf832f01a8ac784331b5 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 1 Aug 2018 16:46:30 +0200 Subject: [PATCH 08/47] bumped Symfony version to 2.8.45 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index f9b22f7700e19..32a90578342e7 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.8.44'; - const VERSION_ID = 20844; + const VERSION = '2.8.45-DEV'; + const VERSION_ID = 20845; const MAJOR_VERSION = 2; const MINOR_VERSION = 8; - const RELEASE_VERSION = 44; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 45; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '11/2018'; const END_OF_LIFE = '11/2019'; From 9d7d3e71243610e83d6e6ae0ea6ba1e1c94156e8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 1 Aug 2018 16:55:31 +0200 Subject: [PATCH 09/47] bumped Symfony version to 3.4.15 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 0684f22a03232..0f4eeeb58abe1 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.14'; - const VERSION_ID = 30414; + const VERSION = '3.4.15-DEV'; + const VERSION_ID = 30415; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; - const RELEASE_VERSION = 14; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 15; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021'; From 83dcbe9096f9b7aebdda17de5ec17850c720c1b6 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 23 Jul 2018 19:52:59 +0200 Subject: [PATCH 10/47] [DoctrineBridge] allow dev versions of Doctrine again --- composer.json | 4 ++-- src/Symfony/Bridge/Doctrine/composer.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index e0a1c53c4f158..6d5065d4d3084 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "require": { "php": "^5.5.9|>=7.0.8", "ext-xml": "*", - "doctrine/common": "~2.4@stable", + "doctrine/common": "~2.4", "fig/link-util": "^1.0", "twig/twig": "^1.35|^2.4.4", "psr/cache": "~1.0", @@ -92,7 +92,7 @@ "doctrine/cache": "~1.6", "doctrine/data-fixtures": "1.0.*", "doctrine/dbal": "~2.4", - "doctrine/orm": "~2.4,>=2.4.5,<=2.7.0", + "doctrine/orm": "~2.4,>=2.4.5", "doctrine/doctrine-bundle": "~1.4", "monolog/monolog": "~1.11", "ocramius/proxy-manager": "~0.4|~1.0|~2.0", diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index 1535f8f42b8f8..420535b7516fd 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^5.5.9|>=7.0.8", - "doctrine/common": "~2.4@stable", + "doctrine/common": "~2.4", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0" }, From 44dbea63308ae8ac29b26d4e5ce9416c08870751 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Wed, 1 Aug 2018 16:08:30 +0200 Subject: [PATCH 11/47] [Security] Call AccessListener after LogoutListener --- .../Tests/Functional/LogoutTest.php | 10 +++++++ .../Functional/app/LogoutAccess/bundles.php | 18 +++++++++++++ .../Functional/app/LogoutAccess/config.yml | 26 +++++++++++++++++++ .../Functional/app/LogoutAccess/routing.yml | 5 ++++ .../Bundle/SecurityBundle/composer.json | 2 +- .../Component/Security/Http/Firewall.php | 13 ++++++++++ .../Security/Http/Tests/FirewallTest.php | 2 +- 7 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/bundles.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/config.yml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/routing.yml diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php index d3c3b77fd5d61..15131290b4ee3 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php @@ -49,4 +49,14 @@ public function testCsrfTokensAreClearedOnLogout() $this->assertFalse($client->getContainer()->get('security.csrf.token_storage')->hasToken('foo')); } + + public function testAccessControlDoesNotApplyOnLogout() + { + $client = $this->createClient(array('test_case' => 'LogoutAccess', 'root_config' => 'config.yml')); + + $client->request('POST', '/login', array('_username' => 'johannes', '_password' => 'test')); + $client->request('GET', '/logout'); + + $this->assertRedirect($client->getResponse(), '/'); + } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/bundles.php new file mode 100644 index 0000000000000..c934b52aee7c0 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\SecurityBundle\SecurityBundle; + +return array( + new FrameworkBundle(), + new SecurityBundle(), +); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/config.yml new file mode 100644 index 0000000000000..2e20735b80236 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/config.yml @@ -0,0 +1,26 @@ +imports: +- { resource: ./../config/framework.yml } + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + providers: + in_memory: + memory: + users: + johannes: { password: test, roles: [ROLE_USER] } + + firewalls: + default: + form_login: + check_path: login + remember_me: true + require_previous_session: false + logout: ~ + anonymous: ~ + stateless: true + + access_control: + - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: .*, roles: IS_AUTHENTICATED_FULLY } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/routing.yml new file mode 100644 index 0000000000000..1dddfca2f8154 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/routing.yml @@ -0,0 +1,5 @@ +login: + path: /login + +logout: + path: /logout diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index c0508ea29b02b..5e8d9458e6eca 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=5.3.9", "ext-xml": "*", - "symfony/security": "^2.8.42|^3.4.12", + "symfony/security": "^2.8.45|^3.4.15", "symfony/security-acl": "~2.7|~3.0.0", "symfony/http-kernel": "~2.7|~3.0.0", "symfony/polyfill-php70": "~1.0" diff --git a/src/Symfony/Component/Security/Http/Firewall.php b/src/Symfony/Component/Security/Http/Firewall.php index b0a58e9a23852..f089c04abeb8c 100644 --- a/src/Symfony/Component/Security/Http/Firewall.php +++ b/src/Symfony/Component/Security/Http/Firewall.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Security\Http\Firewall\AccessListener; /** * Firewall uses a FirewallMap to register security listeners for the given @@ -58,8 +59,16 @@ public function onKernelRequest(GetResponseEvent $event) $exceptionListener->register($this->dispatcher); } + $accessListener = null; + // initiate the listener chain foreach ($authenticationListeners as $listener) { + if ($listener instanceof AccessListener) { + $accessListener = $listener; + + continue; + } + $listener->handle($event); if ($event->hasResponse()) { @@ -70,6 +79,10 @@ public function onKernelRequest(GetResponseEvent $event) if (null !== $logoutListener) { $logoutListener->handle($event); } + + if (!$event->hasResponse() && null !== $accessListener) { + $accessListener->handle($event); + } } public function onKernelFinishRequest(FinishRequestEvent $event) diff --git a/src/Symfony/Component/Security/Http/Tests/FirewallTest.php b/src/Symfony/Component/Security/Http/Tests/FirewallTest.php index bd475bb4e5b1f..66dad46152c02 100644 --- a/src/Symfony/Component/Security/Http/Tests/FirewallTest.php +++ b/src/Symfony/Component/Security/Http/Tests/FirewallTest.php @@ -79,7 +79,7 @@ public function testOnKernelRequestStopsWhenThereIsAResponse() ->getMock() ; $event - ->expects($this->once()) + ->expects($this->at(0)) ->method('hasResponse') ->will($this->returnValue(true)) ; From fe482ccdb1284284611ba439004412e87fb0eb13 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 3 Aug 2018 10:56:54 +0200 Subject: [PATCH 12/47] fix merge --- .../Component/PropertyInfo/Tests/Fixtures/Dummy.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php index 18ce6ef9c2a1a..5b82209e1d417 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php @@ -45,14 +45,6 @@ class Dummy extends ParentDummy * @Groups({"a", "b"}) */ public $collection; - /** - * @var string[][] - */ - public $nestedCollection; - /** - * @var mixed[] - */ - public $mixedCollection; /** * @var string[][] From 9857ca07aa76d747416de89fe8ae50586b507666 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 2 Aug 2018 08:56:36 +0200 Subject: [PATCH 13/47] [travis] merge "same Symfony version" jobs in one --- .travis.yml | 130 ++++++++++-------- .../Tests/phpt/decorate_exception_hander.phpt | 3 +- .../Component/Process/Tests/ProcessTest.php | 6 - 3 files changed, 76 insertions(+), 63 deletions(-) diff --git a/.travis.yml b/.travis.yml index c0367f10720bd..a1eb5e5e59f9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,9 +22,7 @@ matrix: sudo: required group: edge - php: 5.4 - - php: 5.5 - - php: 5.6 - - php: 7.0 + env: php_extra="5.5 5.6 7.0" - php: 7.1 env: deps=high - php: 7.2 @@ -42,15 +40,26 @@ services: mongodb before_install: - | # General configuration + set -e stty cols 120 - PHP=$TRAVIS_PHP_VERSION [ -d ~/.composer ] || mkdir ~/.composer cp .composer/* ~/.composer/ export PHPUNIT=$(readlink -f ./phpunit) export PHPUNIT_X="$PHPUNIT --exclude-group tty,benchmark,intl-data" export COMPOSER_UP='composer update --no-progress --no-suggest --ansi' + export COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n') + find ~/.phpenv -name xdebug.ini -delete + + if [[ $TRAVIS_PHP_VERSION = 5.* || $TRAVIS_PHP_VERSION = hhvm* ]]; then + composer () { + $HOME/.phpenv/versions/7.1/bin/composer config platform.php $(echo ' > $INI - echo memory_limit = -1 >> $INI - echo session.gc_probability = 0 >> $INI - echo opcache.enable_cli = 1 >> $INI - echo hhvm.jit = 0 >> $INI - echo apc.enable_cli = 1 >> $INI - [[ $PHP = 5.* ]] && echo extension = memcache.so >> $INI - if [[ $PHP = 5.* ]]; then - echo extension = mongo.so >> $INI - fi - # tpecl is a helper to compile and cache php extensions tpecl () { local ext_name=$1 @@ -114,6 +105,7 @@ before_install: if [[ -e $ext_cache/$ext_so ]]; then echo extension = $ext_cache/$ext_so >> $INI else + rm ~/.pearrc /tmp/pear 2>/dev/null || true mkdir -p $ext_cache echo yes | pecl install -f $ext_name && cp $ext_dir/$ext_so $ext_cache @@ -121,38 +113,62 @@ before_install: } export -f tpecl - # Matrix lines for intermediate PHP versions are skipped for pull requests - if [[ ! $deps && ! $PHP = ${MIN_PHP%.*} && ! $PHP = hhvm* && $TRAVIS_PULL_REQUEST != false ]]; then - deps=skip - skip=1 - else - COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n') - fi - - | # Install sigchild-enabled PHP to test the Process component on the lowest PHP matrix line - if [[ ! $deps && $PHP = ${MIN_PHP%.*} && ! -d php-$MIN_PHP/sapi ]]; then + if [[ ! $deps && $TRAVIS_PHP_VERSION = ${MIN_PHP%.*} && ! -d php-$MIN_PHP/sapi ]]; then wget http://museum.php.net/php5/php-$MIN_PHP.tar.bz2 -O - | tar -xj && (cd php-$MIN_PHP && ./configure --enable-sigchild --enable-pcntl && make -j2) fi + - | + # php.ini configuration + for PHP in $TRAVIS_PHP_VERSION $php_extra; do + if [[ $PHP = hhvm* ]]; then + INI=/etc/hhvm/php.ini + else + phpenv global $PHP 2>/dev/null || (cd / && wget https://s3.amazonaws.com/travis-php-archives/binaries/ubuntu/14.04/x86_64/php-$PHP.tar.bz2 -O - | tar -xj) + INI=~/.phpenv/versions/$PHP/etc/conf.d/travis.ini + fi + echo date.timezone = Europe/Paris >> $INI + echo memory_limit = -1 >> $INI + echo session.gc_probability = 0 >> $INI + echo opcache.enable_cli = 1 >> $INI + echo hhvm.jit = 0 >> $INI + echo apc.enable_cli = 1 >> $INI + [[ $PHP = 5.* ]] && echo extension = memcache.so >> $INI + if [[ $PHP = 5.* ]]; then + echo extension = mongo.so >> $INI + fi + done + - | # Install extra PHP extensions - if [[ ! $skip && $PHP = 5.* ]]; then - ([[ $deps ]] || tfold ext.symfony_debug 'cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo extension = $(pwd)/modules/symfony_debug.so >> '"$INI") - tfold ext.memcached tpecl memcached-2.1.0 memcached.so $INI - tfold ext.apcu tpecl apcu-4.0.11 apcu.so $INI - elif [[ ! $skip && $PHP = 7.* ]]; then - tfold ext.apcu tpecl apcu-5.1.6 apcu.so $INI - tfold ext.mongodb tpecl mongodb-1.5.0 mongodb.so $INI - fi + for PHP in $TRAVIS_PHP_VERSION $php_extra; do + if [[ $PHP = hhvm* ]]; then + continue + fi + export PHP=$PHP + phpenv global $PHP + INI=~/.phpenv/versions/$PHP/etc/conf.d/travis.ini + if [[ $PHP = 5.* ]]; then + tfold ext.memcached tpecl memcached-2.1.0 memcached.so $INI + tfold ext.apcu tpecl apcu-4.0.11 apcu.so $INI + [[ $deps ]] && continue + ext_cache=~/php-ext/$(php -r "echo basename(ini_get('extension_dir'));")/symfony_debug.so + [[ -e $ext_cache ]] || (tfold ext.symfony_debug "cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && mv modules/symfony_debug.so $ext_cache && phpize --clean") + echo extension = $ext_cache >> $INI + elif [[ $PHP = 7.* ]]; then + tfold ext.apcu tpecl apcu-5.1.6 apcu.so $INI + tfold ext.mongodb tpecl mongodb-1.5.0 mongodb.so $INI + fi + done install: - | # Create local composer packages for each patched components and reference them in composer.json files when cross-testing components if [[ ! $deps ]]; then php .github/build-packages.php HEAD^ src/Symfony/Bridge/PhpUnit - elif [[ ! $skip ]]; then + else export SYMFONY_DEPRECATIONS_HELPER=weak && cp composer.json composer.json.orig && echo -e '{\n"require":{'"$(grep phpunit-bridge composer.json)"'"php":"*"},"minimum-stability":"dev"}' > composer.json && @@ -168,7 +184,7 @@ install: git fetch origin $SYMFONY_VERSION && git checkout -m FETCH_HEAD && COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n') - elif [[ ! $skip ]]; then + else SYMFONY_VERSION=$(cat composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9.]*') fi @@ -177,24 +193,27 @@ install: [[ $deps = high && ${SYMFONY_VERSION%.*} != $(git show $(git ls-remote --heads | grep -FA1 /$SYMFONY_VERSION | tail -n 1):composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9]*' | head -n 1) ]] && LEGACY=,legacy export COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev - if [[ ! $skip && $deps ]]; then mv composer.json.phpunit composer.json; fi - - if [[ ! $skip && $PHP = 7.* ]]; then - ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer require --dev --no-update mongodb/mongodb) - fi + if [[ $deps ]]; then mv composer.json.phpunit composer.json; fi - - if [[ ! $skip ]]; then $COMPOSER_UP; fi - - if [[ ! $skip ]]; then ./phpunit install; fi - | # phpinfo - if [[ ! $PHP = hhvm* ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi + if [[ ! $TRAVIS_PHP_VERSION = hhvm* ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi - | run_tests () { set -e - if [[ $skip ]]; then + export PHP=$1 + if [[ $PHP != $TRAVIS_PHP_VERSION && $TRAVIS_PULL_REQUEST != false ]]; then echo -e "\\n\\e[1;34mIntermediate PHP version $PHP is skipped for pull requests.\\e[0m" - elif [[ $deps = high ]]; then + break + fi + phpenv global ${PHP/hhvm*/hhvm} + tfold 'composer update' $COMPOSER_UP + tfold 'phpunit install' ./phpunit install + if [[ $PHP = 7.* ]]; then + ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer require --dev --no-update mongodb/mongodb) + fi + if [[ $deps = high ]]; then echo "$COMPONENTS" | parallel --gnu -j10% "tfold {} 'cd {} && $COMPOSER_UP && $PHPUNIT_X$LEGACY'" elif [[ $deps = low ]]; then echo "$COMPONENTS" | parallel --gnu -j10% "tfold {} 'cd {} && $COMPOSER_UP --prefer-lowest --prefer-stable && $PHPUNIT_X'" @@ -202,12 +221,13 @@ install: $PHPUNIT --exclude-group no-hhvm,benchmark,intl-data else echo "$COMPONENTS" | parallel --gnu "tfold {} $PHPUNIT_X {}" - tfold tty-group $PHPUNIT --group tty + tfold src/Symfony/Component/Console.tty $PHPUNIT src/Symfony/Component/Console --group tty if [[ $PHP = ${MIN_PHP%.*} ]]; then + export PHP=$MIN_PHP echo -e "1\\n0" | xargs -I{} bash -c "tfold src/Symfony/Component/Process.sigchild{} ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/" fi fi } script: - - (run_tests) + - for PHP in $TRAVIS_PHP_VERSION $php_extra; do (run_tests $PHP); done diff --git a/src/Symfony/Component/Debug/Tests/phpt/decorate_exception_hander.phpt b/src/Symfony/Component/Debug/Tests/phpt/decorate_exception_hander.phpt index 74cf2e9171001..7211afbdf85ea 100644 --- a/src/Symfony/Component/Debug/Tests/phpt/decorate_exception_hander.phpt +++ b/src/Symfony/Component/Debug/Tests/phpt/decorate_exception_hander.phpt @@ -38,8 +38,7 @@ Did you forget a "use" statement for another namespace?" ["line":protected]=> int(%d) ["trace":"Exception":private]=> - array(0) { - } + array(%d) {%A} ["previous":"Exception":private]=> NULL ["severity":protected]=> diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index eea9efa93bda8..8afff96727fbd 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -439,9 +439,6 @@ public function testExitCodeCommandFailed() $this->assertGreaterThan(0, $process->getExitCode()); } - /** - * @group tty - */ public function testTTYCommand() { if ('\\' === \DIRECTORY_SEPARATOR) { @@ -457,9 +454,6 @@ public function testTTYCommand() $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus()); } - /** - * @group tty - */ public function testTTYCommandExitCode() { if ('\\' === \DIRECTORY_SEPARATOR) { From 88181244687543259ab946733bf8c44eb871cfb6 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 3 Aug 2018 12:58:53 +0200 Subject: [PATCH 14/47] fix merge --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index cf0006ea295c6..c0c48d933d596 100644 --- a/.travis.yml +++ b/.travis.yml @@ -245,6 +245,7 @@ install: tfold src/Symfony/Component/Console.tty $PHPUNIT src/Symfony/Component/Console --group tty if [[ $PHP = ${MIN_PHP%.*} ]]; then export PHP=$MIN_PHP + echo -e "1\\n0" | xargs -I{} bash -c "tfold src/Symfony/Component/Process.sigchild{} ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/" fi fi } From 0a450d72c55edb659a07d05f0a55fed7a8d9dfd7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 3 Aug 2018 13:10:36 +0200 Subject: [PATCH 15/47] [travis] fix CI for sigchild+Process --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a1eb5e5e59f9f..d51b72b429d09 100644 --- a/.travis.yml +++ b/.travis.yml @@ -224,7 +224,7 @@ install: tfold src/Symfony/Component/Console.tty $PHPUNIT src/Symfony/Component/Console --group tty if [[ $PHP = ${MIN_PHP%.*} ]]; then export PHP=$MIN_PHP - echo -e "1\\n0" | xargs -I{} bash -c "tfold src/Symfony/Component/Process.sigchild{} ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/" + echo -e "1\\n0" | xargs -I{} bash -c "tfold src/Symfony/Component/Process.sigchild{} SYMFONY_DEPRECATIONS_HELPER=weak ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/" fi fi } From f50ee9b3dcdd19000e15c256091c3d7fcf4fb323 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 3 Aug 2018 14:53:54 +0200 Subject: [PATCH 16/47] [travis] fix requiring mongodb/mongodb before composer up --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d51b72b429d09..390621f402566 100644 --- a/.travis.yml +++ b/.travis.yml @@ -208,11 +208,11 @@ install: break fi phpenv global ${PHP/hhvm*/hhvm} - tfold 'composer update' $COMPOSER_UP - tfold 'phpunit install' ./phpunit install if [[ $PHP = 7.* ]]; then ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer require --dev --no-update mongodb/mongodb) fi + tfold 'composer update' $COMPOSER_UP + tfold 'phpunit install' ./phpunit install if [[ $deps = high ]]; then echo "$COMPONENTS" | parallel --gnu -j10% "tfold {} 'cd {} && $COMPOSER_UP && $PHPUNIT_X$LEGACY'" elif [[ $deps = low ]]; then From 6cb792c9d6705be5ae50d58b2e1e64185b2b1085 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 3 Aug 2018 15:09:12 +0200 Subject: [PATCH 17/47] fix ci --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 390621f402566..882701f0dbdf2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -209,7 +209,7 @@ install: fi phpenv global ${PHP/hhvm*/hhvm} if [[ $PHP = 7.* ]]; then - ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer require --dev --no-update mongodb/mongodb) + ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer config platform.ext-mongodb 1.5.0; composer require --dev --no-update mongodb/mongodb) fi tfold 'composer update' $COMPOSER_UP tfold 'phpunit install' ./phpunit install From 7f895abbddff7282c69d022c3641034422ad16fe Mon Sep 17 00:00:00 2001 From: Sebastiaan Stok Date: Sun, 5 Aug 2018 16:25:16 +0200 Subject: [PATCH 18/47] Update validators.nl.xlf --- .../Validator/Resources/translations/validators.nl.xlf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf index 413a97eb17c6f..bee0ab3b55a8f 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf @@ -316,7 +316,7 @@ This is not a valid UUID. - Deze waarde is geen geldige UUID waarde. + Dit is geen geldige UUID. From 67f3e3ff4e123e6b2d9b8ec53683fa302534dbd7 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sun, 5 Aug 2018 19:43:29 +0200 Subject: [PATCH 19/47] [Security] Remove wrong sentence about ACL --- src/Symfony/Component/Security/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/README.md b/src/Symfony/Component/Security/README.md index b8bba12f20362..f55f8a10eab98 100644 --- a/src/Symfony/Component/Security/README.md +++ b/src/Symfony/Component/Security/README.md @@ -6,7 +6,7 @@ application. It ships with facilities for authenticating using HTTP basic or digest authentication, interactive form login or X.509 certificate login, but also allows you to implement your own authentication strategies. Furthermore, the component provides ways to authorize authenticated users based on their -roles, and it contains an advanced ACL system. +roles. Resources --------- From 6089290543bcc8dac5abe6db7e33e08166765020 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 6 Aug 2018 17:30:25 +0200 Subject: [PATCH 20/47] [HttpFoundation] fix false-positive ConflictingHeadersException --- .../Component/HttpFoundation/Request.php | 21 ++++++-- .../HttpFoundation/Tests/RequestTest.php | 51 ++++++++++++++++++- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 9cc6a9773b5ea..6dc9bbe81e75b 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1943,10 +1943,13 @@ private function getTrustedValues($type, $ip = null) if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { $forwardedValues = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]); - $forwardedValues = preg_match_all(sprintf('{(?:%s)=(?:"?\[?)([a-zA-Z0-9\.:_\-/]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : array(); + $forwardedValues = preg_match_all(sprintf('{(?:%s)="?([a-zA-Z0-9\.:_\-/\[\]]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : array(); if (self::HEADER_CLIENT_PORT === $type) { foreach ($forwardedValues as $k => $v) { - $forwardedValues[$k] = substr_replace($v, '0.0.0.0', 0, strrpos($v, ':')); + if (']' === substr($v, -1) || false === $v = strrchr($v, ':')) { + $v = $this->isSecure() ? ':443' : ':80'; + } + $forwardedValues[$k] = '0.0.0.0'.$v; } } } @@ -1981,9 +1984,17 @@ private function normalizeAndFilterClientIps(array $clientIps, $ip) $firstTrustedIp = null; foreach ($clientIps as $key => $clientIp) { - // Remove port (unfortunately, it does happen) - if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { - $clientIps[$key] = $clientIp = $match[1]; + if (strpos($clientIp, '.')) { + // Strip :port from IPv4 addresses. This is allowed in Forwarded + // and may occur in X-Forwarded-For. + $i = strpos($clientIp, ':'); + if ($i) { + $clientIps[$key] = $clientIp = substr($clientIp, 0, $i); + } + } elseif ('[' == $clientIp[0]) { + // Strip brackets and :port from IPv6 addresses. + $i = strpos($clientIp, ']', 1); + $clientIps[$key] = $clientIp = substr($clientIp, 1, $i - 1); } if (!filter_var($clientIp, FILTER_VALIDATE_IP)) { diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index b7eceef9bca7c..0ee92aab1ba19 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -967,7 +967,7 @@ public function testGetClientIpsWithAgreeingHeaders($httpForwarded, $httpXForwar 'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor, ); - Request::setTrustedProxies(array('88.88.88.88')); + Request::setTrustedProxies(array('88.88.88.88'), -1); $request->initialize(array(), array(), array(), array(), array(), $server); @@ -2071,6 +2071,55 @@ public function testNonstandardRequests($requestUri, $queryString, $expectedPath $this->assertEquals($expectedBaseUrl, $request->getBaseUrl()); $this->assertEquals($expectedBasePath, $request->getBasePath()); } + + public function testTrustedHost() + { + Request::setTrustedProxies(array('1.1.1.1'), -1); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('Forwarded', 'host=localhost:8080'); + $request->headers->set('X-Forwarded-Host', 'localhost:8080'); + + $this->assertSame('localhost:8080', $request->getHttpHost()); + $this->assertSame(8080, $request->getPort()); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('Forwarded', 'host="[::1]:443"'); + $request->headers->set('X-Forwarded-Host', '[::1]:443'); + $request->headers->set('X-Forwarded-Port', 443); + + $this->assertSame('[::1]:443', $request->getHttpHost()); + $this->assertSame(443, $request->getPort()); + } + + public function testTrustedPort() + { + Request::setTrustedProxies(array('1.1.1.1'), -1); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('Forwarded', 'host=localhost:8080'); + $request->headers->set('X-Forwarded-Port', 8080); + + $this->assertSame(8080, $request->getPort()); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('Forwarded', 'host=localhost'); + $request->headers->set('X-Forwarded-Port', 80); + + $this->assertSame(80, $request->getPort()); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('Forwarded', 'host="[::1]"'); + $request->headers->set('X-Forwarded-Proto', 'https'); + $request->headers->set('X-Forwarded-Port', 443); + + $this->assertSame(443, $request->getPort()); + } } class RequestContentProxy extends Request From caaa74cd9be80a9c29ebcd98832da6c339278cb5 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 6 Aug 2018 22:01:26 +0200 Subject: [PATCH 21/47] [travis] cache composer.lock files for deps=low --- .github/rm-invalid-lowest-lock-files.php | 80 ++++++++++++++++++++++++ .travis.yml | 6 +- 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 .github/rm-invalid-lowest-lock-files.php diff --git a/.github/rm-invalid-lowest-lock-files.php b/.github/rm-invalid-lowest-lock-files.php new file mode 100644 index 0000000000000..85d582fe621e2 --- /dev/null +++ b/.github/rm-invalid-lowest-lock-files.php @@ -0,0 +1,80 @@ + Date: Fri, 20 Jul 2018 16:08:49 +0200 Subject: [PATCH 22/47] [Filesystem] Add test to prevent regression when using array|resource with dumpFile --- .../Filesystem/Tests/FilesystemTest.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index 35811d3d983a4..6a7fad69ed54e 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -1201,6 +1201,31 @@ public function testDumpFile() $this->assertStringEqualsFile($filename, 'bar'); } + public function testDumpFileWithArray() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt'; + + $this->filesystem->dumpFile($filename, array('bar')); + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'bar'); + } + + public function testDumpFileWithResource() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt'; + + $resource = fopen('php://memory', 'rw'); + fwrite($resource, 'bar'); + fseek($resource, 0); + + $this->filesystem->dumpFile($filename, $resource); + + fclose($resource); + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'bar'); + } + /** * @group legacy */ From a540aee35c8bb45037a6ad333e07b7d82f453d70 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 7 Aug 2018 17:19:50 +0200 Subject: [PATCH 23/47] [travis] ignore ordering when validating composer.lock files for deps=low --- .github/rm-invalid-lowest-lock-files.php | 27 +++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/.github/rm-invalid-lowest-lock-files.php b/.github/rm-invalid-lowest-lock-files.php index 85d582fe621e2..5515238d9ace1 100644 --- a/.github/rm-invalid-lowest-lock-files.php +++ b/.github/rm-invalid-lowest-lock-files.php @@ -3,7 +3,7 @@ array_shift($_SERVER['argv']); $dirs = $_SERVER['argv']; -function getContentHash($composerJson) +function getRelevantContent(array $composerJson) { $relevantKeys = array( 'name', @@ -27,12 +27,18 @@ function getContentHash($composerJson) $relevantContent['config']['platform'] = $composerJson['config']['platform']; } + return $relevantContent; +} + +function getContentHash(array $composerJson) +{ + $relevantContent = getRelevantContent($composerJson); ksort($relevantContent); return md5(json_encode($relevantContent)); } -$composerLocks = array(); +$composerJsons = array(); foreach ($dirs as $dir) { if (!file_exists($dir.'/composer.lock') || !$composerLock = @json_decode(file_get_contents($dir.'/composer.lock'), true)) { @@ -50,28 +56,29 @@ function getContentHash($composerJson) @unlink($dir.'/composer.lock'); continue; } - $composerLocks[$composerJson['name']] = array($dir, $composerLock, $composerJson); + $composerJsons[$composerJson['name']] = array($dir, $composerLock['packages'], getRelevantContent($composerJson)); } -foreach ($composerLocks as list($dir, $composerLock)) { - foreach ($composerLock['packages'] as $composerJson) { - if (0 !== strpos($version = $composerJson['version'], 'dev-') && '-dev' !== substr($version, -4)) { +foreach ($composerJsons as list($dir, $lockedPackages)) { + foreach ($lockedPackages as $lockedJson) { + if (0 !== strpos($version = $lockedJson['version'], 'dev-') && '-dev' !== substr($version, -4)) { continue; } - if (!isset($composerLocks[$name = $composerJson['name']])) { + if (!isset($composerJsons[$name = $lockedJson['name']])) { echo "$dir/composer.lock references missing $name.\n"; @unlink($dir.'/composer.lock'); continue 2; } foreach (array('minimum-stability', 'prefer-stable', 'repositories') as $key) { - if (array_key_exists($key, $composerLocks[$name][2])) { - $composerJson[$key] = $composerLocks[$name][2][$key]; + if (array_key_exists($key, $composerJsons[$name][2])) { + $lockedJson[$key] = $composerJsons[$name][2][$key]; } } - if (getContentHash($composerJson) !== $composerLocks[$name][1]['content-hash']) { + // use weak comparison to ignore ordering + if (getRelevantContent($lockedJson) != $composerJsons[$name][2]) { echo "$dir/composer.lock is not in sync with $name.\n"; @unlink($dir.'/composer.lock'); continue 2; From 4e92d10b40faf71f90e1164c96d2792054fa81da Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 8 Aug 2018 13:42:34 +0200 Subject: [PATCH 24/47] [DI] fix analyzing lazy refs involved in circular loops --- .../Compiler/AnalyzeServiceReferencesPass.php | 6 ++-- .../DependencyInjection/Dumper/PhpDumper.php | 31 +++++++------------ .../Tests/Dumper/PhpDumperTest.php | 2 +- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php index 19f01b8536cb4..aeebbf0bf18c4 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php @@ -34,15 +34,17 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe private $graph; private $currentDefinition; private $onlyConstructorArguments; + private $hasProxyDumper; private $lazy; private $expressionLanguage; /** * @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls */ - public function __construct($onlyConstructorArguments = false) + public function __construct($onlyConstructorArguments = false, $hasProxyDumper = true) { $this->onlyConstructorArguments = (bool) $onlyConstructorArguments; + $this->hasProxyDumper = (bool) $hasProxyDumper; } /** @@ -97,7 +99,7 @@ protected function processValue($value, $isRoot = false) $targetId, $targetDefinition, $value, - $this->lazy || ($targetDefinition && $targetDefinition->isLazy()), + $this->lazy || ($this->hasProxyDumper && $targetDefinition && $targetDefinition->isLazy()), ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior() ); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index cf6495b9c66f3..5530912c90a9e 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -139,29 +140,19 @@ public function dump(array $options = array()) $this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass); if ($this->getProxyDumper() instanceof NullDumper) { - (new AnalyzeServiceReferencesPass(true))->process($this->container); - $this->circularReferences = array(); - $checkedNodes = array(); - foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { - $currentPath = array($id => $id); - $this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath); - } - foreach ($this->circularReferences as $parent => $ids) { - $path = array($parent); - while (!isset($ids[$parent])) { - foreach ($ids as $id) { - $path[] = $id; - $ids = $this->circularReferences[$id]; - break; - } - } - $path[] = $parent.'". Try running "composer require symfony/proxy-manager-bridge'; + (new AnalyzeServiceReferencesPass(true, false))->process($this->container); + try { + (new CheckCircularReferencesPass())->process($this->container); + } catch (ServiceCircularReferenceException $e) { + $path = $e->getPath(); + end($path); + $path[key($path)] .= '". Try running "composer require symfony/proxy-manager-bridge'; - throw new ServiceCircularReferenceException($parent, $path); + throw new ServiceCircularReferenceException($e->getServiceId(), $path); } } - (new AnalyzeServiceReferencesPass())->process($this->container); + (new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container); $this->circularReferences = array(); $checkedNodes = array(); foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { @@ -367,7 +358,7 @@ private function analyzeCircularReferences(array $edges, &$checkedNodes, &$curre $node = $edge->getDestNode(); $id = $node->getId(); - if ($node->getValue() && (($edge->isLazy() && !$this->getProxyDumper() instanceof NullDumper) || $edge->isWeak())) { + if ($node->getValue() && ($edge->isLazy() || $edge->isWeak())) { // no-op } elseif (isset($currentPath[$id])) { foreach (array_reverse($currentPath) as $parentId) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index b876bf48da95d..9c1b80cdedc9b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -538,7 +538,7 @@ public function testCircularReferenceAllowanceForLazyServices() $dumper = new PhpDumper($container); - $message = 'Circular reference detected for service "bar", path: "bar -> foo -> bar". Try running "composer require symfony/proxy-manager-bridge".'; + $message = 'Circular reference detected for service "foo", path: "foo -> bar -> foo". Try running "composer require symfony/proxy-manager-bridge".'; if (method_exists($this, 'expectException')) { $this->expectException(ServiceCircularReferenceException::class); $this->expectExceptionMessage($message); From a7672bd18777cb55a2cff1f14fc1d795a1c16a26 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 10 Aug 2018 09:34:36 +0200 Subject: [PATCH 25/47] clean up unused code --- src/Symfony/Component/Yaml/Parser.php | 36 --------------------------- 1 file changed, 36 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 284af1f9f932b..36c9f1d913702 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -549,11 +549,6 @@ private function getCurrentLineIndentation() private function getNextEmbedBlock($indentation = null, $inSequence = false) { $oldLineIndentation = $this->getCurrentLineIndentation(); - $blockScalarIndentations = array(); - - if ($this->isBlockScalarHeader()) { - $blockScalarIndentations[] = $oldLineIndentation; - } if (!$this->moveToNextLine()) { return; @@ -612,30 +607,9 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false) $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); - if (empty($blockScalarIndentations) && $this->isBlockScalarHeader()) { - $blockScalarIndentations[] = $this->getCurrentLineIndentation(); - } - - $previousLineIndentation = $this->getCurrentLineIndentation(); - while ($this->moveToNextLine()) { $indent = $this->getCurrentLineIndentation(); - // terminate all block scalars that are more indented than the current line - if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && '' !== trim($this->currentLine)) { - foreach ($blockScalarIndentations as $key => $blockScalarIndentation) { - if ($blockScalarIndentation >= $indent) { - unset($blockScalarIndentations[$key]); - } - } - } - - if (empty($blockScalarIndentations) && !$this->isCurrentLineComment() && $this->isBlockScalarHeader()) { - $blockScalarIndentations[] = $indent; - } - - $previousLineIndentation = $indent; - if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { $this->moveToPreviousLine(); break; @@ -1054,16 +1028,6 @@ private function isStringUnIndentedCollectionItem() return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- '); } - /** - * Tests whether or not the current line is the header of a block scalar. - * - * @return bool - */ - private function isBlockScalarHeader() - { - return (bool) self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine); - } - /** * A local wrapper for `preg_match` which will throw a ParseException if there * is an internal error in the PCRE engine. From d67b86b9e76c874f179985d0b654886c4305c60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sat, 11 Aug 2018 21:30:31 +0200 Subject: [PATCH 26/47] Add missing stderr redirection This seems to have been overlooked in 6c0e6af47a5f36b906892537f5b2fbf15dab30b2, and results in the test suite being polluted on machines where phpdbg is not installed. I updated the code to mimic other occurences of exec in this file. --- src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php b/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php index ce8f62d2c53c0..6674feb94a88e 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php @@ -16,7 +16,7 @@ public function test() $this->markTestSkipped('This test cannot be run on HHVM.'); } - exec('type phpdbg', $output, $returnCode); + exec('type phpdbg 2> /dev/null', $output, $returnCode); if (\PHP_VERSION_ID >= 70000 && 0 === $returnCode) { $php = 'phpdbg -qrr'; From 45754515a5d4c177270503bd261d3a0cf314b8bc Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 17 Aug 2018 19:53:24 +0200 Subject: [PATCH 27/47] fix type error handling when writing values --- .../PropertyAccess/PropertyAccessor.php | 7 +++++- .../Tests/Fixtures/ReturnTyped.php | 5 ++++ .../Tests/PropertyAccessorTest.php | 23 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 3d1885a857278..d639506ef3f9f 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -231,7 +231,12 @@ public static function handleError($type, $message, $file, $line, $context) private static function throwInvalidArgumentException($message, $trace, $i) { - if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file'] && isset($trace[$i]['args'][0])) { + // the type mismatch is not caused by invalid arguments (but e.g. by an incompatible return type hint of the writer method) + if (0 !== strpos($message, 'Argument ')) { + return; + } + + if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file'] && array_key_exists(0, $trace[$i]['args'])) { $pos = strpos($message, $delim = 'must be of the type ') ?: (strpos($message, $delim = 'must be an instance of ') ?: strpos($message, $delim = 'must implement interface ')); $pos += \strlen($delim); $type = $trace[$i]['args'][0]; diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/ReturnTyped.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/ReturnTyped.php index b6a9852715d79..71c4a574c0ecf 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/ReturnTyped.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/ReturnTyped.php @@ -28,4 +28,9 @@ public function addFoo(\DateTime $dateTime) public function removeFoo(\DateTime $dateTime) { } + + public function setName($name): self + { + return 'This does not respect the return type on purpose.'; + } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index ec314a6e69bc2..b05672910a0bb 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -538,6 +538,17 @@ public function testThrowTypeError() $this->propertyAccessor->setValue($object, 'date', 'This is a string, \DateTime expected.'); } + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException + * @expectedExceptionMessage Expected argument of type "DateTime", "NULL" given + */ + public function testThrowTypeErrorWithNullArgument() + { + $object = new TypeHinted(); + + $this->propertyAccessor->setValue($object, 'date', null); + } + public function testSetTypeHint() { $date = new \DateTime(); @@ -579,4 +590,16 @@ public function testDoNotDiscardReturnTypeError() $this->propertyAccessor->setValue($object, 'foos', array(new \DateTime())); } + + /** + * @requires PHP 7 + * + * @expectedException \TypeError + */ + public function testDoNotDiscardReturnTypeErrorWhenWriterMethodIsMisconfigured() + { + $object = new ReturnTyped(); + + $this->propertyAccessor->setValue($object, 'name', 'foo'); + } } From 940ec8f2d5c562bc1b2424f67ab0cbd1f3c59e51 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 14 Aug 2018 11:06:16 +0200 Subject: [PATCH 28/47] [travis][appveyor] use symfony/flex to accelerate builds --- .travis.yml | 11 ++++++++++- appveyor.yml | 4 +++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 007101fcc4d9c..173e3792af344 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,7 +52,7 @@ before_install: if [[ $TRAVIS_PHP_VERSION = 5.* || $TRAVIS_PHP_VERSION = hhvm* ]]; then composer () { - $HOME/.phpenv/versions/7.1/bin/composer config platform.php $(echo ' =2.3' + else + export SYMFONY_REQUIRE=">=$SYMFONY_VERSION" + fi + composer global require symfony/flex dev-master + - | # Legacy tests are skipped when deps=high and when the current branch version has not the same major version number than the next one [[ $deps = high && ${SYMFONY_VERSION%.*} != $(git show $(git ls-remote --heads | grep -FA1 /$SYMFONY_VERSION | tail -n 1):composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9]*' | head -n 1) ]] && LEGACY=,legacy diff --git a/appveyor.yml b/appveyor.yml index e23696c240fc2..d5d23ed3ddfc0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,6 +10,7 @@ init: - SET PATH=c:\php;%PATH% - SET COMPOSER_NO_INTERACTION=1 - SET SYMFONY_DEPRECATIONS_HELPER=strict + - SET "SYMFONY_REQUIRE=>=2.8" - SET ANSICON=121x90 (121x90) - SET SYMFONY_PHPUNIT_VERSION=4.8 - REG ADD "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v DelayedExpansion /t REG_DWORD /d 1 /f @@ -51,9 +52,10 @@ install: - copy /Y php.ini-min php.ini - echo extension=php_openssl.dll >> php.ini - cd c:\projects\symfony - - IF NOT EXIST composer.phar (appveyor DownloadFile https://getcomposer.org/download/1.3.0/composer.phar) + - IF NOT EXIST composer.phar (appveyor DownloadFile https://github.com/composer/composer/releases/download/1.7.1/composer.phar) - php composer.phar self-update - copy /Y .composer\* %APPDATA%\Composer\ + - php composer.phar global require --no-progress symfony/flex dev-master - php .github/build-packages.php "HEAD^" src\Symfony\Bridge\PhpUnit - IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev) - php composer.phar config platform.php 5.3.9 From 2ebc75b9a18551b0eb5f2ae5bfd27c8d25094150 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 18 Aug 2018 22:38:48 +0200 Subject: [PATCH 29/47] [Security\Http] Restore laziness of listener iterator --- .../Component/Security/Http/Firewall.php | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/Security/Http/Firewall.php b/src/Symfony/Component/Security/Http/Firewall.php index b9d83b49cdef4..a0a2aa8411ca0 100644 --- a/src/Symfony/Component/Security/Http/Firewall.php +++ b/src/Symfony/Component/Security/Http/Firewall.php @@ -50,31 +50,38 @@ public function onKernelRequest(GetResponseEvent $event) // register listeners for this firewall $listeners = $this->map->getListeners($event->getRequest()); - $accessListener = null; - $authenticationListeners = array(); - - foreach ($listeners[0] as $listener) { - if ($listener instanceof AccessListener) { - $accessListener = $listener; - } else { - $authenticationListeners[] = $listener; - } - } + $authenticationListeners = $listeners[0]; + $exceptionListener = $listeners[1]; + $logoutListener = isset($listeners[2]) ? $listeners[2] : null; - if (null !== $exceptionListener = $listeners[1]) { + if (null !== $exceptionListener) { $this->exceptionListeners[$event->getRequest()] = $exceptionListener; $exceptionListener->register($this->dispatcher); } - if (null !== $logoutListener = isset($listeners[2]) ? $listeners[2] : null) { - $authenticationListeners[] = $logoutListener; - } + $authenticationListeners = function () use ($authenticationListeners, $logoutListener) { + $accessListener = null; - if (null !== $accessListener) { - $authenticationListeners[] = $accessListener; - } + foreach ($authenticationListeners as $listener) { + if ($listener instanceof AccessListener) { + $accessListener = $listener; + + continue; + } + + yield $listener; + } + + if (null !== $logoutListener) { + yield $logoutListener; + } + + if (null !== $accessListener) { + yield $accessListener; + } + }; - $this->handleRequest($event, $authenticationListeners); + $this->handleRequest($event, $authenticationListeners()); } public function onKernelFinishRequest(FinishRequestEvent $event) From 02e3ec0539b2ec5a870eeb428cd5b3d12f8cd8b3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 19 Aug 2018 10:02:31 +0200 Subject: [PATCH 30/47] [travis] fix composer.lock invalidation for deps=low --- .github/rm-invalid-lowest-lock-files.php | 62 ++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/.github/rm-invalid-lowest-lock-files.php b/.github/rm-invalid-lowest-lock-files.php index 5515238d9ace1..81f58ccdd48b0 100644 --- a/.github/rm-invalid-lowest-lock-files.php +++ b/.github/rm-invalid-lowest-lock-files.php @@ -1,5 +1,11 @@ $dirsByCommit) { + $chs[] = $ch = array(curl_init(), fopen($_SERVER['HOME'].'/.cache/composer/repo/https---repo.packagist.org/provider-'.strtr($name, '/', '$').'.json', 'wb')); + curl_setopt($ch[0], CURLOPT_URL, 'https://repo.packagist.org/p/'.$name.'.json'); + curl_setopt($ch[0], CURLOPT_FILE, $ch[1]); + curl_setopt($ch[0], CURLOPT_SHARE, $sh); + curl_multi_add_handle($mh, $ch[0]); +} + +do { + curl_multi_exec($mh, $active); + curl_multi_select($mh); +} while ($active); + +foreach ($chs as list($ch, $fd)) { + curl_multi_remove_handle($mh, $ch); + curl_close($ch); + fclose($fd); +} + +foreach ($referencedCommits as $name => $dirsByCommit) { + $repo = file_get_contents($_SERVER['HOME'].'/.cache/composer/repo/https---repo.packagist.org/provider-'.strtr($name, '/', '$').'.json'); + $repo = json_decode($repo, true); + + foreach ($repo['packages'][$name] as $version) { + unset($referencedCommits[$name][$version['source']['reference']]); + } +} + +foreach ($referencedCommits as $name => $dirsByCommit) { + foreach ($dirsByCommit as $dirs) { + foreach ($dirs as $dir) { + echo "$dir/composer.lock references old commit for $name.\n"; + @unlink($dir.'/composer.lock'); + } } } From 13a5101502573c60dd77d7f7ae0aa32c89f74f93 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 19 Aug 2018 10:40:01 +0200 Subject: [PATCH 31/47] minor fix for travis --- .github/rm-invalid-lowest-lock-files.php | 8 +++++--- .travis.yml | 2 +- appveyor.yml | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/rm-invalid-lowest-lock-files.php b/.github/rm-invalid-lowest-lock-files.php index 81f58ccdd48b0..2b037519cf757 100644 --- a/.github/rm-invalid-lowest-lock-files.php +++ b/.github/rm-invalid-lowest-lock-files.php @@ -96,7 +96,7 @@ function getContentHash(array $composerJson) } } -if (!$referencedCommits || (isset($_SERVER['TRAVIS_PULL_REQUEST']) && 'false' === $_SERVER['TRAVIS_PULL_REQUEST'])) { +if (!$referencedCommits || (isset($_SERVER['TRAVIS_PULL_REQUEST']) && 'false' !== $_SERVER['TRAVIS_PULL_REQUEST'])) { // cached commits cannot be stale for PRs return; } @@ -142,8 +142,10 @@ function getContentHash(array $composerJson) foreach ($referencedCommits as $name => $dirsByCommit) { foreach ($dirsByCommit as $dirs) { foreach ($dirs as $dir) { - echo "$dir/composer.lock references old commit for $name.\n"; - @unlink($dir.'/composer.lock'); + if (file_exists($dir.'/composer.lock')) { + echo "$dir/composer.lock references old commit for $name.\n"; + @unlink($dir.'/composer.lock'); + } } } } diff --git a/.travis.yml b/.travis.yml index 173e3792af344..6896ac7a16e7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -195,7 +195,7 @@ install: else export SYMFONY_REQUIRE=">=$SYMFONY_VERSION" fi - composer global require symfony/flex dev-master + composer global require --no-progress --no-scripts --no-plugins symfony/flex dev-master - | # Legacy tests are skipped when deps=high and when the current branch version has not the same major version number than the next one diff --git a/appveyor.yml b/appveyor.yml index d5d23ed3ddfc0..8d335c600d716 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -55,7 +55,7 @@ install: - IF NOT EXIST composer.phar (appveyor DownloadFile https://github.com/composer/composer/releases/download/1.7.1/composer.phar) - php composer.phar self-update - copy /Y .composer\* %APPDATA%\Composer\ - - php composer.phar global require --no-progress symfony/flex dev-master + - php composer.phar global require --no-progress --no-scripts --no-plugins symfony/flex dev-master - php .github/build-packages.php "HEAD^" src\Symfony\Bridge\PhpUnit - IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev) - php composer.phar config platform.php 5.3.9 From 48c531c09a6ef8b05b0ca5822e707f9a4d15320e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 19 Aug 2018 14:57:42 +0200 Subject: [PATCH 32/47] [travis] fix composer.lock invalidation for PRs patching several components --- .github/rm-invalid-lowest-lock-files.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/rm-invalid-lowest-lock-files.php b/.github/rm-invalid-lowest-lock-files.php index 2b037519cf757..aca35ca64db33 100644 --- a/.github/rm-invalid-lowest-lock-files.php +++ b/.github/rm-invalid-lowest-lock-files.php @@ -79,7 +79,13 @@ function getContentHash(array $composerJson) continue 2; } - foreach (array('minimum-stability', 'prefer-stable', 'repositories') as $key) { + if (isset($composerJsons[$name][2]['repositories']) && !isset($lockedJson[$key]['repositories'])) { + // the locked package has been patched locally but the lock references a commit, + // which means the referencing package itself is not modified + continue; + } + + foreach (array('minimum-stability', 'prefer-stable') as $key) { if (array_key_exists($key, $composerJsons[$name][2])) { $lockedJson[$key] = $composerJsons[$name][2][$key]; } @@ -92,7 +98,9 @@ function getContentHash(array $composerJson) continue 2; } - $referencedCommits[$name][$lockedJson['source']['reference']][] = $dir; + if ($lockedJson['dist']['reference']) { + $referencedCommits[$name][$lockedJson['dist']['reference']][] = $dir; + } } } From 5bdc755d73a7d6a9304afac35f0576a10014209f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 19 Aug 2018 18:51:04 +0200 Subject: [PATCH 33/47] fix data mapper return type in docblock --- src/Symfony/Component/Form/FormConfigInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/FormConfigInterface.php b/src/Symfony/Component/Form/FormConfigInterface.php index e66570cf5f1e6..b7083544a436b 100644 --- a/src/Symfony/Component/Form/FormConfigInterface.php +++ b/src/Symfony/Component/Form/FormConfigInterface.php @@ -99,7 +99,7 @@ public function getModelTransformers(); /** * Returns the data mapper of the form. * - * @return DataMapperInterface The data mapper + * @return DataMapperInterface|null The data mapper */ public function getDataMapper(); From 92953485a50d8cf99690499f8b353b57d8818b94 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 22 Aug 2018 17:17:35 +0200 Subject: [PATCH 34/47] [HttpKernel] fix forwarding trusted headers as server parameters --- src/Symfony/Component/HttpFoundation/Request.php | 2 +- .../Component/HttpFoundation/Tests/RequestTest.php | 6 +++--- .../HttpKernel/HttpCache/SubRequestHandler.php | 10 +++++++--- .../Tests/Fragment/InlineFragmentRendererTest.php | 11 +++++++++++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 6dc9bbe81e75b..a29037279a698 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1991,7 +1991,7 @@ private function normalizeAndFilterClientIps(array $clientIps, $ip) if ($i) { $clientIps[$key] = $clientIp = substr($clientIp, 0, $i); } - } elseif ('[' == $clientIp[0]) { + } elseif (0 === strpos($clientIp, '[')) { // Strip brackets and :port from IPv6 addresses. $i = strpos($clientIp, ']', 1); $clientIps[$key] = $clientIp = substr($clientIp, 1, $i - 1); diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 0ee92aab1ba19..26335db903dd0 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -868,7 +868,7 @@ public function getClientIpsForwardedProvider() public function getClientIpsProvider() { - // $expected $remoteAddr $httpForwardedFor $trustedProxies + // $expected $remoteAddr $httpForwardedFor $trustedProxies return array( // simple IPv4 array(array('88.88.88.88'), '88.88.88.88', null, null), @@ -882,8 +882,8 @@ public function getClientIpsProvider() // forwarded for with remote IPv4 addr not trusted array(array('127.0.0.1'), '127.0.0.1', '88.88.88.88', null), - // forwarded for with remote IPv4 addr trusted - array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1')), + // forwarded for with remote IPv4 addr trusted + comma + array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88,', array('127.0.0.1')), // forwarded for with remote IPv4 and all FF addrs trusted array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1', '88.88.88.88')), // forwarded for with remote IPv4 range trusted diff --git a/src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php b/src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php index c050256025ce7..21928c1a58a3e 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php @@ -43,6 +43,7 @@ public static function handle(HttpKernelInterface $kernel, Request $request, $ty if (!IpUtils::checkIp($remoteAddr, $trustedProxies)) { foreach (array_filter($trustedHeaders) as $name) { $request->headers->remove($name); + $request->server->remove('HTTP_'.strtoupper(str_replace('-', '_', $name))); } } @@ -61,13 +62,16 @@ public static function handle(HttpKernelInterface $kernel, Request $request, $ty // set trusted values, reusing as much as possible the global trusted settings if ($name = $trustedHeaders[Request::HEADER_FORWARDED]) { $trustedValues[0] .= sprintf(';host="%s";proto=%s', $request->getHttpHost(), $request->getScheme()); - $request->headers->set($name, implode(', ', $trustedValues)); + $request->headers->set($name, $v = implode(', ', $trustedValues)); + $request->server->set('HTTP_'.strtoupper(str_replace('-', '_', $name)), $v); } if ($name = $trustedHeaders[Request::HEADER_CLIENT_IP]) { - $request->headers->set($name, implode(', ', $trustedIps)); + $request->headers->set($name, $v = implode(', ', $trustedIps)); + $request->server->set('HTTP_'.strtoupper(str_replace('-', '_', $name)), $v); } if (!$name && !$trustedHeaders[Request::HEADER_FORWARDED]) { - $request->headers->set('X-Forwarded-For', implode(', ', $trustedIps)); + $request->headers->set('X-Forwarded-For', $v = implode(', ', $trustedIps)); + $request->server->set('HTTP_X_FORWARDED_FOR', $v); Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR'); } diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php index a955da20aa83c..197a720439253 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php @@ -60,6 +60,8 @@ public function testRenderWithObjectsAsAttributes() $subRequest->attributes->replace(array('object' => $object, '_format' => 'html', '_controller' => 'main_controller', '_locale' => 'en')); $subRequest->headers->set('x-forwarded-for', array('127.0.0.1')); $subRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); + $subRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + $subRequest->server->set('HTTP_FORWARDED', 'for="127.0.0.1";host="localhost";proto=http'); $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($subRequest)); @@ -91,6 +93,7 @@ public function testRenderWithTrustedHeaderDisabled() $expectedSubRequest = Request::create('/'); $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); $this->assertSame('foo', $strategy->render('/', Request::create('/'))->getContent()); @@ -178,8 +181,10 @@ public function testESIHeaderIsKeptInSubrequest() $expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); if (Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)) { $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); } $expectedSubRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); + $expectedSubRequest->server->set('HTTP_FORWARDED', 'for="127.0.0.1";host="localhost";proto=http'); $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); @@ -203,6 +208,8 @@ public function testHeadersPossiblyResultingIn304AreNotAssignedToSubrequest() $expectedSubRequest = Request::create('/'); $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); $expectedSubRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); + $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + $expectedSubRequest->server->set('HTTP_FORWARDED', 'for="127.0.0.1";host="localhost";proto=http'); $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); $request = Request::create('/', 'GET', array(), array(), array(), array('HTTP_IF_MODIFIED_SINCE' => 'Fri, 01 Jan 2016 00:00:00 GMT', 'HTTP_IF_NONE_MATCH' => '*')); @@ -216,6 +223,8 @@ public function testFirstTrustedProxyIsSetAsRemote() $expectedSubRequest->server->set('REMOTE_ADDR', '127.0.0.1'); $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); $expectedSubRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); + $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + $expectedSubRequest->server->set('HTTP_FORWARDED', 'for="127.0.0.1";host="localhost";proto=http'); Request::setTrustedProxies(array('1.1.1.1')); @@ -235,6 +244,8 @@ public function testIpAddressOfRangedTrustedProxyIsSetAsRemote() $expectedSubRequest->server->set('REMOTE_ADDR', '127.0.0.1'); $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); $expectedSubRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); + $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + $expectedSubRequest->server->set('HTTP_FORWARDED', 'for="127.0.0.1";host="localhost";proto=http'); Request::setTrustedProxies(array('1.1.1.1/24')); From 8b59d177db2373e28f5c73de38da168a5cc2fc11 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 23 Aug 2018 17:01:50 +0200 Subject: [PATCH 35/47] [Cache] enable Memcached::OPT_TCP_NODELAY to fix perf of misses --- .../Component/Cache/Tests/Adapter/MemcachedAdapterTest.php | 1 + src/Symfony/Component/Cache/Traits/MemcachedTrait.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php index 76e0608006173..d1f87903406fe 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php @@ -89,6 +89,7 @@ public function testDefaultOptions() $this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION)); $this->assertSame(1, $client->getOption(\Memcached::OPT_BINARY_PROTOCOL)); + $this->assertSame(1, $client->getOption(\Memcached::OPT_TCP_NODELAY)); $this->assertSame(1, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE)); } diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php index 7e36164293842..5983d9ebd1da5 100644 --- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php +++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php @@ -134,6 +134,7 @@ public static function createConnection($servers, array $options = array()) $options = array_change_key_case($options, CASE_UPPER); $client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); $client->setOption(\Memcached::OPT_NO_BLOCK, true); + $client->setOption(\Memcached::OPT_TCP_NODELAY, true); if (!array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) { $client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true); } From f245df404f9ffbc3f2a647a660eeee6dc267b050 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 23 Aug 2018 23:19:02 +0200 Subject: [PATCH 36/47] [travis] enable Redis cluster --- .travis.yml | 7 ++++ .../Tests/Adapter/RedisClusterAdapterTest.php | 27 ++++++++++++++ .../Tests/Simple/RedisClusterCacheTest.php | 27 ++++++++++++++ .../Component/Lock/Store/RedisStore.php | 6 ++-- .../Tests/Store/RedisClusterStoreTest.php | 35 +++++++++++++++++++ 5 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/Cache/Tests/Adapter/RedisClusterAdapterTest.php create mode 100644 src/Symfony/Component/Cache/Tests/Simple/RedisClusterCacheTest.php create mode 100644 src/Symfony/Component/Lock/Tests/Store/RedisClusterStoreTest.php diff --git a/.travis.yml b/.travis.yml index 68b36579f4473..9690c4a934b34 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,8 +41,15 @@ services: - memcached - mongodb - redis-server + - docker before_install: + - | + # Start Redis cluster + docker pull grokzen/redis-cluster:4.0.8 + docker run -d -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 --name redis-cluster grokzen/redis-cluster:4.0.8 + export REDIS_CLUSTER_HOSTS='localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005' + - | # General configuration set -e diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisClusterAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisClusterAdapterTest.php new file mode 100644 index 0000000000000..852079c00ce79 --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisClusterAdapterTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +class RedisClusterAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + if (!class_exists('RedisCluster')) { + self::markTestSkipped('The RedisCluster class is required.'); + } + if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) { + self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.'); + } + + self::$redis = new \RedisCluster(null, explode(' ', $hosts)); + } +} diff --git a/src/Symfony/Component/Cache/Tests/Simple/RedisClusterCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/RedisClusterCacheTest.php new file mode 100644 index 0000000000000..99d4e518fb409 --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Simple/RedisClusterCacheTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +class RedisClusterCacheTest extends AbstractRedisCacheTest +{ + public static function setupBeforeClass() + { + if (!class_exists('RedisCluster')) { + self::markTestSkipped('The RedisCluster class is required.'); + } + if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) { + self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.'); + } + + self::$redis = new \RedisCluster(null, explode(' ', $hosts)); + } +} diff --git a/src/Symfony/Component/Lock/Store/RedisStore.php b/src/Symfony/Component/Lock/Store/RedisStore.php index a65e8cb8f5000..e3b1500532376 100644 --- a/src/Symfony/Component/Lock/Store/RedisStore.php +++ b/src/Symfony/Component/Lock/Store/RedisStore.php @@ -54,8 +54,10 @@ public function save(Key $key) $script = ' if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("PEXPIRE", KEYS[1], ARGV[2]) + elseif redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2]) then + return 1 else - return redis.call("set", KEYS[1], ARGV[1], "NX", "PX", ARGV[2]) + return 0 end '; @@ -144,7 +146,7 @@ private function evaluate($script, $resource, array $args) return \call_user_func_array(array($this->redis, 'eval'), array_merge(array($script, 1, $resource), $args)); } - throw new InvalidArgumentException(sprintf('%s() expects been initialized with a Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, \is_object($this->redis) ? \get_class($this->redis) : \gettype($this->redis))); + throw new InvalidArgumentException(sprintf('%s() expects being initialized with a Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, \is_object($this->redis) ? \get_class($this->redis) : \gettype($this->redis))); } /** diff --git a/src/Symfony/Component/Lock/Tests/Store/RedisClusterStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/RedisClusterStoreTest.php new file mode 100644 index 0000000000000..2ee17888eb6d3 --- /dev/null +++ b/src/Symfony/Component/Lock/Tests/Store/RedisClusterStoreTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +/** + * @author Jérémy Derussé + * + * @requires extension redis + */ +class RedisClusterStoreTest extends AbstractRedisStoreTest +{ + public static function setupBeforeClass() + { + if (!class_exists('RedisCluster')) { + self::markTestSkipped('The RedisCluster class is required.'); + } + if (!getenv('REDIS_CLUSTER_HOSTS')) { + self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.'); + } + } + + protected function getRedisConnection() + { + return new \RedisCluster(null, explode(' ', getenv('REDIS_CLUSTER_HOSTS'))); + } +} From 2ac883a99bb543f776ce3dfc49d002fa853ab99b Mon Sep 17 00:00:00 2001 From: David Maicher Date: Thu, 23 Aug 2018 21:04:06 +0200 Subject: [PATCH 37/47] [DoctrineBridge] support __toString as documented for UniqueEntityValidator --- .../Tests/Validator/Constraints/UniqueEntityValidatorTest.php | 2 +- .../Doctrine/Validator/Constraints/UniqueEntityValidator.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index a26ce98107f60..d8b55eb808fc2 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -482,7 +482,7 @@ public function testAssociatedEntity() $this->buildViolation('myMessage') ->atPath('property.path.single') - ->setParameter('{{ value }}', 'object("Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity") identified by (id => 1)') + ->setParameter('{{ value }}', 'foo') ->setInvalidValue($entity1) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->setCause(array($associated, $associated2)) diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index 2015e7fb3acc6..161a187ff98ab 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -186,6 +186,10 @@ private function formatWithIdentifiers(ObjectManager $em, ClassMetadata $class, return $this->formatValue($value, self::PRETTY_DATE); } + if (\method_exists($value, '__toString')) { + return (string) $value; + } + if ($class->getName() !== $idClass = \get_class($value)) { // non unique value might be a composite PK that consists of other entity objects if ($em->getMetadataFactory()->hasMetadataFor($idClass)) { From 41ffba19160f803e6b75e5d8ad4ec5dad008e475 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 24 Aug 2018 14:02:08 +0200 Subject: [PATCH 38/47] [travis] fix composer.lock invalidation for deps=low --- .github/rm-invalid-lowest-lock-files.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/rm-invalid-lowest-lock-files.php b/.github/rm-invalid-lowest-lock-files.php index aca35ca64db33..c71463262171a 100644 --- a/.github/rm-invalid-lowest-lock-files.php +++ b/.github/rm-invalid-lowest-lock-files.php @@ -48,7 +48,6 @@ function getContentHash(array $composerJson) foreach ($dirs as $dir) { if (!file_exists($dir.'/composer.lock') || !$composerLock = @json_decode(file_get_contents($dir.'/composer.lock'), true)) { - echo "$dir/composer.lock not found or invalid.\n"; @unlink($dir.'/composer.lock'); continue; } @@ -62,7 +61,8 @@ function getContentHash(array $composerJson) @unlink($dir.'/composer.lock'); continue; } - $composerJsons[$composerJson['name']] = array($dir, $composerLock['packages'], getRelevantContent($composerJson)); + $composerLock += array('packages' => array(), 'packages-dev' => array()); + $composerJsons[$composerJson['name']] = array($dir, $composerLock['packages'] + $composerLock['packages-dev'], getRelevantContent($composerJson)); } $referencedCommits = array(); From dba8687a5dda71a14676d376f790ba6ba92ab950 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 26 Aug 2018 12:16:17 -0400 Subject: [PATCH 39/47] Instantiate $offset and $maxlen at definition --- .../Component/HttpFoundation/BinaryFileResponse.php | 4 ++-- .../HttpFoundation/Tests/BinaryFileResponseTest.php | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php index b18a3ecbdc629..c0fd95ea6b661 100644 --- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php +++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php @@ -31,8 +31,8 @@ class BinaryFileResponse extends Response * @var File */ protected $file; - protected $offset; - protected $maxlen; + protected $offset = 0; + protected $maxlen = -1; protected $deleteFileAfterSend = false; /** diff --git a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php index e41a2372b9df8..031c9bbca3f22 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php @@ -206,6 +206,19 @@ public function provideFullFileRanges() ); } + public function testUnpreparedResponseSendsFullFile() + { + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200); + + $data = file_get_contents(__DIR__.'/File/Fixtures/test.gif'); + + $this->expectOutputString($data); + $response = clone $response; + $response->sendContent(); + + $this->assertEquals(200, $response->getStatusCode()); + } + /** * @dataProvider provideInvalidRanges */ From 28492a7bb1f37b9f3070b73be1af4280106e369f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 27 Aug 2018 17:17:06 +0200 Subject: [PATCH 40/47] [PhpUnitBridge] keep compat with composer 1.0 --- src/Symfony/Bridge/PhpUnit/bin/simple-phpunit | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit index 197650915fe91..bcfc432f8a139 100755 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit @@ -101,7 +101,8 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__ } $prevRoot = getenv('COMPOSER_ROOT_VERSION'); putenv("COMPOSER_ROOT_VERSION=$PHPUNIT_VERSION.99"); - $exit = proc_close(proc_open("$COMPOSER install --no-dev --prefer-dist --no-suggest --no-progress --ansi", array(), $p, getcwd(), null, array('bypass_shell' => true))); + // --no-suggest is not in the list to keep compat with composer 1.0, which is shipped with Ubuntu 16.04LTS + $exit = proc_close(proc_open("$COMPOSER install --no-dev --prefer-dist --no-progress --ansi", array(), $p, getcwd(), null, array('bypass_shell' => true))); putenv('COMPOSER_ROOT_VERSION'.(false !== $prevRoot ? '='.$prevRoot : '')); if ($exit) { exit($exit); From 0aee31fcd80ec0e42b3cd383a7868acd2178f56d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 27 Aug 2018 19:33:04 +0200 Subject: [PATCH 41/47] updated CHANGELOG for 2.8.45 --- CHANGELOG-2.8.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG-2.8.md b/CHANGELOG-2.8.md index 4760af369f84f..7921dc3bcfa9b 100644 --- a/CHANGELOG-2.8.md +++ b/CHANGELOG-2.8.md @@ -7,6 +7,16 @@ in 2.8 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.8.0...v2.8.1 +* 2.8.45 (2018-08-27) + + * bug #28278 [HttpFoundation] Fix unprepared BinaryFileResponse sends empty file (wackymole) + * bug #28241 [HttpKernel] fix forwarding trusted headers as server parameters (nicolas-grekas) + * bug #28220 [PropertyAccess] fix type error handling when writing values (xabbuh) + * bug #28100 [Security] Call AccessListener after LogoutListener (chalasr) + * bug #28144 [HttpFoundation] fix false-positive ConflictingHeadersException (nicolas-grekas) + * bug #28055 [PropertyInfo] Allow nested collections (jderusse) + * bug #28083 Remove the Expires header when calling Response::expire() (javiereguiluz) + * 2.8.44 (2018-08-01) * security #cve-2018-14774 [HttpKernel] fix trusted headers management in HttpCache and InlineFragmentRenderer (nicolas-grekas) From 97e425cdcbbb502da98f2ada7775cd2fc720dcb2 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 27 Aug 2018 19:33:31 +0200 Subject: [PATCH 42/47] update CONTRIBUTORS for 2.8.45 --- CONTRIBUTORS.md | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index bf391b7ee79ce..629d8b80c98a3 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -24,8 +24,8 @@ Symfony is the result of the work of many people who made the code better - Hugo Hamon (hhamon) - Abdellatif Ait boudad (aitboudad) - Romain Neutron (romain) - - Pascal Borreli (pborreli) - Roland Franssen (ro0) + - Pascal Borreli (pborreli) - Wouter De Jong (wouterj) - Joseph Bielawski (stloyd) - Karma Dordrak (drak) @@ -52,9 +52,9 @@ Symfony is the result of the work of many people who made the code better - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - Bulat Shakirzyanov (avalanche123) + - Dany Maillard (maidmaid) - Peter Rehm (rpet) - Matthias Pigulla (mpdude) - - Dany Maillard (maidmaid) - Saša Stamenković (umpirsky) - Kevin Bond (kbond) - Tobias Nyholm (tobias) @@ -63,9 +63,9 @@ Symfony is the result of the work of many people who made the code better - Miha Vrhovnik - Diego Saint Esteben (dii3g0) - Alexander M. Turek (derrabus) + - Jérémy DERUSSÉ (jderusse) - Konstantin Kudryashov (everzet) - Bilal Amarni (bamarni) - - Jérémy DERUSSÉ (jderusse) - Florin Patan (florinpatan) - Gábor Egyed (1ed) - Mathieu Piot (mpiot) @@ -75,9 +75,9 @@ Symfony is the result of the work of many people who made the code better - Eric Clemmons (ericclemmons) - Jáchym Toušek (enumag) - Charles Sarrazin (csarrazi) + - David Maicher (dmaicher) - Konstantin Myakshin (koc) - Christian Raue - - David Maicher (dmaicher) - Arnout Boks (aboks) - Deni - Henrik Westphal (snc) @@ -99,9 +99,9 @@ Symfony is the result of the work of many people who made the code better - Fran Moreno (franmomu) - Antoine Hérault (herzult) - Paráda József (paradajozsef) + - Grégoire Paris (greg0ire) - Arnaud Le Blanc (arnaud-lb) - Maxime STEINHAUSSER - - Grégoire Paris (greg0ire) - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) - Brice BERNARD (brikou) @@ -124,6 +124,7 @@ Symfony is the result of the work of many people who made the code better - Fabien Pennequin (fabienpennequin) - Gordon Franke (gimler) - Eric GELOEN (gelo) + - Sebastiaan Stok (sstok) - Lars Strojny (lstrojny) - Daniel Wehner (dawehner) - Tugdual Saunier (tucksaun) @@ -131,7 +132,6 @@ Symfony is the result of the work of many people who made the code better - Théo FIDRY (theofidry) - Robert Schönthal (digitalkaoz) - Florian Lonqueu-Brochard (florianlb) - - Sebastiaan Stok (sstok) - Stefano Sala (stefano.sala) - Jérôme Vasseur (jvasseur) - Evgeniy (ewgraf) @@ -144,6 +144,7 @@ Symfony is the result of the work of many people who made the code better - Hidenori Goto (hidenorigoto) - Chris Wilkinson (thewilkybarkid) - Arnaud Kleinpeter (nanocom) + - Jannik Zschiesche (apfelbox) - Guilherme Blanco (guilhermeblanco) - Pablo Godel (pgodel) - Jérémie Augustin (jaugustin) @@ -159,7 +160,6 @@ Symfony is the result of the work of many people who made the code better - Vyacheslav Pavlov - Richard van Laak (rvanlaak) - Richard Shank (iampersistent) - - Jannik Zschiesche (apfelbox) - Thomas Rabaix (rande) - Rouven Weßling (realityking) - Clemens Tolboom @@ -192,6 +192,7 @@ Symfony is the result of the work of many people who made the code better - SpacePossum - Benjamin Dulau (dbenjamin) - Mathieu Lemoine (lemoinem) + - Thomas Calvet (fancyweb) - Christian Schmidt - Andreas Hucks (meandmymonkey) - Noel Guilbert (noel) @@ -200,6 +201,7 @@ Symfony is the result of the work of many people who made the code better - bronze1man - sun (sun) - Larry Garfield (crell) + - Michaël Perrin (michael.perrin) - Martin Schuhfuß (usefulthink) - apetitpa - Matthieu Bontemps (mbontemps) @@ -221,7 +223,6 @@ Symfony is the result of the work of many people who made the code better - Tom Van Looy (tvlooy) - Sven Paulus (subsven) - Yanick Witschi (toflar) - - Thomas Calvet (fancyweb) - Rui Marinho (ruimarinho) - Alessandro Chitolina - Eugene Wissner @@ -230,7 +231,6 @@ Symfony is the result of the work of many people who made the code better - Leo Feyer - Tristan Darricau (nicofuma) - Nikolay Labinskiy (e-moe) - - Michaël Perrin (michael.perrin) - Marcel Beerta (mazen) - Albert Casademont (acasademont) - Loïc Faugeron @@ -253,6 +253,7 @@ Symfony is the result of the work of many people who made the code better - Ruben Gonzalez (rubenrua) - Adam Prager (padam87) - Benoît Burnichon (bburnichon) + - Florent Mata (fmata) - Roman Marintšenko (inori) - Xavier Montaña Carreras (xmontana) - Mickaël Andrieu (mickaelandrieu) @@ -297,7 +298,6 @@ Symfony is the result of the work of many people who made the code better - Roumen Damianoff (roumen) - Antonio J. García Lagar (ajgarlag) - Kim Hemsø Rasmussen (kimhemsoe) - - Florent Mata (fmata) - Wouter Van Hecke - Jérôme Parmentier (lctrs) - Michael Babker (mbabker) @@ -310,6 +310,7 @@ Symfony is the result of the work of many people who made the code better - Chad Sikorra (chadsikorra) - Chris Smith (cs278) - Florian Klein (docteurklein) + - Gary PEGEOT (gary-p) - Manuel Kiessling (manuelkiessling) - Atsuhiro KUBO (iteman) - Andrew Moore (finewolf) @@ -343,6 +344,7 @@ Symfony is the result of the work of many people who made the code better - Thierry Thuon (lepiaf) - Ricard Clau (ricardclau) - Mark Challoner (markchalloner) + - Colin O'Dell (colinodell) - Gennady Telegin (gtelegin) - Ben Davies (bendavies) - Erin Millard @@ -380,7 +382,7 @@ Symfony is the result of the work of many people who made the code better - Grzegorz (Greg) Zdanowski (kiler129) - Kirill chEbba Chebunin (chebba) - Greg Thornton (xdissent) - - Gary PEGEOT (gary-p) + - Sullivan SENECHAL (soullivaneuh) - Costin Bereveanu (schniper) - Loïc Chardonnet (gnusat) - Marek Kalnik (marekkalnik) @@ -432,7 +434,6 @@ Symfony is the result of the work of many people who made the code better - Christopher Davis (chrisguitarguy) - Jan Schumann - Niklas Fiekas - - Colin O'Dell (colinodell) - Markus Bachmann (baachi) - lancergr - Zan Baldwin @@ -486,7 +487,6 @@ Symfony is the result of the work of many people who made the code better - Haralan Dobrev (hkdobrev) - Sebastian Bergmann - Miroslav Sustek - - Sullivan SENECHAL (soullivaneuh) - Pablo Díez (pablodip) - Martin Hujer (martinhujer) - Kevin McBride @@ -510,6 +510,7 @@ Symfony is the result of the work of many people who made the code better - Markus Lanthaler (lanthaler) - Remi Collet - Vicent Soria Durá (vicentgodella) + - Michael Moravec - Anthony Ferrara - Ioan Negulescu - Jakub Škvára (jskvara) @@ -652,6 +653,7 @@ Symfony is the result of the work of many people who made the code better - Szijarto Tamas - Robin Lehrmann (robinlehrmann) - Catalin Dan + - Jaroslav Kuba - Stephan Vock - Benjamin Zikarsky (bzikarsky) - Simon Schick (simonsimcity) @@ -738,7 +740,6 @@ Symfony is the result of the work of many people who made the code better - Marc Morera (mmoreram) - Saif Eddin Gmati (azjezz) - Smaine Milianni (ismail1432) - - Michael Moravec - Andrew Hilobok (hilobok) - Noah Heck (myesain) - Christian Soronellas (theunic) @@ -788,6 +789,7 @@ Symfony is the result of the work of many people who made the code better - Sofiane HADDAG (sofhad) - frost-nzcr4 - Bozhidar Hristov + - Ivan Nikolaev (destillat) - andrey1s - Abhoryo - Fabian Vogler (fabian) @@ -913,6 +915,7 @@ Symfony is the result of the work of many people who made the code better - Michael Tibben - Billie Thompson - Sander Marechal + - ProgMiner - Oleg Golovakhin (doc_tr) - Icode4Food (icode4food) - Radosław Benkel @@ -1001,6 +1004,7 @@ Symfony is the result of the work of many people who made the code better - Denis Kop - Jean-Guilhem Rouel (jean-gui) - jfcixmedia + - Dominic Tubach - Nikita Konstantinov - Martijn Evers - Benjamin Paap (benjaminpaap) @@ -1043,6 +1047,7 @@ Symfony is the result of the work of many people who made the code better - Alexander Cheprasov - Rodrigo Díez Villamuera (rodrigodiez) - e-ivanov + - Einenlum - Jochen Bayer (jocl) - Alex Bowers - Jeremy Bush @@ -1062,7 +1067,6 @@ Symfony is the result of the work of many people who made the code better - David Otton - Will Donohoe - peter - - Jaroslav Kuba - Jérémy Jourdin (jjk801) - BRAMILLE Sébastien (oktapodia) - Artem Kolesnikov (tyomo4ka) @@ -1168,6 +1172,7 @@ Symfony is the result of the work of many people who made the code better - Saem Ghani - Clément LEFEBVRE - Conrad Kleinespel + - Zacharias Luiten - Sebastian Utz - Adrien Gallou (agallou) - Maks Rafalko (bornfree) @@ -1233,6 +1238,7 @@ Symfony is the result of the work of many people who made the code better - WedgeSama - Felds Liscia - Chihiro Adachi (chihiro-adachi) + - Emanuele Panzeri (thepanz) - Tadcka - Beth Binkovitz - Gonzalo Míguez @@ -1305,6 +1311,7 @@ Symfony is the result of the work of many people who made the code better - Benjamin Bender - Jared Farrish - karl.rixon + - raplider - Konrad Mohrfeldt - Lance Chen - Ciaran McNulty (ciaranmcnulty) @@ -1373,12 +1380,12 @@ Symfony is the result of the work of many people who made the code better - Harold Iedema - Arnau González (arnaugm) - Simon Bouland (bouland) - - Ivan Nikolaev (destillat) - Matthew Foster (mfoster) - Paul Seiffert (seiffert) - Vasily Khayrulin (sirian) - Stefan Koopmanschap (skoop) - Stefan Hüsges (tronsha) + - Vlad Gregurco (vgregurco) - Jake Bishop (yakobeyak) - Dan Blows - Matt Wells @@ -1386,6 +1393,7 @@ Symfony is the result of the work of many people who made the code better - stloyd - Andreas - Chris Tickner + - BoShurik - Andrew Coulton - Jeremy Benoist - Michal Gebauer @@ -1399,7 +1407,9 @@ Symfony is the result of the work of many people who made the code better - Luis Muñoz - Matthew Donadio - Houziaux mike + - Phobetor - Andreas + - Markus - Thomas Chmielowiec - shdev - Andrey Ryaguzov @@ -1407,6 +1417,7 @@ Symfony is the result of the work of many people who made the code better - Peter Bex - Manatsawin Hanmongkolchai - Gunther Konig + - Mickael GOETZ - Maciej Schmidt - Greg ORIOL - Dennis Væversted @@ -1418,6 +1429,7 @@ Symfony is the result of the work of many people who made the code better - Mike Francis - Christoph Nissle (derstoffel) - Ionel Scutelnicu (ionelscutelnicu) + - Grenier Kévin (mcsky_biig) - Nicolas Tallefourtané (nicolab) - Botond Dani (picur) - Thierry Marianne (thierrymarianne) @@ -1815,6 +1827,7 @@ Symfony is the result of the work of many people who made the code better - jspee - David Soria Parra - Sergiy Sokolenko + - Ahmed Abdulrahman - dinitrol - Penny Leach - Yurii K From 7e0a16cf2f0b822343b9ddf9edbc0206f0fe3c68 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 27 Aug 2018 19:33:38 +0200 Subject: [PATCH 43/47] updated VERSION for 2.8.45 --- 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 32a90578342e7..276b2a1516b57 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.8.45-DEV'; + const VERSION = '2.8.45'; const VERSION_ID = 20845; const MAJOR_VERSION = 2; const MINOR_VERSION = 8; const RELEASE_VERSION = 45; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '11/2018'; const END_OF_LIFE = '11/2019'; From a8b16054e7de14cf92a933ea5e014895a8720ca2 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 27 Aug 2018 19:44:28 +0200 Subject: [PATCH 44/47] bumped Symfony version to 2.8.46 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 276b2a1516b57..15425cdf92726 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.8.45'; - const VERSION_ID = 20845; + const VERSION = '2.8.46-DEV'; + const VERSION_ID = 20846; const MAJOR_VERSION = 2; const MINOR_VERSION = 8; - const RELEASE_VERSION = 45; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 46; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '11/2018'; const END_OF_LIFE = '11/2019'; From 4623fe74ca2bd58605f426d74ebb82f238e5e318 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 27 Aug 2018 20:13:17 +0200 Subject: [PATCH 45/47] [travis] disable symfony/flex during phpunit install --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6896ac7a16e7f..f646b2997118c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -221,7 +221,11 @@ install: ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer config platform.ext-mongodb 1.5.0; composer require --dev --no-update mongodb/mongodb) fi tfold 'composer update' $COMPOSER_UP - tfold 'phpunit install' ./phpunit install + if [[ $TRAVIS_PHP_VERSION = 5.* || $TRAVIS_PHP_VERSION = hhvm* ]]; then + tfold 'phpunit install' 'composer global remove symfony/flex && ./phpunit install && composer global require --no-progress --no-scripts --no-plugins symfony/flex dev-master' + else + tfold 'phpunit install' ./phpunit install + fi if [[ $deps = high ]]; then echo "$COMPONENTS" | parallel --gnu -j10% "tfold {} 'cd {} && $COMPOSER_UP && $PHPUNIT_X$LEGACY'" elif [[ $deps = low ]]; then From ff81046f3802e176299a1a4176c24dddabb6bb31 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 28 Aug 2018 08:05:40 +0200 Subject: [PATCH 46/47] updated CHANGELOG for 3.4.15 --- CHANGELOG-3.4.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG-3.4.md b/CHANGELOG-3.4.md index f0cde9e267b62..567f31a8c12c3 100644 --- a/CHANGELOG-3.4.md +++ b/CHANGELOG-3.4.md @@ -7,6 +7,21 @@ 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.15 (2018-08-28) + + * bug #28278 [HttpFoundation] Fix unprepared BinaryFileResponse sends empty file (wackymole) + * bug #28284 [PhpUnitBridge] keep compat with composer 1.0 (nicolas-grekas) + * bug #28241 [HttpKernel] fix forwarding trusted headers as server parameters (nicolas-grekas) + * bug #28220 [PropertyAccess] fix type error handling when writing values (xabbuh) + * bug #28249 [Cache] enable Memcached::OPT_TCP_NODELAY to fix perf of misses (nicolas-grekas) + * bug #28252 [DoctrineBridge] support __toString as documented for UniqueEntityValidator (dmaicher) + * bug #28100 [Security] Call AccessListener after LogoutListener (chalasr) + * bug #28060 [DI] Fix false-positive circular ref leading to wrong exceptions or infinite loops at runtime (nicolas-grekas) + * bug #28144 [HttpFoundation] fix false-positive ConflictingHeadersException (nicolas-grekas) + * bug #28012 [PropertyInfo] Allow nested collections (jderusse) + * bug #28055 [PropertyInfo] Allow nested collections (jderusse) + * bug #28083 Remove the Expires header when calling Response::expire() (javiereguiluz) + * 3.4.14 (2018-08-01) * security #cve-2018-14774 [HttpKernel] fix trusted headers management in HttpCache and InlineFragmentRenderer (nicolas-grekas) From 1735abb8a08993fe3de074f9c504250400dd694f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 28 Aug 2018 08:06:12 +0200 Subject: [PATCH 47/47] updated VERSION for 3.4.15 --- 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 0f4eeeb58abe1..ac6471a8b93d9 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.15-DEV'; + const VERSION = '3.4.15'; const VERSION_ID = 30415; const MAJOR_VERSION = 3; const MINOR_VERSION = 4; const RELEASE_VERSION = 15; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '11/2020'; const END_OF_LIFE = '11/2021';