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