1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Rules\Exceptions;
4:
5: use Nette\Utils\Strings;
6: use PHPStan\Analyser\Scope;
7: use PHPStan\Reflection\ReflectionProvider;
8: use function count;
9:
10: /**
11: * @api
12: * @final
13: */
14: class DefaultExceptionTypeResolver implements ExceptionTypeResolver
15: {
16:
17: /**
18: * @param string[] $uncheckedExceptionRegexes
19: * @param string[] $uncheckedExceptionClasses
20: * @param string[] $checkedExceptionRegexes
21: * @param string[] $checkedExceptionClasses
22: */
23: public function __construct(
24: private ReflectionProvider $reflectionProvider,
25: private array $uncheckedExceptionRegexes,
26: private array $uncheckedExceptionClasses,
27: private array $checkedExceptionRegexes,
28: private array $checkedExceptionClasses,
29: )
30: {
31: }
32:
33: public function isCheckedException(string $className, Scope $scope): bool
34: {
35: foreach ($this->uncheckedExceptionRegexes as $regex) {
36: if (Strings::match($className, $regex) !== null) {
37: return false;
38: }
39: }
40:
41: foreach ($this->uncheckedExceptionClasses as $uncheckedExceptionClass) {
42: if ($className === $uncheckedExceptionClass) {
43: return false;
44: }
45: }
46:
47: if (!$this->reflectionProvider->hasClass($className)) {
48: return $this->isCheckedExceptionInternal($className);
49: }
50:
51: $classReflection = $this->reflectionProvider->getClass($className);
52: foreach ($this->uncheckedExceptionClasses as $uncheckedExceptionClass) {
53: if ($classReflection->getName() === $uncheckedExceptionClass) {
54: return false;
55: }
56:
57: if (!$classReflection->isSubclassOf($uncheckedExceptionClass)) {
58: continue;
59: }
60:
61: return false;
62: }
63:
64: return $this->isCheckedExceptionInternal($className);
65: }
66:
67: private function isCheckedExceptionInternal(string $className): bool
68: {
69: foreach ($this->checkedExceptionRegexes as $regex) {
70: if (Strings::match($className, $regex) !== null) {
71: return true;
72: }
73: }
74:
75: foreach ($this->checkedExceptionClasses as $checkedExceptionClass) {
76: if ($className === $checkedExceptionClass) {
77: return true;
78: }
79: }
80:
81: if (!$this->reflectionProvider->hasClass($className)) {
82: return count($this->checkedExceptionRegexes) === 0 && count($this->checkedExceptionClasses) === 0;
83: }
84:
85: $classReflection = $this->reflectionProvider->getClass($className);
86: foreach ($this->checkedExceptionClasses as $checkedExceptionClass) {
87: if ($classReflection->getName() === $checkedExceptionClass) {
88: return true;
89: }
90:
91: if (!$classReflection->isSubclassOf($checkedExceptionClass)) {
92: continue;
93: }
94:
95: return true;
96: }
97:
98: return count($this->checkedExceptionRegexes) === 0 && count($this->checkedExceptionClasses) === 0;
99: }
100:
101: }
102: