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