1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Rules\RestrictedUsage;
4:
5: use PhpParser\Node;
6: use PhpParser\Node\Identifier;
7: use PHPStan\Analyser\Scope;
8: use PHPStan\DependencyInjection\AutowiredExtensions;
9: use PHPStan\DependencyInjection\AutowiredService;
10: use PHPStan\DependencyInjection\ExtensionsCollection;
11: use PHPStan\Reflection\ReflectionProvider;
12: use PHPStan\Rules\Rule;
13: use PHPStan\Rules\RuleErrorBuilder;
14:
15: /**
16: * @implements Rule<Node\Expr\PropertyFetch>
17: */
18: #[AutowiredService]
19: final class RestrictedPropertyUsageRule implements Rule
20: {
21:
22: /**
23: * @param ExtensionsCollection<RestrictedPropertyUsageExtension> $extensions
24: */
25: public function __construct(
26: #[AutowiredExtensions(of: RestrictedPropertyUsageExtension::class)]
27: private ExtensionsCollection $extensions,
28: private ReflectionProvider $reflectionProvider,
29: )
30: {
31: }
32:
33: public function getNodeType(): string
34: {
35: return Node\Expr\PropertyFetch::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: $extensions = $this->extensions->getAll();
48: if ($extensions === []) {
49: return [];
50: }
51:
52: $propertyName = $node->name->name;
53: $propertyCalledOnType = $scope->getType($node->var);
54: $referencedClasses = $propertyCalledOnType->getObjectClassNames();
55:
56: $errors = [];
57:
58: foreach ($referencedClasses as $referencedClass) {
59: if (!$this->reflectionProvider->hasClass($referencedClass)) {
60: continue;
61: }
62:
63: $classReflection = $this->reflectionProvider->getClass($referencedClass);
64: if (!$classReflection->hasInstanceProperty($propertyName)) {
65: continue;
66: }
67:
68: $propertyReflection = $classReflection->getInstanceProperty($propertyName, $scope);
69: foreach ($extensions as $extension) {
70: $restrictedUsage = $extension->isRestrictedPropertyUsage($propertyReflection, $scope);
71: if ($restrictedUsage === null) {
72: continue;
73: }
74:
75: $errors[] = RuleErrorBuilder::message($restrictedUsage->errorMessage)
76: ->identifier($restrictedUsage->identifier)
77: ->build();
78: }
79: }
80:
81: return $errors;
82: }
83:
84: }
85: