Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8551ec7

Browse files
feature #25707 [DI] ServiceProviderInterface, implementation for ServiceLocator (kejwmen)
This PR was merged into the 4.3-dev branch. Discussion ---------- [DI] ServiceProviderInterface, implementation for ServiceLocator | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #25686 | License | MIT | Doc PR | applicable here? Implements #25686 Not sure if it needs any additional documentation, @nicolas-grekas? Commits ------- aaf5422 [DI][Contracts] add and implement ServiceProviderInterface
2 parents 708639f + aaf5422 commit 8551ec7

14 files changed

+112
-9
lines changed

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ class ServiceLocator extends BaseServiceLocator
2222
{
2323
private $factory;
2424
private $serviceMap;
25+
private $serviceTypes;
2526

26-
public function __construct(\Closure $factory, array $serviceMap)
27+
public function __construct(\Closure $factory, array $serviceMap, array $serviceTypes = null)
2728
{
2829
$this->factory = $factory;
2930
$this->serviceMap = $serviceMap;
31+
$this->serviceTypes = $serviceTypes;
3032
parent::__construct($serviceMap);
3133
}
3234

@@ -37,4 +39,12 @@ public function get($id)
3739
{
3840
return isset($this->serviceMap[$id]) ? ($this->factory)(...$this->serviceMap[$id]) : parent::get($id);
3941
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public function getProvidedServices(): array
47+
{
48+
return $this->serviceTypes ?? $this->serviceTypes = array_map(function () { return '?'; }, $this->serviceMap);
49+
}
4050
}

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ CHANGELOG
1313
* added `ReverseContainer`: a container that turns services back to their ids
1414
* added ability to define an index for a tagged collection
1515
* added ability to define an index for services in an injected service locator argument
16+
* made `ServiceLocator` implement `ServiceProviderInterface`
1617
* deprecated support for non-string default env() parameters
1718

1819
4.2.0

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Psr\Container\ContainerInterface;
1515
use Symfony\Component\DependencyInjection\Definition;
1616
use Symfony\Component\DependencyInjection\Reference;
17+
use Symfony\Contracts\Service\ServiceProviderInterface;
1718

1819
/**
1920
* Compiler pass to inject their service locator to service subscribers.
@@ -26,7 +27,7 @@ class ResolveServiceSubscribersPass extends AbstractRecursivePass
2627

2728
protected function processValue($value, $isRoot = false)
2829
{
29-
if ($value instanceof Reference && $this->serviceLocator && ContainerInterface::class === (string) $value) {
30+
if ($value instanceof Reference && $this->serviceLocator && \in_array((string) $value, [ContainerInterface::class, ServiceProviderInterface::class], true)) {
3031
return new Reference($this->serviceLocator);
3132
}
3233

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,13 +1234,14 @@ private function doResolveServices($value, array &$inlineServices = [], $isConst
12341234
return $count;
12351235
});
12361236
} elseif ($value instanceof ServiceLocatorArgument) {
1237-
$refs = [];
1237+
$refs = $types = [];
12381238
foreach ($value->getValues() as $k => $v) {
12391239
if ($v) {
12401240
$refs[$k] = [$v];
1241+
$types[$k] = $v instanceof TypedReference ? $v->getType() : '?';
12411242
}
12421243
}
1243-
$value = new ServiceLocator(\Closure::fromCallable([$this, 'resolveServices']), $refs);
1244+
$value = new ServiceLocator(\Closure::fromCallable([$this, 'resolveServices']), $refs, $types);
12441245
} elseif ($value instanceof Reference) {
12451246
$value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices, $isConstructorArgument);
12461247
} elseif ($value instanceof Definition) {

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,7 @@ private function dumpValue($value, bool $interpolate = true): string
15461546

15471547
if ($value instanceof ServiceLocatorArgument) {
15481548
$serviceMap = '';
1549+
$serviceTypes = '';
15491550
foreach ($value->getValues() as $k => $v) {
15501551
if (!$v) {
15511552
continue;
@@ -1559,11 +1560,12 @@ private function dumpValue($value, bool $interpolate = true): string
15591560
$this->export(ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $v->getInvalidBehavior() && !\is_string($load) ? $this->generateMethodName($id).($load ? '.php' : '') : null),
15601561
$this->export($load)
15611562
);
1563+
$serviceTypes .= sprintf("\n %s => %s,", $this->export($k), $this->export($v instanceof TypedReference ? $v->getType() : '?'));
15621564
$this->locatedIds[$id] = true;
15631565
}
15641566
$this->addGetService = true;
15651567

1566-
return sprintf('new \%s($this->getService, [%s%s])', ServiceLocator::class, $serviceMap, $serviceMap ? "\n " : '');
1568+
return sprintf('new \%s($this->getService, [%s%s], [%s%s])', ServiceLocator::class, $serviceMap, $serviceMap ? "\n " : '', $serviceTypes, $serviceTypes ? "\n " : '');
15671569
}
15681570
} finally {
15691571
list($this->definitionVariables, $this->referenceVariables) = $scope;

src/Symfony/Component/DependencyInjection/ServiceLocator.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@
1212
namespace Symfony\Component\DependencyInjection;
1313

1414
use Psr\Container\ContainerExceptionInterface;
15-
use Psr\Container\ContainerInterface as PsrContainerInterface;
1615
use Psr\Container\NotFoundExceptionInterface;
1716
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
1817
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
1918
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
2019
use Symfony\Contracts\Service\ServiceLocatorTrait;
20+
use Symfony\Contracts\Service\ServiceProviderInterface;
2121
use Symfony\Contracts\Service\ServiceSubscriberInterface;
2222

2323
/**
2424
* @author Robin Chalas <[email protected]>
2525
* @author Nicolas Grekas <[email protected]>
2626
*/
27-
class ServiceLocator implements PsrContainerInterface
27+
class ServiceLocator implements ServiceProviderInterface
2828
{
2929
use ServiceLocatorTrait {
3030
get as private doGet;

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ protected function getContainer_EnvVarProcessorsLocatorService()
7272
{
7373
return $this->services['container.env_var_processors_locator'] = new \Symfony\Component\DependencyInjection\Argument\ServiceLocator($this->getService, [
7474
'rot13' => ['services', 'Symfony\\Component\\DependencyInjection\\Tests\\Dumper\\Rot13EnvVarProcessor', 'getRot13EnvVarProcessorService', false],
75+
], [
76+
'rot13' => '?',
7577
]);
7678
}
7779

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_service_locator_argument.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ protected function getBarService()
7272
'foo3' => [false, 'foo3', 'getFoo3Service', false],
7373
'foo4' => ['privates', 'foo4', NULL, 'BOOM'],
7474
'foo5' => ['services', 'foo5', NULL, false],
75+
], [
76+
'foo1' => '?',
77+
'foo2' => '?',
78+
'foo3' => '?',
79+
'foo4' => '?',
80+
'foo5' => '?',
7581
]);
7682

7783
return $instance;

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ protected function getFooServiceService()
7575
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => ['services', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', 'getTestServiceSubscriberService', false],
7676
'bar' => ['services', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', 'getTestServiceSubscriberService', false],
7777
'baz' => ['privates', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'getCustomDefinitionService', false],
78+
], [
79+
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition',
80+
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber',
81+
'bar' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition',
82+
'baz' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition',
7883
]))->withContext('foo_service', $this));
7984
}
8085

