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