Skip to content

Commit de4aab2

Browse files
Merge branch '4.4' into 5.0
* 4.4: [DI] Fix CheckTypeDeclarationPass [Security/Http] don't require the session to be started when tracking its id [DI] fix preloading script generation
2 parents f9da646 + b4242fc commit de4aab2

File tree

9 files changed

+172
-17
lines changed

9 files changed

+172
-17
lines changed

Compiler/CheckTypeDeclarationsPass.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1818
use Symfony\Component\DependencyInjection\Container;
1919
use Symfony\Component\DependencyInjection\Definition;
20-
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
2120
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
2221
use Symfony\Component\DependencyInjection\Exception\InvalidParameterTypeException;
2322
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
@@ -207,7 +206,7 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar
207206
if ('' === preg_replace('/'.$envPlaceholderUniquePrefix.'_\w+_[a-f0-9]{32}/U', '', $value, -1, $c) && 1 === $c) {
208207
try {
209208
$value = $this->container->resolveEnvPlaceholders($value, true);
210-
} catch (EnvNotFoundException | RuntimeException $e) {
209+
} catch (\Exception $e) {
211210
// If an env placeholder cannot be resolved, we skip the validation.
212211
return;
213212
}
@@ -250,7 +249,11 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar
250249
return;
251250
}
252251

