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\AutowiredExtensions;
10: use PHPStan\DependencyInjection\AutowiredService;
11: use PHPStan\DependencyInjection\ExtensionsCollection;
12: use PHPStan\Reflection\ReflectionProvider;
13: use PHPStan\Rules\Rule;
14: use PHPStan\Rules\RuleErrorBuilder;
15: use PHPStan\Rules\RuleLevelHelper;
16: use PHPStan\Type\ErrorType;
17: use PHPStan\Type\Type;
18:
19: /**
20: * @implements Rule<Node\Expr\ClassConstFetch>
21: */
22: #[AutowiredService]
23: final class RestrictedClassConstantUsageRule implements Rule
24: {
25:
26: /**
27: * @param ExtensionsCollection<RestrictedClassConstantUsageExtension> $extensions
28: */
29: public function __construct(
30: #[AutowiredExtensions(of: RestrictedClassConstantUsageExtension::class)]
31: private ExtensionsCollection $extensions,
32: private ReflectionProvider $reflectionProvider,
33: private RuleLevelHelper $ruleLevelHelper,
34: )
35: {
36: }
37:
38: public function getNodeType(): string
39: {
40: return Node\Expr\ClassConstFetch::class;
41: }
42:
43: /**
44: * @api
45: */
46: public function processNode(Node $node, Scope $scope): array
47: {
48: if (!$node->name instanceof Identifier) {
49: return [];
50: }
51:
52: $extensions = $this->extensions->getAll();
53: if ($extensions === []) {
54: return [];
55: }
56:
57: $constantName = $node->name->name;
58: $referencedClasses = [];
59:
60: if ($node->class instanceof Name) {
61: $referencedClasses[] = $scope->resolveName($node->class);
62: } else {
63: $classTypeResult = $this->ruleLevelHelper->findTypeToCheck(
64: $scope,
65: $node->class,
66: '', // We don't care about the error message
67: static fn (Type $type): bool => $type->canAccessConstants()->yes() && $type->hasConstant($constantName)->yes(),
68: );
69:
70: if ($classTypeResult->getType() instanceof ErrorType) {
71: return [];
72: }
73:
74: $referencedClasses = $classTypeResult->getReferencedClasses();
75: }
76:
77: $errors = [];
78: foreach ($referencedClasses as $referencedClass) {
79: if (!$this->reflectionProvider->hasClass($referencedClass)) {
80: continue;
81: }
82:
83: $classReflection = $this->reflectionProvider->getClass($referencedClass);
84: if (!$classReflection->hasConstant($constantName)) {
85: continue;
86: }
87:
88: $constantReflection = $classReflection->getConstant($constantName);
89: foreach ($extensions as $extension) {
90: $restrictedUsage = $extension->isRestrictedClassConstantUsage($constantReflection, $scope);
91: if ($restrictedUsage === null) {
92: continue;
93: }
94:
95: if ($classReflection->getName() !== $constantReflection->getDeclaringClass()->getName()) {
96: $rewrittenConstantReflection = new RewrittenDeclaringClassClassConstantReflection($classReflection, $constantReflection);
97: $rewrittenRestrictedUsage = $extension->isRestrictedClassConstantUsage($rewrittenConstantReflection, $scope);
98: if ($rewrittenRestrictedUsage === null) {
99: continue;
100: }
101: }
102:
103: $errors[] = RuleErrorBuilder::message($restrictedUsage->errorMessage)
104: ->identifier($restrictedUsage->identifier)
105: ->build();
106: }
107: }
108:
109: return $errors;
110: }
111:
112: }
113: