Skip to content

Commit b8e94b1

Browse files
committedApr 15, 2019
Make container xml config option nullable
1 parent 6cac146 commit b8e94b1

13 files changed

+219
-54
lines changed
 

Diff for: ‎extension.neon

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
parameters:
22
symfony:
3+
container_xml_path: null
34
constant_hassers: true
45
console_application_loader: null
56

Diff for: ‎src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public function processNode(Node $node, Scope $scope): array
6262
return [];
6363
}
6464

65-
$serviceId = ServiceMap::getServiceIdFromNode($node->args[0]->value, $scope);
65+
$serviceId = $this->serviceMap::getServiceIdFromNode($node->args[0]->value, $scope);
6666
if ($serviceId !== null) {
6767
$service = $this->serviceMap->getService($serviceId);
6868
if ($service !== null && !$service->isPublic()) {

Diff for: ‎src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function processNode(Node $node, Scope $scope): array
5959
return [];
6060
}
6161

62-
$serviceId = ServiceMap::getServiceIdFromNode($node->args[0]->value, $scope);
62+
$serviceId = $this->serviceMap::getServiceIdFromNode($node->args[0]->value, $scope);
6363
if ($serviceId !== null) {
6464
$service = $this->serviceMap->getService($serviceId);
6565
$serviceIdType = $scope->getType($node->args[0]->value);

Diff for: ‎src/Symfony/DefaultServiceMap.php

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Symfony;
4+
5+
use PhpParser\Node\Expr;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Type\TypeUtils;
8+
use function count;
9+
10+
final class DefaultServiceMap implements ServiceMap
11+
{
12+
13+
/** @var \PHPStan\Symfony\ServiceDefinition[] */
14+
private $services;
15+
16+
/**
17+
* @param \PHPStan\Symfony\ServiceDefinition[] $services
18+
*/
19+
public function __construct(array $services)
20+
{
21+
$this->services = $services;
22+
}
23+
24+
/**
25+
* @return \PHPStan\Symfony\ServiceDefinition[]
26+
*/
27+
public function getServices(): array
28+
{
29+
return $this->services;
30+
}
31+
32+
public function getService(string $id): ?ServiceDefinition
33+
{
34+
return $this->services[$id] ?? null;
35+
}
36+
37+
public static function getServiceIdFromNode(Expr $node, Scope $scope): ?string
38+
{
39+
$strings = TypeUtils::getConstantStrings($scope->getType($node));
40+
return count($strings) === 1 ? $strings[0]->getValue() : null;
41+
}
42+
43+
}

Diff for: ‎src/Symfony/FakeServiceMap.php

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Symfony;
4+
5+
use PhpParser\Node\Expr;
6+
use PHPStan\Analyser\Scope;
7+
8+
final class FakeServiceMap implements ServiceMap
9+
{
10+
11+
/**
12+
* @return \PHPStan\Symfony\ServiceDefinition[]
13+
*/
14+
public function getServices(): array
15+
{
16+
return [];
17+
}
18+
19+
public function getService(string $id): ?ServiceDefinition
20+
{
21+
return null;
22+
}
23+
24+
public static function getServiceIdFromNode(Expr $node, Scope $scope): ?string
25+
{
26+
return null;
27+
}
28+
29+
}

Diff for: ‎src/Symfony/ServiceMap.php

+4-27
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,17 @@
44

55
use PhpParser\Node\Expr;
66
use PHPStan\Analyser\Scope;
7-
use PHPStan\Type\TypeUtils;
8-
use function count;
97

10-
final class ServiceMap
8+
interface ServiceMap
119
{
1210

13-
/** @var \PHPStan\Symfony\ServiceDefinition[] */
14-
private $services;
15-
16-
/**
17-
* @param \PHPStan\Symfony\ServiceDefinition[] $services
18-
*/
19-
public function __construct(array $services)
20-
{
21-
$this->services = $services;
22-
}
23-
2411
/**
2512
* @return \PHPStan\Symfony\ServiceDefinition[]
2613
*/
27-
public function getServices(): array
28-
{
29-
return $this->services;
30-
}
14+
public function getServices(): array;
3115

32-
public function getService(string $id): ?ServiceDefinition
33-
{
34-
return $this->services[$id] ?? null;
35-
}
16+
public function getService(string $id): ?ServiceDefinition;
3617

37-
public static function getServiceIdFromNode(Expr $node, Scope $scope): ?string
38-
{
39-
$strings = TypeUtils::getConstantStrings($scope->getType($node));
40-
return count($strings) === 1 ? $strings[0]->getValue() : null;
41-
}
18+
public static function getServiceIdFromNode(Expr $node, Scope $scope): ?string;
4219

4320
}

Diff for: ‎src/Symfony/XmlServiceMapFactory.php

+7-3
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,20 @@
1010
final class XmlServiceMapFactory implements ServiceMapFactory
1111
{
1212

13-
/** @var string */
13+
/** @var string|null */
1414
private $containerXml;
1515

16-
public function __construct(string $containerXml)
16+
public function __construct(?string $containerXml)
1717
{
1818
$this->containerXml = $containerXml;
1919
}
2020

2121
public function create(): ServiceMap
2222
{
23+
if ($this->containerXml === null) {
24+
return new FakeServiceMap();
25+
}
26+
2327
$fileContents = file_get_contents($this->containerXml);
2428
if ($fileContents === false) {
2529
throw new XmlContainerNotExistsException(sprintf('Container %s does not exist or cannot be parsed', $this->containerXml));
@@ -70,7 +74,7 @@ public function create(): ServiceMap
7074
);
7175
}
7276

73-
return new ServiceMap($services);
77+
return new DefaultServiceMap($services);
7478
}
7579

7680
}

Diff for: ‎src/Type/Symfony/ServiceDynamicReturnTypeExtension.php

+6-6
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType
2424
private $constantHassers;
2525

2626
/** @var \PHPStan\Symfony\ServiceMap */
27-
private $symfonyServiceMap;
27+
private $serviceMap;
2828

2929
public function __construct(string $className, bool $constantHassers, ServiceMap $symfonyServiceMap)
3030
{
3131
$this->className = $className;
3232
$this->constantHassers = $constantHassers;
33-
$this->symfonyServiceMap = $symfonyServiceMap;
33+
$this->serviceMap = $symfonyServiceMap;
3434
}
3535

3636
public function getClass(): string
@@ -65,9 +65,9 @@ private function getGetTypeFromMethodCall(
6565
return $returnType;
6666
}
6767

68-
$serviceId = ServiceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
68+
$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
6969
if ($serviceId !== null) {
70-
$service = $this->symfonyServiceMap->getService($serviceId);
70+
$service = $this->serviceMap->getService($serviceId);
7171
if ($service !== null && !$service->isSynthetic()) {
7272
return new ObjectType($service->getClass() ?? $serviceId);
7373
}
@@ -87,9 +87,9 @@ private function getHasTypeFromMethodCall(
8787
return $returnType;
8888
}
8989

90-
$serviceId = ServiceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
90+
$serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
9191
if ($serviceId !== null) {
92-
$service = $this->symfonyServiceMap->getService($serviceId);
92+
$service = $this->serviceMap->getService($serviceId);
9393
return new ConstantBooleanType($service !== null && $service->isPublic());
9494
}
9595

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Symfony;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Symfony\XmlServiceMapFactory;
7+
use PHPStan\Testing\RuleTestCase;
8+
9+
final class ContainerInterfacePrivateServiceRuleFakeTest extends RuleTestCase
10+
{
11+
12+
protected function getRule(): Rule
13+
{
14+
return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(null))->create());
15+
}
16+
17+
public function testGetPrivateService(): void
18+
{
19+
$this->analyse(
20+
[
21+
__DIR__ . '/ExampleController.php',
22+
],
23+
[]
24+
);
25+
}
26+
27+
public function testGetPrivateServiceInLegacyServiceSubscriber(): void
28+
{
29+
if (!interface_exists('Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface')) {
30+
self::markTestSkipped('The test needs Symfony\Component\DependencyInjection\ServiceSubscriberInterface class.');
31+
}
32+
33+
$this->analyse(
34+
[
35+
__DIR__ . '/ExampleLegacyServiceSubscriber.php',
36+
__DIR__ . '/ExampleLegacyServiceSubscriberFromAbstractController.php',
37+
__DIR__ . '/ExampleLegacyServiceSubscriberFromLegacyController.php',
38+
],
39+
[]
40+
);
41+
}
42+
43+
public function testGetPrivateServiceInServiceSubscriber(): void
44+
{
45+
if (!interface_exists('Symfony\Contracts\Service\ServiceSubscriberInterface')) {
46+
self::markTestSkipped('The test needs Symfony\Contracts\Service\ServiceSubscriberInterface class.');
47+
}
48+
49+
$this->analyse(
50+
[
51+
__DIR__ . '/ExampleServiceSubscriber.php',
52+
__DIR__ . '/ExampleServiceSubscriberFromAbstractController.php',
53+
__DIR__ . '/ExampleServiceSubscriberFromLegacyController.php',
54+
],
55+
[]
56+
);
57+
}
58+
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Symfony;
4+
5+
use PhpParser\PrettyPrinter\Standard;
6+
use PHPStan\Rules\Rule;
7+
use PHPStan\Symfony\XmlServiceMapFactory;
8+
use PHPStan\Testing\RuleTestCase;
9+
use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension;
10+
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
11+
12+
final class ContainerInterfaceUnknownServiceRuleFakeTest extends RuleTestCase
13+
{
14+
15+
protected function getRule(): Rule
16+
{
17+
return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(null))->create(), new Standard());
18+
}
19+
20+
/**
21+
* @return \PHPStan\Type\MethodTypeSpecifyingExtension[]
22+
*/
23+
protected function getMethodTypeSpecifyingExtensions(): array
24+
{
25+
return [
26+
new ServiceTypeSpecifyingExtension(Controller::class, new Standard()),
27+
];
28+
}
29+
30+
public function testGetPrivateService(): void
31+
{
32+
$this->analyse(
33+
[
34+
__DIR__ . '/ExampleController.php',
35+
],
36+
[]
37+
);
38+
}
39+
40+
}

