Skip to content

Commit d6c8cd2

Browse files
authored
Merge pull request #250 from mglaman/entity-data-repository
EntityDataRepository for entity type data
2 parents b0709df + 7a451e3 commit d6c8cd2

File tree

7 files changed

+153
-67
lines changed

7 files changed

+153
-67
lines changed

extension.neon

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,28 @@ parameters:
1313
- engine
1414
drupal:
1515
drupal_root: '%currentWorkingDirectory%'
16-
entityTypeStorageMapping:
17-
node: Drupal\node\NodeStorage
18-
taxonomy_term: Drupal\taxonomy\TermStorage
19-
user: Drupal\user\UserStorage
20-
block: Drupal\Core\Config\Entity\ConfigEntityStorage
21-
entityStorageMapping:
22-
node: Drupal\node\Entity\Node
23-
taxonomy_term: Drupal\taxonomy\Entity\Term
24-
user: Drupal\user\Entity\User
25-
block: Drupal\block\Entity\Block
16+
entityMapping:
17+
node:
18+
class: Drupal\node\Entity\Node
19+
storage: Drupal\node\NodeStorage
20+
taxonomy_term:
21+
class: Drupal\taxonomy\Entity\Term
22+
storage: Drupal\taxonomy\TermStorage
23+
user:
24+
class: Drupal\user\Entity\User
25+
storage: Drupal\user\UserStorage
26+
block:
27+
class: Drupal\block\Entity\Block
28+
storage: Drupal\Core\Config\Entity\ConfigEntityStorage
2629
parametersSchema:
2730
drupal: structure([
2831
drupal_root: string()
29-
entityTypeStorageMapping: arrayOf(string())
30-
entityStorageMapping: arrayOf(string())
32+
entityMapping: arrayOf(
33+
structure([
34+
class: string()
35+
storage: string()
36+
])
37+
)
3138
])
3239
rules:
3340
- mglaman\PHPStanDrupal\Rules\Classes\PluginManagerInspectionRule
@@ -38,17 +45,16 @@ rules:
3845
services:
3946
-
4047
class: mglaman\PHPStanDrupal\Drupal\ServiceMap
48+
-
49+
class: mglaman\PHPStanDrupal\Drupal\EntityDataRepository
50+
arguments:
51+
entityMapping: %drupal.entityMapping%
4152

4253
-
4354
class: mglaman\PHPStanDrupal\Type\EntityTypeManagerGetStorageDynamicReturnTypeExtension
44-
arguments:
45-
reflectionProvider: @reflectionProvider
46-
entityTypeStorageMapping: %drupal.entityTypeStorageMapping%
4755
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
4856
-
4957
class: mglaman\PHPStanDrupal\Type\EntityStorage\EntityStorageDynamicReturnTypeExtension
50-
arguments:
51-
entityStorageMapping: %drupal.entityStorageMapping%
5258
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
5359
-
5460
class: mglaman\PHPStanDrupal\Type\ContainerDynamicReturnTypeExtension

src/Drupal/EntityData.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace mglaman\PHPStanDrupal\Drupal;
4+
5+
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
6+
use Drupal\Core\Entity\ContentEntityStorageInterface;
7+
use Drupal\Core\Entity\EntityStorageInterface;
8+
use mglaman\PHPStanDrupal\Type\EntityStorage\ConfigEntityStorageType;
9+
use mglaman\PHPStanDrupal\Type\EntityStorage\ContentEntityStorageType;
10+
use mglaman\PHPStanDrupal\Type\EntityStorage\EntityStorageType;
11+
use PHPStan\Type\ObjectType;
12+
13+
final class EntityData
14+
{
15+
16+
/**
17+
* @var string
18+
*/
19+
private $entityTypeId;
20+
21+
/**
22+
* @var string|null
23+
*/
24+
private $className;
25+
26+
/**
27+
* @var string|null
28+
*/
29+
private $storageClassName;
30+
31+
public function __construct(string $entityTypeId, array $definition)
32+
{
33+
$this->entityTypeId = $entityTypeId;
34+
$this->className = $definition['class'] ?? null;
35+
$this->storageClassName = $definition['storage'] ?? null;
36+
}
37+
38+
public function getClassType(): ?ObjectType
39+
{
40+
return $this->className === null ? null : new ObjectType($this->className);
41+
}
42+
43+
public function getStorageType(): ?ObjectType
44+
{
45+
if ($this->storageClassName === null) {
46+
// @todo get entity type class reflection and return proper storage for entity type
47+
// example: config storage, sqlcontententitystorage, etc.
48+
// $className = reflectedDecision.
49+
return null;
50+
}
51+
52+
$storageType = new ObjectType($this->storageClassName);
53+
if ((new ObjectType(EntityStorageInterface::class))->isSuperTypeOf($storageType)->no()) {
54+
return null;
55+
}
56+
if ((new ObjectType(ConfigEntityStorageInterface::class))->isSuperTypeOf($storageType)->yes()) {
57+
return new ConfigEntityStorageType($this->entityTypeId, $this->storageClassName);
58+
}
59+
if ((new ObjectType(ContentEntityStorageInterface::class))->isSuperTypeOf($storageType)->yes()) {
60+
return new ContentEntityStorageType($this->entityTypeId, $this->storageClassName);
61+
}
62+
return new EntityStorageType($this->entityTypeId, $this->storageClassName);
63+
}
64+
}