253-
if ('iterable' === $type && (\is_array($value) || is_subclass_of($class, \Traversable::class))) {
252+
if ('iterable' === $type && (\is_array($value) || 'array' === $class || is_subclass_of($class, \Traversable::class))) {
253+
return;
254+
}
255+
256+
if ($type === $class) {
254257
return;
255258
}
256259

Container.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\DependencyInjection;
1313

14+
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
15+
use Symfony\Component\DependencyInjection\Argument\ServiceLocator as ArgumentServiceLocator;
1416
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
1517
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1618
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
@@ -22,6 +24,10 @@
2224
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
2325
use Symfony\Contracts\Service\ResetInterface;
2426

27+
// Help opcache.preload discover always-needed symbols
28+
class_exists(RewindableGenerator::class);
29+
class_exists(ArgumentServiceLocator::class);
30+
2531
/**
2632
* Container is a dependency injection container.
2733
*

Dumper/PhpDumper.php

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class PhpDumper extends Dumper
8282
private $inlinedRequires = [];
8383
private $circularReferences = [];
8484
private $singleUsePrivateIds = [];
85+
private $preload = [];
8586
private $addThrow = false;
8687
private $addGetService = false;
8788
private $locatedIds = [];
@@ -143,6 +144,7 @@ public function dump(array $options = [])
143144
'hot_path_tag' => 'container.hot_path',
144145
'inline_factories_parameter' => 'container.dumper.inline_factories',
145146
'inline_class_loader_parameter' => 'container.dumper.inline_class_loader',
147+
'preload_classes' => [],
146148
'service_locator_tag' => 'container.service_locator',
147149
'build_time' => time(),
148150
], $options);
@@ -227,8 +229,12 @@ public function dump(array $options = [])
227229

228230
$proxyClasses = $this->inlineFactories ? $this->generateProxyClasses() : null;
229231

232+
if ($options['preload_classes']) {
233+
$this->preload = array_combine($options['preload_classes'], $options['preload_classes']);
234+
}
235+
230236
$code =
231-
$this->startClass($options['class'], $baseClass, $preload).
237+
$this->startClass($options['class'], $baseClass).
232238
$this->addServices($services).
233239
$this->addDeprecatedAliases().
234240
$this->addDefaultParametersMethod()
@@ -303,7 +309,7 @@ public function dump(array $options = [])
303309
$id = hash('crc32', $hash.$time);
304310
$this->asFiles = false;
305311

306-
if ($preload && null !== $autoloadFile = $this->getAutoloadFile()) {
312+
if ($this->preload && null !== $autoloadFile = $this->getAutoloadFile()) {
307313
$autoloadFile = substr($this->export($autoloadFile), 2, -1);
308314

309315
$code[$options['class'].'.preload.php'] = <<<EOF
@@ -321,8 +327,13 @@ public function dump(array $options = [])
321327
322328
EOF;
323329

324-
foreach ($preload as $class) {
325-
$code[$options['class'].'.preload.php'] .= sprintf("\$classes[] = '%s';\n", $class);
330+
foreach ($this->preload as $class) {
331+
if (!$class || false !== strpos($class, '$')) {
332+
continue;
333+
}
334+
if (!(class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) || (new \ReflectionClass($class))->isUserDefined()) {
335+
$code[$options['class'].'.preload.php'] .= sprintf("\$classes[] = '%s';\n", $class);
336+
}
326337
}
327338

328339
$code[$options['class'].'.preload.php'] .= <<<'EOF'
@@ -368,6 +379,7 @@ public function dump(array $options = [])
368379
$this->circularReferences = [];
369380
$this->locatedIds = [];
370381
$this->exportedVariables = [];
382+
$this->preload = [];
371383

372384
$unusedEnvs = [];
373385
foreach ($this->container->getEnvCounters() as $env => $use) {
@@ -546,8 +558,10 @@ private function addServiceInclude(string $cId, Definition $definition): string
546558
if ($this->inlineRequires && (!$this->isHotPath($definition) || $this->getProxyDumper()->isProxyCandidate($definition))) {
547559
$lineage = [];
548560
foreach ($this->inlinedDefinitions as $def) {
549-
if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
550-
$this->collectLineage($class, $lineage);
561+
if (!$def->isDeprecated()) {
562+
foreach ($this->getClasses($def) as $class) {
563+
$this->collectLineage($class, $lineage);
564+
}
551565
}
552566
}
553567

@@ -556,9 +570,10 @@ private function addServiceInclude(string $cId, Definition $definition): string
556570
&& ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior
557571
&& $this->container->has($id)
558572
&& $this->isTrivialInstance($def = $this->container->findDefinition($id))
559-
&& \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())
560573
) {
561-
$this->collectLineage($class, $lineage);
574+
foreach ($this->getClasses($def) as $class) {
575+
$this->collectLineage($class, $lineage);
576+
}
562577
}
563578
}
564579

@@ -808,6 +823,12 @@ protected function {$methodName}($lazyInitialization)
808823

809824
if ($definition->isDeprecated()) {
810825
$code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
826+
} else {
827+
foreach ($this->inlinedDefinitions as $def) {
828+
foreach ($this->getClasses($def) as $class) {
829+
$this->preload[$class] = $class;
830+
}
831+
}
811832
}
812833

813834
if ($this->getProxyDumper()->isProxyCandidate($definition)) {
@@ -964,7 +985,15 @@ private function addServices(array &$services = null): string
964985
$definitions = $this->container->getDefinitions();
965986
ksort($definitions);
966987
foreach ($definitions as $id => $definition) {
967-
$services[$id] = $definition->isSynthetic() ? null : $this->addService($id, $definition);
988+
if (!$definition->isSynthetic()) {
989+
$services[$id] = $this->addService($id, $definition);
990+
} else {
991+
$services[$id] = null;
992+
993+
foreach ($this->getClasses($definition) as $class) {
994+
$this->preload[$class] = $class;
995+
}
996+
}
968997
}
969998

970999
foreach ($definitions as $id => $definition) {
@@ -1065,7 +1094,7 @@ private function addNewInstance(Definition $definition, string $return = '', str
10651094
return $return.sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)).$tail;
10661095
}
10671096

1068-
private function startClass(string $class, string $baseClass, ?array &$preload): string
1097+
private function startClass(string $class, string $baseClass): string
10691098
{
10701099
$namespaceLine = !$this->asFiles && $this->namespace ? "\nnamespace {$this->namespace};\n" : '';
10711100

@@ -1128,7 +1157,7 @@ public function __construct()
11281157
$code .= $this->addMethodMap();
11291158
$code .= $this->asFiles && !$this->inlineFactories ? $this->addFileMap() : '';
11301159
$code .= $this->addAliases();
1131-
$code .= $this->addInlineRequires($preload);
1160+
$code .= $this->addInlineRequires();
11321161
$code .= <<<EOF
11331162
}
11341163
@@ -1328,7 +1357,7 @@ protected function {$methodNameAlias}()
13281357
return $code;
13291358
}
13301359

1331-
private function addInlineRequires(?array &$preload): string
1360+
private function addInlineRequires(): string
13321361
{
13331362
if (!$this->hotPathTag || !$this->inlineRequires) {
13341363
return '';
@@ -1346,8 +1375,7 @@ private function addInlineRequires(?array &$preload): string
13461375
$inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]);
13471376

13481377
foreach ($inlinedDefinitions as $def) {
1349-
if (\is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
1350-
$preload[$class] = $class;
1378+
foreach ($this->getClasses($def) as $class) {
13511379
$this->collectLineage($class, $lineage);
13521380
}
13531381
}
@@ -2067,4 +2095,29 @@ private function getAutoloadFile(): ?string
20672095

20682096
return null;
20692097
}
2098+
2099+
private function getClasses(Definition $definition): array
2100+
{
2101+
$classes = [];
2102+
2103+
while ($definition instanceof Definition) {
2104+
$classes[] = trim($definition->getClass(), '\\');
2105+
$factory = $definition->getFactory();
2106+
2107+
if (!\is_array($factory)) {
2108+
$factory = [$factory];
2109+
}
2110+
2111+
if (\is_string($factory[0])) {
2112+
if (false !== $i = strrpos($factory[0], '::')) {
2113+
$factory[0] = substr($factory[0], 0, $i);
2114+
}
2115+
$classes[] = trim($factory[0], '\\');
2116+
}
2117+
2118+
$definition = $factory[0];
2119+
}
2120+
2121+
return array_filter($classes);
2122+
}
20702123
}

Tests/Compiler/CheckTypeDeclarationsPassTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,42 @@ public function testProcessFactoryDoesNotLoadCodeByDefault()
536536
$this->addToAssertionCount(1);
537537
}
538538

539+
public function testProcessFactoryForTypeSameAsClass()
540+
{
541+
$container = new ContainerBuilder();
542+
543+
$container->register('foo', Foo::class);
544+
$container->register('bar', 'callable')
545+
->setFactory([
546+
new Reference('foo'),
547+
'createCallable',
548+
]);
549+
$container->register('bar_call', BarMethodCall::class)
550+
->addMethodCall('setCallable', [new Reference('bar')]);
551+
552+
(new CheckTypeDeclarationsPass(true))->process($container);
553+
554+
$this->addToAssertionCount(1);
555+
}
556+
557+
public function testProcessFactoryForIterableTypeAndArrayClass()
558+
{
559+
$container = new ContainerBuilder();
560+
561+
$container->register('foo', Foo::class);
562+
$container->register('bar', 'array')
563+
->setFactory([
564+
new Reference('foo'),
565+
'createArray',
566+
]);
567+
$container->register('bar_call', BarMethodCall::class)
568+
->addMethodCall('setIterable', [new Reference('bar')]);
569+
570+
(new CheckTypeDeclarationsPass(true))->process($container);
571+
572+
$this->addToAssertionCount(1);
573+
}
574+
539575
public function testProcessPassingBuiltinTypeDoesNotLoadCodeByDefault()
540576
{
541577
$container = new ContainerBuilder();

Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,14 @@ public static function createBarArguments(\stdClass $stdClass, \stdClass $stdCla
1313
{
1414
return new Bar($stdClass);
1515
}
16+
17+
public static function createCallable(): callable
18+
{
19+
return function() {};
20+
}
21+
22+
public static function createArray(): array
23+
{
24+
return [];
25+
}
1626
}

Tests/Fixtures/php/services9_as_files.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,24 @@ class ProjectServiceContainer extends Container
529529
}
530530
}
531531

532+
[ProjectServiceContainer.preload.php] => <?php
533+
%A
534+
535+
$classes = [];
536+
$classes[] = 'Bar\FooClass';
537+
$classes[] = 'Baz';
538+
$classes[] = 'ConfClass';
539+
$classes[] = 'Bar';
540+
$classes[] = 'BazClass';
541+
$classes[] = 'Foo';
542+
$classes[] = 'LazyContext';
543+
$classes[] = 'FooBarBaz';
544+
$classes[] = 'FactoryClass';
545+
$classes[] = 'Request';
546+
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
547+
548+
%A
549+
532550
[ProjectServiceContainer.php] => <?php
533551

534552
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.

Tests/Fixtures/php/services9_inlined_factories.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,16 @@ class ProjectServiceContainer extends Container
534534

535535
$classes = [];
536536
$classes[] = 'Bar\FooClass';
537+
$classes[] = 'Baz';
538+
$classes[] = 'ConfClass';
539+
$classes[] = 'Bar';
540+
$classes[] = 'BazClass';
541+
$classes[] = 'Foo';
542+
$classes[] = 'LazyContext';
543+
$classes[] = 'FooBarBaz';
544+
$classes[] = 'FactoryClass';
545+
$classes[] = 'Request';
546+
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
537547

538548
%A
539549

Tests/Fixtures/php/services9_lazy_inlined_factories.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ class FooClass_%s extends \Bar\FooClass implements \ProxyManager\Proxy\VirtualPr
163163
%A
164164
}
165165

166+
[ProjectServiceContainer.preload.php] => <?php
167+
%A
168+
169+
$classes = [];
170+
$classes[] = 'Bar\FooClass';
171+
$classes[] = 'Bar\FooLazyClass';
172+
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
173+
174+
%A
175+
166176
[ProjectServiceContainer.php] => <?php
167177

168178
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.

Tests/Fixtures/php/services_non_shared_lazy_as_files.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ class ProjectServiceContainer extends Container
8383
}
8484
}
8585

86+
[ProjectServiceContainer.preload.php] => <?php
87+
%A
88+
89+
$classes = [];
90+
$classes[] = 'Bar\FooLazyClass';
91+
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
92+
93+
%A
94+
8695
[ProjectServiceContainer.php] => <?php
8796

8897
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.

0 commit comments

Comments
 (0)