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