FileInspector::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
namespace IntegerNet\TodoReminder;
4
5
use Gitonomy\Git\Repository;
6
use PhpParser\Comment;
7
use PhpParser\Node;
8
use PhpParser\NodeTraverser;
9
use PhpParser\NodeVisitorAbstract;
10
use PhpParser\Parser;
11
12
class FileInspector
13
{
14
    /**
15
     * @var Repository
16
     */
17
    private $repository;
18
    /**
19
     * @var Parser
20
     */
21
    private $parser;
22
23 6
    public function __construct(Repository $repository, Parser $parser)
24
    {
25 6
        $this->repository = $repository;
26 6
        $this->parser = $parser;
27 6
    }
28
29
    /**
30
     *
31
     * Command line version, given $FILE and $LINE of comment:
32
     *
33
     * git blame -p -L $LINE,$LINE $FILE
34
     * => commit hash $COMMIT
35
     *
36
     * git log --oneline --follow -M $COMMIT..HEAD -- $FILE
37
     * => commits to file since $COMMIT
38
     *
39
     * wc
40
     * => count
41
     *
42
     *
43
     * @param string $filePath
44
     * @return TodoComments
45
     */
46 6
    public function findTodoComments(string $filePath): TodoComments
47
    {
48 6
        if (0 === strpos($filePath, $this->repository->getPath())) {
49
            $filePath = (string)substr($filePath, strlen($this->repository->getPath()));
50
        }
51 6
        $fullPath = $this->repository->getPath() . '/' . $filePath;
52 6
        $fileContents = file_get_contents($fullPath);
53 6
        if ($fileContents === false) {
54
            throw new \RuntimeException('File not readable: ' . $filePath);
55
        }
56
        $ast = $this->parser->parse($fileContents);
57
        if ($ast === null) {
58
            throw new \RuntimeException('File could not be parsed: ' . $filePath);
59
        }
60
        $traverser = new NodeTraverser();
61 6
        $commentVisitor = new class extends NodeVisitorAbstract
62
        {
63 6
            /**
64 6
             * @var Comment[]
65
             */
66 6
            public $comments = [];
67 6
68
            public function enterNode(Node $node)
69
            {
70 6
                $comments = $node->getAttribute('comments');
71
                foreach ((array)$comments as $comment) {
72 6
                    /** @var Comment $comment */
73 6
                    if (preg_match('{\bTODO\b}i', $comment->getText())) {
74
                        $this->comments[] = $comment;
75 6
                    }
76 6
                }
77
            }
78 6
        };
79
        $traverser->addVisitor($commentVisitor);
80 6
        $traverser->traverse($ast);
81
82 6
        $todoComments = new TodoComments();
83 6
        foreach ($commentVisitor->comments as $comment) {
84 6
            /** @var Comment $comment */
85 6
            $line = $comment->getLine();
86 6
            $introducedInCommit =
87 6
                $this->repository->getBlame('HEAD', $filePath, "$line,$line")->getLine(1)->getCommit();
88 6
            $notDoneSince =
89
                $this->repository->getLog([$introducedInCommit->getHash() . "..HEAD"], [$filePath])->count();
90
            $todoComments->append(
91
                new TodoComment(
92 6
                    $filePath,
93
                    $comment->getLine(),
94
                    $comment->getReformattedText(),
95
                    $notDoneSince
96
                )
97
            );
98
        }
99
        return $todoComments;
100
    }
101
}
102