8000 [DI] Add ServiceLocatorArgument to generate array-based locators opti… · symfony/symfony@a5b4efb · GitHub
[go: up one dir, main page]

Skip to content

Commit a5b4efb

Browse files
[DI] Add ServiceLocatorArgument to generate array-based locators optimized for OPcache shared memory
1 parent 0e9ded3 commit a5b4efb

36 files changed

+533
-198
lines changed

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\DependencyInjection\Alias;
1818
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1919
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
20+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
2021
use Symfony\Component\DependencyInjection\ContainerBuilder;
2122
use Symfony\Component\DependencyInjection\Definition;
2223
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
@@ -334,6 +335,8 @@ protected function describeContainerDefinition(Definition $definition, array $op
334335
$argumentsInformation[] = sprintf('Service(%s)', (string) $argument);
335336
} elseif ($argument instanceof IteratorArgument) {
336337
$argumentsInformation[] = sprintf('Iterator (%d element(s))', count($argument->getValues()));
338+
} elseif ($argument instanceof ServiceLocatorArgument) {
339+
$argumentsInformation[] = sprintf('Service locator (%d element(s))', count($argument->getValues()));
337340
} elseif ($argument instanceof Definition) {
338341
$argumentsInformation[] = 'Inlined Service';
339342
} else {

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\DependencyInjection\Alias;
1515
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1616
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
17+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1718
use Symfony\Component\DependencyInjection\ContainerBuilder;
1819
use Symfony\Component\DependencyInjection\Definition;
1920
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
@@ -387,8 +388,8 @@ private function getArgumentNodes(array $arguments, \DOMDocument $dom)
387388
if ($argument instanceof Reference) {
388389
$argumentXML->setAttribute('type', 'service');
389390
$argumentXML->setAttribute('id', (string) $argument);
390-
} elseif ($argument instanceof IteratorArgument) {
391-
$argumentXML->setAttribute('type', 'iterator');
391+
} elseif ($argument instanceof IteratorArgument || $argument instanceof ServiceLocatorArgument) {
392+
$argumentXML->setAttribute('type', $argument instanceof IteratorArgument ? 'iterator' : 'service_locator');
392393

393394
foreach ($this->getArgumentNodes($argument->getValues(), $dom) as $childArgumentXML) {
394395
$argumentXML->appendChild($childArgumentXML);

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php

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

1212
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
1313

14-
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1514
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15+
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
1616
use Symfony\Component\DependencyInjection\ContainerBuilder;
1717
use Symfony\Component\DependencyInjection\Reference;
1818

@@ -32,7 +32,7 @@ public function process(ContainerBuilder $container)
3232

3333
foreach ($definitions as $id => $definition) {
3434
if ($id && '.' !== $id[0] && (!$definition->isPublic() || $definition->isPrivate()) && !$definition->getErrors() && !$definition->isAbstract()) {
35-
$privateServices[$id] = new ServiceClosureArgument(new Reference($id, ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE));
35+
$privateServices[$id] = new Reference($id, ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE);
3636
}
3737
}
3838

@@ -44,13 +44,15 @@ public function process(ContainerBuilder $container)
4444
$alias = $aliases[$target];
4545
}
4646
if (isset($definitions[$target]) && !$definitions[$target]->getErrors() && !$definitions[$target]->isAbstract()) {
47-
$privateServices[$id] = new ServiceClosureArgument(new Reference($target, ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE));
47+
$privateServices[$id] = new Reference($target, ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE);
4848
}
4949
}
5050
}
5151

5252
if ($privateServices) {
53-
$definitions['test.private_services_locator']->replaceArgument(0, $privateServices);
53+
$id = (string) ServiceLocatorTagPass::register($container, $privateServices);
54+
$container->setDefinition('test.private_services_locator', $container->getDefinition($id))->setPublic(true);
55+
$container->removeDefinition($id);
5456
}
5557
}
5658
}

src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,9 @@
5858

5959
<service id="session_listener" class="Symfony\Component\HttpKernel\EventListener\SessionListener">
6060
<tag name="kernel.event_subscriber" />
61-
<argument type="service">
62-
<service class="Symfony\Component\DependencyInjection\ServiceLocator">
63-
<tag name="container.service_locator" />
64-
<argument type="collection">
65-
<argument key="session" type="service" id="session" on-invalid="ignore" />
66-
<argument key="initialized_session" type="service" id="session" on-invalid="ignore_uninitialized" />
67-
</argument>
68-
</service>
61+
<argument type="service_locator">
62+
<argument key="session" type="service" id="session" on-invalid="ignore" />
63+
<argument key="initialized_session" type="service" id="session" on-invalid="ignore_uninitialized" />
6964
</argument>
7065
</service>
7166

