Skip to content

Commit 6902f4a

Browse files
authored
add no getDoctrine() rule (#159)
* add no getDoctrine() rule * misc
1 parent 83cf086 commit 6902f4a

File tree

8 files changed

+124
-166
lines changed

8 files changed

+124
-166
lines changed

config/symfony-rules.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ rules:
55
- Symplify\PHPStanRules\Rules\Symfony\NoListenerWithoutContractRule
66
- Symplify\PHPStanRules\Rules\Symfony\NoStringInGetSubscribedEventsRule
77
- Symplify\PHPStanRules\Rules\Symfony\RequireInvokableControllerRule
8+
9+
# dependency injection
10+
- Symplify\PHPStanRules\Rules\Symfony\NoGetDoctrineInControllerRule
11+
- Symplify\PHPStanRules\Rules\Symfony\NoGetInControllerRule

src/Enum/RuleIdentifier.php

Lines changed: 2 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -6,169 +6,70 @@
66

77
final class RuleIdentifier
88
{
9-
/**
10-
* @var string
11-
*/
129
public const UPPERCASE_CONSTANT = 'symplify.uppercaseConstant';
1310

14-
/**
15-
* @var string
16-
*/
1711
public const SEE_ANNOTATION_TO_TEST = 'symplify.seeAnnotationToTest';
1812

19-
/**
20-
* @var string
21-
*/
2213
public const REQUIRE_ATTRIBUTE_NAME = 'symplify.requireAttributeName';
2314

24-
/**
25-
* @var string
26-
*/
2715
public const RECTOR_PHP_RULE_IMPLEMENTS_MIN_VERSION = 'rector.phpRuleImplementsMinVersion';
2816

29-
/**
30-
* @var string
31-
*/
3217
public const RECTOR_UPGRADE_DOWNGRADE_REGISTERED_IN_SET = 'rector.upgradeDowngradeRegisteredInSet';
3318

34-
/**
35-
* @var string
36-
*/
3719
public const PHP_PARSER_NO_LEADING_BACKSLASH_IN_NAME = 'phpParser.noLeadingBackslashInName';
3820

39-
/**
40-
* @var string
41-
*/
4221
public const RECTOR_NO_INSTANCE_OF_STATIC_REFLECTION = 'rector.noInstanceOfStaticReflection';
4322

44-
/**
45-
* @var string
46-
*/
4723
public const RECTOR_NO_CLASS_REFLECTION_STATIC_REFLECTION = 'rector.noClassReflectionStaticReflection';
4824

49-
/**
50-
* @var string
51-
*/
5225
public const PARENT_METHOD_VISIBILITY_OVERRIDE = 'symplify.parentMethodVisibilityOverride';
5326

54-
/**
55-
* @var string
56-
*/
5727
public const NO_RETURN_SETTER_METHOD = 'symplify.noReturnSetterMethod';
5828

59-
/**
60-
* @var string
61-
*/
6229
public const FORBIDDEN_STATIC_CLASS_CONST_FETCH = 'symplify.forbiddenStaticClassConstFetch';
6330

64-
/**
65-
* @var string
66-
*/
6731
public const PREFERRED_CLASS = 'symplify.preferredClass';
6832

69-
/**
70-
* @var string
71-
*/
7233
public const NO_TEST_MOCKS = 'symplify.noTestMocks';
7334

74-
/**
75-
* @var string
76-
*/
7735
public const NO_GLOBAL_CONST = 'symplify.noGlobalConst';
7836

79-
/**
80-
* @var string
81-
*/
8237
public const NO_ENTITY_OUTSIDE_ENTITY_NAMESPACE = 'symplify.noEntityOutsideEntityNamespace';
8338

84-
/**
85-
* @var string
86-
*/
8739
public const FORBIDDEN_NODE = 'symplify.forbiddenNode';
8840

89-
/**
90-
* @var string
91-
*/
9241
public const MULTIPLE_CLASS_LIKE_IN_FILE = 'symplify.multipleClassLikeInFile';
9342

94-
/**
95-
* @var string
96-
*/
9743
public const FORBIDDEN_FUNC_CALL = 'symplify.forbiddenFuncCall';
9844

99-
/**
100-
* @var string
101-
*/
10245
public const REQUIRE_ATTRIBUTE_NAMESPACE = 'symplify.requireAttributeNamespace';
10346

104-
/**
105-
* @var string
106-
*/
10747
public const FORBIDDEN_ARRAY_METHOD_CALL = 'symplify.forbiddenArrayMethodCall';
10848

109-
/**
110-
* @var string
111-
*/
11249
public const FORBIDDEN_EXTEND_OF_NON_ABSTRACT_CLASS = 'symplify.forbiddenExtendOfNonAbstractClass';
11350

114-
/**
115-
* @var string
116-
*/
11751
public const EXPLICIT_ABSTRACT_PREFIX_NAME = 'symplify.explicitAbstractPrefixName';
11852

119-
/**
120-
* @var string
121-
*/
12253
public const EXPLICIT_INTERFACE_SUFFIX_NAME = 'symplify.explicitInterfaceSuffixName';
12354

124-
/**
125-
* @var string
126-
*/
12755
public const EXPLICIT_TRAIT_SUFFIX_NAME = 'symplify.explicitTraitSuffixName';
12856

129-
/**
130-
* @var string
131-
*/
13257
public const REQUIRE_UNIQUE_ENUM_CONSTANT = 'symplify.requireUniqueEnumConstant';
13358

134-
/**
135-
* @var string
136-
*/
13759
public const REQUIRE_EXCEPTION_NAMESPACE = 'symplify.requireExceptionNamespace';
13860

139-
/**
140-
* @var string
141-
*/
14261
public const CLASS_NAME_RESPECTS_PARENT_SUFFIX = 'symplify.classNameRespectsParentSuffix';
14362

144-
/**
145-
* @var string
146-
*/
14763
public const REQUIRED_INTERFACE_CONTRACT_NAMESPACE = 'symplify.requiredInterfaceContractNamespace';
14864

149-
/**
150-
* @var string
151-
*/
15265
public const SYMFONY_REQUIRE_INVOKABLE_CONTROLLER = 'symfony.requireInvokableController';
15366

154-
/**
155-
* @var string
156-
*/
15767
public const NO_VALUE_OBJECT_IN_SERVICE_CONSTRUCTOR = 'symplify.noValueObjectInServiceConstructor';
15868

159-
/**
160-
* @var string
161-
*/
16269
public const DOCTRINE_NO_REPOSITORY_CALL_IN_DATA_FIXTURES = 'doctrine.noRepositoryCallInDataFixtures';
16370

164-
/**
165-
* @var string
166-
*/
16771
public const PHPUNIT_NO_DOCUMENT_MOCKING = 'phpunit.noDocumentMocking';
16872

169-
/**
170-
* @var string
171-
*/
17273
public const NO_DYNAMIC_NAME = 'symplify.noDynamicName';
17374

17475
public const NO_REFERENCE = 'symplify.noReference';
@@ -200,4 +101,6 @@ final class RuleIdentifier
200101
public const REQUIRE_QUERY_BUILDER_ON_REPOSITORY = 'doctrine.requireQueryBuilderOnRepository';
201102

202103
public const NO_GET_IN_CONTROLLER = 'symfony.noGetInController';
104+
105+
public const NO_GET_DOCTRINE_IN_CONTROLLER = 'symfony.noGetDoctrineInController';
203106
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\NodeAnalyzer;
6+
7+
use PhpParser\Node\Expr\MethodCall;
8+
use PhpParser\Node\Expr\Variable;
9+
use PhpParser\Node\Identifier;
10+
11+
final class MethodCallNameAnalyzer
12+
{
13+
public static function isThisMethodCall(MethodCall $methodCall, string $methodName): bool
14+
{
15+
if (! $methodCall->name instanceof Identifier) {
16+
return false;
17+
}
18+
19+
if ($methodCall->name->toString() !== $methodName) {
20+
return false;
21+
}
22+
23+
// is "$this"?
24+
if (! $methodCall->var instanceof Variable) {
25+
return false;
26+
}
27+
28+
if (! is_string($methodCall->var->name)) {
29+
return false;
30+
}
31+
32+
return $methodCall->var->name === 'this';
33+
}
34+
}

src/Rules/Doctrine/NoEntityMockingRule.php

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHPStan\Rules\RuleErrorBuilder;
1414
use Symplify\PHPStanRules\Doctrine\DoctrineEntityDocumentAnalyser;
1515
use Symplify\PHPStanRules\Enum\RuleIdentifier;
16+
use Symplify\PHPStanRules\NodeAnalyzer\MethodCallNameAnalyzer;
1617

1718
/**
1819
* The ORM entities and ODM documents should never be mocked, as it leads to typeless code.
@@ -44,7 +45,7 @@ public function getNodeType(): string
4445
*/
4546
public function processNode(Node $node, Scope $scope): array
4647
{
47-
if (! $this->isCreateMockMethod($node)) {
48+
if (! MethodCallNameAnalyzer::isThisMethodCall($node, 'createMock')) {
4849
return [];
4950
}
5051

@@ -70,18 +71,4 @@ public function processNode(Node $node, Scope $scope): array
7071

7172
return [];
7273
}
73-
74-
private function isCreateMockMethod(MethodCall $methodCall): bool
75-
{
76-
if ($methodCall->isFirstClassCallable()) {
77-
return false;
78-
}
79-
80-
if (! $methodCall->name instanceof Identifier) {
81-
return false;
82-
}
83-
84-
$methodName = $methodCall->name->toString();
85-
return $methodName === 'createMock';
86-
}
8774
}

src/Rules/Symfony/NoAbstractControllerConstructorRule.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* Check if abstract controller has constructor, as it should use
1717
* #[Require] instead to avoid parent constructor override
1818
*
19-
* @see \Symplify\PHPStanRules\Tests\PHPStan\Rule\NoAbstractControllerConstructorRule\NoAbstractControllerConstructorRuleTest
19+
* @see \Symplify\PHPStanRules\Tests\Rules\Symfony\NoAbstractControllerConstructorRule\NoAbstractControllerConstructorRuleTest
2020
*
2121
* @implements Rule<Class_>
2222
*/
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Rules\Symfony;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\MethodCall;
9+
use PHPStan\Analyser\Scope;
10+
use PHPStan\Rules\Rule;
11+
use PHPStan\Rules\RuleErrorBuilder;
12+
use Symplify\PHPStanRules\Enum\RuleIdentifier;
13+
use Symplify\PHPStanRules\NodeAnalyzer\MethodCallNameAnalyzer;
14+
use Symplify\PHPStanRules\Symfony\NodeAnalyzer\SymfonyControllerAnalyzer;
15+
16+
/**
17+
* @implements Rule<MethodCall>
18+
*/
19+
final class NoGetDoctrineInControllerRule implements Rule
20+
{
21+
/**
22+
* @var string
23+
*/
24+
private const ERROR_MESSAGE = 'Do not use $this->getDoctrine() method in controller. Use __construct(EntityManagerInterface $entityManager) instead';
25+
26+
public function getNodeType(): string
27+
{
28+
return MethodCall::class;
29+
}
30+
31+
/**
32+
* @param MethodCall $node
33+
*/
34+
public function processNode(Node $node, Scope $scope): array
35+
{
36+
if (! MethodCallNameAnalyzer::isThisMethodCall($node, 'getDoctrine')) {
37+
return [];
38+
}
39+
40+
if (! SymfonyControllerAnalyzer::isControllerScope($scope)) {
41+
return [];
42+
}
43+
44+
$ruleError = RuleErrorBuilder::message(self::ERROR_MESSAGE)
45+
->file($scope->getFile())
46+
->line($node->getStartLine())
47+
->identifier(RuleIdentifier::NO_GET_DOCTRINE_IN_CONTROLLER)
48+
->build();
49+
50+
return [$ruleError];
51+
}
52+
}

0 commit comments

Comments
 (0)