Skip to content

Commit 6a93a79

Browse files
committed
Merge branch '5.3' into 5.4
* 5.3: [DependencyInjection] fix support for "new" in initializers on PHP 8.1 add translation for zh_TW 41830 missing translations for serbian sr latn Changed some translations to be more in line with native Serbian language [HttpFoundation] Fix ianaCodesReasonPhrasesProvider to consume a local file Add armenian translation for #41835 Add swedish translation for #41835 Added missing danish translations and fixed typo in validators.da.xlf
2 parents 511aa4b + 597a639 commit 6a93a79

File tree

8 files changed

+176
-17
lines changed

8 files changed

+176
-17
lines changed

Compiler/AutowirePass.php

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class AutowirePass extends AbstractRecursivePass
4040
private $decoratedClass;
4141
private $decoratedId;
4242
private $methodCalls;
43+
private $defaultArgument;
4344
private $getPreviousValue;
4445
private $decoratedMethodIndex;
4546
private $decoratedMethodArgumentIndex;
@@ -48,6 +49,10 @@ class AutowirePass extends AbstractRecursivePass
4849
public function __construct(bool $throwOnAutowireException = true)
4950
{
5051
$this->throwOnAutowiringException = $throwOnAutowireException;
52+
$this->defaultArgument = new class() {
53+
public $value;
54+
public $names;
55+
};
5156
}
5257

5358
/**
@@ -62,6 +67,7 @@ public function process(ContainerBuilder $container)
6267
$this->decoratedClass = null;
6368
$this->decoratedId = null;
6469
$this->methodCalls = null;
70+
$this->defaultArgument->names = null;
6571
$this->getPreviousValue = null;
6672
$this->decoratedMethodIndex = null;
6773
$this->decoratedMethodArgumentIndex = null;
@@ -159,8 +165,9 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot,
159165
$this->decoratedId = $decoratedDefinition[1] ?? $this->currentId.'.inner';
160166
}
161167

168+
$patchedIndexes = [];
169+
162170
foreach ($this->methodCalls as $i => $call) {
163-
$this->decoratedMethodIndex = $i;
164171
[$method, $arguments] = $call;
165172

166173
if ($method instanceof \ReflectionFunctionAbstract) {
@@ -177,13 +184,39 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot,
177184
}
178185
}
179186

180-
$arguments = $this->autowireMethod($reflectionMethod, $arguments, $checkAttributes);
187+
$arguments = $this->autowireMethod($reflectionMethod, $arguments, $checkAttributes, $i);
181188

182189
if ($arguments !== $call[1]) {
183190
$this->methodCalls[$i][1] = $arguments;
191+
$patchedIndexes[] = $i;
184192
}
185193
}
186194

195+
// use named arguments to skip complex default values
196+
foreach ($patchedIndexes as $i) {
197+
$namedArguments = null;
198+
$arguments = $this->methodCalls[$i][1];
199+
200+
foreach ($arguments as $j => $value) {
201+
if ($namedArguments && !$value instanceof $this->defaultArgument) {
202+
unset($arguments[$j]);
203+
$arguments[$namedArguments[$j]] = $value;
204+
}
205+
if ($namedArguments || !$value instanceof $this->defaultArgument) {
206+
continue;
207+
}
208+
209+
if (\PHP_VERSION_ID >= 80100 && (\is_array($value->value) ? $value->value : \is_object($value->value))) {
210+
unset($arguments[$j]);
211+
$namedArguments = $value->names;
212+
} else {
213+
$arguments[$j] = $value->value;
214+
}
215+
}
216+
217+
$this->methodCalls[$i][1] = $arguments;
218+
}
219+
187220
return $this->methodCalls;
188221
}
189222

@@ -194,16 +227,19 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot,
194227
*
195228
* @throws AutowiringFailedException
196229
*/
197-
private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes): array
230+
private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes, int $methodIndex): array
198231
{
199232
$class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId;
200233
$method = $reflectionMethod->name;
201234
$parameters = $reflectionMethod->getParameters();
202235
if ($reflectionMethod->isVariadic()) {
203236
array_pop($parameters);
204237
}
238+
$this->defaultArgument->names = new \ArrayObject();
205239

206240
foreach ($parameters as $index => $parameter) {
241+
$this->defaultArgument->names[$index] = $parameter->name;
242+
207243
if (\array_key_exists($index, $arguments) && '' !== $arguments[$index]) {
208244
continue;
209245
}
@@ -241,7 +277,8 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
241277
// be false when isOptional() returns true. If the
242278
// argument *is* optional, allow it to be missing
243279
if ($parameter->isOptional()) {
244-
continue;
280+
--$index;
281+
break;
245282
}
246283
$type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, false);
247284
$type = $type ? sprintf('is type-hinted "%s"', ltrim($type, '\\')) : 'has no type-hint';
@@ -250,7 +287,8 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
250287
}
251288

