Test Failed
Push — main ( 46eee6...5832e5 )
by mikhail
03:28
created

UncaughtExceptionVisitor::isInTryBlock()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 10
ccs 6
cts 6
cp 1
rs 10
c 0
b 0
f 0
cc 4
nc 4
nop 1
crap 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SavinMikhail\CommentsDensity\MissingDocblock\Visitors;
6
7
use phpDocumentor\Reflection\DocBlockFactory;
8
use PhpParser\Node;
9
use PhpParser\Node\Expr\MethodCall;
10
use PhpParser\Node\Expr\Throw_;
11
use PhpParser\Node\Expr\Variable;
12
use PhpParser\Node\Stmt\Class_;
13
use PhpParser\Node\Stmt\ClassMethod;
14
use PhpParser\Node\Stmt\TryCatch;
15
use PhpParser\NodeVisitorAbstract;
16
use ReflectionClass;
17
18
final class UncaughtExceptionVisitor extends NodeVisitorAbstract
19
{
20
    private MethodRegistrar $methodRegistrar;
21
    private ExceptionChecker $exceptionChecker;
22
    private DocBlockFactory $docBlockFactory;
23
24
    public function __construct(?Class_ $class)
25
    {
26
        $this->methodRegistrar = new MethodRegistrar($class);
27
        $this->exceptionChecker = new ExceptionChecker();
28
        $this->docBlockFactory = DocBlockFactory::createInstance();
29
    }
30
31
    public function hasUncaughtException(): bool
32
    {
33
        return $this->exceptionChecker->hasUncaughtThrows;
34
    }
35 39
36
    public function enterNode(Node $node): ?Node
37 39
    {
38
        if ($node instanceof ClassMethod) {
39 32
            $this->methodRegistrar->registerClassMethod($node);
40
        }
41 32
42 32
        if ($node instanceof TryCatch) {
43 32
            $this->exceptionChecker->pushTryCatch($node);
44
        }
45 32
46 32
        if ($node instanceof Throw_) {
47 2
            $this->exceptionChecker->checkIfExceptionIsCaught($node);
48
        }
49 30
50 30
        if ($node instanceof MethodCall) {
51 30
            $this->checkMethodCallForThrowingUncaughtException($node);
52
        }
53 6
54 6
        return null;
55 6
    }
56 6
57
    public function leaveNode(Node $node): ?Node
58
    {
59
        if ($node instanceof TryCatch) {
60
            $this->exceptionChecker->popTryCatch();
61 10
        }
62
63 10
        return null;
64 4
    }
65 7
66 2
    private function checkMethodCallForThrowingUncaughtException(MethodCall $node): void
67 5
    {
68
        $methodName = $node->name->name;
0 ignored issues
show
Bug introduced by
Accessing name on the interface PhpParser\Node suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
69
        if (!isset($node->var->name)) {
70
            return;
71
        }
72 7
73
        $objectName = (string) $node->var->name;
74 7
        $class = $this->methodRegistrar->getVariableTypes()[$objectName] ?? null;
75 7
76
        if (!$class) {
77
            return;
78
        }
79 7
80
        $exceptions = $this->getMethodThrownExceptions($class, $methodName);
81 7
        foreach ($exceptions as $exception) {
82
            $throwNode = new Throw_(new Variable($exception), $node->getAttributes());
83 7
            $this->exceptionChecker->checkIfExceptionIsCaught($throwNode);
84 2
        }
85
    }
86
87 5
    private function getMethodThrownExceptions(string $className, string $methodName): array
88 5
    {
89 4
        if (!class_exists($className)) {
90 4
            return [];
91
        }
92
93
        $reflectionClass = new ReflectionClass($className);
94 39
        if (!$reflectionClass->hasMethod($methodName)) {
95
            return [];
96 39
        }
97 32
98
        $reflectionMethod = $reflectionClass->getMethod($methodName);
99
        $docComment = $reflectionMethod->getDocComment();
100 39
101 7
        if (!$docComment) {
102
            return [];
103
        }
104 39
105 7
        $docBlock = $this->docBlockFactory->create($docComment);
106
107
        $exceptions = [];
108 39
        foreach ($docBlock->getTagsByName('throws') as $tag) {
109 7
            $exceptions[] = (string)$tag->getType();
110
        }
111
112 39
        return $exceptions;
113
    }
114
}
115