Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7c31147

Browse filesBrowse files
Merge branch '6.4' into 7.2
* 6.4: [WebProfilerBundle] Fix tests [Cache] Tests for Redis Replication with cache [BrowserKit] Fix submitting forms with empty file fields [WebProfilerBundle] Fix interception for non conventional redirects [DependencyInjection] Do not preload functions [DependencyInjection] Fix cloned lazy services not sharing their dependencies when dumped with PhpDumper [HttpClient] Fix activity tracking leading to negative timeout errors [Security] Return null instead of empty username to fix deprecation notice [DependencyInjection] Fix env default processor with scalar node
2 parents 1d321c4 + e066329 commit 7c31147
Copy full SHA for 7c31147
Expand file treeCollapse file tree

14 files changed

+258
-44
lines changed

‎Compiler/InlineServiceDefinitionsPass.php

Copy file name to clipboardExpand all lines: Compiler/InlineServiceDefinitionsPass.php
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ private function isInlineableDefinition(string $id, Definition $definition): boo
223223
return false;
224224
}
225225

226-
return $this->container->getDefinition($srcId)->isShared();
226+
$srcDefinition = $this->container->getDefinition($srcId);
227+
228+
return $srcDefinition->isShared() && !$srcDefinition->isLazy();
227229
}
228230
}

‎Compiler/ValidateEnvPlaceholdersPass.php

Copy file name to clipboardExpand all lines: Compiler/ValidateEnvPlaceholdersPass.php
+48-11Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,8 @@ public function process(ContainerBuilder $container): void
4646
$defaultBag = new ParameterBag($resolvingBag->all());
4747
$envTypes = $resolvingBag->getProvidedTypes();
4848
foreach ($resolvingBag->getEnvPlaceholders() + $resolvingBag->getUnusedEnvPlaceholders() as $env => $placeholders) {
49-
$values = [];
50-
if (false === $i = strpos($env, ':')) {
51-
$default = $defaultBag->has("env($env)") ? $defaultBag->get("env($env)") : self::TYPE_FIXTURES['string'];
52-
$defaultType = null !== $default ? get_debug_type($default) : 'string';
53-
$values[$defaultType] = $default;
54-
} else {
55-
$prefix = substr($env, 0, $i);
56-
foreach ($envTypes[$prefix] ?? ['string'] as $type) {
57-
$values[$type] = self::TYPE_FIXTURES[$type] ?? null;
58-
}
59-
}
49+
$values = $this->getPlaceholderValues($env, $defaultBag, $envTypes);
50+
6051
foreach ($placeholders as $placeholder) {
6152
BaseNode::setPlaceholder($placeholder, $values);
6253
}
@@ -97,4 +88,50 @@ public function getExtensionConfig(): array
9788
$this->extensionConfig = [];
9889
}
9990
}
91+
92+
/**
93+
* @param array<string, list<string>> $envTypes
94+
*
95+
* @return array<string, mixed>
96+
*/
97+
private function getPlaceholderValues(string $env, ParameterBag $defaultBag, array $envTypes): array
98+
{
99+
if (false === $i = strpos($env, ':')) {
100+
[$default, $defaultType] = $this->getParameterDefaultAndDefaultType("env($env)", $defaultBag);
101+
102+
return [$defaultType => $default];
103+
}
104+
105+
$prefix = substr($env, 0, $i);
106+
if ('default' === $prefix) {
107+
$parts = explode(':', $env);
108+
array_shift($parts); // Remove 'default' prefix
109+
$parameter = array_shift($parts); // Retrieve and remove parameter
110+
111+
[$defaultParameter, $defaultParameterType] = $this->getParameterDefaultAndDefaultType($parameter, $defaultBag);
112+
113+
return [
114+
$defaultParameterType => $defaultParameter,
115+
...$this->getPlaceholderValues(implode(':', $parts), $defaultBag, $envTypes),
116+
];
117+
}
118+
119+
$values = [];
120+
foreach ($envTypes[$prefix] ?? ['string'] as $type) {
121+
$values[$type] = self::TYPE_FIXTURES[$type] ?? null;
122+
}
123+
124+
return $values;
125+
}
126+
127+
/**
128+
* @return array{0: string, 1: string}
129+
*/
130+
private function getParameterDefaultAndDefaultType(string $name, ParameterBag $defaultBag): array
131+
{
132+
$default = $defaultBag->has($name) ? $defaultBag->get($name) : self::TYPE_FIXTURES['string'];
133+
$defaultType = null !== $default ? get_debug_type($default) : 'string';
134+
135+
return [$default, $defaultType];
136+
}
100137
}