src/Symfony/Component/DependencyInjection/Tests/ServiceLocatorTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,21 @@ public function testInvoke()
8686
$this->assertSame('baz', $locator('bar'));
8787
$this->assertNull($locator('dummy'), '->__invoke() should return null on invalid service');
8888
}
89+
90+
public function testProvidesServicesInformation()
91+
{
92+
$locator = new ServiceLocator([
93+
'foo' => function () { return 'bar'; },
94+
'bar' => function (): string { return 'baz'; },
95+
'baz' => function (): ?string { return 'zaz'; },
96+
]);
97+
98+
$this->assertSame($locator->getProvidedServices(), [
99+
'foo' => '?',
100+
'bar' => 'string',
101+
'baz' => '?string',
102+
]);
103+
}
89104
}
90105

91106
class SomeServiceSubscriber implements ServiceSubscriberInterface

src/Symfony/Component/DependencyInjection/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"require": {
1919
"php": "^7.1.3",
2020
"psr/container": "^1.0",
21-
"symfony/contracts": "^1.0"
21+
"symfony/contracts": "^1.1"
2222
},
2323
"require-dev": {
2424
"symfony/yaml": "~3.4|~4.0",

src/Symfony/Contracts/CHANGELOG.md

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

77
* added `HttpClient` namespace with contracts for implementing flexible HTTP clients
88
* added `EventDispatcher\EventDispatcherInterface`
9+
* added `ServiceProviderInterface`
910

1011
1.0.0
1112
-----

src/Symfony/Contracts/Service/ServiceLocatorTrait.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use Psr\Container\NotFoundExceptionInterface;
1616

1717
/**
18-
* A trait to help implement PSR-11 service locators.
18+
* A trait to help implement ServiceProviderInterface.
1919
*
2020
* @author Robin Chalas <[email protected]>
2121
* @author Nicolas Grekas <[email protected]>
@@ -24,6 +24,7 @@ trait ServiceLocatorTrait
2424
{
2525
private $factories;
2626
private $loading = [];
27+
private $providedTypes;
2728

2829
/**
2930
* @param callable[] $factories
@@ -66,6 +67,28 @@ public function get($id)
6667
}
6768
}
6869

70+
/**
71+
* {@inheritdoc}
72+
*/
73+
public function getProvidedServices(): array
74+
{
75+
if (null === $this->providedTypes) {
76+
$this->providedTypes = [];
77+
78+
foreach ($this->factories as $name => $factory) {
79+
if (!\is_callable($factory)) {
80+
$this->providedTypes[$name] = '?';
81+
} else {
82+
$type = (new \ReflectionFunction($factory))->getReturnType();
83+
84+
$this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').$type->getName() : '?';
85+
}
86+
}
87+
}
88+
89+
return $this->providedTypes;
90+
}
91+
6992
private function createNotFoundException(string $id): NotFoundExceptionInterface
7093
{
7194
if (!$alternatives = array_keys($this->factories)) {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Contracts\Service;
13+
14+
use Psr\Container\ContainerInterface;
15+
16+
/**
17+
* A ServiceProviderInterface exposes the identifiers and the types of services provided by a container.
18+
*
19+
* @author Nicolas Grekas <[email protected]>
20+
* @author Mateusz Sip <[email protected]>
21+
*/
22+
interface ServiceProviderInterface extends ContainerInterface
23+
{
24+
/**
25+
* Returns an associative array of service types keyed by the identifiers provided by the current container.
26+
*
27+
* Examples:
28+
*
29+
* * ['logger' => 'Psr\Log\LoggerInterface'] means the object provides a service named "logger" that implements Psr\Log\LoggerInterface
30+
* * ['foo' => '?'] means the container provides service name "foo" of unspecified type
31+
* * ['bar' => '?Bar\Baz'] means the container provides a service "bar" of type Bar\Baz|null
32+
*
33+
* @return string[] The provided service types, keyed by service names
34+
*/
35+
public function getProvidedServices(): array;
36+
}

0 commit comments

Comments
 (0)