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\Node\StaticMethodCallableNode;
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<StaticMethodCallableNode>
20: */
21: final class RestrictedStaticMethodCallableUsageRule implements Rule
22: {
23:
24: public function __construct(
25: private Container $container,
26: private ReflectionProvider $reflectionProvider,
27: private RuleLevelHelper $ruleLevelHelper,
28: )
29: {
30: }
31:
32: public function getNodeType(): string
33: {
34: return StaticMethodCallableNode::class;
35: }
36:
37: /**
38: * @api
39: */
40: public function processNode(Node $node, Scope $scope): array
41: {
42: if (!$node->getName() instanceof Identifier) {
43: return [];
44: }
45:
46: /** @var RestrictedMethodUsageExtension[] $extensions */
47: $extensions = $this->container->getServicesByTag(RestrictedMethodUsageExtension::METHOD_EXTENSION_TAG);
48: if ($extensions === []) {
49: return [];
50: }
51:
52: $methodName = $node->getName()->name;
53: $referencedClasses = [];
54:
55: if ($node->getClass() instanceof Name) {
56: $referencedClasses[] = $scope->resolveName($node->getClass());
57: } else {
58: $classTypeResult = $this->ruleLevelHelper->findTypeToCheck(
59: $scope,
60: $node->getClass(),
61: '', // We don't care about the error message
62: static fn (Type $type): bool => $type->canCallMethods()->yes() && $type->hasMethod($methodName)->yes(),
63: );
64:
65: if ($classTypeResult->getType() instanceof ErrorType) {
66: return [];
67: }
68:
69: $referencedClasses = $classTypeResult->getReferencedClasses();
70: }
71:
72: $errors = [];
73: foreach ($referencedClasses as $referencedClass) {
74: if (!$this->reflectionProvider->hasClass($referencedClass)) {
75: continue;
76: }
77:
78: $classReflection = $this->reflectionProvider->getClass($referencedClass);
79: if (!$classReflection->hasMethod($methodName)) {
80: continue;
81: }
82:
83: $methodReflection = $classReflection->getMethod($methodName, $scope);
84: foreach ($extensions as $extension) {
85: $restrictedUsage = $extension->isRestrictedMethodUsage($methodReflection, $scope);
86: if ($restrictedUsage === null) {
87: continue;
88: }
89:
90: if ($classReflection->getName() !== $methodReflection->getDeclaringClass()->getName()) {
91: $rewrittenMethodReflection = new RewrittenDeclaringClassMethodReflection($classReflection, $methodReflection);
92: $rewrittenRestrictedUsage = $extension->isRestrictedMethodUsage($rewrittenMethodReflection, $scope);
93: if ($rewrittenRestrictedUsage === null) {
94: continue;
95: }
96: }
97:
98: $errors[] = RuleErrorBuilder::message($restrictedUsage->errorMessage)
99: ->identifier($restrictedUsage->identifier)
100: ->build();
101: }
102: }
103:
104: return $errors;
105: }
106:
107: }
108: