Completed
Push — master ( 3f2b6b...56185f )
by Scott
06:20
created

PhpStanLoader::getReflector()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 1
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace exussum12\CoverageChecker;
3
4
use ReflectionFunction;
5
use ReflectionMethod;
6
use Reflector;
7
8
/**
9
 * Class PhpStanLoader
10
 * Used for parsing phpstan standard output
11
 * @package exussum12\CoverageChecker
12
 */
13
class PhpStanLoader implements FileChecker
14
{
15
    protected $lineRegex = '/^\s+(?<lineNumber>[0-9]+)/';
16
17
    protected $file;
18
    protected $relatedRegex = '#(function|method) (?:(?P<class>.*?)::)?(?P<function>.*?)[ \(]#';
19
20
    /**
21
     * @var array
22
     */
23
    protected $invalidLines = [];
24
25
    /**
26
     * @param string $filename the path to the phpstan.txt file
27
     */
28
    public function __construct($filename)
29
    {
30
        $this->file = fopen($filename, 'r');
31
    }
32
33
    /**
34
     * {@inheritdoc}
35
     */
36
    public function getLines()
37
    {
38
        $filename = '';
39
        $lineNumber = 0;
40
        while (($line = fgets($this->file)) !== false) {
41
            $filename = $this->checkForFileName($line, $filename);
42
            if ($lineNumber = $this->getLineNumber($line, $lineNumber)) {
43
                $error = $this->getMessage($line);
44
                $this->handleRelatedError($filename, $lineNumber, $error);
45
46
                $this->addError($filename, $lineNumber, $error);
47
            }
48
        }
49
50
        $this->trimLines();
51
52
        return $this->invalidLines;
53
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    public function isValidLine($file, $lineNumber)
59
    {
60
        return empty($this->invalidLines[$file][$lineNumber]);
61
    }
62
63
64
    /**
65
     * {@inheritdoc}
66
     */
67
    public function handleNotFoundFile()
68
    {
69
        return true;
70
    }
71
72
    /**
73
     * @param string $line
74
     * @param string $currentFile
75
     * @return string the currentFileName
76
     */
77
    protected function checkForFilename($line, $currentFile)
78
    {
79
        if (strpos($line, " Line ")) {
80
            return trim(str_replace('Line', '', $line));
81
        }
82
        return $currentFile;
83
    }
84
85
    protected function getLineNumber($line, $currentLineNumber)
86
    {
87
        $matches = [];
88
        if (!preg_match($this->lineRegex, $line, $matches)) {
89
            if (preg_match('#^\s{3,}#', $line)) {
90
                return $currentLineNumber;
91
            }
92
93
            return false;
94
        }
95
96
        return $matches['lineNumber'];
97
    }
98
99
    protected function getMessage($line)
100
    {
101
        return trim(preg_replace($this->lineRegex, '', $line));
102
    }
103
104
    protected function trimLines()
105
    {
106
        array_walk_recursive($this->invalidLines, function (&$item) {
107
            if (is_string($item)) {
108
                $item = trim($item);
109
            }
110
        });
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116
    public static function getDescription()
117
    {
118
        return 'Parses the text output of phpstan';
119
    }
120
121
    protected function handleRelatedError($filename, $line, $error)
122
    {
123
124
        $matches = [];
125
        if (preg_match($this->relatedRegex, $error, $matches)) {
126
            $error = sprintf(
127
                '%s (used %s line %d)',
128
                $error,
129
                $filename,
130
                $line
131
            );
132
133
            $reflection = $this->getReflector($matches);
134
            if ($reflection && ($filename = $reflection->getFileName())) {
135
                $currentLine = $reflection->getStartLine();
136
                while ($currentLine < $reflection->getEndLine()) {
137
                    $this->addError($filename, $currentLine++, $error);
138
                }
139
            }
140
        }
141
    }
142
143
    /**
144
     * @param $filename
145
     * @param $lineNumber
146
     * @param $error
147
     */
148
    protected function addError($filename, $lineNumber, $error)
149
    {
150
        if (!isset($this->invalidLines[$filename][$lineNumber])) {
151
            $this->invalidLines[$filename][$lineNumber] = '';
152
        }
153
        $this->invalidLines[$filename][$lineNumber] .= $error . ' ';
154
    }
155
156
    /**
157
     * @param $matches
158
     * @return Reflector
159
     */
160
    protected function getReflector($matches)
161
    {
162
        if ($matches['class']) {
163
            return new ReflectionMethod(
164
                $matches['class'],
165
                $matches['function']
166
            );
167
        }
168
169
        return new ReflectionFunction(
170
            $matches['function']
171
        );
172
    }
173
}
174