Skip to content

Commit 1753adb

Browse files
committed
[FEATURE]: Add rules for container
1 parent 90a5a15 commit 1753adb

26 files changed

+470
-53
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,14 @@ $myIntAttribute = $site->getAttribute('myIntAttribute');
143143
// PHPStan will now know that $myStringAttribute is of type string
144144
$myStringAttribute = $site->getAttribute('myStringAttribute');
145145
```
146+
147+
### Check for private Services
148+
You have to provide a path to App_KernelDevelopmentDebugContainer.xml or similar XML file describing your container.
149+
This is generated by [ssch/typo3-debug-dump-pass](https://github.com/sabbelasichon/typo3-debug-dump-pass) in your /var/cache/{TYPO3_CONTEXT}/ folder.
150+
151+
```NEON
152+
parameters:
153+
typo3:
154+
containerXmlPath: var/cache/development/App_KernelDevelopmentDebugContainer.xml
155+
```
156+

build.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
passthru="true"
6161
checkreturn="true"
6262
>
63+
<arg line="--memory-limit=-1"/>
6364
</exec>
6465
</target>
6566

extension.neon

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
includes:
2+
- rules.neon
3+
14
services:
25
-
36
class: SaschaEgerer\PhpstanTypo3\Reflection\RepositoryFindMethodsClassReflectionExtension
@@ -51,10 +54,6 @@ services:
5154
siteGetAttributeMapping: %typo3.siteGetAttributeMapping%
5255
tags:
5356
- phpstan.rules.rule
54-
-
55-
class: SaschaEgerer\PhpstanTypo3\Rule\ValidatorResolverOptionsRule
56-
tags:
57-
- phpstan.rules.rule
5857
-
5958
class: SaschaEgerer\PhpstanTypo3\Type\RepositoryQueryDynamicReturnTypeExtension
6059
tags:
@@ -105,6 +104,16 @@ services:
105104
class: SaschaEgerer\PhpstanTypo3\Type\DateTimeAspectGetDynamicReturnTypeExtension
106105
tags:
107106
- phpstan.broker.dynamicMethodReturnTypeExtension
107+
-
108+
class: SaschaEgerer\PhpstanTypo3\Service\PrivateServiceAnalyzer
109+
# service map
110+
typo3.serviceMapFactory:
111+
class: SaschaEgerer\PhpstanTypo3\Contract\ServiceMapFactory
112+
factory: SaschaEgerer\PhpstanTypo3\Service\XmlServiceMapFactory
113+
arguments:
114+
containerXmlPath: %typo3.containerXmlPath%
115+
-
116+
factory: @typo3.serviceMapFactory::create()
108117
parameters:
109118
bootstrapFiles:
110119
- phpstan.bootstrap.php

phpstan.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
includes:
22
- vendor/phpstan/phpstan-strict-rules/rules.neon
3+
- vendor/phpstan/phpstan-phpunit/extension.neon
34
- extension.neon
45
- tests/Unit/Type/data/context-get-aspect-return-types.neon
56
- tests/Unit/Type/data/request-get-attribute-return-types.neon

rules.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
rules:
2+
- SaschaEgerer\PhpstanTypo3\Rule\ValidatorResolverOptionsRule
3+
- SaschaEgerer\PhpstanTypo3\Rule\ContainerInterfacePrivateServiceRule
4+
- SaschaEgerer\PhpstanTypo3\Rule\GeneralUtilityMakeInstancePrivateServiceRule
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1-
<?php
2-
declare(strict_types=1);
1+
<?php declare(strict_types = 1);
32

43
namespace SaschaEgerer\PhpstanTypo3\Contract;
54

5+
use PhpParser\Node\Expr;
6+
use PHPStan\Analyser\Scope;
67
use SaschaEgerer\PhpstanTypo3\Service\ServiceDefinition;
78

8-
interface ServiceMapInterface
9+
interface ServiceMap
910
{
11+
1012
/**
1113
* @return ServiceDefinition[]
1214
*/
1315
public function getServiceDefinitions(): array;
1416

1517
public function getServiceDefinitionById(string $id): ?ServiceDefinition;
18+
19+
public function getServiceIdFromNode(Expr $node, Scope $scope): ?string;
20+
1621
}

src/Contract/ServiceMapFactory.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace SaschaEgerer\PhpstanTypo3\Contract;
4+
5+
interface ServiceMapFactory
6+
{
7+
8+
public function create(): ServiceMap;
9+
10+
}

src/Contract/ServiceMapFactoryInterface.php

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace SaschaEgerer\PhpstanTypo3\Rule;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\MethodCall;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Type\ObjectType;
10+
use SaschaEgerer\PhpstanTypo3\Service\PrivateServiceAnalyzer;
11+
12+
/**
13+
* @implements Rule<MethodCall>
14+
*/
15+
final class ContainerInterfacePrivateServiceRule implements Rule
16+
{
17+
18+
private PrivateServiceAnalyzer $privateServiceAnalyzer;
19+
20+
public function __construct(PrivateServiceAnalyzer $privateServiceAnalyzer)
21+
{
22+
$this->privateServiceAnalyzer = $privateServiceAnalyzer;
23+
}
24+
25+
public function getNodeType(): string
26+
{
27+
return MethodCall::class;
28+
}
29+
30+
public function processNode(Node $node, Scope $scope): array
31+
{
32+
if ($this->shouldSkip($node, $scope)) {
33+
return [];
34+
}
35+
36+
return $this->privateServiceAnalyzer->analyze($node, $scope);
37+
}
38+
39+
private function shouldSkip(MethodCall $node, Scope $scope): bool
40+
{
41+
if (!$node->name instanceof Node\Identifier) {
42+
return true;
43+
}
44+
45+
$methodCallArguments = $node->getArgs();
46+
47+
if (!isset($methodCallArguments[0])) {
48+
return true;
49+
}
50+
51+
$methodCallName = $node->name->name;
52+
53+
if ($methodCallName !== 'get') {
54+
return true;
55+
}
56+
57+
$argType = $scope->getType($node->var);
58+
59+
$isPsrContainerType = (new ObjectType('Psr\Container\ContainerInterface'))->isSuperTypeOf($argType);
60+
61+
return !$isPsrContainerType->yes();
62+
}
63+
64+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace SaschaEgerer\PhpstanTypo3\Rule;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\StaticCall;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\Rule;
9+
use SaschaEgerer\PhpstanTypo3\Service\PrivateServiceAnalyzer;
10+
use TYPO3\CMS\Core\Utility\GeneralUtility;
11+
12+
/**
13+
* @implements Rule<StaticCall>
14+
*/
15+
final class GeneralUtilityMakeInstancePrivateServiceRule implements Rule
16+
{
17+
18+
private PrivateServiceAnalyzer $privateServiceAnalyzer;
19+
20+
public function __construct(PrivateServiceAnalyzer $privateServiceAnalyzer)
21+
{
22+
$this->privateServiceAnalyzer = $privateServiceAnalyzer;
23+
}
24+
25+
public function getNodeType(): string
26+
{
27+
return StaticCall::class;
28+
}
29+
30+
public function processNode(Node $node, Scope $scope): array
31+
{
32+
if ($this->shouldSkip($node)) {
33+
return [];
34+
}
35+
36+
return $this->privateServiceAnalyzer->analyze($node, $scope);
37+
}
38+
39+
private function shouldSkip(StaticCall $node): bool
40+
{
41+
if (!$node->name instanceof Node\Identifier) {
42+
return true;
43+
}
44+
45+
$methodCallArguments = $node->getArgs();
46+
47+
if (!isset($methodCallArguments[0])) {
48+
return true;
49+
}
50+
51+
$methodCallName = $node->name->name;
52+
53+
if ($methodCallName !== 'makeInstance') {
54+
return true;
55+
}
56+
57+
if (count($methodCallArguments) > 1) {
58+
return true;
59+
}
60+
61+
if (!$node->class instanceof Node\Name\FullyQualified) {
62+
return true;
63+
}
64+
65+
return $node->class->toString() !== GeneralUtility::class;
66+
}
67+
68+
}

0 commit comments

Comments
 (0)