Completed
Push — master ( bb7a53...c89193 )
by Kyle
13s queued 11s
created

PHPParserVersion70::parseParenthesisExpression()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 21
ccs 11
cts 11
cp 1
crap 2
rs 9.584
c 0
b 0
f 0
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()
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...
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)
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...
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()) {
0 ignored issues
show
Unused Code introduced by
$tokenType is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->parseScalarOrCallableTypeHint($name); of type PDepend\Source\AST\ASTSc...T\ASTTypeCallable|false adds false to the return on line 242 which is incompatible with the return type documented by PDepend\Source\Language\...ersion70::parseTypeHint of type PDepend\Source\AST\ASTNode. It seems like you forgot to handle an error condition.
Loading history...
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
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),
0 ignored issues
show
Documentation introduced by
$this->parseTypeBody($class) is of type object<PDepend\Source\AS...actASTClassOrInterface>, but the function expects a object<PDepend\Source\AST\ASTNode>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
383
                $tokens
384
            )
385 4
        );
386
        $class->setTokens($tokens);
0 ignored issues
show
Documentation introduced by
$tokens is of type null|array, but the function expects a array<integer,object<PDe...ource\Tokenizer\Token>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->parseStati...ix($expr->getChild(0)); (PDepend\Source\AST\ASTMemberPrimaryPrefix) is incompatible with the return type documented by PDepend\Source\Language\...PrimaryPrefixForVersion of type PDepend\Source\AST\ASTExpression.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
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);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->parseMemberPrimaryPrefix($node); (PDepend\Source\AST\ASTMemberPrimaryPrefix) is incompatible with the return type documented by PDepend\Source\Language\...PrimaryPrefixForVersion of type PDepend\Source\AST\ASTExpression.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
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()
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...
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()
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...
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