diff --git a/phpunit b/phpunit index cb572db266..c0ffe8ddef 100755 --- a/phpunit +++ b/phpunit @@ -1,7 +1,7 @@ #!/usr/bin/env php isConfigEnabled($container, $config['validation'])) { - throw new LogicException('Validation support cannot be enabled as the Translation component is not installed.'); - } - if (class_exists(Translator::class)) { $loader->load('identity_translator.xml'); } @@ -184,6 +180,10 @@ class FrameworkExtension extends Extension } if ($this->isConfigEnabled($container, $config['form'])) { + if (!class_exists('Symfony\Component\Form\Form')) { + throw new LogicException('Form support cannot be enabled as the Form component is not installed.'); + } + $this->formConfigEnabled = true; $this->registerFormConfiguration($config, $container, $loader); @@ -231,6 +231,10 @@ class FrameworkExtension extends Extension $this->registerPropertyAccessConfiguration($config['property_access'], $container, $loader); if ($this->isConfigEnabled($container, $config['serializer'])) { + if (!class_exists('Symfony\Component\Serializer\Serializer')) { + throw new LogicException('Serializer support cannot be enabled as the Serializer component is not installed.'); + } + $this->registerSerializerConfiguration($config['serializer'], $container, $loader); } @@ -923,6 +927,7 @@ class FrameworkExtension extends Extension return 2 === substr_count($file->getBasename(), '.') && preg_match('/\.\w+$/', $file->getBasename()); }) ->in($dirs) + ->sortByName() ; foreach ($finder as $file) { @@ -1032,7 +1037,7 @@ class FrameworkExtension extends Extension private function registerMappingFilesFromDir($dir, callable $fileRecorder) { - foreach (Finder::create()->followLinks()->files()->in($dir)->name('/\.(xml|ya?ml)$/') as $file) { + foreach (Finder::create()->followLinks()->files()->in($dir)->name('/\.(xml|ya?ml)$/')->sortByName() as $file) { $fileRecorder($file->getExtension(), $file->getRealPath()); } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 8351c86112..4b5270659e 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -349,7 +349,7 @@ class SecurityExtension extends Extension // Switch user listener if (isset($firewall['switch_user'])) { $listenerKeys[] = 'switch_user'; - $listeners[] = new Reference($this->createSwitchUserListener($container, $id, $firewall['switch_user'], $defaultProvider, $firewall['stateless'])); + $listeners[] = new Reference($this->createSwitchUserListener($container, $id, $firewall['switch_user'], $defaultProvider, $firewall['stateless'], $providerIds)); } // Access listener @@ -594,7 +594,7 @@ class SecurityExtension extends Extension return $exceptionListenerId; } - private function createSwitchUserListener($container, $id, $config, $defaultProvider = null, $stateless) + private function createSwitchUserListener($container, $id, $config, $defaultProvider, $stateless, $providerIds) { $userProvider = isset($config['provider']) ? $this->getUserProviderId($config['provider']) : $defaultProvider; diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 03183aafe0..93e49ad9af 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -30,7 +30,7 @@ "symfony/dom-crawler": "~3.4|~4.0", "symfony/event-dispatcher": "~3.4|~4.0", "symfony/form": "~3.4|~4.0", - "symfony/framework-bundle": "~3.4|~4.0", + "symfony/framework-bundle": "~3.4-rc1|~4.0-rc1", "symfony/http-foundation": "~3.4|~4.0", "symfony/translation": "~3.4|~4.0", "symfony/twig-bundle": "~3.4|~4.0", @@ -40,7 +40,7 @@ "symfony/var-dumper": "~3.4|~4.0", "symfony/yaml": "~3.4|~4.0", "symfony/expression-language": "~3.4|~4.0", - "doctrine/doctrine-bundle": "~1.4", + "doctrine/doctrine-bundle": "~1.5", "twig/twig": "~1.34|~2.4" }, "conflict": { diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index f2d121be5a..94ac508c1b 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -544,7 +544,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface $this->{$loading}[$id] = true; try { - $service = $this->createService($definition, $id); + $service = $this->createService($definition, new \SplObjectStorage(), $id); } finally { unset($this->{$loading}[$id]); } @@ -978,8 +978,12 @@ class ContainerBuilder extends Container implements TaggedContainerInterface * @throws RuntimeException When the service is a synthetic service * @throws InvalidArgumentException When configure callable is not callable */ - private function createService(Definition $definition, $id, $tryProxy = true) + private function createService(Definition $definition, \SplObjectStorage $inlinedDefinitions, $id = null, $tryProxy = true) { + if (null === $id && isset($inlinedDefinitions[$definition])) { + return $inlinedDefinitions[$definition]; + } + if ($definition instanceof ChildDefinition) { throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.', $id)); } @@ -998,11 +1002,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface ->instantiateProxy( $this, $definition, - $id, function () use ($definition, $id) { - return $this->createService($definition, $id, false); + $id, function () use ($definition, $inlinedDefinitions, $id) { + return $this->createService($definition, $inlinedDefinitions, $id, false); } ); - $this->shareService($definition, $proxy, $id); + $this->shareService($definition, $proxy, $id, $inlinedDefinitions); return $proxy; } @@ -1013,7 +1017,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface require_once $parameterBag->resolveValue($definition->getFile()); } - $arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments()))); + $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlinedDefinitions); if (null !== $id && $definition->isShared() && isset($this->services[$id]) && ($tryProxy || !$definition->isLazy())) { return $this->services[$id]; @@ -1021,7 +1025,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface if (null !== $factory = $definition->getFactory()) { if (is_array($factory)) { - $factory = array($this->resolveServices($parameterBag->resolveValue($factory[0])), $factory[1]); + $factory = array($this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlinedDefinitions), $factory[1]); } elseif (!is_string($factory)) { throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id)); } @@ -1047,16 +1051,16 @@ class ContainerBuilder extends Container implements TaggedContainerInterface if ($tryProxy || !$definition->isLazy()) { // share only if proxying failed, or if not a proxy - $this->shareService($definition, $service, $id); + $this->shareService($definition, $service, $id, $inlinedDefinitions); } - $properties = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties()))); + $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlinedDefinitions); foreach ($properties as $name => $value) { $service->$name = $value; } foreach ($definition->getMethodCalls() as $call) { - $this->callMethod($service, $call); + $this->callMethod($service, $call, $inlinedDefinitions); } if ($callable = $definition->getConfigurator()) { @@ -1066,7 +1070,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface if ($callable[0] instanceof Reference) { $callable[0] = $this->get((string) $callable[0], $callable[0]->getInvalidBehavior()); } elseif ($callable[0] instanceof Definition) { - $callable[0] = $this->createService($callable[0], null); + $callable[0] = $this->createService($callable[0], $inlinedDefinitions); } } @@ -1089,10 +1093,15 @@ class ContainerBuilder extends Container implements TaggedContainerInterface * the real service instances and all expressions evaluated */ public function resolveServices($value) + { + return $this->doResolveServices($value, new \SplObjectStorage()); + } + + private function doResolveServices($value, \SplObjectStorage $inlinedDefinitions) { if (is_array($value)) { foreach ($value as $k => $v) { - $value[$k] = $this->resolveServices($v); + $value[$k] = $this->doResolveServices($v, $inlinedDefinitions); } } elseif ($value instanceof ServiceClosureArgument) { $reference = $value->getValues()[0]; @@ -1137,7 +1146,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface } elseif ($value instanceof Reference) { $value = $this->get((string) $value, $value->getInvalidBehavior()); } elseif ($value instanceof Definition) { - $value = $this->createService($value, null); + $value = $this->createService($value, $inlinedDefinitions); } elseif ($value instanceof Expression) { $value = $this->getExpressionLanguage()->evaluate($value, array('container' => $this)); } @@ -1434,7 +1443,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface return $this->proxyInstantiator; } - private function callMethod($service, $call) + private function callMethod($service, $call, \SplObjectStorage $inlinedDefinitions) { foreach (self::getServiceConditionals($call[1]) as $s) { if (!$this->has($s)) { @@ -1447,7 +1456,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface } } - call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])))); + call_user_func_array(array($service, $call[0]), $this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlinedDefinitions)); } /** @@ -1457,9 +1466,14 @@ class ContainerBuilder extends Container implements TaggedContainerInterface * @param object $service * @param string|null $id */ - private function shareService(Definition $definition, $service, $id) + private function shareService(Definition $definition, $service, $id, \SplObjectStorage $inlinedDefinitions) { - if (null !== $id && $definition->isShared()) { + if (!$definition->isShared()) { + return; + } + if (null === $id) { + $inlinedDefinitions[$definition] = $service; + } else { $this->services[$id] = $service; unset($this->loading[$id], $this->alreadyLoading[$id]); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index c7af2c2e2c..2a0fdcc289 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -353,6 +353,7 @@ EOTXT if (isset($checkedNodes[$id])) { continue; } + $checkedNodes[$id] = true; if ($node->getValue() && ($edge->isLazy() || $edge->isWeak())) { // no-op @@ -364,10 +365,8 @@ EOTXT } else { $currentPath[$id] = $id; $this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath); + array_pop($currentPath); } - - $checkedNodes[$id] = true; - array_pop($currentPath); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index be71581497..936b13ea2a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1051,6 +1051,28 @@ class ContainerBuilderTest extends TestCase $this->assertTrue($classInList); } + public function testInlinedDefinitions() + { + $container = new ContainerBuilder(); + + $definition = new Definition('BarClass'); + + $container->register('bar_user', 'BarUserClass') + ->addArgument($definition) + ->setProperty('foo', $definition); + + $container->register('bar', 'BarClass') + ->setProperty('foo', $definition) + ->addMethodCall('setBaz', array($definition)); + + $barUser = $container->get('bar_user'); + $bar = $container->get('bar'); + + $this->assertSame($barUser->foo, $barUser->bar); + $this->assertSame($bar->foo, $bar->getBaz()); + $this->assertNotSame($bar->foo, $barUser->foo); + } + public function testInitializePropertiesBeforeMethodCalls() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php index ab07fadc25..bc653a744c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php @@ -9,7 +9,7 @@ function sc_configure($instance) $instance->configure(); } -class BarClass +class BarClass extends BazClass { protected $baz; public $foo = 'foo'; diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php index b801101cba..b67736b0ec 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -34,8 +34,13 @@ class FileType extends AbstractType if ($options['multiple']) { $data = array(); + $files = $event->getData(); - foreach ($event->getData() as $file) { + if (!is_array($files)) { + $files = array(); + } + + foreach ($files as $file) { if ($requestHandler->isFileUpload($file)) { $data[] = $file; } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php index 8ac62c8dfb..5ba1dc5a51 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php @@ -159,6 +159,24 @@ class FileTypeTest extends BaseTypeTest $this->assertCount(1, $form->getData()); } + /** + * @dataProvider requestHandlerProvider + */ + public function testSubmitNonArrayValueWhenMultiple(RequestHandlerInterface $requestHandler) + { + $form = $this->factory + ->createBuilder(static::TESTED_TYPE, null, array( + 'multiple' => true, + )) + ->setRequestHandler($requestHandler) + ->getForm(); + $form->submit(null); + + $this->assertSame(array(), $form->getData()); + $this->assertSame(array(), $form->getNormData()); + $this->assertSame(array(), $form->getViewData()); + } + public function requestHandlerProvider() { return array( diff --git a/src/Symfony/Component/Lock/Tests/Store/MemcachedStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/MemcachedStoreTest.php index eb030fba0f..f4ab571f56 100644 --- a/src/Symfony/Component/Lock/Tests/Store/MemcachedStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/MemcachedStoreTest.php @@ -26,7 +26,10 @@ class MemcachedStoreTest extends AbstractStoreTest { $memcached = new \Memcached(); $memcached->addServer(getenv('MEMCACHED_HOST'), 11211); - if (false === $memcached->getStats()) { + $memcached->get('foo'); + $code = $memcached->getResultCode(); + + if (\Memcached::RES_SUCCESS !== $code && \Memcached::RES_NOTFOUND !== $code) { self::markTestSkipped('Unable to connect to the memcache host'); } } diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index 4dc52d36b8..8bcbd15933 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -1452,20 +1452,7 @@ Array ) EOTXT; - - if (\PHP_VERSION_ID >= 70200) { - $expected = << Standard input code - [1] => a - [2] => - [3] => b -) - -EOTXT; - } - $this->assertSame($expected, $p->getOutput()); + $this->assertSame($expected, str_replace('Standard input code', '-', $p->getOutput())); } public function provideEscapeArgument() diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 67ce5cefaa..9593f8d3b4 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -43,7 +43,6 @@ class ContextListener implements ListenerInterface private $dispatcher; private $registered; private $trustResolver; - private $logoutOnUserChange = true; /** * @param TokenStorageInterface $tokenStorage