1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of PDepend. |
4
|
|
|
* |
5
|
|
|
* PHP Version 5 |
6
|
|
|
* |
7
|
|
|
* Copyright (c) 2008-2017 Manuel Pichler <[email protected]>. |
8
|
|
|
* All rights reserved. |
9
|
|
|
* |
10
|
|
|
* Redistribution and use in source and binary forms, with or without |
11
|
|
|
* modification, are permitted provided that the following conditions |
12
|
|
|
* are met: |
13
|
|
|
* |
14
|
|
|
* * Redistributions of source code must retain the above copyright |
15
|
|
|
* notice, this list of conditions and the following disclaimer. |
16
|
|
|
* |
17
|
|
|
* * Redistributions in binary form must reproduce the above copyright |
18
|
|
|
* notice, this list of conditions and the following disclaimer in |
19
|
|
|
* the documentation and/or other materials provided with the |
20
|
|
|
* distribution. |
21
|
|
|
* |
22
|
|
|
* * Neither the name of Manuel Pichler nor the names of his |
23
|
|
|
* contributors may be used to endorse or promote products derived |
24
|
|
|
* from this software without specific prior written permission. |
25
|
|
|
* |
26
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
27
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
28
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
29
|
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
30
|
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
31
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
32
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
33
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
34
|
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
35
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
36
|
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
37
|
|
|
* POSSIBILITY OF SUCH DAMAGE. |
38
|
|
|
* |
39
|
|
|
* @copyright 2008-2017 Manuel Pichler. All rights reserved. |
40
|
|
|
* @license http://www.opensource.org/licenses/bsd-license.php BSD License |
41
|
|
|
* @since 2.3 |
42
|
|
|
*/ |
43
|
|
|
|
44
|
|
|
namespace PDepend\Source\Language\PHP; |
45
|
|
|
|
46
|
|
|
use PDepend\Source\AST\ASTAllocationExpression; |
47
|
|
|
use PDepend\Source\AST\ASTExpression; |
48
|
|
|
use PDepend\Source\AST\ASTNode; |
49
|
|
|
use PDepend\Source\Tokenizer\Tokens; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Concrete parser implementation that supports features up to PHP version 7.0. |
53
|
|
|
* |
54
|
|
|
* TODO: |
55
|
|
|
* - Tokens: trait, callable, insteadof |
56
|
|
|
* - allowed as |
57
|
|
|
* - method |
58
|
|
|
* - constant |
59
|
|
|
* - not allowed as |
60
|
|
|
* - class |
61
|
|
|
* - interface |
62
|
|
|
* - trait |
63
|
|
|
* |
64
|
|
|
* @copyright 2008-2017 Manuel Pichler. All rights reserved. |
65
|
|
|
* @license http://www.opensource.org/licenses/bsd-license.php BSD License |
66
|
|
|
* @since 2.3 |
67
|
|
|
*/ |
68
|
|
|
abstract class PHPParserVersion70 extends PHPParserVersion56 |
69
|
|
|
{ |
70
|
|
|
/** |
71
|
|
|
* @param integer $tokenType |
72
|
|
|
* @return boolean |
73
|
|
|
*/ |
74
|
30 |
|
protected function isConstantName($tokenType) |
75
|
|
|
{ |
76
|
|
|
switch ($tokenType) { |
77
|
30 |
|
case Tokens::T_CALLABLE: |
78
|
30 |
|
case Tokens::T_TRAIT: |
79
|
30 |
|
case Tokens::T_EXTENDS: |
80
|
30 |
|
case Tokens::T_IMPLEMENTS: |
81
|
30 |
|
case Tokens::T_STATIC: |
82
|
30 |
|
case Tokens::T_ABSTRACT: |
83
|
30 |
|
case Tokens::T_FINAL: |
84
|
30 |
|
case Tokens::T_PUBLIC: |
85
|
30 |
|
case Tokens::T_PROTECTED: |
86
|
30 |
|
case Tokens::T_PRIVATE: |
87
|
30 |
|
case Tokens::T_CONST: |
88
|
30 |
|
case Tokens::T_ENDDECLARE: |
89
|
30 |
|
case Tokens::T_ENDFOR: |
90
|
30 |
|
case Tokens::T_ENDFOREACH: |
91
|
30 |
|
case Tokens::T_ENDIF: |
92
|
30 |
|
case Tokens::T_ENDWHILE: |
93
|
30 |
|
case Tokens::T_LOGICAL_AND: |
94
|
30 |
|
case Tokens::T_GLOBAL: |
95
|
30 |
|
case Tokens::T_GOTO: |
96
|
30 |
|
case Tokens::T_INSTANCEOF: |
97
|
30 |
|
case Tokens::T_INSTEADOF: |
98
|
30 |
|
case Tokens::T_INTERFACE: |
99
|
30 |
|
case Tokens::T_NAMESPACE: |
100
|
30 |
|
case Tokens::T_NEW: |
101
|
30 |
|
case Tokens::T_LOGICAL_OR: |
102
|
30 |
|
case Tokens::T_LOGICAL_XOR: |
103
|
30 |
|
case Tokens::T_TRY: |
104
|
30 |
|
case Tokens::T_USE: |
105
|
30 |
|
case Tokens::T_VAR: |
106
|
30 |
|
case Tokens::T_EXIT: |
107
|
30 |
|
case Tokens::T_LIST: |
108
|
30 |
|
case Tokens::T_CLONE: |
109
|
30 |
|
case Tokens::T_INCLUDE: |
110
|
30 |
|
case Tokens::T_INCLUDE_ONCE: |
111
|
30 |
|
case Tokens::T_THROW: |
112
|
30 |
|
case Tokens::T_ARRAY: |
113
|
30 |
|
case Tokens::T_PRINT: |
114
|
30 |
|
case Tokens::T_ECHO: |
115
|
30 |
|
case Tokens::T_REQUIRE: |
116
|
30 |
|
case Tokens::T_REQUIRE_ONCE: |
117
|
30 |
|
case Tokens::T_RETURN: |
118
|
30 |
|
case Tokens::T_ELSE: |
119
|
30 |
|
case Tokens::T_ELSEIF: |
120
|
30 |
|
case Tokens::T_DEFAULT: |
121
|
30 |
|
case Tokens::T_BREAK: |
122
|
30 |
|
case Tokens::T_CONTINUE: |
123
|
30 |
|
case Tokens::T_SWITCH: |
124
|
30 |
|
case Tokens::T_YIELD: |
125
|
30 |
|
case Tokens::T_FUNCTION: |
126
|
30 |
|
case Tokens::T_IF: |
127
|
30 |
|
case Tokens::T_ENDSWITCH: |
128
|
30 |
|
case Tokens::T_FINALLY: |
129
|
30 |
|
case Tokens::T_FOR: |
130
|
30 |
|
case Tokens::T_FOREACH: |
131
|
30 |
|
case Tokens::T_DECLARE: |
132
|
30 |
|
case Tokens::T_CASE: |
133
|
30 |
|
case Tokens::T_DO: |
134
|
30 |
|
case Tokens::T_WHILE: |
135
|
30 |
|
case Tokens::T_AS: |
136
|
30 |
|
case Tokens::T_CATCH: |
137
|
|
|
//case Tokens::T_DIE: |
138
|
30 |
|
case Tokens::T_SELF: |
139
|
30 |
|
case Tokens::T_PARENT: |
140
|
12 |
|
return true; |
141
|
|
|
} |
142
|
24 |
|
return parent::isConstantName($tokenType); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* @param integer $tokenType |
147
|
|
|
* @return bool |
148
|
|
|
*/ |
149
|
24 |
|
protected function isMethodName($tokenType) |
150
|
|
|
{ |
151
|
|
|
switch ($tokenType) { |
152
|
24 |
|
case Tokens::T_CLASS: |
153
|
2 |
|
return true; |
154
|
|
|
} |
155
|
24 |
|
return $this->isConstantName($tokenType); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* @return \PDepend\Source\AST\ASTNode |
160
|
|
|
*/ |
161
|
6 |
View Code Duplication |
protected function parsePostfixIdentifier() |
|
|
|
|
162
|
|
|
{ |
163
|
6 |
|
$tokenType = $this->tokenizer->peek(); |
164
|
6 |
|
switch (true) { |
165
|
6 |
|
case ($this->isConstantName($tokenType)): |
166
|
4 |
|
$node = $this->parseLiteral(); |
167
|
4 |
|
break; |
168
|
2 |
|
default: |
169
|
2 |
|
$node = parent::parsePostfixIdentifier(); |
170
|
2 |
|
break; |
171
|
2 |
|
} |
172
|
6 |
|
return $this->parseOptionalIndexExpression($node); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* @param \PDepend\Source\AST\AbstractASTCallable $callable |
177
|
|
|
* @return \PDepend\Source\AST\AbstractASTCallable |
178
|
|
|
*/ |
179
|
66 |
View Code Duplication |
protected function parseCallableDeclarationAddition($callable) |
|
|
|
|
180
|
|
|
{ |
181
|
66 |
|
$this->consumeComments(); |
182
|
66 |
|
if (Tokens::T_COLON != $this->tokenizer->peek()) { |
183
|
52 |
|
return $callable; |
184
|
|
|
} |
185
|
|
|
|
186
|
28 |
|
$this->consumeToken(Tokens::T_COLON); |
187
|
|
|
|
188
|
28 |
|
$type = $this->parseReturnTypeHint(); |
189
|
28 |
|
$callable->addChild($type); |
190
|
|
|
|
191
|
28 |
|
return $callable; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* @return \PDepend\Source\AST\ASTType |
196
|
|
|
*/ |
197
|
28 |
|
protected function parseReturnTypeHint() |
198
|
|
|
{ |
199
|
28 |
|
$this->consumeComments(); |
200
|
|
|
|
201
|
28 |
|
switch ($tokenType = $this->tokenizer->peek()) { |
|
|
|
|
202
|
28 |
|
case Tokens::T_ARRAY: |
203
|
4 |
|
$type = $this->parseArrayType(); |
204
|
4 |
|
break; |
205
|
24 |
|
case Tokens::T_SELF: |
206
|
|
|
$type = $this->parseSelfType(); |
207
|
|
|
break; |
208
|
24 |
|
case Tokens::T_PARENT: |
209
|
|
|
$type = $this->parseParentType(); |
210
|
|
|
break; |
211
|
24 |
|
default: |
212
|
24 |
|
$type = $this->parseTypeHint(); |
213
|
24 |
|
break; |
214
|
28 |
|
} |
215
|
28 |
|
return $type; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Parses a type hint that is valid in the supported PHP version. |
220
|
|
|
* |
221
|
|
|
* @return \PDepend\Source\AST\ASTNode |
222
|
|
|
* @since 2.3 |
223
|
|
|
*/ |
224
|
32 |
|
protected function parseTypeHint() |
225
|
|
|
{ |
226
|
32 |
|
switch ($this->tokenizer->peek()) { |
227
|
32 |
|
case Tokens::T_STRING: |
228
|
32 |
|
case Tokens::T_BACKSLASH: |
229
|
32 |
|
case Tokens::T_NAMESPACE: |
230
|
28 |
|
$name = $this->parseQualifiedName(); |
231
|
|
|
|
232
|
28 |
|
if ($this->isScalarOrCallableTypeHint($name)) { |
233
|
24 |
|
$type = $this->parseScalarOrCallableTypeHint($name); |
|
|
|
|
234
|
24 |
|
} else { |
235
|
4 |
|
$type = $this->builder->buildAstClassOrInterfaceReference($name); |
236
|
|
|
} |
237
|
28 |
|
break; |
238
|
4 |
|
default: |
239
|
4 |
|
$type = parent::parseTypeHint(); |
240
|
4 |
|
break; |
241
|
32 |
|
} |
242
|
32 |
|
return $type; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Parses any expression that is surrounded by an opening and a closing |
247
|
|
|
* parenthesis |
248
|
|
|
* |
249
|
|
|
* @return \PDepend\Source\AST\ASTExpression |
250
|
|
|
*/ |
251
|
28 |
|
protected function parseParenthesisExpression() |
252
|
|
|
{ |
253
|
28 |
|
$this->tokenStack->push(); |
254
|
28 |
|
$this->consumeComments(); |
255
|
28 |
|
|
256
|
28 |
|
$expr = $this->builder->buildAstExpression(); |
257
|
28 |
|
$expr = $this->parseBraceExpression( |
258
|
28 |
|
$expr, |
259
|
24 |
|
$this->consumeToken(Tokens::T_PARENTHESIS_OPEN), |
260
|
4 |
|
Tokens::T_PARENTHESIS_CLOSE |
261
|
|
|
); |
262
|
4 |
|
|
263
|
|
|
while ($this->tokenizer->peek() === Tokens::T_PARENTHESIS_OPEN) { |
264
|
|
|
$function = $this->builder->buildAstFunctionPostfix($expr->getImage()); |
265
|
|
|
$function->addChild($expr); |
266
|
|
|
$function->addChild($this->parseArguments()); |
267
|
|
|
$expr = $function; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
return $this->setNodePositionsAndReturn($expr); |
271
|
24 |
|
} |
272
|
|
|
|
273
|
24 |
|
/** |
274
|
24 |
|
* Tests if the given image is a PHP 7 type hint. |
275
|
24 |
|
* |
276
|
24 |
|
* @param string $image |
277
|
24 |
|
* @return boolean |
278
|
24 |
|
*/ |
279
|
|
|
protected function isScalarOrCallableTypeHint($image) |
280
|
|
|
{ |
281
|
|
|
switch (strtolower($image)) { |
282
|
|
|
case 'int': |
283
|
|
|
case 'bool': |
284
|
|
|
case 'float': |
285
|
|
|
case 'string': |
286
|
|
|
case 'callable': |
287
|
|
|
return true; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
return false; |
291
|
|
|
} |
292
|
|
|
|
293
|
12 |
|
/** |
294
|
|
|
* Parses a scalar type hint or a callable type hint. |
295
|
12 |
|
* |
296
|
8 |
|
* @param string $image |
297
|
|
|
* @return \PDepend\Source\AST\ASTType |
298
|
6 |
|
*/ |
299
|
|
|
protected function parseScalarOrCallableTypeHint($image) |
300
|
|
|
{ |
301
|
|
|
switch (strtolower($image)) { |
302
|
|
|
case 'int': |
303
|
|
|
case 'bool': |
304
|
|
|
case 'float': |
305
|
|
|
case 'string': |
306
|
|
|
return $this->builder->buildAstScalarType($image); |
307
|
|
|
case 'callable': |
308
|
12 |
|
return $this->builder->buildAstTypeCallable(); |
309
|
|
|
} |
310
|
12 |
|
|
311
|
12 |
|
return false; |
312
|
6 |
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
8 |
|
* Parse the type reference used in an allocation expression. |
316
|
|
|
* |
317
|
8 |
|
* @param \PDepend\Source\AST\ASTAllocationExpression $allocation |
318
|
|
|
* @return \PDepend\Source\AST\ASTNode |
319
|
8 |
|
* @since 2.3 |
320
|
8 |
|
*/ |
321
|
|
|
protected function parseAllocationExpressionTypeReference(ASTAllocationExpression $allocation) |
322
|
8 |
|
{ |
323
|
8 |
|
if ($newAllocation = $this->parseAnonymousClassDeclaration($allocation)) { |
324
|
8 |
|
return $newAllocation; |
325
|
8 |
|
} |
326
|
8 |
|
return parent::parseAllocationExpressionTypeReference($allocation); |
327
|
8 |
|
} |
328
|
8 |
|
|
329
|
8 |
|
/** |
330
|
8 |
|
* Attempts to the next sequence of tokens as an anonymous class and adds it to the allocation expression |
331
|
8 |
|
* |
332
|
|
|
* @param \PDepend\Source\AST\ASTAllocationExpression $allocation |
333
|
8 |
|
* |
334
|
8 |
|
* @return null|\PDepend\Source\AST\ASTAnonymousClass |
335
|
8 |
|
*/ |
336
|
|
|
protected function parseAnonymousClassDeclaration(ASTAllocationExpression $allocation) |
337
|
8 |
|
{ |
338
|
8 |
|
$this->consumeComments(); |
339
|
|
|
if (Tokens::T_CLASS !== $this->tokenizer->peek()) { |
340
|
8 |
|
return null; |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
$classOrInterface = $this->classOrInterface; |
344
|
|
|
|
345
|
|
|
$this->tokenStack->push(); |
346
|
|
|
|
347
|
8 |
|
$this->consumeToken(Tokens::T_CLASS); |
348
|
|
|
$this->consumeComments(); |
349
|
|
|
|
350
|
|
|
$class = $this->builder->buildAnonymousClass(); |
351
|
|
|
$class->setName( |
352
|
8 |
|
sprintf( |
353
|
8 |
|
'class@anonymous%s0x%s', |
354
|
8 |
|
$this->compilationUnit->getFileName(), |
355
|
|
|
uniqid('') |
356
|
8 |
|
) |
357
|
8 |
|
); |
358
|
8 |
|
$class->setCompilationUnit($this->compilationUnit); |
359
|
|
|
$class->setUserDefined(); |
360
|
8 |
|
|
361
|
|
|
if ($this->isNextTokenArguments()) { |
362
|
8 |
|
$class->addChild($this->parseArguments()); |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
$this->consumeComments(); |
366
|
|
|
$tokenType = $this->tokenizer->peek(); |
367
|
|
|
|
368
|
|
View Code Duplication |
if ($tokenType === Tokens::T_EXTENDS) { |
|
|
|
|
369
|
30 |
|
$class = $this->parseClassExtends($class); |
370
|
|
|
|
371
|
30 |
|
$this->consumeComments(); |
372
|
30 |
|
$tokenType = $this->tokenizer->peek(); |
373
|
2 |
|
} |
374
|
|
|
|
375
|
30 |
|
if ($tokenType === Tokens::T_IMPLEMENTS) { |
376
|
|
|
$this->consumeToken(Tokens::T_IMPLEMENTS); |
377
|
|
|
$this->parseInterfaceList($class); |
378
|
30 |
|
} |
379
|
|
|
|
380
|
|
|
$allocation->addChild( |
381
|
|
|
$this->setNodePositionsAndReturn( |
382
|
|
|
$this->parseTypeBody($class), |
|
|
|
|
383
|
|
|
$tokens |
384
|
|
|
) |
385
|
4 |
|
); |
386
|
|
|
$class->setTokens($tokens); |
|
|
|
|
387
|
4 |
|
|
388
|
4 |
|
$this->classOrInterface = $classOrInterface; |
389
|
2 |
|
|
390
|
|
|
return $allocation; |
391
|
4 |
|
} |
392
|
|
|
|
393
|
|
|
/** |
394
|
|
|
* @param \PDepend\Source\AST\ASTNode $node |
395
|
4 |
|
* @return \PDepend\Source\AST\ASTNode |
396
|
|
|
*/ |
397
|
|
|
protected function parseOptionalMemberPrimaryPrefix(ASTNode $node) |
398
|
|
|
{ |
399
|
|
|
$this->consumeComments(); |
400
|
|
|
if (Tokens::T_DOUBLE_COLON === $this->tokenizer->peek()) { |
401
|
|
|
return $this->parseStaticMemberPrimaryPrefix($node); |
402
|
|
|
} |
403
|
|
|
if ($this->tokenizer->peek() === Tokens::T_OBJECT_OPERATOR) { |
404
|
|
|
return $this->parseMemberPrimaryPrefix($node); |
405
|
|
|
} |
406
|
|
|
return $node; |
407
|
8 |
|
} |
408
|
|
|
|
409
|
8 |
|
/** |
410
|
8 |
|
* @param \PDepend\Source\AST\ASTExpression $expr |
411
|
|
|
* @return \PDepend\Source\AST\ASTExpression |
412
|
|
|
*/ |
413
|
|
|
protected function parseParenthesisExpressionOrPrimaryPrefixForVersion(ASTExpression $expr) |
414
|
|
|
{ |
415
|
|
|
$this->consumeComments(); |
416
|
|
|
if (Tokens::T_DOUBLE_COLON === $this->tokenizer->peek()) { |
417
|
|
|
return $this->parseStaticMemberPrimaryPrefix($expr->getChild(0)); |
|
|
|
|
418
|
|
|
} |
419
|
|
|
if ($this->tokenizer->peek() === Tokens::T_OBJECT_OPERATOR) { |
420
|
|
|
$node = count($expr->getChildren()) === 0 ? $expr : $expr->getChild(0); |
421
|
8 |
|
return $this->parseMemberPrimaryPrefix($node); |
|
|
|
|
422
|
|
|
} |
423
|
8 |
|
return $expr; |
424
|
8 |
|
} |
425
|
|
|
|
426
|
|
|
/** |
427
|
8 |
|
* This method will be called when the base parser cannot handle an expression |
428
|
8 |
|
* in the base version. In this method you can implement version specific |
429
|
8 |
|
* expressions. |
430
|
|
|
* |
431
|
8 |
|
* @return \PDepend\Source\AST\ASTNode |
432
|
8 |
|
* @throws \PDepend\Source\Parser\UnexpectedTokenException |
433
|
8 |
|
* @since 2.3 |
434
|
8 |
|
*/ |
435
|
8 |
|
protected function parseOptionalExpressionForVersion() |
436
|
8 |
|
{ |
437
|
8 |
|
if ($expression = $this->parseExpressionVersion70()) { |
438
|
|
|
return $expression; |
439
|
8 |
|
} |
440
|
|
|
return parent::parseOptionalExpressionForVersion(); |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
/** |
444
|
|
|
* In this method we implement parsing of PHP 7.0 specific expressions. |
445
|
|
|
* |
446
|
|
|
* @return \PDepend\Source\AST\ASTNode |
447
|
|
|
* @since 2.3 |
448
|
|
|
*/ |
449
|
|
View Code Duplication |
protected function parseExpressionVersion70() |
|
|
|
|
450
|
|
|
{ |
451
|
|
|
$this->consumeComments(); |
452
|
|
|
$nextTokenType = $this->tokenizer->peek(); |
453
|
|
|
|
454
|
|
|
switch ($nextTokenType) { |
455
|
|
|
case Tokens::T_SPACESHIP: |
456
|
16 |
|
case Tokens::T_COALESCE: |
457
|
|
|
$token = $this->consumeToken($nextTokenType); |
458
|
16 |
|
|
459
|
|
|
$expr = $this->builder->buildAstExpression($token->image); |
460
|
16 |
|
$expr->configureLinesAndColumns( |
461
|
|
|
$token->startLine, |
462
|
|
|
$token->endLine, |
463
|
|
|
$token->startColumn, |
464
|
|
|
$token->endColumn |
465
|
|
|
); |
466
|
|
|
|
467
|
16 |
|
return $expr; |
468
|
|
|
} |
469
|
16 |
|
} |
470
|
|
|
|
471
|
|
|
/** |
472
|
|
|
* This method will parse a formal parameter. A formal parameter is at least |
473
|
|
|
* a variable name, but can also contain a default parameter value. |
474
|
|
|
* |
475
|
|
|
* <code> |
476
|
2 |
|
* // -- ------- |
477
|
|
|
* function foo(Bar $x, $y = 42) {} |
478
|
2 |
|
* // -- ------- |
479
|
2 |
|
* </code> |
480
|
|
|
* |
481
|
|
|
* @return \PDepend\Source\AST\ASTFormalParameter |
482
|
|
|
* @since 2.0.7 |
483
|
|
|
*/ |
484
|
|
View Code Duplication |
protected function parseFormalParameter() |
|
|
|
|
485
|
|
|
{ |
486
|
|
|
$parameter = $this->builder->buildAstFormalParameter(); |
487
|
|
|
|
488
|
2 |
|
if (Tokens::T_ELLIPSIS === $this->tokenizer->peek()) { |
489
|
|
|
$this->consumeToken(Tokens::T_ELLIPSIS); |
490
|
2 |
|
$this->consumeComments(); |
491
|
|
|
|
492
|
2 |
|
$parameter->setVariableArgList(); |
493
|
2 |
|
} |
494
|
|
|
|
495
|
|
|
$parameter->addChild($this->parseVariableDeclarator()); |
496
|
2 |
|
|
497
|
|
|
return $parameter; |
498
|
2 |
|
} |
499
|
2 |
|
|
500
|
2 |
|
/** |
501
|
2 |
|
* @param array $fragments |
502
|
|
|
* @return void |
503
|
2 |
|
*/ |
504
|
2 |
|
protected function parseUseDeclarationForVersion(array $fragments) |
505
|
|
|
{ |
506
|
2 |
|
if (Tokens::T_CURLY_BRACE_OPEN === $this->tokenizer->peek()) { |
507
|
|
|
return $this->parseUseDeclarationVersion70($fragments); |
508
|
2 |
|
} |
509
|
2 |
|
return parent::parseUseDeclarationForVersion($fragments); |
510
|
|
|
} |
511
|
|
|
|
512
|
2 |
|
/** |
513
|
2 |
|
* @param array $fragments |
514
|
|
|
* @return void |
515
|
|
|
*/ |
516
|
2 |
|
protected function parseUseDeclarationVersion70(array $fragments) |
517
|
2 |
|
{ |
518
|
|
|
$namespacePrefixReplaced = $this->namespacePrefixReplaced; |
519
|
2 |
|
|
520
|
|
|
$this->consumeToken(Tokens::T_CURLY_BRACE_OPEN); |
521
|
2 |
|
$this->consumeComments(); |
522
|
2 |
|
|
523
|
|
|
do { |
524
|
2 |
|
$nextToken = $this->tokenizer->peek(); |
525
|
2 |
|
switch ($nextToken) { |
526
|
|
|
case Tokens::T_CONST: |
527
|
|
|
case Tokens::T_FUNCTION: |
528
|
|
|
$this->consumeToken($nextToken); |
529
|
|
|
} |
530
|
|
|
|
531
|
8 |
|
$subFragments = $this->parseQualifiedNameRaw(); |
532
|
|
|
$this->consumeComments(); |
533
|
8 |
|
|
534
|
8 |
|
$image = $this->parseNamespaceImage($subFragments); |
535
|
|
|
|
536
|
2 |
|
if (Tokens::T_COMMA != $this->tokenizer->peek()) { |
537
|
2 |
|
break; |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
$this->consumeToken(Tokens::T_COMMA); |
541
|
|
|
$this->consumeComments(); |
542
|
|
|
|
543
|
|
|
// Add mapping between image and qualified name to symbol table |
544
|
|
|
$this->useSymbolTable->add($image, join('', array_merge($fragments, $subFragments))); |
545
|
|
|
} while (true); |
546
|
|
|
|
547
|
|
|
$this->useSymbolTable->add($image, join('', array_merge($fragments, $subFragments))); |
548
|
|
|
|
549
|
|
|
$this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE); |
550
|
|
|
$this->consumeComments(); |
551
|
|
|
|
552
|
|
|
$this->namespacePrefixReplaced = $namespacePrefixReplaced; |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
/** |
556
|
|
|
* @param array $previousElements |
557
|
|
|
* @return string |
558
|
|
|
*/ |
559
|
|
|
protected function parseQualifiedNameElement(array $previousElements) |
560
|
|
|
{ |
561
|
|
|
if (Tokens::T_CURLY_BRACE_OPEN !== $this->tokenizer->peek()) { |
562
|
|
|
return parent::parseQualifiedNameElement($previousElements); |
563
|
|
|
} |
564
|
|
|
if (count($previousElements) >= 2 && '\\' === end($previousElements)) { |
565
|
|
|
return null; |
566
|
|
|
} |
567
|
|
|
$this->throwUnexpectedTokenException($this->tokenizer->next()); |
568
|
|
|
} |
569
|
|
|
} |
570
|
|
|
|
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.