252289
// specifically pass the default value
253-
$arguments[$index] = $parameter->getDefaultValue();
290+
$arguments[$index] = clone $this->defaultArgument;
291+
$arguments[$index]->value = $parameter->getDefaultValue();
254292

255293
continue;
256294
}
@@ -260,7 +298,8 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
260298
$failureMessage = $this->createTypeNotFoundMessageCallback($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
261299

262300
if ($parameter->isDefaultValueAvailable()) {
263-
$value = $parameter->getDefaultValue();
301+
$value = clone $this->defaultArgument;
302+
$value->value = $parameter->getDefaultValue();
264303
} elseif (!$parameter->allowsNull()) {
265304
throw new AutowiringFailedException($this->currentId, $failureMessage);
266305
}
@@ -281,6 +320,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
281320
} else {
282321
$arguments[$index] = new TypedReference($this->decoratedId, $this->decoratedClass);
283322
$this->getPreviousValue = $getValue;
323+
$this->decoratedMethodIndex = $methodIndex;
284324
$this->decoratedMethodArgumentIndex = $index;
285325

286326
continue;
@@ -292,8 +332,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
292332

293333
if ($parameters && !isset($arguments[++$index])) {
294334
while (0 <= --$index) {
295-
$parameter = $parameters[$index];
296-
if (!$parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== $arguments[$index]) {
335+
if (!$arguments[$index] instanceof $this->defaultArgument) {
297336
break;
298337
}
299338
unset($arguments[$index]);

Compiler/CheckArgumentsValidityPass.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,13 @@ protected function processValue($value, bool $isRoot = false)
3939
}
4040

4141
$i = 0;
42+
$hasNamedArgs = false;
4243
foreach ($value->getArguments() as $k => $v) {
44+
if (\PHP_VERSION_ID >= 80000 && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $k)) {
45+
$hasNamedArgs = true;
46+
continue;
47+
}
48+
4349
if ($k !== $i++) {
4450
if (!\is_int($k)) {
4551
$msg = sprintf('Invalid constructor argument for service "%s": integer expected but found string "%s". Check your service definition.', $this->currentId, $k);
@@ -57,11 +63,27 @@ protected function processValue($value, bool $isRoot = false)
5763
throw new RuntimeException($msg);
5864
}
5965
}
66+
67+
if ($hasNamedArgs) {
68+
$msg = sprintf('Invalid constructor argument for service "%s": cannot use positional argument after named argument. Check your service definition.', $this->currentId);
69+
$value->addError($msg);
70+
if ($this->throwExceptions) {
71+
throw new RuntimeException($msg);
72+
}
73+
74+
break;
75+
}
6076
}
6177

6278
foreach ($value->getMethodCalls() as $methodCall) {
6379
$i = 0;
80+
$hasNamedArgs = false;
6481
foreach ($methodCall[1] as $k => $v) {
82+
if (\PHP_VERSION_ID >= 80000 && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $k)) {
83+
$hasNamedArgs = true;
84+
continue;
85+
}
86+
6587
if ($k !== $i++) {
6688
if (!\is_int($k)) {
6789
$msg = sprintf('Invalid argument for method call "%s" of service "%s": integer expected but found string "%s". Check your service definition.', $methodCall[0], $this->currentId, $k);
@@ -79,6 +101,16 @@ protected function processValue($value, bool $isRoot = false)
79101
throw new RuntimeException($msg);
80102
}
81103
}
104+
105+
if ($hasNamedArgs) {
106+
$msg = sprintf('Invalid argument for method call "%s" of service "%s": cannot use positional argument after named argument. Check your service definition.', $methodCall[0], $this->currentId);
107+
$value->addError($msg);
108+
if ($this->throwExceptions) {
109+
throw new RuntimeException($msg);
110+
}
111+
112+
break;
113+
}
82114
}
83115
}
84116

Compiler/InlineServiceDefinitionsPass.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public function process(ContainerBuilder $container)
110110
protected function processValue($value, bool $isRoot = false)
111111
{
112112
if ($value instanceof ArgumentInterface) {
113-
// Reference found in ArgumentInterface::getValues() are not inlineable
113+
// References found in ArgumentInterface::getValues() are not inlineable
114114
return $value;
115115
}
116116

Dumper/PhpDumper.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -739,8 +739,8 @@ private function addServiceMethodCalls(Definition $definition, string $variableN
739739
$calls = '';
740740
foreach ($definition->getMethodCalls() as $k => $call) {
741741
$arguments = [];
742-
foreach ($call[1] as $value) {
743-
$arguments[] = $this->dumpValue($value);
742+
foreach ($call[1] as $i => $value) {
743+
$arguments[] = (\is_string($i) ? $i.': ' : '').$this->dumpValue($value);
744744
}
745745

746746
$witherAssignation = '';
@@ -1132,8 +1132,8 @@ private function addNewInstance(Definition $definition, string $return = '', str
11321132
}
11331133

11341134
$arguments = [];
1135-
foreach ($definition->getArguments() as $value) {
1136-
$arguments[] = $this->dumpValue($value);
1135+
foreach ($definition->getArguments() as $i => $value) {
1136+
$arguments[] = (\is_string($i) ? $i.': ' : '').$this->dumpValue($value);
11371137
}
11381138

11391139
if (null !== $definition->getFactory()) {

Tests/Compiler/CheckArgumentsValidityPassTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,22 @@ public function testProcess()
4646
*/
4747
public function testException(array $arguments, array $methodCalls)
4848
{
49-
$this->expectException(RuntimeException::class);
5049
$container = new ContainerBuilder();
5150
$definition = $container->register('foo');
5251
$definition->setArguments($arguments);
5352
$definition->setMethodCalls($methodCalls);
5453

5554
$pass = new CheckArgumentsValidityPass();
55+
$this->expectException(RuntimeException::class);
5656
$pass->process($container);
5757
}
5858

5959
public function definitionProvider()
6060
{
6161
return [
62-
[[null, 'a' => 'a'], []],
62+
[['a' => 'a', null], []],
6363
[[1 => 1], []],
64-
[[], [['baz', [null, 'a' => 'a']]]],
64+
[[], [['baz', ['a' => 'a', null]]]],
6565
[[], [['baz', [1 => 1]]]],
6666
];
6767
}
@@ -70,7 +70,7 @@ public function testNoException()
7070
{
7171
$container = new ContainerBuilder();
7272
$definition = $container->register('foo');
73-
$definition->setArguments([null, 'a' => 'a']);
73+
$definition->setArguments(['a' => 'a', null]);
7474

7575
$pass = new CheckArgumentsValidityPass(false);
7676
$pass->process($container);

Tests/Dumper/PhpDumperTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute;
4646
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum;
4747
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument;
48+
use Symfony\Component\DependencyInjection\Tests\Fixtures\NewInInitializer;
4849
use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory;
4950
use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator;
5051
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1;
@@ -1207,6 +1208,24 @@ public function testDumpHandlesObjectClassNames()
12071208
$this->assertInstanceOf(\stdClass::class, $container->get('bar'));
12081209
}
12091210

1211+
/**
1212+
* @requires PHP 8.1
1213+
*/
1214+
public function testNewInInitializer()
1215+
{
1216+
$container = new ContainerBuilder();
1217+
$container
1218+
->register('foo', NewInInitializer::class)
1219+
->setPublic(true)
1220+
->setAutowired(true)
1221+
->setArguments(['$bar' => 234]);
1222+
1223+
$container->compile();
1224+
1225+
$dumper = new PhpDumper($container);
1226+
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_new_in_initializer.php', $dumper->dump());
1227+
}
1228+
12101229
/**
12111230
* @requires PHP 8.1
12121231
*/

Tests/Fixtures/NewInInitializer.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
4+
5+
class NewInInitializer
6+
{
7+
public function __construct($foo = new \stdClass(), $bar = 123)
8+
{
9+
}
10+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
4+
use Symfony\Component\DependencyInjection\ContainerInterface;
5+
use Symfony\Component\DependencyInjection\Container;
6+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
7+
use Symfony\Component\DependencyInjection\Exception\LogicException;
8+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
9+
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
10+
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
11+
12+
/**
13+
* This class has been auto-generated
14+
* by the Symfony Dependency Injection Component.
15+
*
16+
* @final
17+
*/
18+
class ProjectServiceContainer extends Container
19+
{
20+
private $parameters = [];
21+
22+
public function __construct()
23+
{
24+
$this->services = $this->privates = [];
25+
$this->methodMap = [
26+
'foo' => 'getFooService',
27+
];
28+
29+
$this->aliases = [];
30+
}
31+
32+
public function compile(): void
33+
{
34+
throw new LogicException('You cannot compile a dumped container that was already compiled.');
35+
}
36+
37+
public function isCompiled(): bool
38+
{
39+
return true;
40+
}
41+
42+
public function getRemovedIds(): array
43+
{
44+
return [
45+
'Psr\\Container\\ContainerInterface' => true,
46+
'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
47+
];
48+
}
49+
50+
/**
51+
* Gets the public 'foo' shared autowired service.
52+
*
53+
* @return \Symfony\Component\DependencyInjection\Tests\Fixtures\NewInInitializer
54+
*/
55+
protected function getFooService()
56+
{
57+
return $this->services['foo'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\NewInInitializer(bar: 234);
58+
}
59+
}

0 commit comments

Comments
 (0)