src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,8 @@
2424

2525
<service id="test.session.listener" class="Symfony\Component\HttpKernel\EventListener\TestSessionListener">
2626
<tag name="kernel.event_subscriber" />
27-
<argument type="service">
28-
<service class="Symfony\Component\DependencyInjection\ServiceLocator">
29-
<tag name="container.service_locator" />
30-
<argument type="collection">
31-
<argument key="session" type="service" id="session" on-invalid="ignore" />
32-
</argument>
33-
</service>
27+
<argument type="service_locator">
28+
<argument key="session" type="service" id="session" on-invalid="ignore" />
3429
</argument>
3530
</service>
3631

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"php": "^7.1.3",
2020
"ext-xml": "*",
2121
"symfony/cache": "~3.4|~4.0",
22-
"symfony/dependency-injection": "^4.1.1",
22+
"symfony/dependency-injection": "^4.2",
2323
"symfony/config": "~4.2",
2424
"symfony/event-dispatcher": "^4.1",
2525
"symfony/http-foundation": "^4.1",

src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,9 @@
2727
<service id="Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface" alias="security.token_storage" />
2828

2929
<service id="security.helper" class="Symfony\Component\Security\Core\Security">
30-
<argument type="service">
31-
<service class="Symfony\Component\DependencyInjection\ServiceLocator">
32-
<tag name="container.service_locator" />
33-
<argument type="collection">
34-
<argument key="security.token_storage" type="service" id="security.token_storage" />
35-
<argument key="security.authorization_checker" type="service" id="security.authorization_checker" />
36-
</argument>
37-
</service>
30+
<argument type="service_locator">
31+
<argument key="security.token_storage" type="service" id="security.token_storage" />
32+
<argument key="security.authorization_checker" type="service" id="security.authorization_checker" />
3833
</argument>
3934
</service>
4035
<service id="Symfony\Component\Security\Core\Security" alias="security.helper" />

