Skip to content

Commit 1b7b77b

Browse files
authored
Merge pull request #12 from mzk/improve
Add support for symfony Controllers
2 parents e6454d1 + 085bf55 commit 1b7b77b

7 files changed

+126
-4
lines changed

phpstan.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
includes:
22
- vendor/phpstan/phpstan-phpunit/extension.neon
33
- vendor/phpstan/phpstan-phpunit/rules.neon
4+
5+
parameters:
6+
excludes_analyse:
7+
- %rootDir%/../../../tests/*/data/*

src/Rules/ContainerInterfacePrivateServiceRule.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPStan\Analyser\Scope;
99
use PHPStan\Rules\Rule;
1010
use PHPStan\Type\ObjectType;
11+
use PHPStan\Type\ThisType;
1112
use PhpParser\Node;
1213
use PhpParser\Node\Arg;
1314
use PhpParser\Node\Expr\MethodCall;
@@ -34,8 +35,10 @@ public function processNode(Node $node, Scope $scope): array
3435
{
3536
if ($node instanceof MethodCall && $node->name === 'get') {
3637
$type = $scope->getType($node->var);
37-
if ($type instanceof ObjectType
38-
&& \in_array($type->getClassName(), ['Symfony\Component\DependencyInjection\ContainerInterface', 'Symfony\Bundle\FrameworkBundle\Controller\Controller'], \true)
38+
$baseController = new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\Controller');
39+
$isInstanceOfController = $type instanceof ThisType && $baseController->isSuperTypeOf($type)->yes();
40+
$isContainerInterface = $type instanceof ObjectType && $type->getClassName() === 'Symfony\Component\DependencyInjection\ContainerInterface';
41+
if (($isContainerInterface || $isInstanceOfController)
3942
&& isset($node->args[0])
4043
&& $node->args[0] instanceof Arg
4144
) {

src/Rules/ContainerInterfaceUnknownServiceRule.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
use PHPStan\Analyser\Scope;
99
use PHPStan\Rules\Rule;
1010
use PHPStan\Type\ObjectType;
11+
use PHPStan\Type\ThisType;
1112
use PhpParser\Node;
1213
use PhpParser\Node\Arg;
1314
use PhpParser\Node\Expr\MethodCall;
15+
use PhpParser\Node\Expr\Variable;
1416

1517
final class ContainerInterfaceUnknownServiceRule implements Rule
1618
{
@@ -34,10 +36,13 @@ public function processNode(Node $node, Scope $scope): array
3436
{
3537
if ($node instanceof MethodCall && $node->name === 'get') {
3638
$type = $scope->getType($node->var);
37-
if ($type instanceof ObjectType
38-
&& \in_array($type->getClassName(), ['Symfony\Component\DependencyInjection\ContainerInterface', 'Symfony\Bundle\FrameworkBundle\Controller\Controller'], \true)
39+
$baseController = new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\Controller');
40+
$isInstanceOfController = $type instanceof ThisType && $baseController->isSuperTypeOf($type)->yes();
41+
$isContainerInterface = $type instanceof ObjectType && $type->getClassName() === 'Symfony\Component\DependencyInjection\ContainerInterface';
42+
if (($isInstanceOfController || $isContainerInterface)
3943
&& isset($node->args[0])
4044
&& $node->args[0] instanceof Arg
45+
&& !$node->args[0]->value instanceof Variable
4146
) {
4247
$service = $this->serviceMap->getServiceFromNode($node->args[0]->value);
4348
if ($service === \null) {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace Lookyman\PHPStan\Symfony\Rules;
6+
7+
use Lookyman\PHPStan\Symfony\ServiceMap;
8+
use PHPStan\Rules\Rule;
9+
10+
final class ContainerInterfacePrivateServiceRuleTest extends \PHPStan\Testing\RuleTestCase
11+
{
12+
13+
protected function setUp()
14+
{
15+
include_once __DIR__ . '/data/Controller.php';
16+
}
17+
18+
protected function getRule(): Rule
19+
{
20+
$serviceMap = new ServiceMap(__DIR__ . '/../container.xml');
21+
22+
return new ContainerInterfacePrivateServiceRule($serviceMap);
23+
}
24+
25+
public function testGetPrivateService()
26+
{
27+
$this->analyse([__DIR__ . '/data/ExampleController.php'], [
28+
[
29+
'Service "private" is private.',
30+
14,
31+
],
32+
]);
33+
}
34+
35+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace Lookyman\PHPStan\Symfony\Rules;
6+
7+
use Lookyman\PHPStan\Symfony\ServiceMap;
8+
use PHPStan\Rules\Rule;
9+
10+
final class ContainerInterfaceUnknownServiceRuleTest extends \PHPStan\Testing\RuleTestCase
11+
{
12+
13+
protected function setUp()
14+
{
15+
include_once __DIR__ . '/data/Controller.php';
16+
}
17+
18+
protected function getRule(): Rule
19+
{
20+
$serviceMap = new ServiceMap(__DIR__ . '/../container.xml');
21+
22+
return new ContainerInterfaceUnknownServiceRule($serviceMap);
23+
}
24+
25+
public function testGetUnknownService()
26+
{
27+
$this->analyse([__DIR__ . '/data/ExampleController.php'], [
28+
[
29+
'Service "service.not.found" is not registered in the container.',
30+
20,
31+
],
32+
]);
33+
}
34+
35+
}

tests/Rules/data/Controller.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace Symfony\Bundle\FrameworkBundle\Controller;
6+
7+
abstract class Controller
8+
{
9+
10+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace Lookyman\PHPStan\Symfony\Rules\data;
6+
7+
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
8+
9+
final class ExampleController extends Controller
10+
{
11+
12+
public function getPrivateServiceAction()
13+
{
14+
$service = $this->get('private');
15+
$service->noMethod();
16+
}
17+
18+
public function getUnknownService()
19+
{
20+
$service = $this->get('service.not.found');
21+
$service->noMethod();
22+
}
23+
24+
public function getVariableService(string $serviceKey)
25+
{
26+
$service = $this->get($serviceKey);
27+
$service->noMethod();
28+
}
29+
30+
}

0 commit comments

Comments
 (0)