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 4d8bb18

Browse filesBrowse files
mateuszsipnicolas-grekas
authored andcommitted
[DI][Contracts] add and implement ServiceProviderInterface
1 parent d4326b2 commit 4d8bb18
Copy full SHA for 4d8bb18

14 files changed

+112
-9
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Argument/ServiceLocator.php
+11-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CHANGELOG
1010
* added support for deprecating aliases
1111
* made `ContainerParametersResource` final and not implement `Serializable` anymore
1212
* added ability to define an index for a tagged collection
13+
* made `ServiceLocator` implement `ServiceProviderInterface`
1314

1415
4.2.0
1516
-----

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveServiceSubscribersPass.php
+2-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+3-2Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+3-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/ServiceLocator.php
+2-2Lines 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 <robin.chalas@gmail.com>
2525
* @author Nicolas Grekas <p@tchwork.com>
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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php
+2Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_service_locator_argument.php
+6Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php
+5Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/ServiceLocatorTest.php
+15Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/composer.json
+1-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Contracts/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
-----
66

77
* added `HttpClient` namespace with contracts for implementing flexible HTTP clients
8+
* added `ServiceProviderInterface`
89

910
1.0.0
1011
-----

‎src/Symfony/Contracts/Service/ServiceLocatorTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Contracts/Service/ServiceLocatorTrait.php
+24-1Lines 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 <robin.chalas@gmail.com>
2121
* @author Nicolas Grekas <p@tchwork.com>
@@ -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)) {
+36Lines 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 <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\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 <p@tchwork.com>
20+
* @author Mateusz Sip <mateusz.sip@gmail.com>
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.