Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7cc794d

Browse files
committed
Check that doctrine entities are not final
1 parent 78376cb commit 7cc794d

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

‎rules.neon‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ services:
3333
reportUnknownTypes: %doctrine.reportUnknownTypes%
3434
tags:
3535
- phpstan.rules.rule
36+
37+
conditionalTags:
38+
PHPStan\Rules\Doctrine\ORM\EntityRule:
39+
phpstan.rules.rule: %featureToggles.bleedingEdge%
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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 EntityRule 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+
try {
51+
$metadata = $objectManager->getClassMetadata($className);
52+
} catch (\Doctrine\ORM\Mapping\MappingException $e) {
53+
return [];
54+
}
55+
56+
$classMetadataInfo = 'Doctrine\ORM\Mapping\ClassMetadataInfo';
57+
if (!$metadata instanceof $classMetadataInfo) {
58+
return [];
59+
}
60+
61+
return [sprintf(
62+
'Entity class %s is final which can cause problems with proxies.',
63+
$className
64+
)];
65+
}
66+
67+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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<EntityRule>
12+
*/
13+
class EntityRuleTest extends RuleTestCase
14+
{
15+
16+
protected function getRule(): Rule
17+
{
18+
return new EntityRule(
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 'correct entity' => [
49+
__DIR__ . '/data/MyEntity.php',
50+
[],
51+
];
52+
}
53+
54+
}
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+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /