Skip to content

Commit 929440a

Browse files
committed
Added support for turning off constant hassers
1 parent 298e1cc commit 929440a

File tree

5 files changed

+80
-35
lines changed

5 files changed

+80
-35
lines changed

Diff for: README.md

+20-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,24 @@ parameters:
3232
container_xml_path: %rootDir%/../../../var/cache/dev/srcDevDebugProjectContainer.xml
3333
```
3434

35-
## Limitations
36-
3735
You have to provide a path to `srcDevDebugProjectContainer.xml` or similar xml file describing your container.
36+
37+
## Constant hassers
38+
39+
Sometimes, when you are dealing with optional dependencies, the `::has()` methods can cause problems. For example, the following construct would complain that the condition is always either on or off, depending on whether you have the dependency for `service` installed:
40+
41+
```php
42+
if ($this->has('service')) {
43+
// ...
44+
}
45+
```
46+
47+
In that case, you can disable the `::has()` method return type resolving like this:
48+
49+
```
50+
parameters:
51+
symfony:
52+
constant_hassers: false
53+
```
54+
55+
Be aware that it may hide genuine errors in your application.

Diff for: extension.neon

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
parameters:
2+
symfony:
3+
constant_hassers: true
4+
15
rules:
26
- PHPStan\Rules\Symfony\ContainerInterfacePrivateServiceRule
37
- PHPStan\Rules\Symfony\ContainerInterfaceUnknownServiceRule
@@ -12,13 +16,13 @@ services:
1216

1317
# ControllerTrait::get()/has() return type
1418
-
15-
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface)
19+
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, %symfony.constant_hassers%)
1620
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
1721
-
18-
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller)
22+
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, %symfony.constant_hassers%)
1923
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
2024
-
21-
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController)
25+
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, %symfony.constant_hassers%)
2226
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
2327

2428
# ControllerTrait::has() type specification

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType
2020
/** @var string */
2121
private $className;
2222

23+
/** @var bool */
24+
private $constantHassers;
25+
2326
/** @var \PHPStan\Symfony\ServiceMap */
2427
private $symfonyServiceMap;
2528

26-
public function __construct(string $className, ServiceMap $symfonyServiceMap)
29+
public function __construct(string $className, bool $constantHassers, ServiceMap $symfonyServiceMap)
2730
{
2831
$this->className = $className;
32+
$this->constantHassers = $constantHassers;
2933
$this->symfonyServiceMap = $symfonyServiceMap;
3034
}
3135

@@ -79,7 +83,7 @@ private function getHasTypeFromMethodCall(
7983
): Type
8084
{
8185
$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
82-
if (!isset($methodCall->args[0])) {
86+
if (!isset($methodCall->args[0]) || !$this->constantHassers) {
8387
return $returnType;
8488
}
8589

Diff for: tests/Symfony/ExampleContainer.php

+27-27
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ class Container_14d7103555 extends Nette\DI\Container
1010
'PhpParser\PrettyPrinter\Standard' => [1 => ['3_PhpParser_PrettyPrinter_Standard']],
1111
'PHPStan\Symfony\ServiceMap' => [1 => ['4']],
1212
'PHPStan\Symfony\ServiceMapFactory' => [1 => ['symfony.serviceMapFactory']],
13-
'PHPStan\Type\DynamicMethodReturnTypeExtension' => [1 => ['6', '7', '9', '11']],
13+
'PHPStan\Type\DynamicMethodReturnTypeExtension' => [1 => ['6', '10', '11', '12']],
1414
'PHPStan\Type\Symfony\RequestDynamicReturnTypeExtension' => [1 => ['6']],
15-
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension' => [1 => ['7', '9', '11']],
16-
'PHPStan\Type\MethodTypeSpecifyingExtension' => [1 => ['8', '10', '12']],
17-
'PHPStan\Analyser\TypeSpecifierAwareExtension' => [1 => ['8', '10', '12']],
18-
'PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension' => [1 => ['8', '10', '12']],
15+
'PHPStan\Type\MethodTypeSpecifyingExtension' => [1 => ['7', '8', '9']],
16+
'PHPStan\Analyser\TypeSpecifierAwareExtension' => [1 => ['7', '8', '9']],
17+
'PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension' => [1 => ['7', '8', '9']],
18+
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension' => [1 => ['10', '11', '12']],
1919
'Nette\DI\Container' => [1 => ['container']],
2020
],
2121
'services' => [
@@ -26,17 +26,17 @@ class Container_14d7103555 extends Nette\DI\Container
2626
'symfony.serviceMapFactory' => 'PHPStan\Symfony\ServiceMapFactory',
2727
4 => 'PHPStan\Symfony\ServiceMap',
2828
6 => 'PHPStan\Type\Symfony\RequestDynamicReturnTypeExtension',
29-
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
3029
'PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension',
31-
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
3230
'PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension',
33-
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
3431
'PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension',
32+
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
33+
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
34+
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
3535
],
3636
'tags' => [
3737
'phpstan.rules.rule' => ['rules.0' => true, 'rules.1' => true],
38-
'phpstan.broker.dynamicMethodReturnTypeExtension' => [6 => true, true, 9 => true, 11 => true],
39-
'phpstan.typeSpecifier.methodTypeSpecifyingExtension' => [8 => true, 10 => true, 12 => true],
38+
'phpstan.broker.dynamicMethodReturnTypeExtension' => [6 => true, 10 => true, true, true],
39+
'phpstan.typeSpecifier.methodTypeSpecifyingExtension' => [7 => true, true, true],
4040
],
4141
'aliases' => [],
4242
];
@@ -45,7 +45,7 @@ class Container_14d7103555 extends Nette\DI\Container
4545
public function __construct(array $params = [])
4646
{
4747
$this->parameters = $params;
48-
$this->parameters += ['symfony' => ['container_xml_path' => '']];
48+
$this->parameters += ['symfony' => ['container_xml_path' => '', 'constant_hassers' => true]];
4949
}
5050

5151

@@ -97,53 +97,53 @@ public function createService__6(): PHPStan\Type\Symfony\RequestDynamicReturnTyp
9797
}
9898

9999

100-
public function createService__7(): PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension
100+
public function createService__7(): PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension
101101
{
102-
$service = new PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension('Symfony\Bundle\FrameworkBundle\Controller\AbstractController', $this->getService('4'));
102+
$service = new PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(
103+
'Symfony\Bundle\FrameworkBundle\Controller\AbstractController',
104+
$this->getService('3_PhpParser_PrettyPrinter_Standard')
105+
);
103106
return $service;
104107
}
105108

106109

107110
public function createService__8(): PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension
108111
{
109112
$service = new PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(
110-
'Symfony\Bundle\FrameworkBundle\Controller\AbstractController',
113+
'Symfony\Bundle\FrameworkBundle\Controller\Controller',
111114
$this->getService('3_PhpParser_PrettyPrinter_Standard')
112115
);
113116
return $service;
114117
}
115118

116119

117-
public function createService__9(): PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension
120+
public function createService__9(): PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension
118121
{
119-
$service = new PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension('Symfony\Bundle\FrameworkBundle\Controller\Controller', $this->getService('4'));
122+
$service = new PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(
123+
'Symfony\Component\DependencyInjection\ContainerInterface',
124+
$this->getService('3_PhpParser_PrettyPrinter_Standard')
125+
);
120126
return $service;
121127
}
122128

123129

124-
public function createService__10(): PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension
130+
public function createService__10(): PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension
125131
{
126-
$service = new PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(
127-
'Symfony\Bundle\FrameworkBundle\Controller\Controller',
128-
$this->getService('3_PhpParser_PrettyPrinter_Standard')
129-
);
132+
$service = new PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension('Symfony\Bundle\FrameworkBundle\Controller\AbstractController', true, $this->getService('4'));
130133
return $service;
131134
}
132135

133136

134137
public function createService__11(): PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension
135138
{
136-
$service = new PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension('Symfony\Component\DependencyInjection\ContainerInterface', $this->getService('4'));
139+
$service = new PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension('Symfony\Bundle\FrameworkBundle\Controller\Controller', true, $this->getService('4'));
137140
return $service;
138141
}
139142

140143

141-
public function createService__12(): PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension
144+
public function createService__12(): PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension
142145
{
143-
$service = new PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(
144-
'Symfony\Component\DependencyInjection\ContainerInterface',
145-
$this->getService('3_PhpParser_PrettyPrinter_Standard')
146-
);
146+
$service = new PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension('Symfony\Component\DependencyInjection\ContainerInterface', true, $this->getService('4'));
147147
return $service;
148148
}
149149

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

+20-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public function testServices(string $expression, string $type): void
1818
__DIR__ . '/ExampleController.php',
1919
$expression,
2020
$type,
21-
new ServiceDynamicReturnTypeExtension(Controller::class, (new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create())
21+
new ServiceDynamicReturnTypeExtension(Controller::class, true, (new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create())
2222
);
2323
}
2424

@@ -34,4 +34,23 @@ public function servicesProvider(): Iterator
3434
yield ['$has4', 'bool'];
3535
}
3636

37+
/**
38+
* @dataProvider constantHassersOffProvider
39+
*/
40+
public function testConstantHassersOff(string $expression, string $type): void
41+
{
42+
$this->processFile(
43+
__DIR__ . '/ExampleController.php',
44+
$expression,
45+
$type,
46+
new ServiceDynamicReturnTypeExtension(Controller::class, false, (new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create())
47+
);
48+
}
49+
50+
public function constantHassersOffProvider(): Iterator
51+
{
52+
yield ['$has1', 'bool'];
53+
yield ['$has2', 'bool'];
54+
}
55+
3756
}

0 commit comments

Comments
 (0)