Completed
Pull Request — master (#30)
by Scott
02:21
created

PhpStanLoader   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 205
Duplicated Lines 4.39 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 3
Bugs 0 Features 0
Metric Value
dl 9
loc 205
rs 9.6
c 3
b 0
f 0
wmc 32
lcom 1
cbo 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A parseLines() 0 21 4
A getErrorsOnLine() 9 9 2
A handleNotFoundFile() 0 4 1
A checkForFilename() 0 7 2
A getLineNumber() 0 13 3
A getMessage() 0 4 1
A isExtendedMessage() 0 4 1
A trimLines() 0 8 2
A getDescription() 0 4 1
B handleRelatedError() 0 21 5
A addError() 0 7 2
A getReflector() 0 8 2
A appendError() 0 6 1
A getClassReflector() 0 10 2
A getFunctionReflector() 0 9 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
namespace exussum12\CoverageChecker;
3
4
use ReflectionFunction;
5
use ReflectionFunctionAbstract;
6
use ReflectionMethod;
7
use Reflector;
8
9
/**
10
 * Class PhpStanLoader
11
 * Used for parsing phpstan standard output
12
 * @package exussum12\CoverageChecker
13
 */
14
class PhpStanLoader implements FileChecker
15
{
16
    protected $lineRegex = '/^\s+(?<lineNumber>[0-9]+)/';
17
18
    protected $file;
19
    protected $relatedRegex = '#(function|method) (?:(?P<class>.*?)::)?(?P<function>.*?)[ \(]#';
20
21
    /**
22
     * @var array
23
     */
24
    protected $invalidLines = [];
25
26
    /**
27
     * @param string $filename the path to the phpstan.txt file
28
     */
29
    public function __construct($filename)
30
    {
31
        $this->file = fopen($filename, 'r');
32
    }
33
34
    /**
35
     * {@inheritdoc}
36
     */
37
    public function parseLines()
38
    {
39
        $filename = '';
40
        $lineNumber = 0;
41
        while (($line = fgets($this->file)) !== false) {
42
            $filename = $this->checkForFilename($line, $filename);
43
            if ($lineNumber = $this->getLineNumber($line, $lineNumber)) {
44
                $error = $this->getMessage($line);
45
                if ($this->isExtendedMessage($line)) {
46
                    $this->appendError($filename, $lineNumber, $error);
47
                    continue;
48
                }
49
                $this->handleRelatedError($filename, $lineNumber, $error);
50
                $this->addError($filename, $lineNumber, $error);
51
            }
52
        }
53
54
        $this->trimLines();
55
56
        return array_keys($this->invalidLines);
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62 View Code Duplication
    public function getErrorsOnLine($file, $lineNumber)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
63
    {
64
        $errors = [];
65
        if (isset($this->invalidLines[$file][$lineNumber])) {
66
            $errors = $this->invalidLines[$file][$lineNumber];
67
        }
68
69
        return $errors;
70
    }
71
72
73
    /**
74
     * {@inheritdoc}
75
     */
76
    public function handleNotFoundFile()
77
    {
78
        return true;
79
    }
80
81
    /**
82
     * @param string $line
83
     * @param string $currentFile
84
     * @return string the currentFileName
85
     */
86
    protected function checkForFilename($line, $currentFile)
87
    {
88
        if (strpos($line, " Line ")) {
89
            return trim(str_replace('Line', '', $line));
90
        }
91
        return $currentFile;
92
    }
93
94
    protected function getLineNumber($line, $currentLineNumber)
95
    {
96
        $matches = [];
97
        if (!preg_match($this->lineRegex, $line, $matches)) {
98
            if (preg_match('#^\s{3,}#', $line)) {
99
                return $currentLineNumber;
100
            }
101
102
            return false;
103
        }
104
105
        return $matches['lineNumber'];
106
    }
107
108
    protected function getMessage($line)
109
    {
110
        return trim(preg_replace($this->lineRegex, '', $line));
111
    }
112
113
    protected function isExtendedMessage($line)
114
    {
115
        return preg_match($this->lineRegex, $line) === 0;
116
    }
117
118
    protected function trimLines()
119
    {
120
        array_walk_recursive($this->invalidLines, function (&$item) {
121
            if (is_string($item)) {
122
                $item = trim($item);
123
            }
124
        });
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    public static function getDescription()
131
    {
132
        return 'Parses the text output of phpstan';
133
    }
134
135
    protected function handleRelatedError($filename, $line, $error)
136
    {
137
138
        $matches = [];
139
        if (preg_match($this->relatedRegex, $error, $matches)) {
140
            $error = sprintf(
141
                '%s (used %s line %d)',
142
                $error,
143
                $filename,
144
                $line
145
            );
146
147
            $reflection = $this->getReflector($matches);
148
            if ($reflection && ($filename = $reflection->getFileName())) {
149
                $currentLine = $reflection->getStartLine();
150
                while ($currentLine < $reflection->getEndLine()) {
151
                    $this->addError($filename, $currentLine++, $error);
152
                }
153
            }
154
        }
155
    }
156
157
    /**
158
     * @param string $filename
159
     * @param int $lineNumber
160
     * @param string $error
161
     */
162
    protected function addError($filename, $lineNumber, $error)
163
    {
164
        if (!isset($this->invalidLines[$filename][$lineNumber])) {
165
            $this->invalidLines[$filename][$lineNumber] = [];
166
        }
167
        $this->invalidLines[$filename][$lineNumber][] = $error;
168
    }
169
170
    /**
171
     * @param array $matches
172
     * @return ReflectionFunctionAbstract
173
     */
174
    protected function getReflector($matches)
175
    {
176
        if ($matches['class']) {
177
            return $this->getClassReflector($matches);
178
        }
179
180
        return $this->getFunctionReflector($matches);
181
    }
182
183
    private function appendError($filename, $lineNumber, $error)
184
    {
185
        end($this->invalidLines[$filename][$lineNumber]);
186
        $key = key($this->invalidLines[$filename][$lineNumber]);
187
        $this->invalidLines[$filename][$lineNumber][$key] .= ' ' . $error;
188
    }
189
190
    /**
191
     * @param $matches
192
     * @return bool|ReflectionMethod
193
     */
194
    protected function getClassReflector($matches)
195
    {
196
        if (!method_exists($matches['class'], $matches['function'])) {
197
            return false;
198
        }
199
        return new ReflectionMethod(
200
            $matches['class'],
201
            $matches['function']
202
        );
203
    }
204
205
    /**
206
     * @param $matches
207
     * @return bool|ReflectionFunction
208
     */
209
    protected function getFunctionReflector($matches)
210
    {
211
        if (!function_exists($matches['function'])) {
212
            return false;
213
        }
214
        return new ReflectionFunction(
215
            $matches['function']
216
        );
217
    }
218
}
219