‎Dumper/PhpDumper.php

Copy file name to clipboardExpand all lines: Dumper/PhpDumper.php
+18-16Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ class %s extends {$options['class']}
338338
EOF;
339339

340340
foreach ($this->preload as $class) {
341-
if (!$class || str_contains($class, '$') || \in_array($class, ['int', 'float', 'string', 'bool', 'resource', 'object', 'array', 'null', 'callable', 'iterable', 'mixed', 'void'], true)) {
341+
if (!$class || str_contains($class, '$') || \in_array($class, ['int', 'float', 'string', 'bool', 'resource', 'object', 'array', 'null', 'callable', 'iterable', 'mixed', 'void', 'never'], true)) {
342342
continue;
343343
}
344344
if (!(class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) || (new \ReflectionClass($class))->isUserDefined()) {
@@ -831,8 +831,7 @@ private function addService(string $id, Definition $definition): array
831831
if ($class = $definition->getClass()) {
832832
$class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class);
833833
$return[] = \sprintf(str_starts_with($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\'));
834-
} elseif ($definition->getFactory()) {
835-
$factory = $definition->getFactory();
834+
} elseif ($factory = $definition->getFactory()) {
836835
if (\is_string($factory) && !str_starts_with($factory, '@=')) {
837836
$return[] = \sprintf('@return object An instance returned by %s()', $factory);
838837
} elseif (\is_array($factory) && (\is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) {
@@ -1152,9 +1151,7 @@ private function addNewInstance(Definition $definition, string $return = '', ?st
11521151
$arguments[] = (\is_string($i) ? $i.': ' : '').$this->dumpValue($value);
11531152
}
11541153

1155-
if (null !== $definition->getFactory()) {
1156-
$callable = $definition->getFactory();
1157-
1154+
if ($callable = $definition->getFactory()) {
11581155
if ('current' === $callable && [0] === array_keys($definition->getArguments()) && \is_array($value) && [0] === array_keys($value)) {
11591156
return $return.$this->dumpValue($value[0]).$tail;
11601157
}
@@ -2197,6 +2194,12 @@ private function isSingleUsePrivateNode(ServiceReferenceGraphNode $node): bool
21972194
if ($edge->isLazy() || !$value instanceof Definition || !$value->isShared()) {
21982195
return false;
21992196
}
2197+
2198+
// When the source node is a proxy or ghost, it will construct its references only when the node itself is initialized.
2199+
// Since the node can be cloned before being fully initialized, we do not know how often its references are used.
2200+
if ($this->getProxyDumper()->isProxyCandidate($value)) {
2201+
return false;
2202+
}
22002203
$ids[$edge->getSourceNode()->getId()] = true;
22012204
}
22022205

@@ -2305,7 +2308,6 @@ private function getAutoloadFile(): ?string
23052308
private function getClasses(Definition $definition, string $id): array
23062309
{
23072310
$classes = [];
2308-
$resolve = $this->container->getParameterBag()->resolveValue(...);
23092311

23102312
while ($definition instanceof Definition) {
23112313
foreach ($definition->getTag($this->preloadTags[0]) as $tag) {
@@ -2317,24 +2319,24 @@ private function getClasses(Definition $definition, string $id): array
23172319
}
23182320

23192321
if ($class = $definition->getClass()) {
2320-
$classes[] = trim($resolve($class), '\\');
2322+
$classes[] = trim($class, '\\');
23212323
}
23222324
$factory = $definition->getFactory();
23232325

2326+
if (\is_string($factory) && !str_starts_with($factory, '@=') && str_contains($factory, '::')) {
2327+
$factory = explode('::', $factory);
2328+
}
2329+
23242330
if (!\is_array($factory)) {
2325-
$factory = [$factory];
2331+
$definition = $factory;
2332+
continue;
23262333
}
23272334

2328-
if (\is_string($factory[0])) {
2329-
$factory[0] = $resolve($factory[0]);
2335+
$definition = $factory[0] ?? null;
23302336

2331-
if (false !== $i = strrpos($factory[0], '::')) {
2332-
$factory[0] = substr($factory[0], 0, $i);
2333-
}
2337+
if (\is_string($definition)) {
23342338
$classes[] = trim($factory[0], '\\');
23352339
}
2336-
2337-
$definition = $factory[0];
23382340
}
23392341

23402342
return $classes;

‎Tests/Compiler/ValidateEnvPlaceholdersPassTest.php

Copy file name to clipboardExpand all lines: Tests/Compiler/ValidateEnvPlaceholdersPassTest.php
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,36 @@ public function testDefaultEnvWithoutPrefixIsValidatedInConfig()
7373
$this->doProcess($container);
7474
}
7575

76+
public function testDefaultProcessorWithScalarNode()
77+
{
78+
$container = new ContainerBuilder();
79+
$container->setParameter('parameter_int', 12134);
80+
$container->setParameter('env(FLOATISH)', 4.2);
81+
$container->registerExtension($ext = new EnvExtension());
82+
$container->prependExtensionConfig('env_extension', $expected = [
83+
'scalar_node' => '%env(default:parameter_int:FLOATISH)%',
84+
]);
85+
86+
$this->doProcess($container);
87+
$this->assertSame($expected, $container->resolveEnvPlaceholders($ext->getConfig()));
88+
}
89+
90+
public function testDefaultProcessorAndAnotherProcessorWithScalarNode()
91+
{
92+
$this->expectException(InvalidTypeException::class);
93+
$this->expectExceptionMessageMatches('/^Invalid type for path "env_extension\.scalar_node"\. Expected one of "bool", "int", "float", "string", but got one of "int", "array"\.$/');
94+
95+
$container = new ContainerBuilder();
96+
$container->setParameter('parameter_int', 12134);
97+
$container->setParameter('env(JSON)', '{ "foo": "bar" }');
98+
$container->registerExtension($ext = new EnvExtension());
99+
$container->prependExtensionConfig('env_extension', [
100+
'scalar_node' => '%env(default:parameter_int:json:JSON)%',
101+
]);
102+
103+
$this->doProcess($container);
104+
}
105+
76106
public function testEnvsAreValidatedInConfigWithInvalidPlaceholder()
77107
{
78108
$this->expectException(InvalidTypeException::class);

‎Tests/Dumper/PhpDumperTest.php

Copy file name to clipboardExpand all lines: Tests/Dumper/PhpDumperTest.php
+55Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
use Symfony\Component\DependencyInjection\Tests\Compiler\SingleMethodInterface;
5757
use Symfony\Component\DependencyInjection\Tests\Compiler\Wither;
5858
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
59+
use Symfony\Component\DependencyInjection\Tests\Fixtures\DependencyContainer;
60+
use Symfony\Component\DependencyInjection\Tests\Fixtures\DependencyContainerInterface;
5961
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute;
6062
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum;
6163
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument;
@@ -1668,6 +1670,59 @@ public function testWitherWithStaticReturnType()
16681670
$this->assertInstanceOf(Foo::class, $wither->foo);
16691671
}
16701672

1673+
public function testCloningLazyGhostWithDependency()
1674+
{
1675+
$container = new ContainerBuilder();
1676+
$container->register('dependency', \stdClass::class);
1677+
$container->register(DependencyContainer::class)
1678+
->addArgument(new Reference('dependency'))
1679+
->setLazy(true)
1680+
->setPublic(true);
1681+
1682+
$container->compile();
1683+
$dumper = new PhpDumper($container);
1684+
$dump = $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Service_CloningLazyGhostWithDependency']);
1685+
eval('?>'.$dump);
1686+
1687+
$container = new \Symfony_DI_PhpDumper_Service_CloningLazyGhostWithDependency();
1688+
1689+
$bar = $container->get(DependencyContainer::class);
1690+
$this->assertInstanceOf(DependencyContainer::class, $bar);
1691+
1692+
$first_clone = clone $bar;
1693+
$second_clone = clone $bar;
1694+
1695+
$this->assertSame($first_clone->dependency, $second_clone->dependency);
1696+
}
1697+
1698+
public function testCloningProxyWithDependency()
1699+
{
1700+
$container = new ContainerBuilder();
1701+
$container->register('dependency', \stdClass::class);
1702+
$container->register(DependencyContainer::class)
1703+
->addArgument(new Reference('dependency'))
1704+
->setLazy(true)
1705+
->addTag('proxy', [
1706+
'interface' => DependencyContainerInterface::class,
1707+
])
1708+
->setPublic(true);
1709+
1710+
$container->compile();
1711+
$dumper = new PhpDumper($container);
1712+
$dump = $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Service_CloningProxyWithDependency']);
1713+
eval('?>'.$dump);
1714+
1715+
$container = new \Symfony_DI_PhpDumper_Service_CloningProxyWithDependency();
1716+
1717+
$bar = $container->get(DependencyContainer::class);
1718+
$this->assertInstanceOf(DependencyContainerInterface::class, $bar);
1719+
1720+
$first_clone = clone $bar;
1721+
$second_clone = clone $bar;
1722+
1723+
$this->assertSame($first_clone->getDependency(), $second_clone->getDependency());
1724+
}
1725+
16711726
public function testCurrentFactoryInlining()
16721727
{
16731728
$container = new ContainerBuilder();
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
13+
14+
class DependencyContainer implements DependencyContainerInterface
15+
{
16+
public function __construct(
17+
public mixed $dependency,
18+
) {
19+
}
20+
21+
public function getDependency(): mixed
22+
{
23+
return $this->dependency;
24+
}
25+
}
+17Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
13+
14+
interface DependencyContainerInterface
15+
{
16+
public function getDependency(): mixed;
17+
}

‎Tests/Fixtures/config/child.expected.yml

Copy file name to clipboardExpand all lines: Tests/Fixtures/config/child.expected.yml
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ services:
1111
- container.decorator: { id: bar, inner: b }
1212
file: file.php
1313
lazy: true
14-
arguments: [!service { class: Class1 }]
14+
arguments: ['@b']
15+
b:
16+
class: Class1
1517
bar:
1618
alias: foo
1719
public: true

‎Tests/Fixtures/config/from_callable.expected.yml

Copy file name to clipboardExpand all lines: Tests/Fixtures/config/from_callable.expected.yml
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@ services:
88
class: stdClass
99
public: true
1010
lazy: true
11-
arguments: [[!service { class: stdClass }, do]]
11+
arguments: [['@bar', do]]
1212
factory: [Closure, fromCallable]
13+
bar:
14+
class: stdClass

‎Tests/Fixtures/php/closure_proxy.php

Copy file name to clipboardExpand all lines: Tests/Fixtures/php/closure_proxy.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,6 @@ protected function createProxy($class, \Closure $factory)
5555
*/
5656
protected static function getClosureProxyService($container, $lazyLoad = true)
5757
{
58-
return $container->services['closure_proxy'] = new class(fn () => new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo()) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure implements \Symfony\Component\DependencyInjection\Tests\Compiler\SingleMethodInterface { public function theMethod() { return $this->service->cloneFoo(...\func_get_args()); } };
58+
return $container->services['closure_proxy'] = new class(fn () => ($container->privates['foo'] ??= new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo())) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure implements \Symfony\Component\DependencyInjection\Tests\Compiler\SingleMethodInterface { public function theMethod() { return $this->service->cloneFoo(...\func_get_args()); } };
5959
}
6060
}

‎Tests/Fixtures/php/lazy_closure.php

Copy file name to clipboardExpand all lines: Tests/Fixtures/php/lazy_closure.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ protected function createProxy($class, \Closure $factory)
5757
*/
5858
protected static function getClosure1Service($container, $lazyLoad = true)
5959
{
60-
return $container->services['closure1'] = (new class(fn () => new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo()) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure { public function cloneFoo(?\stdClass $bar = null): \Symfony\Component\DependencyInjection\Tests\Compiler\Foo { return $this->service->cloneFoo(...\func_get_args()); } })->cloneFoo(...);
60+
return $container->services['closure1'] = (new class(fn () => ($container->privates['foo'] ??= new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo())) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure { public function cloneFoo(?\stdClass $bar = null): \Symfony\Component\DependencyInjection\Tests\Compiler\Foo { return $this->service->cloneFoo(...\func_get_args()); } })->cloneFoo(...);
6161
}
6262

6363
/**
@@ -67,6 +67,6 @@ protected static function getClosure1Service($container, $lazyLoad = true)
6767
*/
6868
protected static function getClosure2Service($container, $lazyLoad = true)
6969
{
70-
return $container->services['closure2'] = (new class(fn () => new \Symfony\Component\DependencyInjection\Tests\Compiler\FooVoid()) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure { public function __invoke(string $name): void { $this->service->__invoke(...\func_get_args()); } })->__invoke(...);
70+
return $container->services['closure2'] = (new class(fn () => ($container->privates['foo_void'] ??= new \Symfony\Component\DependencyInjection\Tests\Compiler\FooVoid())) extends \Symfony\Component\DependencyInjection\Argument\LazyClosure { public function __invoke(string $name): void { $this->service->__invoke(...\func_get_args()); } })->__invoke(...);
7171
}
7272
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.