Passed
Push — main ( bdd3af...bc6954 )
by mikhail
03:49
created

UncaughtExceptionVisitor::enterNode()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
dl 0
loc 19
ccs 10
cts 10
cp 1
rs 9.6111
c 1
b 0
f 0
cc 5
nc 16
nop 1
crap 5
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 41
    public function __construct(?Class_ $class)
25
    {
26 41
        $this->methodRegistrar = new MethodRegistrar($class);
27 41
        $this->exceptionChecker = new ExceptionChecker();
28 41
        $this->docBlockFactory = DocBlockFactory::createInstance();
29
    }
30
31 41
    public function hasUncaughtException(): bool
32
    {
33 41
        return $this->exceptionChecker->hasUncaughtThrows;
34
    }
35
36 41
    public function enterNode(Node $node): ?Node
37
    {
38 41
        if ($node instanceof ClassMethod) {
39 34
            $this->methodRegistrar->registerClassMethod($node);
40
        }
41
42 41
        if ($node instanceof TryCatch) {
43 7
            $this->exceptionChecker->pushTryCatch($node);
44
        }
45
46 41
        if ($node instanceof Throw_) {
47 7
            $this->exceptionChecker->checkIfExceptionIsCaught($node);
48
        }
49
50 41
        if ($node instanceof MethodCall) {
51 7
            $this->checkMethodCallForThrowingUncaughtException($node);
52
        }
53
54 41
        return null;
55
    }
56
57 41
    public function leaveNode(Node $node): ?Node
58
    {
59 41
        if ($node instanceof TryCatch) {
60 7
            $this->exceptionChecker->popTryCatch();
61
        }
62
63 41
        return null;
64
    }
65
66 7
    private function checkMethodCallForThrowingUncaughtException(MethodCall $node): void
67
    {
68 7
        $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 7
        if (!isset($node->var->name)) {
70
            return;
71
        }
72
73 7
        $objectName = (string) $node->var->name;
74 7
        $class = $this->methodRegistrar->getVariableTypes()[$objectName] ?? null;
75
76 7
        if (!$class) {
77 2
            return;
78
        }
79
80 5
        $exceptions = $this->getMethodThrownExceptions($class, $methodName);
81 5
        foreach ($exceptions as $exception) {
82 4
            $throwNode = new Throw_(new Variable($exception), $node->getAttributes());
83 4
            $this->exceptionChecker->checkIfExceptionIsCaught($throwNode);
84
        }
85
    }
86
87 5
    private function getMethodThrownExceptions(string $className, string $methodName): array
88
    {
89 5
        if (!class_exists($className)) {
90
            return [];
91
        }
92
93 5
        $reflectionClass = new ReflectionClass($className);
94 5
        if (!$reflectionClass->hasMethod($methodName)) {
95
            return [];
96
        }
97
98 5
        $reflectionMethod = $reflectionClass->getMethod($methodName);
99 5
        $docComment = $reflectionMethod->getDocComment();
100
101 5
        if (!$docComment) {
102
            return [];
103
        }
104
105 5
        $docBlock = $this->docBlockFactory->create($docComment);
106
107 5
        $exceptions = [];
108 5
        foreach ($docBlock->getTagsByName('throws') as $tag) {
109 4
            $exceptions[] = (string)$tag->getType();
110
        }
111
112 5
        return $exceptions;
113
    }
114
}
115