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\Container;
9: use PHPStan\Node\MethodCallableNode;
10: use PHPStan\Reflection\ReflectionProvider;
11: use PHPStan\Rules\Rule;
12: use PHPStan\Rules\RuleErrorBuilder;
13:
14: /**
15: * @implements Rule<MethodCallableNode>
16: */
17: final class RestrictedMethodCallableUsageRule implements Rule
18: {
19:
20: public function __construct(
21: private Container $container,
22: private ReflectionProvider $reflectionProvider,
23: )
24: {
25: }
26:
27: public function getNodeType(): string
28: {
29: return MethodCallableNode::class;
30: }
31:
32: /**
33: * @api
34: */
35: public function processNode(Node $node, Scope $scope): array
36: {
37: if (!$node->getName() instanceof Identifier) {
38: return [];
39: }
40:
41: /** @var RestrictedMethodUsageExtension[] $extensions */
42: $extensions = $this->container->getServicesByTag(RestrictedMethodUsageExtension::METHOD_EXTENSION_TAG);
43: if ($extensions === []) {
44: return [];
45: }
46:
47: $methodName = $node->getName()->name;
48: $methodCalledOnType = $scope->getType($node->getVar());
49: $referencedClasses = $methodCalledOnType->getObjectClassNames();
50:
51: $errors = [];
52:
53: foreach ($referencedClasses as $referencedClass) {
54: if (!$this->reflectionProvider->hasClass($referencedClass)) {
55: continue;
56: }
57:
58: $classReflection = $this->reflectionProvider->getClass($referencedClass);
59: if (!$classReflection->hasMethod($methodName)) {
60: continue;
61: }
62:
63: $methodReflection = $classReflection->getMethod($methodName, $scope);
64: foreach ($extensions as $extension) {
65: $restrictedUsage = $extension->isRestrictedMethodUsage($methodReflection, $scope);
66: if ($restrictedUsage === null) {
67: continue;
68: }
69:
70: $errors[] = RuleErrorBuilder::message($restrictedUsage->errorMessage)
71: ->identifier($restrictedUsage->identifier)
72: ->build();
73: }
74: }
75:
76: return $errors;
77: }
78:
79: }
80: