ILIAS  release_7 Revision v7.30-3-g800a261c036
LegacyClassUsageRule.php
Go to the documentation of this file.
1<?php
2
20
21use PhpParser\Node\Expr\CallLike;
22use PHPStan\Reflection\ReflectionProvider;
23use PHPStan\Rules\Rule;
24use PhpParser\Node;
25use PHPStan\Analyser\Scope;
26use PHPStan\Rules\RuleErrorBuilder;
27
28abstract class LegacyClassUsageRule implements Rule
29{
30 protected ReflectionProvider $reflectionProvider;
31 protected \PHPStan\Rules\Generics\GenericAncestorsCheck $genericAncestorsCheck;
32 private array $forbidden_classes = [];
33 private array $ancestor_cache = [];
34
35 public function __construct(
36 ReflectionProvider $reflectionProvider,
37 \PHPStan\Rules\Generics\GenericAncestorsCheck $genericAncestorsCheck
38 ) {
39 $this->reflectionProvider = $reflectionProvider;
40 $this->genericAncestorsCheck = $genericAncestorsCheck;
41
42 // Determine possible class-names (parents and children) of the forbidden classes
44
45 foreach ($this->getForbiddenClasses() as $forbidden_class) {
46 $ancestors = $this->getClassAncestors($forbidden_class);
47 $this->cacheAncestors($forbidden_class, $ancestors);
48 $forbidden_classes = array_merge(
50 $ancestors
51 );
52 }
53
54 $this->forbidden_classes = array_unique($forbidden_classes);
55 }
56
57 private function getClassAncestors(string $class_name): array
58 {
59 if (isset($this->ancestor_cache[$class_name])) {
60 return $this->ancestor_cache[$class_name];
61 }
62
63 $ancestors[] = $class_name;
64
65 try {
66 $reflection = $this->reflectionProvider->getClass($class_name);
67 $ancestors = array_merge($ancestors, $reflection->getParentClassesNames());
68 } catch (\PHPStan\Broker\ClassNotFoundException $e) {
69 // Do nothing
70 } finally {
71 unset($reflection);
72 }
73 return array_unique($ancestors);
74 }
75
76 private function cacheAncestors($class_name, array $ancestor_classes): void
77 {
78 $this->ancestor_cache[$class_name] = $ancestor_classes;
79 }
80
81 public function getNodeType(): string
82 {
83 return CallLike::class;
84 }
85
86 abstract protected function getForbiddenClasses(): array;
87
88 abstract protected function getHumanReadableRuleName(): string;
89
90 abstract protected function getRelevantILIASVersion(): int;
91
92 final public function processNode(Node $node, Scope $scope): array
93 {
94 switch (true) {
95 case $node instanceof Node\Expr\StaticCall:
96 case $node instanceof Node\Expr\New_:
97 if ($node->class instanceof Node\Name) {
98 $class_name = $node->class->toString();
99 } else {
100 return [];
101 }
102 break;
103 default:
104 return [];
105 }
106
107 $class_names_to_test = $this->getClassAncestors($class_name);
108
109 $array_intersect = array_intersect($class_names_to_test, $this->forbidden_classes);
110 if ($array_intersect !== []) {
111 $this->cacheAncestors($class_name, $class_names_to_test);
112 return [
113 RuleErrorBuilder::message("Usage of $class_name is forbidden.")
114 ->metadata([
115 'rule' => $this->getHumanReadableRuleName(),
116 'version' => $this->getRelevantILIASVersion(),
117 ])
118 ->build()
119 ];
120 }
121
122 return [];
123 }
124}
An exception for terminatinating execution or to throw for unit testing.
cacheAncestors($class_name, array $ancestor_classes)
__construct(ReflectionProvider $reflectionProvider, \PHPStan\Rules\Generics\GenericAncestorsCheck $genericAncestorsCheck)
PHPStan Rules Generics GenericAncestorsCheck $genericAncestorsCheck
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...