Skip to content

Commit 207a6ae

Browse files
enumagondrejmirtes
authored andcommitted
Check that doctrine entities are not final
1 parent 78376cb commit 207a6ae

File tree

5 files changed

+149
-0
lines changed

5 files changed

+149
-0
lines changed

rules.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,9 @@ services:
3333
reportUnknownTypes: %doctrine.reportUnknownTypes%
3434
tags:
3535
- phpstan.rules.rule
36+
-
37+
class: PHPStan\Rules\Doctrine\ORM\EntityNotFinalRule
38+
39+
conditionalTags:
40+
PHPStan\Rules\Doctrine\ORM\EntityNotFinalRule:
41+
phpstan.rules.rule: %featureToggles.bleedingEdge%
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Doctrine\ORM;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Rules\Rule;
8+
use PHPStan\Type\Doctrine\ObjectMetadataResolver;
9+
use function sprintf;
10+
11+
/**
12+
* @implements Rule<Node\Stmt\Class_>
13+
*/
14+
class EntityNotFinalRule implements Rule
15+
{
16+
17+
/** @var \PHPStan\Type\Doctrine\ObjectMetadataResolver */
18+
private $objectMetadataResolver;
19+
20+
public function __construct(ObjectMetadataResolver $objectMetadataResolver)
21+
{
22+
$this->objectMetadataResolver = $objectMetadataResolver;
23+
}
24+
25+
public function getNodeType(): string
26+
{
27+
return Node\Stmt\Class_::class;
28+
}
29+
30+
public function processNode(Node $node, Scope $scope): array
31+
{
32+
if (! $node->isFinal()) {
33+
return [];
34+
}
35+
36+
$objectManager = $this->objectMetadataResolver->getObjectManager();
37+
if ($objectManager === null) {
38+
return [];
39+
}
40+
41+
$className = (string) $node->namespacedName;
42+
try {
43+
if ($objectManager->getMetadataFactory()->isTransient($className)) {
44+
return [];
45+
}
46+
} catch (\ReflectionException $e) {
47+
return [];
48+
}
49+
50+
return [sprintf(
51+
'Entity class %s is final which can cause problems with proxies.',
52+
$className
53+
)];
54+
}
55+
56+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Doctrine\ORM;
4+
5+
use Iterator;
6+
use PHPStan\Rules\Rule;
7+
use PHPStan\Testing\RuleTestCase;
8+
use PHPStan\Type\Doctrine\ObjectMetadataResolver;
9+
10+
/**
11+
* @extends RuleTestCase<EntityNotFinalRule>
12+
*/
13+
class EntityNotFinalRuleTest extends RuleTestCase
14+
{
15+
16+
protected function getRule(): Rule
17+
{
18+
return new EntityNotFinalRule(
19+
new ObjectMetadataResolver($this->createReflectionProvider(), __DIR__ . '/entity-manager.php', null)
20+
);
21+
}
22+
23+
/**
24+
* @dataProvider ruleProvider
25+
* @param string $file
26+
* @param mixed[] $expectedErrors
27+
*/
28+
public function testRule(string $file, array $expectedErrors): void
29+
{
30+
$this->analyse([$file], $expectedErrors);
31+
}
32+
33+
/**
34+
* @return \Iterator<mixed[]>
35+
*/
36+
public function ruleProvider(): Iterator
37+
{
38+
yield 'final entity' => [
39+
__DIR__ . '/data/FinalEntity.php',
40+
[
41+
[
42+
'Entity class PHPStan\Rules\Doctrine\ORM\FinalEntity is final which can cause problems with proxies.',
43+
10,
44+
],
45+
],
46+
];
47+
48+
yield 'final non-entity' => [
49+
__DIR__ . '/data/FinalNonEntity.php',
50+
[],
51+
];
52+
53+
yield 'correct entity' => [
54+
__DIR__ . '/data/MyEntity.php',
55+
[],
56+
];
57+
}
58+
59+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Doctrine\ORM;
4+
5+
use Doctrine\ORM\Mapping as ORM;
6+
7+
/**
8+
* @ORM\Entity()
9+
*/
10+
final class FinalEntity
11+
{
12+
/**
13+
* @ORM\Id()
14+
* @ORM\GeneratedValue()
15+
* @ORM\Column(type="integer")
16+
*
17+
* @var int
18+
*/
19+
private $id;
20+
21+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Doctrine\ORM;
4+
5+
final class FinalNonEntity
6+
{
7+
}

0 commit comments

Comments
 (0)