Diff for: ‎tests/Symfony/ServiceMapTest.php renamed to ‎tests/Symfony/DefaultServiceMapTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use Iterator;
66
use PHPUnit\Framework\TestCase;
77

8-
final class ServiceMapTest extends TestCase
8+
final class DefaultServiceMapTest extends TestCase
99
{
1010

1111
/**

Diff for: ‎tests/Symfony/NeonTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ public function testExtensionNeon(): void
2525
$class = $loader->load(function (Compiler $compiler): void {
2626
$compiler->addExtension('rules', new RulesExtension());
2727
$compiler->addConfig(['parameters' => ['rootDir' => __DIR__]]);
28-
$compiler->loadConfig(__DIR__ . '/config.neon');
2928
$compiler->loadConfig(__DIR__ . '/../../extension.neon');
29+
$compiler->loadConfig(__DIR__ . '/config.neon');
3030
}, $key);
3131
/** @var \Nette\DI\Container $container */
3232
$container = new $class();

Diff for: ‎tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php

+26-14
Original file line numberDiff line numberDiff line change
@@ -12,45 +12,57 @@ final class ServiceDynamicReturnTypeExtensionTest extends ExtensionTestCase
1212
/**
1313
* @dataProvider servicesProvider
1414
*/
15-
public function testServices(string $expression, string $type): void
15+
public function testServices(string $expression, string $type, ?string $container): void
1616
{
1717
$this->processFile(
1818
__DIR__ . '/ExampleController.php',
1919
$expression,
2020
$type,
21-
new ServiceDynamicReturnTypeExtension(Controller::class, true, (new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create())
21+
new ServiceDynamicReturnTypeExtension(Controller::class, true, (new XmlServiceMapFactory($container))->create())
2222
);
2323
}
2424

2525
public function servicesProvider(): Iterator
2626
{
27-
yield ['$service1', 'Foo'];
28-
yield ['$service2', 'object'];
29-
yield ['$service3', 'object'];
30-
yield ['$service4', 'object'];
31-
yield ['$has1', 'true'];
32-
yield ['$has2', 'false'];
33-
yield ['$has3', 'bool'];
34-
yield ['$has4', 'bool'];
27+
yield ['$service1', 'Foo', __DIR__ . '/container.xml'];
28+
yield ['$service2', 'object', __DIR__ . '/container.xml'];
29+
yield ['$service3', 'object', __DIR__ . '/container.xml'];
30+
yield ['$service4', 'object', __DIR__ . '/container.xml'];
31+
yield ['$has1', 'true', __DIR__ . '/container.xml'];
32+
yield ['$has2', 'false', __DIR__ . '/container.xml'];
33+
yield ['$has3', 'bool', __DIR__ . '/container.xml'];
34+
yield ['$has4', 'bool', __DIR__ . '/container.xml'];
35+
36+
yield ['$service1', 'object', null];
37+
yield ['$service2', 'object', null];
38+
yield ['$service3', 'object', null];
39+
yield ['$service4', 'object', null];
40+
yield ['$has1', 'bool', null];
41+
yield ['$has2', 'bool', null];
42+
yield ['$has3', 'bool', null];
43+
yield ['$has4', 'bool', null];
3544
}
3645

3746
/**
3847
* @dataProvider constantHassersOffProvider
3948
*/
40-
public function testConstantHassersOff(string $expression, string $type): void
49+
public function testConstantHassersOff(string $expression, string $type, ?string $container): void
4150
{
4251
$this->processFile(
4352
__DIR__ . '/ExampleController.php',
4453
$expression,
4554
$type,
46-
new ServiceDynamicReturnTypeExtension(Controller::class, false, (new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create())
55+
new ServiceDynamicReturnTypeExtension(Controller::class, false, (new XmlServiceMapFactory($container))->create())
4756
);
4857
}
4958

5059
public function constantHassersOffProvider(): Iterator
5160
{
52-
yield ['$has1', 'bool'];
53-
yield ['$has2', 'bool'];
61+
yield ['$has1', 'bool', __DIR__ . '/container.xml'];
62+
yield ['$has2', 'bool', __DIR__ . '/container.xml'];
63+
64+
yield ['$has1', 'bool', null];
65+
yield ['$has2', 'bool', null];
5466
}
5567

5668
}

0 commit comments

Comments
 (0)
Please sign in to comment.