diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 8da21ec1213f2..e5e58745ee337 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -4,7 +4,7 @@
| Bug fix? | yes/no
| New feature? | yes/no
| Deprecations? | yes/no
-| Tickets | Fix #...
+| Tickets | Fix #...
| License | MIT
| Doc PR | symfony/symfony-docs#...
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
index 9e84aa504a590..7866328e3a644 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
@@ -236,6 +236,25 @@ public function testTypeNotGuessableNoServicesFound()
}
}
+ /**
+ * @requires PHP 8
+ */
+ public function testTypeNotGuessableUnionType()
+ {
+ $this->expectException('Symfony\Component\DependencyInjection\Exception\AutowiringFailedException');
+ $this->expectExceptionMessage('Cannot autowire service "a": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\UnionClasses::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionA|Symfony\Component\DependencyInjection\Tests\Compiler\CollisionB" but this class was not found.');
+ $container = new ContainerBuilder();
+
+ $container->register(CollisionA::class);
+ $container->register(CollisionB::class);
+
+ $aDefinition = $container->register('a', UnionClasses::class);
+ $aDefinition->setAutowired(true);
+
+ $pass = new AutowirePass();
+ $pass->process($container);
+ }
+
public function testTypeNotGuessableWithTypeSet()
{
$container = new ContainerBuilder();
@@ -319,6 +338,40 @@ public function testOptionalParameter()
$this->assertEquals(Foo::class, $definition->getArgument(2));
}
+ /**
+ * @requires PHP 8
+ */
+ public function testParameterWithNullUnionIsSkipped()
+ {
+ $container = new ContainerBuilder();
+
+ $optDefinition = $container->register('opt', UnionNull::class);
+ $optDefinition->setAutowired(true);
+
+ (new AutowirePass())->process($container);
+
+ $definition = $container->getDefinition('opt');
+ $this->assertNull($definition->getArgument(0));
+ }
+
+ /**
+ * @requires PHP 8
+ */
+ public function testParameterWithNullUnionIsAutowired()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register(CollisionInterface::class, CollisionA::class);
+
+ $optDefinition = $container->register('opt', UnionNull::class);
+ $optDefinition->setAutowired(true);
+
+ (new AutowirePass())->process($container);
+
+ $definition = $container->getDefinition('opt');
+ $this->assertEquals(CollisionInterface::class, $definition->getArgument(0));
+ }
+
public function testDontTriggerAutowiring()
{
$container = new ContainerBuilder();
@@ -435,6 +488,21 @@ public function testScalarArgsCannotBeAutowired()
}
}
+ /**
+ * @requires PHP 8
+ */
+ public function testUnionScalarArgsCannotBeAutowired()
+ {
+ $this->expectException('Symfony\Component\DependencyInjection\Exception\AutowiringFailedException');
+ $this->expectExceptionMessage('Cannot autowire service "union_scalars": argument "$timeout" of method "Symfony\Component\DependencyInjection\Tests\Compiler\UnionScalars::__construct()" is type-hinted "int|float", you should configure its value explicitly.');
+ $container = new ContainerBuilder();
+
+ $container->register('union_scalars', UnionScalars::class)
+ ->setAutowired(true);
+
+ (new AutowirePass())->process($container);
+ }
+
public function testNoTypeArgsCannotBeAutowired()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php
index d5db48cc30645..a2cb3bda5ed96 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Definition\BaseNode;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Resource\FileResource;
@@ -128,6 +129,23 @@ public function testThrowingExtensionsGetMergedBag()
$this->assertSame(['FOO'], array_keys($container->getParameterBag()->getEnvPlaceholders()));
}
+
+ public function testReuseEnvPlaceholderGeneratedByPreviousExtension()
+ {
+ if (!property_exists(BaseNode::class, 'placeholderUniquePrefixes')) {
+ $this->markTestSkipped('This test requires symfony/config ^4.4.11|^5.0.11|^5.1.3');
+ }
+
+ $container = new ContainerBuilder();
+ $container->registerExtension(new FooExtension());
+ $container->registerExtension(new TestCccExtension());
+ $container->prependExtensionConfig('foo', ['bool_node' => '%env(bool:MY_ENV_VAR)%']);
+ $container->prependExtensionConfig('test_ccc', ['bool_node' => '%env(bool:MY_ENV_VAR)%']);
+
+ (new MergeExtensionConfigurationPass())->process($container);
+
+ $this->addToAssertionCount(1);
+ }
}
class FooConfiguration implements ConfigurationInterface
@@ -139,6 +157,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->children()
->scalarNode('bar')->end()
->scalarNode('baz')->end()
+ ->booleanNode('bool_node')->end()
->end();
return $treeBuilder;
@@ -166,6 +185,8 @@ public function load(array $configs, ContainerBuilder $container)
$container->getParameterBag()->get('env(BOZ)');
$container->resolveEnvPlaceholders($config['baz']);
}
+
+ $container->setParameter('foo.param', 'ccc');
}
}
@@ -194,3 +215,36 @@ public function load(array $configs, ContainerBuilder $container)
throw new \Exception();
}
}
+
+final class TestCccConfiguration implements ConfigurationInterface
+{
+ public function getConfigTreeBuilder(): TreeBuilder
+ {
+ $treeBuilder = new TreeBuilder('test_ccc');
+ $treeBuilder->getRootNode()
+ ->children()
+ ->booleanNode('bool_node')->end()
+ ->end();
+
+ return $treeBuilder;
+ }
+}
+
+final class TestCccExtension extends Extension
+{
+ public function getAlias(): string
+ {
+ return 'test_ccc';
+ }
+
+ public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface
+ {
+ return new TestCccConfiguration();
+ }
+
+ public function load(array $configs, ContainerBuilder $container)
+ {
+ $configuration = $this->getConfiguration($configs, $container);
+ $this->processConfiguration($configuration, $configs);
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
index daf4ed7456515..e974f7c2b3a30 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
@@ -902,12 +902,12 @@ public function testfindTaggedServiceIds()
->addTag('bar', ['bar' => 'bar'])
->addTag('foo', ['foofoo' => 'foofoo'])
;
- $this->assertEquals($builder->findTaggedServiceIds('foo'), [
+ $this->assertEquals([
'foo' => [
['foo' => 'foo'],
['foofoo' => 'foofoo'],
],
- ], '->findTaggedServiceIds() returns an array of service ids and its tag attributes');
+ ], $builder->findTaggedServiceIds('foo'), '->findTaggedServiceIds() returns an array of service ids and its tag attributes');
$this->assertEquals([], $builder->findTaggedServiceIds('foobar'), '->findTaggedServiceIds() returns an empty array if there is annotated services');
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php
index 0171aa667537d..be5253a6e6c06 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php
@@ -266,10 +266,10 @@ public function testTags()
$def->addTag('foo', ['foo' => 'bar']);
$this->assertEquals([[], ['foo' => 'bar']], $def->getTag('foo'), '->addTag() can adds the same tag several times');
$def->addTag('bar', ['bar' => 'bar']);
- $this->assertEquals($def->getTags(), [
+ $this->assertEquals([
'foo' => [[], ['foo' => 'bar']],
'bar' => [['bar' => 'bar']],
- ], '->getTags() returns all tags');
+ ], $def->getTags(), '->getTags() returns all tags');
}
public function testSetArgument()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index 93000ab82eb5a..0122094acd708 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -113,7 +113,7 @@ public function testDumpRelativeDir()
$container = new ContainerBuilder();
$container->setDefinition('test', $definition);
- $container->setParameter('foo', 'wiz'.\dirname(__DIR__));
+ $container->setParameter('foo', 'file://'.\dirname(__DIR__));
$container->setParameter('bar', __DIR__);
$container->setParameter('baz', '%bar%/PhpDumperTest.php');
$container->setParameter('buz', \dirname(__DIR__, 2));
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
index 91eb058e40805..2891d4307ca56 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
@@ -4,6 +4,10 @@
use Psr\Log\LoggerInterface;
+if (PHP_VERSION_ID >= 80000) {
+ require __DIR__.'/uniontype_classes.php';
+}
+
class Foo
{
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/uniontype_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/uniontype_classes.php
new file mode 100644
index 0000000000000..3a0c77c53941c
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/uniontype_classes.php
@@ -0,0 +1,24 @@
+services['test'] = new \stdClass(('wiz'.\dirname(__DIR__, 1)), [('wiz'.\dirname(__DIR__, 1)) => (\dirname(__DIR__, 2).'/')]);
+ return $this->services['test'] = new \stdClass(('file://'.\dirname(__DIR__, 1)), [('file://'.\dirname(__DIR__, 1)) => (\dirname(__DIR__, 2).'/')]);
}
public function getParameter(string $name)
@@ -102,7 +102,7 @@ private function getDynamicParameter(string $name)
protected function getDefaultParameters(): array
{
return [
- 'foo' => ('wiz'.\dirname(__DIR__, 1)),
+ 'foo' => ('file://'.\dirname(__DIR__, 1)),
'bar' => __DIR__,
'baz' => (__DIR__.'/PhpDumperTest.php'),
'buz' => \dirname(__DIR__, 2),
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt
index d7ab192e9aa2b..c7291046caf79 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt
@@ -402,7 +402,11 @@ class getFooBarService extends ProjectServiceContainer
*/
public static function do($container, $lazyLoad = true)
{
- return new \Bar\FooClass(($container->services['deprecated_service'] ?? $container->load('getDeprecatedServiceService')));
+ $container->factories['foo_bar'] = function () use ($container) {
+ return new \Bar\FooClass(($container->services['deprecated_service'] ?? $container->load('getDeprecatedServiceService')));
+ };
+
+ return $container->factories['foo_bar']();
}
}
@@ -574,7 +578,11 @@ class getNonSharedFooService extends ProjectServiceContainer
{
include_once $container->targetDir.''.'/Fixtures/includes/foo.php';
- return new \Bar\FooClass();
+ $container->factories['non_shared_foo'] = function () use ($container) {
+ return new \Bar\FooClass();
+ };
+
+ return $container->factories['non_shared_foo']();
}
}
@@ -891,6 +899,10 @@ class ProjectServiceContainer extends Container
use Symfony\Component\DependencyInjection\Dumper\Preloader;
+if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) {
+ return;
+}
+
require dirname(__DIR__, %d).'%svendor/autoload.php';
require __DIR__.'/Container%s/ProjectServiceContainer.php';
require __DIR__.'/Container%s/getThrowingOneService.php';
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
index 277da470b544c..671a1b11b3417 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
@@ -275,7 +275,11 @@ protected function getFoo_BazService()
*/
protected function getFooBarService()
{
- return new \Bar\FooClass(($this->services['deprecated_service'] ?? $this->getDeprecatedServiceService()));
+ $this->factories['foo_bar'] = function () {
+ return new \Bar\FooClass(($this->services['deprecated_service'] ?? $this->getDeprecatedServiceService()));
+ };
+
+ return $this->factories['foo_bar']();
}
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt
index 34f1689ce6077..cf0543d4eeea8 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt
@@ -300,7 +300,11 @@ class ProjectServiceContainer extends Container
*/
protected function getFooBarService()
{
- return new \Bar\FooClass(($this->services['deprecated_service'] ?? $this->getDeprecatedServiceService()));
+ $this->factories['foo_bar'] = function () {
+ return new \Bar\FooClass(($this->services['deprecated_service'] ?? $this->getDeprecatedServiceService()));
+ };
+
+ return $this->factories['foo_bar']();
}
/**
@@ -398,7 +402,11 @@ class ProjectServiceContainer extends Container
{
include_once $this->targetDir.''.'/Fixtures/includes/foo.php';
- return new \Bar\FooClass();
+ $this->factories['non_shared_foo'] = function () {
+ return new \Bar\FooClass();
+ };
+
+ return $this->factories['non_shared_foo']();
}
/**
@@ -546,6 +554,10 @@ class ProjectServiceContainer extends Container
use Symfony\Component\DependencyInjection\Dumper\Preloader;
+if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) {
+ return;
+}
+
require dirname(__DIR__, %d).'%svendor/autoload.php';
require __DIR__.'/Container%s/ProjectServiceContainer.php';
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
index 92c7299ac3050..fbbb83a3e3c31 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
@@ -171,6 +171,10 @@ if (!\class_exists('FooClass_%s', false)) {
use Symfony\Component\DependencyInjection\Dumper\Preloader;
+if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) {
+ return;
+}
+
require dirname(__DIR__, %d).'%svendor/autoload.php';
require __DIR__.'/Container%s/ProjectServiceContainer.php';
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 1554588ac381e..4905ade2dd408 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
@@ -287,11 +287,15 @@ protected function getFoo2Service()
*/
protected function getFoo4Service()
{
- $instance = new \stdClass();
+ $this->factories['foo4'] = function () {
+ $instance = new \stdClass();
- $instance->foobar = ($this->services['foobar4'] ?? $this->getFoobar4Service());
+ $instance->foobar = ($this->services['foobar4'] ?? $this->getFoobar4Service());
- return $instance;
+ return $instance;
+ };
+
+ return $this->factories['foo4']();
}
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php
index 9f2bb02158a92..99595f647851f 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_errored_definition.php
@@ -275,7 +275,11 @@ protected function getFoo_BazService()
*/
protected function getFooBarService()
{
- return new \Bar\FooClass(($this->services['deprecated_service'] ?? $this->getDeprecatedServiceService()));
+ $this->factories['foo_bar'] = function () {
+ return new \Bar\FooClass(($this->services['deprecated_service'] ?? $this->getDeprecatedServiceService()));
+ };
+
+ return $this->factories['foo_bar']();
}
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy.php
index 4295512b087c0..048e4fdf59b59 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy.php
@@ -57,7 +57,7 @@ protected function createProxy($class, \Closure $factory)
*/
protected function getBarService()
{
- return $this->services['bar'] = new \stdClass($this->getFooService());
+ return $this->services['bar'] = new \stdClass((isset($this->factories['service_container']['foo']) ? $this->factories['service_container']['foo']() : $this->getFooService()));
}
/**
@@ -69,7 +69,11 @@ protected function getFooService($lazyLoad = true)
{
// lazy factory for stdClass
- return new \stdClass();
+ $this->factories['service_container']['foo'] = function ($lazyLoad = true) {
+ return new \stdClass();
+ };
+
+ return $this->factories['service_container']['foo']();
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt
index a47c983a72bd2..a42d1e29db3e2 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt
@@ -30,7 +30,11 @@ class getNonSharedFooService extends ProjectServiceContainer
{
include_once $container->targetDir.''.'/Fixtures/includes/foo_lazy.php';
- return new \Bar\FooLazyClass();
+ $container->factories['non_shared_foo'] = function ($lazyLoad = true) {
+ return new \Bar\FooLazyClass();
+ };
+
+ return $container->factories['non_shared_foo']();
}
}
@@ -110,6 +114,10 @@ class ProjectServiceContainer extends Container
use Symfony\Component\DependencyInjection\Dumper\Preloader;
+if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) {
+ return;
+}
+
require dirname(__DIR__, %d).'%svendor/autoload.php';
require __DIR__.'/Container%s/ProjectServiceContainer.php';
require __DIR__.'/Container%s/getNonSharedFooService.php';
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_service_locator_argument.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_service_locator_argument.php
index 14873b484c2d1..c695bb8e49e17 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_service_locator_argument.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_service_locator_argument.php
@@ -107,7 +107,11 @@ protected function getFoo2Service()
*/
protected function getFoo3Service()
{
- return new \stdClass();
+ $this->factories['service_container']['foo3'] = function () {
+ return new \stdClass();
+ };
+
+ return $this->factories['service_container']['foo3']();
}
/**
diff --git a/src/Symfony/Component/DomCrawler/Tests/FormTest.php b/src/Symfony/Component/DomCrawler/Tests/FormTest.php
index 88987b05b5a74..db390efccada2 100644
--- a/src/Symfony/Component/DomCrawler/Tests/FormTest.php
+++ b/src/Symfony/Component/DomCrawler/Tests/FormTest.php
@@ -170,25 +170,28 @@ public function testMultiValuedFields()
');
$this->assertEquals(
- array_keys($form->all()),
- ['foo[2]', 'foo[3]', 'bar[foo][0]', 'bar[foo][foobar]']
+ ['foo[2]', 'foo[3]', 'bar[foo][0]', 'bar[foo][foobar]'],
+ array_keys($form->all())
);
- $this->assertEquals($form->get('foo[2]')->getValue(), 'foo');
- $this->assertEquals($form->get('foo[3]')->getValue(), 'foo');
- $this->assertEquals($form->get('bar[foo][0]')->getValue(), 'foo');
- $this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foo');
+ $this->assertEquals('foo', $form->get('foo[2]')->getValue());
+ $this->assertEquals('foo', $form->get('foo[3]')->getValue());
+ $this->assertEquals('foo', $form->get('bar[foo][0]')->getValue());
+ $this->assertEquals('foo', $form->get('bar[foo][foobar]')->getValue());
$form['foo[2]'] = 'bar';
$form['foo[3]'] = 'bar';
- $this->assertEquals($form->get('foo[2]')->getValue(), 'bar');
- $this->assertEquals($form->get('foo[3]')->getValue(), 'bar');
+ $this->assertEquals('bar', $form->get('foo[2]')->getValue());
+ $this->assertEquals('bar', $form->get('foo[3]')->getValue());
$form['bar'] = ['foo' => ['0' => 'bar', 'foobar' => 'foobar']];
- $this->assertEquals($form->get('bar[foo][0]')->getValue(), 'bar');
- $this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foobar');
+ $this->assertEquals('bar', $form->get('bar[foo][0]')->getValue());
+ $this->assertEquals(
+ 'foobar',
+ $form->get('bar[foo][foobar]')->getValue()
+ );
}
/**
@@ -979,7 +982,7 @@ public function testGetPhpValuesWithEmptyTextarea()
$nodes = $dom->getElementsByTagName('form');
$form = new Form($nodes->item(0), 'http://example.com');
- $this->assertEquals($form->getPhpValues(), ['example' => '']);
+ $this->assertEquals(['example' => ''], $form->getPhpValues());
}
public function testGetReturnTypes()
diff --git a/src/Symfony/Component/ErrorHandler/ErrorHandler.php b/src/Symfony/Component/ErrorHandler/ErrorHandler.php
index 930470ecfd24d..55164d6509553 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorHandler.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorHandler.php
@@ -429,18 +429,6 @@ public function handleError(int $type, string $message, string $file, int $line)
}
$scope = $this->scopedErrors & $type;
- if (4 < $numArgs = \func_num_args()) {
- $context = $scope ? (func_get_arg(4) ?: []) : [];
- } else {
- $context = [];
- }
-
- if (isset($context['GLOBALS']) && $scope) {
- $e = $context; // Whatever the signature of the method,
- unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
- $context = $e;
- }
-
if (false !== strpos($message, "@anonymous\0")) {
$logMessage = $this->parseAnonymousClass($message);
} else {
@@ -502,6 +490,8 @@ public function handleError(int $type, string $message, string $file, int $line)
// `return trigger_error($e, E_USER_ERROR);` allows this error handler
// to make $e get through the __toString() barrier.
+ $context = 4 < \func_num_args() ? (func_get_arg(4) ?: []) : [];
+
foreach ($context as $e) {
if ($e instanceof \Throwable && $e->__toString() === $message) {
self::$toStringException = $e;
diff --git a/src/Symfony/Component/ErrorHandler/Tests/Exception/FlattenExceptionTest.php b/src/Symfony/Component/ErrorHandler/Tests/Exception/FlattenExceptionTest.php
index 8ff72d23e1190..35b33d2800acc 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/Exception/FlattenExceptionTest.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/Exception/FlattenExceptionTest.php
@@ -161,9 +161,9 @@ public function testPreviousError()
$flattened = FlattenException::createFromThrowable($exception)->getPrevious();
- $this->assertEquals($flattened->getMessage(), 'Oh noes!', 'The message is copied from the original exception.');
- $this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.');
- $this->assertEquals($flattened->getClass(), 'ParseError', 'The class is set to the class of the original exception');
+ $this->assertEquals('Oh noes!', $flattened->getMessage(), 'The message is copied from the original exception.');
+ $this->assertEquals(42, $flattened->getCode(), 'The code is copied from the original exception.');
+ $this->assertEquals('ParseError', $flattened->getClass(), 'The class is set to the class of the original exception');
}
/**
@@ -289,7 +289,7 @@ function () {},
$this->assertSame(['resource', 'stream'], $array[$i++]);
$args = $array[$i++];
- $this->assertSame($args[0], 'object');
+ $this->assertSame('object', $args[0]);
$this->assertTrue('Closure' === $args[1] || is_subclass_of($args[1], '\Closure'), 'Expect object class name to be Closure or a subclass of Closure.');
$this->assertSame(['array', [['integer', 1], ['integer', 2]]], $array[$i++]);
@@ -304,7 +304,7 @@ function () {},
$this->assertSame(['float', INF], $array[$i++]);
// assertEquals() does not like NAN values.
- $this->assertEquals($array[$i][0], 'float');
+ $this->assertEquals('float', $array[$i][0]);
$this->assertNan($array[$i][1]);
}
@@ -344,7 +344,7 @@ public function testTooBigArray()
$flattened = FlattenException::createFromThrowable($exception);
$trace = $flattened->getTrace();
- $this->assertSame($trace[1]['args'][0], ['array', ['array', '*SKIPPED over 10000 entries*']]);
+ $this->assertSame(['array', ['array', '*SKIPPED over 10000 entries*']], $trace[1]['args'][0]);
$serializeTrace = serialize($trace);
diff --git a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php
index c2b8b0d4187e8..a40cee00bb994 100644
--- a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php
+++ b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php
@@ -187,7 +187,7 @@ private function getEventFromTypeDeclaration(ContainerBuilder $container, string
|| !($r = $container->getReflectionClass($class, false))
|| !$r->hasMethod($method)
|| 1 > ($m = $r->getMethod($method))->getNumberOfParameters()
- || !($type = $m->getParameters()[0]->getType())
+ || !($type = $m->getParameters()[0]->getType()) instanceof \ReflectionNamedType
|| $type->isBuiltin()
|| Event::class === ($name = $type->getName())
) {
diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceLoader.php b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceLoader.php
index d8630dd854dbe..f0e1067b90710 100644
--- a/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceLoader.php
+++ b/src/Symfony/Component/Form/ChoiceList/Factory/Cache/ChoiceLoader.php
@@ -46,6 +46,6 @@ public function loadChoicesForValues(array $values, callable $value = null)
*/
public function loadValuesForChoices(array $choices, callable $value = null)
{
- $this->getOption()->loadValuesForChoices($choices, $value);
+ return $this->getOption()->loadValuesForChoices($choices, $value);
}
}
diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php
index 7a3d8db6a4ecc..c03b1a323910f 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php
@@ -13,6 +13,8 @@
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
+use Symfony\Component\PropertyAccess\Exception\AccessException;
+use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
@@ -46,7 +48,7 @@ public function mapDataToForms($data, iterable $forms)
$config = $form->getConfig();
if (!$empty && null !== $propertyPath && $config->getMapped()) {
- $form->setData($this->propertyAccessor->getValue($data, $propertyPath));
+ $form->setData($this->getPropertyValue($data, $propertyPath));
} else {
$form->setData($config->getData());
}
@@ -76,16 +78,32 @@ public function mapFormsToData(iterable $forms, &$data)
$propertyValue = $form->getData();
// If the field is of type DateTimeInterface and the data is the same skip the update to
// keep the original object hash
- if ($propertyValue instanceof \DateTimeInterface && $propertyValue == $this->propertyAccessor->getValue($data, $propertyPath)) {
+ if ($propertyValue instanceof \DateTimeInterface && $propertyValue == $this->getPropertyValue($data, $propertyPath)) {
continue;
}
// If the data is identical to the value in $data, we are
// dealing with a reference
- if (!\is_object($data) || !$config->getByReference() || $propertyValue !== $this->propertyAccessor->getValue($data, $propertyPath)) {
+ if (!\is_object($data) || !$config->getByReference() || $propertyValue !== $this->getPropertyValue($data, $propertyPath)) {
$this->propertyAccessor->setValue($data, $propertyPath, $propertyValue);
}
}
}
}
+
+ private function getPropertyValue($data, $propertyPath)
+ {
+ try {
+ return $this->propertyAccessor->getValue($data, $propertyPath);
+ } catch (AccessException $e) {
+ if (!$e instanceof UninitializedPropertyException
+ // For versions without UninitializedPropertyException check the exception message
+ && (class_exists(UninitializedPropertyException::class) || false === strpos($e->getMessage(), 'You should initialize it'))
+ ) {
+ throw $e;
+ }
+
+ return null;
+ }
+ }
}
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
index d004044f0bc82..20679250c8d62 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
@@ -126,11 +126,11 @@ public function transform($value)
*/
public function reverseTransform($value)
{
- if (!\is_string($value)) {
+ if (null !== $value && !\is_string($value)) {
throw new TransformationFailedException('Expected a string.');
}
- if ('' === $value) {
+ if (null === $value || '' === $value) {
return null;
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
index 6921ffa27fe42..ec7d81aef9361 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
@@ -390,7 +390,7 @@ private function addSubForm(FormBuilderInterface $builder, string $name, ChoiceV
'value' => $choiceView->value,
'label' => $choiceView->label,
'attr' => $choiceView->attr,
- 'translation_domain' => $options['translation_domain'],
+ 'translation_domain' => $options['choice_translation_domain'],
'block_name' => 'entry',
];
diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
index b21c442a83545..cdf4cde4ef3e0 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
@@ -153,7 +153,10 @@ public function validate($form, Constraint $formConstraint)
foreach ($form as $child) {
if (!$child->isSynchronized()) {
$childrenSynchronized = false;
- break;
+
+ $fieldFormConstraint = new Form();
+ $this->context->setNode($this->context->getValue(), $child, $this->context->getMetadata(), $this->context->getPropertyPath());
+ $validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $fieldFormConstraint);
}
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php
index 2dbe47851d6b8..97e16caea8f70 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php
@@ -97,6 +97,7 @@ public function guessTypeForConstraint(Constraint $constraint)
case 'long':
return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\IntegerType', [], Guess::MEDIUM_CONFIDENCE);
+ case \DateTime::class:
case '\DateTime':
return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateType', [], Guess::MEDIUM_CONFIDENCE);
diff --git a/src/Symfony/Component/Form/Resources/translations/validators.de.xlf b/src/Symfony/Component/Form/Resources/translations/validators.de.xlf
index a9a183197edc6..fe4353120d256 100644
--- a/src/Symfony/Component/Form/Resources/translations/validators.de.xlf
+++ b/src/Symfony/Component/Form/Resources/translations/validators.de.xlf
@@ -14,6 +14,10 @@