src/Symfony/Bundle/SecurityBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"ext-xml": "*",
2121
"symfony/config": "^4.2",
2222
"symfony/security": "~4.2",
23-
"symfony/dependency-injection": "^3.4.3|^4.0.3",
23+
"symfony/dependency-injection": "^4.2",
2424
"symfony/http-kernel": "^4.1"
2525
},
2626
"require-dev": {

src/Symfony/Component/DependencyInjection/Argument/IteratorArgument.php

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,12 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Argument;
1313

14-
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
15-
use Symfony\Component\DependencyInjection\Reference;
16-
1714
/**
1815
* Represents a collection of values to lazily iterate over.
1916
*
2017
* @author Titouan Galopin <galopintitouan@gmail.com>
2118
*/
2219
class IteratorArgument implements ArgumentInterface
2320
{
24-
private $values;
25-
26-
/**
27-
* @param Reference[] $values
28-
*/
29-
public function __construct(array $values)
30-
{
31-
$this->setValues($values);
32-
}
33-
34-
/**
35-
* @return array The values to lazily iterate over
36-
*/
37-
public function getValues()
38-
{
39-
return $this->values;
40-
}
41-
42-
/**
43-
* @param Reference[] $values The service references to lazily iterate over
44-
*/
45-
public function setValues(array $values)
46-
{
47-
foreach ($values as $k => $v) {
48-
if (null !== $v && !$v instanceof Reference) {
49-
throw new InvalidArgumentException(sprintf('An IteratorArgument must hold only Reference instances, "%s" given.', is_object($v) ? get_class($v) : gettype($v)));
50-
}
51-
}
52-
53-
$this->values = $values;
54-
}
21+
use ReferenceSetArgumentTrait;
5522
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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\Argument;
13+
14+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
15+
use Symfony\Component\DependencyInjection\Reference;
16+
17+
/**
18+
* @author Titouan Galopin <galopintitouan@gmail.com>
19+
* @author Nicolas Grekas <p@tchwork.com>
20+
*/
21+
trait ReferenceSetArgumentTrait
22+
{
23+
private $values;
24+
25+
/**
26+
* @param Reference[] $values
27+
*/
28+
public function __construct(array $values)
29+
{
30+
$this->setValues($values);
31+
}
32+
33+
/**
34+
* @return array The values to lazily iterate over
35+
*/
36+
public function getValues()
37+
{
38+
return $this->values;
39+
}
40+
41+
/**
42+
* @param Reference[] $values The service references to lazily iterate over
43+
*/
44+
public function setValues(array $values)
45+
{
46+
foreach ($values as $k => $v) {
47+
if (null !== $v && !$v instanceof Reference) {
48+
throw new InvalidArgumentException(sprintf('A %s must hold only Reference instances, "%s" given.', __CLASS__, is_object($v) ? get_class($v) : gettype($v)));
49+
}
50+
}
51+
52+
$this->values = $values;
53+
}
54+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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\Argument;
13+
14+
use Symfony\Component\DependencyInjection\ServiceLocator as BaseServiceLocator;
15+
16+
/**
17+
* @author Nicolas Grekas <p@tchwork.com>
18+
*
19+
* @internal
20+
*/
21+
class ServiceLocator extends BaseServiceLocator
22+
{
23+
private $factory;
24+
private $serviceMap;
25+
26+
public function __construct(\Closure $factory, array $serviceMap)
27+
{
28+
$this->factory = $factory;
29+
$this->serviceMap = $serviceMap;
30+
parent::__construct($serviceMap);
31+
}
32+
33+
/**
34+
* {@inheritdoc}
35+
*/
36+
public function get($id)
37+
{
38+
return isset($this->serviceMap[$id]) ? ($this->factory)(...$this->serviceMap[$id]) : parent::get($id);
39+
}
40+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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\Argument;
13+
14+
/**
15+
* Represents a closure acting as a service locator.
16+
*
17+
* @author Nicolas Grekas <p@tchwork.com>
18+
*/
19+
class ServiceLocatorArgument implements ArgumentInterface
20+
{
21+
use ReferenceSetArgumentTrait;
22+
}

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
-----
66

77
* added `ServiceSubscriberTrait`
8+
* added `ServiceLocatorArgument` for creating optimized service-locators
89

910
4.1.0
1011
-----

src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
*/
2424
class DefinitionErrorExceptionPass extends AbstractRecursivePass
2525
{
26+
private $analyzingPass;
27+
28+
public function __construct(AnalyzeServiceReferencesPass $analyzingPass = null)
29+
{
30+
$this->analyzingPass = $analyzingPass;
31+
}
32+
2633
/**
2734
* {@inheritdoc}
2835
*/
@@ -33,6 +40,10 @@ protected function processValue($value, $isRoot = false)
3340
}
3441

3542
if ($isRoot && !$value->isPublic()) {
43+
if ($this->analyzingPass) {
44+
$this->analyzingPass->process($this->container);
45+
$this->analyzingPass = null;
46+
}
3647
$graph = $this->container->getCompiler()->getServiceReferenceGraph();
3748
$runtimeException = false;
3849
foreach ($graph->getNode($this->currentId)->getInEdges() as $edge) {

src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public function __construct()
8383
new RemoveAbstractDefinitionsPass(),
8484
new RemoveUnusedDefinitionsPass(),
8585
new InlineServiceDefinitionsPass(new AnalyzeServiceReferencesPass()),
86-
new DefinitionErrorExceptionPass(),
86+
new DefinitionErrorExceptionPass(new AnalyzeServiceReferencesPass()),
8787
new CheckExceptionOnInvalidReferenceBehaviorPass(),
8888
new ResolveHotPathPass(),
8989
));

src/Symfony/Component/DependencyInjection/Compiler/RegisterEnvVarProcessorsPass.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

14-
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1514
use Symfony\Component\DependencyInjection\ContainerBuilder;
1615
use Symfony\Component\DependencyInjection\EnvVarProcessor;
1716
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
1817
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1918
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
20-
use Symfony\Component\DependencyInjection\ServiceLocator;
2119
use Symfony\Component\DependencyInjection\Reference;
2220

2321
/**
@@ -41,7 +39,7 @@ public function process(ContainerBuilder $container)
4139
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EnvVarProcessorInterface::class));
4240
}
4341
foreach ($class::getProvidedTypes() as $prefix => $type) {
44-
$processors[$prefix] = new ServiceClosureArgument(new Reference($id));
42+
$processors[$prefix] = new Reference($id);
4543
$types[$prefix] = self::validateProvidedTypes($type, $class);
4644
}
4745
}
@@ -56,9 +54,8 @@ public function process(ContainerBuilder $container)
5654
}
5755

5856
if ($processors) {
59-
$container->register('container.env_var_processors_locator', ServiceLocator::class)
57+
$container->setAlias('container.env_var_processors_locator', (string) ServiceLocatorTagPass::register($container, $processors))
6058
->setPublic(true)
61-
->setArguments(array($processors))
6259
;
6360
}
6461
}

0 commit comments

Comments
 (0)
0