1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Rules\RestrictedUsage;
4:
5: use PhpParser\Node;
6: use PhpParser\Node\Identifier;
7: use PHPStan\Analyser\Scope;
8: use PHPStan\DependencyInjection\AutowiredExtensions;
9: use PHPStan\DependencyInjection\AutowiredService;
10: use PHPStan\DependencyInjection\ExtensionsCollection;
11: use PHPStan\Node\MethodCallableNode;
12: use PHPStan\Reflection\ReflectionProvider;
13: use PHPStan\Rules\Rule;
14: use PHPStan\Rules\RuleErrorBuilder;
15:
16: /**
17: * @implements Rule<MethodCallableNode>
18: */
19: #[AutowiredService]
20: final class RestrictedMethodCallableUsageRule implements Rule
21: {
22:
23: /**
24: * @param ExtensionsCollection<RestrictedMethodUsageExtension> $extensions
25: */
26: public function __construct(
27: #[AutowiredExtensions(of: RestrictedMethodUsageExtension::class)]
28: private ExtensionsCollection $extensions,
29: private ReflectionProvider $reflectionProvider,
30: )
31: {
32: }
33:
34: public function getNodeType(): string
35: {
36: return MethodCallableNode::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: $extensions = $this->extensions->getAll();
49: if ($extensions === []) {
50: return [];
51: }
52:
53: $methodName = $node->getName()->name;
54: $methodCalledOnType = $scope->getType($node->getVar());
55: $referencedClasses = $methodCalledOnType->getObjectClassNames();
56:
57: $errors = [];
58:
59: foreach ($referencedClasses as $referencedClass) {
60: if (!$this->reflectionProvider->hasClass($referencedClass)) {
61: continue;
62: }
63:
64: $classReflection = $this->reflectionProvider->getClass($referencedClass);
65: if (!$classReflection->hasMethod($methodName)) {
66: continue;
67: }
68:
69: $methodReflection = $classReflection->getMethod($methodName, $scope);
70: foreach ($extensions as $extension) {
71: $restrictedUsage = $extension->isRestrictedMethodUsage($methodReflection, $scope);
72: if ($restrictedUsage === null) {
73: continue;
74: }
75:
76: $errors[] = RuleErrorBuilder::message($restrictedUsage->errorMessage)
77: ->identifier($restrictedUsage->identifier)
78: ->build();
79: }
80: }
81:
82: return $errors;
83: }
84:
85: }
86: