1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Rules\RestrictedUsage;
4:
5: use PhpParser\Node;
6: use PhpParser\Node\Identifier;
7: use PhpParser\Node\Name;
8: use PHPStan\Analyser\Scope;
9: use PHPStan\DependencyInjection\AutowiredService;
10: use PHPStan\DependencyInjection\Container;
11: use PHPStan\Reflection\ReflectionProvider;
12: use PHPStan\Rules\Rule;
13: use PHPStan\Rules\RuleErrorBuilder;
14: use PHPStan\Rules\RuleLevelHelper;
15: use PHPStan\Type\ErrorType;
16: use PHPStan\Type\Type;
17:
18: /**
19: * @implements Rule<Node\Expr\StaticPropertyFetch>
20: */
21: #[AutowiredService]
22: final class RestrictedStaticPropertyUsageRule implements Rule
23: {
24:
25: public function __construct(
26: private Container $container,
27: private ReflectionProvider $reflectionProvider,
28: private RuleLevelHelper $ruleLevelHelper,
29: )
30: {
31: }
32:
33: public function getNodeType(): string
34: {
35: return Node\Expr\StaticPropertyFetch::class;
36: }
37:
38: /**
39: * @api
40: */
41: public function processNode(Node $node, Scope $scope): array
42: {
43: if (!$node->name instanceof Identifier) {
44: return [];
45: }
46:
47: /** @var RestrictedPropertyUsageExtension[] $extensions */
48: $extensions = $this->container->getServicesByTag(RestrictedPropertyUsageExtension::PROPERTY_EXTENSION_TAG);
49: if ($extensions === []) {
50: return [];
51: }
52:
53: $propertyName = $node->name->name;
54: $referencedClasses = [];
55:
56: if ($node->class instanceof Name) {
57: $referencedClasses[] = $scope->resolveName($node->class);
58: } else {
59: $classTypeResult = $this->ruleLevelHelper->findTypeToCheck(
60: $scope,
61: $node->class,
62: '', // We don't care about the error message
63: static fn (Type $type): bool => $type->canAccessProperties()->yes() && $type->hasProperty($propertyName)->yes(),
64: );
65:
66: if ($classTypeResult->getType() instanceof ErrorType) {
67: return [];
68: }
69:
70: $referencedClasses = $classTypeResult->getReferencedClasses();
71: }
72:
73: $errors = [];
74: foreach ($referencedClasses as $referencedClass) {
75: if (!$this->reflectionProvider->hasClass($referencedClass)) {
76: continue;
77: }
78:
79: $classReflection = $this->reflectionProvider->getClass($referencedClass);
80: if (!$classReflection->hasProperty($propertyName)) {
81: continue;
82: }
83:
84: $propertyReflection = $classReflection->getProperty($propertyName, $scope);
85: foreach ($extensions as $extension) {
86: $restrictedUsage = $extension->isRestrictedPropertyUsage($propertyReflection, $scope);
87: if ($restrictedUsage === null) {
88: continue;
89: }
90:
91: if ($classReflection->getName() !== $propertyReflection->getDeclaringClass()->getName()) {
92: $rewrittenPropertyReflection = new RewrittenDeclaringClassPropertyReflection($classReflection, $propertyReflection);
93: $rewrittenRestrictedUsage = $extension->isRestrictedPropertyUsage($rewrittenPropertyReflection, $scope);
94: if ($rewrittenRestrictedUsage === null) {
95: continue;
96: }
97: }
98:
99: $errors[] = RuleErrorBuilder::message($restrictedUsage->errorMessage)
100: ->identifier($restrictedUsage->identifier)
101: ->build();
102: }
103: }
104:
105: return $errors;
106: }
107:
108: }
109: