Skip to content

Commit

Permalink
Added support for turning off constant hassers
Browse files Browse the repository at this point in the history
  • Loading branch information
lookyman committed Oct 30, 2018
1 parent 298e1cc commit 929440a
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 35 deletions.
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,24 @@ parameters:
container_xml_path: %rootDir%/../../../var/cache/dev/srcDevDebugProjectContainer.xml
```

## Limitations

You have to provide a path to `srcDevDebugProjectContainer.xml` or similar xml file describing your container.

## Constant hassers

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:

```php
if ($this->has('service')) {
// ...
}
```

In that case, you can disable the `::has()` method return type resolving like this:

```
parameters:
symfony:
constant_hassers: false
```

Be aware that it may hide genuine errors in your application.
10 changes: 7 additions & 3 deletions extension.neon
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
parameters:
symfony:
constant_hassers: true

rules:
- PHPStan\Rules\Symfony\ContainerInterfacePrivateServiceRule
- PHPStan\Rules\Symfony\ContainerInterfaceUnknownServiceRule
Expand All @@ -12,13 +16,13 @@ services:

# ControllerTrait::get()/has() return type
-
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface)
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, %symfony.constant_hassers%)
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
-
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller)
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, %symfony.constant_hassers%)
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
-
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController)
class: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, %symfony.constant_hassers%)
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]

# ControllerTrait::has() type specification
Expand Down
8 changes: 6 additions & 2 deletions src/Type/Symfony/ServiceDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType
/** @var string */
private $className;

/** @var bool */
private $constantHassers;

/** @var \PHPStan\Symfony\ServiceMap */
private $symfonyServiceMap;

public function __construct(string $className, ServiceMap $symfonyServiceMap)
public function __construct(string $className, bool $constantHassers, ServiceMap $symfonyServiceMap)
{
$this->className = $className;
$this->constantHassers = $constantHassers;
$this->symfonyServiceMap = $symfonyServiceMap;
}

Expand Down Expand Up @@ -79,7 +83,7 @@ private function getHasTypeFromMethodCall(
): Type
{
$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
if (!isset($methodCall->args[0])) {
if (!isset($methodCall->args[0]) || !$this->constantHassers) {
return $returnType;
}

Expand Down
54 changes: 27 additions & 27 deletions tests/Symfony/ExampleContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ class Container_14d7103555 extends Nette\DI\Container
'PhpParser\PrettyPrinter\Standard' => [1 => ['3_PhpParser_PrettyPrinter_Standard']],
'PHPStan\Symfony\ServiceMap' => [1 => ['4']],
'PHPStan\Symfony\ServiceMapFactory' => [1 => ['symfony.serviceMapFactory']],
'PHPStan\Type\DynamicMethodReturnTypeExtension' => [1 => ['6', '7', '9', '11']],
'PHPStan\Type\DynamicMethodReturnTypeExtension' => [1 => ['6', '10', '11', '12']],
'PHPStan\Type\Symfony\RequestDynamicReturnTypeExtension' => [1 => ['6']],
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension' => [1 => ['7', '9', '11']],
'PHPStan\Type\MethodTypeSpecifyingExtension' => [1 => ['8', '10', '12']],
'PHPStan\Analyser\TypeSpecifierAwareExtension' => [1 => ['8', '10', '12']],
'PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension' => [1 => ['8', '10', '12']],
'PHPStan\Type\MethodTypeSpecifyingExtension' => [1 => ['7', '8', '9']],
'PHPStan\Analyser\TypeSpecifierAwareExtension' => [1 => ['7', '8', '9']],
'PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension' => [1 => ['7', '8', '9']],
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension' => [1 => ['10', '11', '12']],
'Nette\DI\Container' => [1 => ['container']],
],
'services' => [
Expand All @@ -26,17 +26,17 @@ class Container_14d7103555 extends Nette\DI\Container
'symfony.serviceMapFactory' => 'PHPStan\Symfony\ServiceMapFactory',
4 => 'PHPStan\Symfony\ServiceMap',
6 => 'PHPStan\Type\Symfony\RequestDynamicReturnTypeExtension',
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
'PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension',
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
'PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension',
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
'PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension',
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
'PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension',
],
'tags' => [
'phpstan.rules.rule' => ['rules.0' => true, 'rules.1' => true],
'phpstan.broker.dynamicMethodReturnTypeExtension' => [6 => true, true, 9 => true, 11 => true],
'phpstan.typeSpecifier.methodTypeSpecifyingExtension' => [8 => true, 10 => true, 12 => true],
'phpstan.broker.dynamicMethodReturnTypeExtension' => [6 => true, 10 => true, true, true],
'phpstan.typeSpecifier.methodTypeSpecifyingExtension' => [7 => true, true, true],
],
'aliases' => [],
];
Expand All @@ -45,7 +45,7 @@ class Container_14d7103555 extends Nette\DI\Container
public function __construct(array $params = [])
{
$this->parameters = $params;
$this->parameters += ['symfony' => ['container_xml_path' => '']];
$this->parameters += ['symfony' => ['container_xml_path' => '', 'constant_hassers' => true]];
}


Expand Down Expand Up @@ -97,53 +97,53 @@ public function createService__6(): PHPStan\Type\Symfony\RequestDynamicReturnTyp
}


public function createService__7(): PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension
public function createService__7(): PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension
{
$service = new PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension('Symfony\Bundle\FrameworkBundle\Controller\AbstractController', $this->getService('4'));
$service = new PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(
'Symfony\Bundle\FrameworkBundle\Controller\AbstractController',
$this->getService('3_PhpParser_PrettyPrinter_Standard')
);
return $service;
}


public function createService__8(): PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension
{
$service = new PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(
'Symfony\Bundle\FrameworkBundle\Controller\AbstractController',
'Symfony\Bundle\FrameworkBundle\Controller\Controller',
$this->getService('3_PhpParser_PrettyPrinter_Standard')
);
return $service;
}


public function createService__9(): PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension
public function createService__9(): PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension
{
$service = new PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension('Symfony\Bundle\FrameworkBundle\Controller\Controller', $this->getService('4'));
$service = new PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(
'Symfony\Component\DependencyInjection\ContainerInterface',
$this->getService('3_PhpParser_PrettyPrinter_Standard')
);
return $service;
}


public function createService__10(): PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension
public function createService__10(): PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension
{
$service = new PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(
'Symfony\Bundle\FrameworkBundle\Controller\Controller',
$this->getService('3_PhpParser_PrettyPrinter_Standard')
);
$service = new PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension('Symfony\Bundle\FrameworkBundle\Controller\AbstractController', true, $this->getService('4'));
return $service;
}


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


public function createService__12(): PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension
public function createService__12(): PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension
{
$service = new PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension(
'Symfony\Component\DependencyInjection\ContainerInterface',
$this->getService('3_PhpParser_PrettyPrinter_Standard')
);
$service = new PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension('Symfony\Component\DependencyInjection\ContainerInterface', true, $this->getService('4'));
return $service;
}

Expand Down
21 changes: 20 additions & 1 deletion tests/Type/Symfony/ServiceDynamicReturnTypeExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public function testServices(string $expression, string $type): void
__DIR__ . '/ExampleController.php',
$expression,
$type,
new ServiceDynamicReturnTypeExtension(Controller::class, (new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create())
new ServiceDynamicReturnTypeExtension(Controller::class, true, (new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create())
);
}

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

/**
* @dataProvider constantHassersOffProvider
*/
public function testConstantHassersOff(string $expression, string $type): void
{
$this->processFile(
__DIR__ . '/ExampleController.php',
$expression,
$type,
new ServiceDynamicReturnTypeExtension(Controller::class, false, (new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create())
);
}

public function constantHassersOffProvider(): Iterator
{
yield ['$has1', 'bool'];
yield ['$has2', 'bool'];
}

}

0 comments on commit 929440a

Please sign in to comment.