src/Drupal/EntityDataRepository.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace mglaman\PHPStanDrupal\Drupal;
4+
5+
use PHPStan\Reflection\ReflectionProvider;
6+
7+
final class EntityDataRepository
8+
{
9+
10+
/**
11+
* @var array<string, array<string, string>>
12+
*/
13+
private $entityMapping;
14+
/**
15+
* @var array<string, EntityData|null>
16+
*/
17+
private $entityData;
18+
19+
public function __construct(array $entityMapping)
20+
{
21+
$this->entityMapping = $entityMapping;
22+
}
23+
24+
public function get(string $entityTypeId): EntityData
25+
{
26+
if (!isset($this->entityData[$entityTypeId])) {
27+
$this->entityData[$entityTypeId] = new EntityData(
28+
$entityTypeId,
29+
$this->entityMapping[$entityTypeId] ?? []
30+
);
31+
}
32+
return $this->entityData[$entityTypeId];
33+
}
34+
}

src/Type/EntityStorage/EntityStorageDynamicReturnTypeExtension.php

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace mglaman\PHPStanDrupal\Type\EntityStorage;
44

55
use Drupal\Core\Entity\EntityStorageInterface;
6+
use mglaman\PHPStanDrupal\Drupal\EntityDataRepository;
67
use PhpParser\Node\Expr\MethodCall;
78
use PHPStan\Analyser\Scope;
89
use PHPStan\Reflection\MethodReflection;
@@ -18,19 +19,13 @@ class EntityStorageDynamicReturnTypeExtension implements DynamicMethodReturnType
1819
{
1920

2021
/**
21-
* @var string[]
22+
* @var EntityDataRepository
2223
*/
23-
private $entityStorageMapping;
24+
private $entityDataRepository;
2425

25-
/**
26-
* EntityStorageDynamicReturnTypeExtension constructor.
27-
*
28-
* @param string[] $entityStorageMapping
29-
*/
30-
public function __construct(
31-
array $entityStorageMapping = []
32-
) {
33-
$this->entityStorageMapping = $entityStorageMapping;
26+
public function __construct(EntityDataRepository $entityDataRepository)
27+
{
28+
$this->entityDataRepository = $entityDataRepository;
3429
}
3530

3631
public function getClass(): string
@@ -64,12 +59,10 @@ public function getTypeFromMethodCall(
6459
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
6560
}
6661

67-
$entityClassName = $this->entityStorageMapping[$callerType->getEntityTypeId()] ?? null;
68-
if (null === $entityClassName) {
62+
$type = $this->entityDataRepository->get($callerType->getEntityTypeId())->getClassType();
63+
if ($type === null) {
6964
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
7065
}
71-
72-
$type = new ObjectType($entityClassName);
7366
if (\in_array($methodReflection->getName(), ['load', 'loadUnchanged'], true)) {
7467
return TypeCombinator::addNull($type);
7568
}

src/Type/EntityTypeManagerGetStorageDynamicReturnTypeExtension.php

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
66
use Drupal\Core\Entity\ContentEntityStorageInterface;
7+
use mglaman\PHPStanDrupal\Drupal\EntityDataRepository;
78
use mglaman\PHPStanDrupal\Type\EntityStorage\ConfigEntityStorageType;
89
use mglaman\PHPStanDrupal\Type\EntityStorage\ContentEntityStorageType;
910
use mglaman\PHPStanDrupal\Type\EntityStorage\EntityStorageType;
@@ -21,26 +22,20 @@
2122

2223
class EntityTypeManagerGetStorageDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
2324
{
24-
/**
25-
* @var ReflectionProvider
26-
*/
27-
private $reflectionProvider;
2825

2926
/**
30-
* @var string[]
27+
* @var EntityDataRepository
3128
*/
32-
private $entityTypeStorageMapping;
29+
private $entityDataRepository;
3330

3431
/**
3532
* EntityTypeManagerGetStorageDynamicReturnTypeExtension constructor.
3633
*
37-
* @param ReflectionProvider $reflectionProvider
38-
* @param string[] $entityTypeStorageMapping
34+
* @param EntityDataRepository $entityDataRepository
3935
*/
40-
public function __construct(ReflectionProvider $reflectionProvider, array $entityTypeStorageMapping = [])
36+
public function __construct(EntityDataRepository $entityDataRepository)
4137
{
42-
$this->reflectionProvider = $reflectionProvider;
43-
$this->entityTypeStorageMapping = $entityTypeStorageMapping;
38+
$this->entityDataRepository = $entityDataRepository;
4439
}
4540

4641
public function getClass(): string
@@ -86,23 +81,11 @@ public function getTypeFromMethodCall(
8681

8782
$entityTypeId = $arg1->value;
8883

89-
if (isset($this->entityTypeStorageMapping[$entityTypeId])) {
90-
$storageClassName = $this->entityTypeStorageMapping[$entityTypeId];
91-
$interfaces = \array_keys($this->reflectionProvider->getClass($storageClassName)->getInterfaces());
92-
93-
if (\in_array(ConfigEntityStorageInterface::class, $interfaces, true)) {
94-
return new ConfigEntityStorageType($entityTypeId, $storageClassName);
95-
}
96-
97-
if (\in_array(ContentEntityStorageInterface::class, $interfaces, true)) {
98-
return new ContentEntityStorageType($entityTypeId, $storageClassName);
99-
}
100-
101-
return new EntityStorageType($entityTypeId, $storageClassName);
84+
$storageType = $this->entityDataRepository->get($entityTypeId)->getStorageType();
85+
if ($storageType !== null) {
86+
return $storageType;
10287
}
10388

104-
// @todo get entity type class reflection and return proper storage for entity type
105-
// example: config storage, sqlcontententitystorage, etc.
10689
if ($returnType instanceof ObjectType) {
10790
return new EntityStorageType($entityTypeId, $returnType->getClassName());
10891
}
Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
parameters:
22
drupal:
33
drupal_root: tests/fixtures/drupal
4-
entityTypeStorageMapping:
5-
content_entity_using_custom_storage: Drupal\phpstan_fixtures\CustomContentEntityStorage
6-
config_entity_using_default_storage: Drupal\Core\Config\Entity\ConfigEntityStorage
7-
config_entity_using_custom_storage: Drupal\phpstan_fixtures\CustomConfigEntityStorage
8-
entityStorageMapping:
9-
content_entity_using_default_storage: Drupal\phpstan_fixtures\Entity\ContentEntityUsingDefaultStorage
10-
content_entity_using_custom_storage: Drupal\phpstan_fixtures\Entity\ContentEntityUsingCustomStorage
11-
config_entity_using_default_storage: Drupal\phpstan_fixtures\Entity\ConfigEntityUsingDefaultStorage
12-
config_entity_using_custom_storage: Drupal\phpstan_fixtures\Entity\ConfigEntityUsingCustomStorage
4+
entityMapping:
5+
content_entity_using_custom_storage:
6+
class: Drupal\phpstan_fixtures\Entity\ContentEntityUsingCustomStorage
7+
storage: Drupal\phpstan_fixtures\CustomContentEntityStorage
8+
config_entity_using_default_storage:
9+
class: Drupal\phpstan_fixtures\Entity\ConfigEntityUsingDefaultStorage
10+
storage: Drupal\Core\Config\Entity\ConfigEntityStorage
11+
config_entity_using_custom_storage:
12+
class: Drupal\phpstan_fixtures\Entity\ConfigEntityUsingCustomStorage
13+
storage: Drupal\phpstan_fixtures\CustomConfigEntityStorage
14+
content_entity_using_default_storage:
15+
class: Drupal\phpstan_fixtures\Entity\ContentEntityUsingDefaultStorage
1316
includes:
1417
- ../../../extension.neon
1518
- ../../../vendor/phpstan/phpstan-deprecation-rules/rules.neon

tests/src/Type/data/entity-type-manager.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
assertType('Drupal\taxonomy\TermStorage', $etm->getStorage('taxonomy_term'));
1313
assertType('Drupal\Core\Entity\EntityStorageInterface', $etm->getStorage('search_api_index'));
1414
assertType('Drupal\Core\Config\Entity\ConfigEntityStorage', $etm->getStorage('block'));
15+
// @todo fix this assert with a lookup on known entity class type.
16+
// assertType('Drupal\Core\Entity\Sql\SqlContentEntityStorage', $etm->getStorage('content_entity_using_default_storage'));
17+
assertType('Drupal\Core\Entity\EntityStorageInterface', $etm->getStorage('content_entity_using_default_storage'));
1518
assertType('Drupal\phpstan_fixtures\CustomContentEntityStorage', $etm->getStorage('content_entity_using_custom_storage'));
1619
assertType('Drupal\Core\Config\Entity\ConfigEntityStorage', $etm->getStorage('config_entity_using_default_storage'));
1720
assertType('Drupal\phpstan_fixtures\CustomConfigEntityStorage', $etm->getStorage('config_entity_using_custom_storage'));

0 commit comments

Comments
 (0)