Skip to content

Commit 00e7f8f

Browse files
committed
Bleeding edge - report entity mapping exception
1 parent 9fa37f6 commit 00e7f8f

File tree

4 files changed

+119
-0
lines changed

4 files changed

+119
-0
lines changed

rules.neon

+6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ rules:
2222
- PHPStan\Rules\Doctrine\ORM\RepositoryMethodCallRule
2323
- PHPStan\Rules\Doctrine\ORM\EntityNotFinalRule
2424

25+
conditionalTags:
26+
PHPStan\Rules\Doctrine\ORM\EntityMappingExceptionRule:
27+
phpstan.rules.rule: %featureToggles.bleedingEdge%
28+
2529
services:
2630
-
2731
class: PHPStan\Rules\Doctrine\ORM\QueryBuilderDqlRule
@@ -36,6 +40,8 @@ services:
3640
allowNullablePropertyForRequiredField: %doctrine.allowNullablePropertyForRequiredField%
3741
tags:
3842
- phpstan.rules.rule
43+
-
44+
class: PHPStan\Rules\Doctrine\ORM\EntityMappingExceptionRule
3945
-
4046
class: PHPStan\Rules\Doctrine\ORM\EntityNotFinalRule
4147
-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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\Node\InClassNode;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Type\Doctrine\ObjectMetadataResolver;
10+
11+
/**
12+
* @implements Rule<InClassNode>
13+
*/
14+
class EntityMappingExceptionRule implements Rule
15+
{
16+
17+
/** @var \PHPStan\Type\Doctrine\ObjectMetadataResolver */
18+
private $objectMetadataResolver;
19+
20+
public function __construct(
21+
ObjectMetadataResolver $objectMetadataResolver
22+
)
23+
{
24+
$this->objectMetadataResolver = $objectMetadataResolver;
25+
}
26+
27+
public function getNodeType(): string
28+
{
29+
return InClassNode::class;
30+
}
31+
32+
public function processNode(Node $node, Scope $scope): array
33+
{
34+
$class = $scope->getClassReflection();
35+
if ($class === null) {
36+
return [];
37+
}
38+
39+
$objectManager = $this->objectMetadataResolver->getObjectManager();
40+
if ($objectManager === null) {
41+
return [];
42+
}
43+
44+
$className = $class->getName();
45+
try {
46+
if ($objectManager->getMetadataFactory()->isTransient($className)) {
47+
return [];
48+
}
49+
} catch (\ReflectionException $e) {
50+
return [];
51+
}
52+
53+
try {
54+
$objectManager->getClassMetadata($className);
55+
} catch (\Doctrine\ORM\Mapping\MappingException $e) {
56+
return [$e->getMessage()];
57+
}
58+
59+
return [];
60+
}
61+
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Doctrine\ORM;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
use PHPStan\Type\Doctrine\ObjectMetadataResolver;
8+
9+
/**
10+
* @extends RuleTestCase<EntityMappingExceptionRule>
11+
*/
12+
class EntityMappingExceptionRuleTest extends RuleTestCase
13+
{
14+
15+
protected function getRule(): Rule
16+
{
17+
return new EntityMappingExceptionRule(
18+
new ObjectMetadataResolver($this->createReflectionProvider(), __DIR__ . '/entity-manager.php', null)
19+
);
20+
}
21+
22+
public function testValidEntity(): void
23+
{
24+
$this->analyse([__DIR__ . '/data-attributes/enum-type.php'], []);
25+
}
26+
27+
public function testInvalidEntity(): void
28+
{
29+
$this->analyse([__DIR__ . '/data-attributes/enum-type-without-pk.php'], [
30+
[
31+
'No identifier/primary key specified for Entity "PHPStan\Rules\Doctrine\ORMAttributes\FooWithoutPK". Every Entity must have an identifier/primary key.',
32+
7,
33+
],
34+
]);
35+
}
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php // lint >= 8.1
2+
3+
namespace PHPStan\Rules\Doctrine\ORMAttributes;
4+
5+
use Doctrine\ORM\Mapping as ORM;
6+
7+
#[ORM\Entity]
8+
class FooWithoutPK
9+
{
10+
11+
#[ORM\Column(type: "string", enumType: FooEnum::class)]
12+
public FooEnum $type;
13+
14+
}

0 commit comments

Comments
 (0)