Skip to content

Commit d190073

Browse files
authored
[complexity] Add NoJustPropertyAssignRule (#177)
1 parent 0fa6440 commit d190073

File tree

7 files changed

+217
-0
lines changed

7 files changed

+217
-0
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,48 @@ usort($items, function (array $apples) {
272272

273273
<br>
274274

275+
### NoJustPropertyAssignRule
276+
277+
Instead of assigning service property to a variable, use the property directly
278+
279+
```yaml
280+
rules:
281+
- Symplify\PHPStanRules\Rules\Complexity\NoJustPropertyAssignRule
282+
```
283+
284+
```php
285+
class SomeClass
286+
{
287+
// ...
288+
289+
public function run()
290+
{
291+
$someService = $this->someService;
292+
$someService->run();
293+
}
294+
}
295+
```
296+
297+
:x:
298+
299+
<br>
300+
301+
```php
302+
class SomeClass
303+
{
304+
// ...
305+
306+
public function run()
307+
{
308+
$this->someService->run();
309+
}
310+
}
311+
```
312+
313+
:+1:
314+
315+
<br>
316+
275317
### ForbiddenExtendOfNonAbstractClassRule
276318

277319
Only abstract classes can be extended

config/code-complexity-rules.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
rules:
22
- Symplify\PHPStanRules\Rules\NoDynamicNameRule
3+
- Symplify\PHPStanRules\Rules\Complexity\NoJustPropertyAssignRule

src/Enum/RuleIdentifier.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,6 @@ final class RuleIdentifier
6767
public const MAXIMUM_IGNORED_ERROR_COUNT = 'symplify.maximumIgnoredErrorCount';
6868

6969
public const STRING_FILE_ABSOLUTE_PATH_EXISTS = 'symplify.stringFileAbsolutePathExists';
70+
71+
public const NO_JUST_PROPERTY_ASSIGN = 'symplify.noJustPropertyAssign';
7072
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace Symplify\PHPStanRules\Rules\Complexity;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\Assign;
7+
use PhpParser\Node\Expr\PropertyFetch;
8+
use PhpParser\Node\Expr\Variable;
9+
use PHPStan\Analyser\Scope;
10+
use PHPStan\Rules\Rule;
11+
use PHPStan\Rules\RuleError;
12+
use PHPStan\Rules\RuleErrorBuilder;
13+
use Symfony\Component\Form\AbstractType;
14+
use Symplify\PHPStanRules\Enum\RuleIdentifier;
15+
16+
/**
17+
* @see \Symplify\PHPStanRules\Tests\Rules\Complexity\NoJustPropertyAssignRule\NoJustPropertyAssignRuleTest
18+
*
19+
* @implements Rule<Assign>
20+
*/
21+
final class NoJustPropertyAssignRule implements Rule
22+
{
23+
/**
24+
* @var string
25+
*/
26+
public const ERROR_MESSAGE = 'Instead of assigning service property to a variable, use the property directly';
27+
28+
public function getNodeType(): string
29+
{
30+
return Assign::class;
31+
}
32+
33+
/**
34+
* @param Assign $node
35+
* @return RuleError[]
36+
*/
37+
public function processNode(Node $node, Scope $scope): array
38+
{
39+
if (! $this->isLocalPropertyFetchAssignToVariable($node, $scope)) {
40+
return [];
41+
}
42+
43+
if ($this->shouldSkipCurrentClass($scope)) {
44+
return [];
45+
}
46+
47+
return [RuleErrorBuilder::message(self::ERROR_MESSAGE)
48+
->identifier(RuleIdentifier::NO_JUST_PROPERTY_ASSIGN)
49+
->build()];
50+
}
51+
52+
private function isLocalPropertyFetchAssignToVariable(Assign $assign, Scope $scope): bool
53+
{
54+
if (! $assign->var instanceof Variable) {
55+
return false;
56+
}
57+
58+
if (! $assign->expr instanceof PropertyFetch) {
59+
return false;
60+
}
61+
62+
$propertyFetch = $assign->expr;
63+
if (! $propertyFetch->var instanceof Variable) {
64+
return false;
65+
}
66+
67+
if ($propertyFetch->var->name !== 'this') {
68+
return false;
69+
}
70+
71+
$exprType = $scope->getType($assign->expr);
72+
return $exprType->isObject()->yes();
73+
}
74+
75+
private function shouldSkipCurrentClass(Scope $scope): bool
76+
{
77+
// skip entities as rather static
78+
if (str_contains($scope->getFile(), '/Document/') || str_contains($scope->getFile(), '/Entity/')) {
79+
return true;
80+
}
81+
82+
if ($scope->isInClass()) {
83+
$classReflection = $scope->getClassReflection();
84+
85+
// skip Symfony form types as rather static
86+
if ($classReflection->isSubclassOf(AbstractType::class)) {
87+
return true;
88+
}
89+
}
90+
91+
return false;
92+
}
93+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Complexity\NoJustPropertyAssignRule\Fixture;
6+
7+
use Doctrine\Persistence\ObjectManager;
8+
9+
final class ServiceAssign
10+
{
11+
private ObjectManager $manager;
12+
13+
public function __construct(ObjectManager $manager)
14+
{
15+
$this->manager = $manager;
16+
}
17+
18+
public function someMethod()
19+
{
20+
$manager = $this->manager;
21+
}
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Complexity\NoJustPropertyAssignRule\Fixture;
6+
7+
final class SkipScalarAssign
8+
{
9+
private string $name;
10+
11+
public function __construct(string $name)
12+
{
13+
$this->name = $name;
14+
}
15+
16+
public function someMethod()
17+
{
18+
$name = $this->name;
19+
}
20+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Complexity\NoJustPropertyAssignRule;
6+
7+
use Iterator;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Testing\RuleTestCase;
10+
use PHPUnit\Framework\Attributes\DataProvider;
11+
use Symplify\PHPStanRules\Rules\Complexity\NoJustPropertyAssignRule;
12+
13+
final class NoJustPropertyAssignRuleTest extends RuleTestCase
14+
{
15+
/**
16+
* @param mixed[] $expectedErrorsWithLines
17+
*/
18+
#[DataProvider('provideData')]
19+
public function testRule(string $filePath, array $expectedErrorsWithLines): void
20+
{
21+
$this->analyse([$filePath], $expectedErrorsWithLines);
22+
}
23+
24+
public static function provideData(): Iterator
25+
{
26+
yield [__DIR__ . '/Fixture/ServiceAssign.php', [
27+
[NoJustPropertyAssignRule::ERROR_MESSAGE, 20],
28+
]];
29+
30+
yield [__DIR__ . '/Fixture/SkipScalarAssign.php', []];
31+
}
32+
33+
protected function getRule(): Rule
34+
{
35+
return new NoJustPropertyAssignRule();
36+
}
37+
}

0 commit comments

Comments
 (0)