Completed
Push — master ( dcd98f...8e1797 )
by Luis
07:11 queued 02:59
created

TokenParser::saveIdentifier()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 40
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 24
nc 7
nop 1
dl 0
loc 40
ccs 23
cts 23
cp 1
crap 7
rs 6.7272
c 0
b 0
f 0
1
<?php
2
/**
3
 * PHP version 7.1
4
 *
5
 * This source file is subject to the license that is bundled with this package in the file LICENSE.
6
 */
7
namespace PhUml\Parser;
8
9
use PhUml\Code\Structure;
10
11
class TokenParser
12
{
13
    /** @var array */
14
    private $structure;
15
16
    /** @var int */
17
    private $lastToken;
18
19
    /** @var StructureBuilder */
20
    private $builder;
21
22
    /** @var Definitions */
23
    private $definitions;
24
25
    /** @var RelationsResolver */
26
    private $resolver;
27
28 60
    public function __construct(
29
        Definitions $definitions = null,
30
        RelationsResolver $resolver = null,
31
        StructureBuilder $builder = null
32
    ) {
33 60
        $this->builder = $builder ?? new StructureBuilder();
34 60
        $this->definitions = $definitions ?? new Definitions();
35 60
        $this->resolver = $resolver ?? new RelationsResolver();
36 60
    }
37
38 33
    public function parse(CodeFinder $finder): Structure
39
    {
40 33
        foreach ($finder->files() as $code) {
41 30
            $this->initParserAttributes();
42 30
            $this->process(token_get_all($code));
43 30
            $this->storeClassOrInterface();
44
        }
45 33
        $this->resolver->resolve($this->definitions);
46 33
        return $this->builder->buildFromDefinitions($this->definitions);
47
    }
48
49 30
    private function process(array $tokens): void
50
    {
51 30
        foreach ($tokens as $token) {
52 30
            if (is_array($token)) {
53 30
                $this->processComplex(...$token);
0 ignored issues
show
Bug introduced by
$token is expanded, but the parameter $type of PhUml\Parser\TokenParser::processComplex() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

53
                $this->processComplex(/** @scrutinizer ignore-type */ ...$token);
Loading history...
Bug introduced by
The call to PhUml\Parser\TokenParser::processComplex() has too few arguments starting with value. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

53
                $this->/** @scrutinizer ignore-call */ 
54
                       processComplex(...$token);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
54
            } else {
55 30
                $this->processSimple($token);
56
            }
57
        }
58 30
    }
59
60 30
    private function processSimple(string $token): void
61
    {
62 10
        switch ($token) {
63 20
            case '(':
64 12
                break;
65 20
            case ',':
66 12
                $this->resetTypeHint();
67 12
                break;
68 20
            case '=':
69 3
                $this->resetToken();
70 3
                break;
71 20
            case ')':
72 12
                $this->saveMethodDefinition();
73 12
                break;
74
            default:
75
                // Ignore everything else
76 30
                $this->lastToken = null;
77
        }
78 30
    }
79
80 30
    private function processComplex(int $type, string $value): void
81
    {
82
        switch ($type) {
83 30
            case T_WHITESPACE:
84 30
                break;
85 30
            case T_VAR:
86 30
            case T_ARRAY:
87 30
            case T_CONSTANT_ENCAPSED_STRING:
88 30
            case T_LNUMBER:
89 30
            case T_DNUMBER:
90 30
            case T_PAAMAYIM_NEKUDOTAYIM:
91 6
                $this->resetToken();
92 6
                break;
93 30
            case T_FUNCTION:
94 12
                $this->startMethodDefinition($type);
95 12
                break;
96 30
            case T_INTERFACE:
97 30
            case T_CLASS:
98 30
                $this->startClassOrInterfaceDefinition($type);
99 30
                break;
100 30
            case T_IMPLEMENTS:
101 30
            case T_EXTENDS:
102 12
                $this->startExtendsOrImplementsDeclaration($type);
103 12
                break;
104 30
            case T_VARIABLE:
105 18
                $this->saveAttributeOrParameter($value);
106 18
                break;
107 30
            case T_STRING:
108 30
                $this->saveIdentifier($value);
109 30
                break;
110 30
            case T_PUBLIC:
111 30
            case T_PROTECTED:
112 30
            case T_PRIVATE:
113 18
                $this->saveModifier($type, $value);
114 18
                break;
115 30
            case T_DOC_COMMENT:
116 6
                $this->saveDocBlock($value);
117 6
                break;
118
            default:
119
                // Ignore everything else
120 30
                $this->lastToken = null;
121
                // And reset the docblock
122 30
                $this->structure['docblock'] = null;
123
        }
124 30
    }
125
126 30
    private function initParserAttributes(): void
127
    {
128 30
        $this->structure = [
129
            'class' => null,
130
            'interface' => null,
131
            'function' => null,
132
            'attributes' => [],
133
            'functions' => [],
134
            'typehint' => null,
135
            'params' => [],
136
            'implements' => [],
137
            'extends' => null,
138
            'modifier' => 'public',
139
            'docblock' => null,
140
        ];
141
142 30
        $this->lastToken = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type integer of property $lastToken.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
143 30
    }
144
145 12
    private function resetTypeHint(): void
146
    {
147 12
        $this->structure['typehint'] = null;
148 12
    }
149
150 6
    private function resetToken(): void
151
    {
152 6
        if ($this->lastToken !== T_FUNCTION) {
153 6
            $this->lastToken = null;
154
        }
155 6
    }
156
157 12
    private function startMethodDefinition(int $type): void
158
    {
159 12
        switch ($this->lastToken) {
160 8
            case null:
161 12
            case T_PUBLIC:
162 3
            case T_PROTECTED:
163 3
            case T_PRIVATE:
164 12
                $this->lastToken = $type;
165 12
                break;
166
            default:
167
                $this->lastToken = null;
168
        }
169 12
    }
170
171 30
    private function startClassOrInterfaceDefinition(int $type): void
172
    {
173 30
        if ($this->lastToken === null) {
174
            // New initial interface or class token
175
            // Store the class or interface definition if there is any in the
176
            // parser arrays ( There might be more than one class/interface per
177
            // file )
178 30
            $this->storeClassOrInterface();
179
180
            // Remember the last token
181 30
            $this->lastToken = $type;
182
        } else {
183
            $this->lastToken = null;
184
        }
185 30
    }
186
187 12
    private function startExtendsOrImplementsDeclaration(int $type): void
188
    {
189 12
        if ($this->lastToken === null) {
190 12
            $this->lastToken = $type;
191
        } else {
192
            $this->lastToken = null;
193
        }
194 12
    }
195
196 12
    private function saveMethodDefinition(): void
197
    {
198 12
        if ($this->lastToken === T_FUNCTION) {
199
            // The function declaration has been closed
200
201
            // Add the current function
202 12
            $this->structure['functions'][] = [
203 12
                $this->structure['function'],
204 12
                $this->structure['modifier'],
205 12
                $this->structure['params'],
206 12
                $this->structure['docblock']
207
            ];
208
            // Reset the last token
209 12
            $this->lastToken = null;
210
            //Reset the modifier state
211 12
            $this->structure['modifier'] = 'public';
212
            // Reset the params array
213 12
            $this->structure['params'] = [];
214 12
            $this->structure['typehint'] = null;
215
            // Reset the function name
216 12
            $this->structure['function'] = null;
217
            // Reset the docblock
218 12
            $this->structure['docblock'] = null;
219
        } else {
220 3
            $this->lastToken = null;
221
        }
222 12
    }
223
224 18
    private function saveAttributeOrParameter(string $identifier): void
225
    {
226 18
        switch ($this->lastToken) {
227 18
            case T_PUBLIC:
228 18
            case T_PROTECTED:
229 18
            case T_PRIVATE:
230
                // A new class attribute
231 9
                $this->structure['attributes'][] = [
232 9
                    $identifier,
233 9
                    $this->structure['modifier'],
234 9
                    $this->structure['docblock'],
235
                ];
236 9
                $this->lastToken = null;
237 9
                $this->structure['modifier'] = 'public';
238 9
                $this->structure['docblock'] = null;
239 9
                break;
240 12
            case T_FUNCTION:
241
                // A new function parameter
242 12
                $this->structure['params'][] = [
243 12
                    $this->structure['typehint'],
244 12
                    $identifier,
245
                ];
246 12
                break;
247
        }
248 18
    }
249
250 30
    private function saveIdentifier(string $identifier): void
251
    {
252 30
        switch ($this->lastToken) {
253 30
            case T_IMPLEMENTS:
254
                // Add interface to implements array
255 6
                $this->structure['implements'][] = $identifier;
256
                // We do not reset the last token here, because
257
                // there might be multiple interfaces
258 6
                break;
259 30
            case T_EXTENDS:
260
                // Set the superclass
261 9
                $this->structure['extends'] = $identifier;
262
                // Reset the last token
263 9
                $this->lastToken = null;
264 9
                break;
265 30
            case T_FUNCTION:
266
                // Add the current function only if there is no function name already
267
                // Because if we know the function name already this is a type hint
268 12
                if ($this->structure['function'] === null) {
269
                    // Function name
270 12
                    $this->structure['function'] = $identifier;
271
                } else {
272
                    // Type hint
273 12
                    $this->structure['typehint'] = $identifier;
274
                }
275 12
                break;
276 30
            case T_CLASS:
277
                // Set the class name
278 24
                $this->structure['class'] = $identifier;
279
                // Reset the last token
280 24
                $this->lastToken = null;
281 24
                break;
282 18
            case T_INTERFACE:
283
                // Set the interface name
284 12
                $this->structure['interface'] = $identifier;
285
                // Reset the last Token
286 12
                $this->lastToken = null;
287 12
                break;
288
            default:
289 12
                $this->lastToken = null;
290
        }
291 30
    }
292
293 18
    private function saveModifier(int $type, string $modifier): void
294
    {
295 18
        if ($this->lastToken === null) {
296 18
            $this->lastToken = $type;
297 18
            $this->structure['modifier'] = $modifier;
298
        } else {
299
            $this->lastToken = null;
300
        }
301 18
    }
302
303 6
    private function saveDocBlock(string $comment): void
304
    {
305 6
        if ($this->lastToken === null) {
306 6
            $this->structure['docblock'] = $comment;
307
        } else {
308
            $this->lastToken = null;
309
            $this->structure['docblock'] = null;
310
        }
311 6
    }
312
313 30
    private function storeClassOrInterface(): void
314
    {
315 30
        $this->definitions->add($this->structure);
316 30
        $this->initParserAttributes();
317 30
    }
318
}
319