Completed
Pull Request — master (#366)
by Markus
15:00
created

AbstractPHPParser::parseListExpression()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
nc 3
nop 0
dl 0
loc 39
ccs 26
cts 26
cp 1
crap 5
rs 8.9848
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
 */
42
43
namespace PDepend\Source\Language\PHP;
44
45
use PDepend\Source\AST\AbstractASTCallable;
46
use PDepend\Source\AST\AbstractASTClassOrInterface;
47
use PDepend\Source\AST\AbstractASTType;
48
use PDepend\Source\AST\ASTAllocationExpression;
49
use PDepend\Source\AST\ASTArguments;
50
use PDepend\Source\AST\ASTArray;
51
use PDepend\Source\AST\ASTCatchStatement;
52
use PDepend\Source\AST\ASTClass;
53
use PDepend\Source\AST\ASTDeclareStatement;
54
use PDepend\Source\AST\ASTExpression;
55
use PDepend\Source\AST\ASTIndexExpression;
56
use PDepend\Source\AST\ASTInterface;
57
use PDepend\Source\AST\ASTNamespace;
58
use PDepend\Source\AST\ASTNode;
59
use PDepend\Source\AST\ASTStatement;
60
use PDepend\Source\AST\ASTSwitchStatement;
61
use PDepend\Source\AST\ASTTrait;
62
use PDepend\Source\AST\ASTValue;
63
use PDepend\Source\AST\State;
64
use PDepend\Source\Builder\Builder;
65
use PDepend\Source\Parser\InvalidStateException;
66
use PDepend\Source\Parser\MissingValueException;
67
use PDepend\Source\Parser\NoActiveScopeException;
68
use PDepend\Source\Parser\TokenStreamEndException;
69
use PDepend\Source\Parser\UnexpectedTokenException;
70
use PDepend\Source\Tokenizer\Token;
71
use PDepend\Source\Tokenizer\Tokenizer;
72
use PDepend\Source\Tokenizer\Tokens;
73
use PDepend\Util\Cache\CacheDriver;
74
use PDepend\Util\Log;
75
use PDepend\Util\Type;
76
77
/**
78
 * The php source parser.
79
 *
80
 * With the default settings the parser includes annotations, better known as
81
 * doc comment tags, in the generated result. This means it extracts the type
82
 * information of @var tags for properties, and types in @return + @throws tags
0 ignored issues
show
Documentation introduced by
The doc-type + could not be parsed: Unknown type name "+" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
83
 * of functions and methods. The current implementation tries to ignore all
84
 * scalar types from <b>boolean</b> to <b>void</b>. You should disable this
85
 * feature for project that have more or less invalid doc comments, because it
86
 * could produce invalid results.
87
 *
88
 * <code>
89
 *   $parser->setIgnoreAnnotations();
90
 * </code>
91
 *
92
 * <b>Note</b>: Due to the fact that it is possible to use the same name for
93
 * multiple classes and interfaces, and there is no way to determine to which
94
 * package it belongs, while the parser handles class, interface or method
95
 * signatures, the parser could/will create a code tree that doesn't reflect the
96
 * real source structure.
97
 *
98
 * @copyright 2008-2017 Manuel Pichler. All rights reserved.
99
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
100
 */
101
abstract class AbstractPHPParser
102
{
103
    /**
104
     * Regular expression for inline type definitions in regular comments. This
105
     * kind of type is supported by IDEs like Netbeans or eclipse.
106
     */
107
    const REGEXP_INLINE_TYPE = '(^\s*/\*\s*
108
                                 @var\s+
109
                                   \$[a-zA-Z_\x7f-\xff\\\\][a-zA-Z0-9_\x7f-\xff]*\s+
110
                                   (.*?)
111
                                \s*\*/\s*$)ix';
112
113
    /**
114
     * Regular expression for types defined in <b>throws</b> annotations of
115
     * method or function doc comments.
116
     */
117
    const REGEXP_THROWS_TYPE = '(\*\s*
118
                             @throws\s+
119
                               ([a-zA-Z_\x7f-\xff\\\\][a-zA-Z0-9_\x7f-\xff\\\\]*)
120
                            )ix';
121
122
    /**
123
     * Regular expression for types defined in annotations like <b>return</b> or
124
     * <b>var</b> in doc comments of functions and methods.
125
     */
126
    const REGEXP_RETURN_TYPE = '(\*\s*
127
                     @return\s+
128
                      (array\(\s*
129
                        (\w+\s*=>\s*)?
130
                        ([a-zA-Z_\x7f-\xff\\\\][a-zA-Z0-9_\x7f-\xff\|\\\\]*)\s*
131
                      \)
132
                      |
133
                      ([a-zA-Z_\x7f-\xff\\\\][a-zA-Z0-9_\x7f-\xff\|\\\\]*))\s+
134
                      |
135
                       ([a-zA-Z_\x7f-\xff\\\\][a-zA-Z0-9_\x7f-\xff\|\\\\]*)\[\]
136
                    )ix';
137
138
    /**
139
     * Regular expression for types defined in annotations like <b>return</b> or
140
     * <b>var</b> in doc comments of functions and methods.
141
     */
142
    const REGEXP_VAR_TYPE = '(\*\s*
143
                      @var\s+
144
                       (array\(\s*
145
                         (\w+\s*=>\s*)?
146
                         ([a-zA-Z_\x7f-\xff\\\\][a-zA-Z0-9_\x7f-\xff\|\\\\]*)\s*
147
                       \)
148
                       |
149
                       ([a-zA-Z_\x7f-\xff\\\\][a-zA-Z0-9_\x7f-\xff\|\\\\]*))\s+
150
                       |
151
                       ([a-zA-Z_\x7f-\xff\\\\][a-zA-Z0-9_\x7f-\xff\|\\\\]*)\[\]\s+
152
                       |
153
                       (array)\(\s*\)\s+
154
                     )ix';
155
156
    /**
157
     * Internal state flag, that will be set to <b>true</b> when the parser has
158
     * prefixed a qualified name with the actual namespace.
159
     *
160
     * @var boolean
161
     */
162
    protected $namespacePrefixReplaced = false;
163
164
    /**
165
     * The name of the last detected namespace.
166
     *
167
     * @var string
168
     */
169
    private $namespaceName;
170
171
    /**
172
     * Last parsed package tag.
173
     *
174
     * @var string
175
     */
176
    private $packageName = Builder::DEFAULT_NAMESPACE;
177
178
    /**
179
     * The package defined in the file level comment.
180
     *
181
     * @var string
182
     */
183
    private $globalPackageName = Builder::DEFAULT_NAMESPACE;
184
185
    /**
186
     * The used data structure builder.
187
     *
188
     * @var Builder
189
     */
190
    protected $builder;
191
192
    /**
193
     * The currently parsed file instance.
194
     *
195
     * @var \PDepend\Source\AST\ASTCompilationUnit
196
     */
197
    protected $compilationUnit;
198
199
    /**
200
     * The symbol table used to handle PHP 5.3 use statements.
201
     *
202
     * @var \PDepend\Source\Parser\SymbolTable
203
     */
204
    protected $useSymbolTable;
205
206
    /**
207
     * The last parsed doc comment or <b>null</b>.
208
     *
209
     * @var string
210
     */
211
    private $docComment;
212
213
    /**
214
     * Bitfield of last parsed modifiers.
215
     *
216
     * @var integer
217
     */
218
    private $modifiers = 0;
219
220
    /**
221
     * The actually parsed class or interface instance.
222
     *
223
     * @var \PDepend\Source\AST\AbstractASTClassOrInterface
224
     */
225
    protected $classOrInterface;
226
227
    /**
228
     * If this property is set to <b>true</b> the parser will ignore all doc
229
     * comment annotations.
230
     *
231
     * @var boolean
232
     */
233
    private $ignoreAnnotations = false;
234
235
    /**
236
     * Stack with all active token scopes.
237
     *
238
     * @var \PDepend\Source\Tokenizer\TokenStack
239
     */
240
    protected $tokenStack;
241
242
    /**
243
     * Used identifier builder instance.
244
     *
245
     * @var \PDepend\Util\IdBuilder
246
     * @since 0.9.12
247
     */
248
    private $idBuilder = null;
249
250
    /**
251
     * The maximum valid nesting level allowed.
252
     *
253
     * @var   integer
254
     * @since 0.9.12
255
     */
256
    private $maxNestingLevel = 1024;
257
258
    /**
259
     *
260
     * @var \PDepend\Util\Cache\CacheDriver
261
     * @since 0.10.0
262
     */
263
    protected $cache;
264
265
    /*
266
     * The used code tokenizer.
267
     *
268
     * @var \PDepend\Source\Tokenizer\Tokenizer
269
     */
270
    protected $tokenizer;
271
272
    /**
273
     * Constructs a new source parser.
274
     *
275
     * @param \PDepend\Source\Tokenizer\Tokenizer $tokenizer
276
     * @param \PDepend\Source\Builder\Builder $builder
277
     * @param \PDepend\Util\Cache\CacheDriver $cache
278
     */
279 2374
    public function __construct(Tokenizer $tokenizer, Builder $builder, CacheDriver $cache)
280
    {
281 2374
        $this->tokenizer = $tokenizer;
282 2374
        $this->builder   = $builder;
283 2374
        $this->cache     = $cache;
284
285 2374
        $this->idBuilder    = new \PDepend\Util\IdBuilder();
286 2374
        $this->tokenStack     = new \PDepend\Source\Parser\TokenStack();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \PDepend\Source\Parser\TokenStack() of type object<PDepend\Source\Parser\TokenStack> is incompatible with the declared type object<PDepend\Source\Tokenizer\TokenStack> of property $tokenStack.

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...
287 2374
        $this->useSymbolTable = new \PDepend\Source\Parser\SymbolTable();
288
289 2374
        $this->builder->setCache($this->cache);
290 2374
    }
291
292
    /**
293
     * Sets the ignore annotations flag. This means that the parser will ignore
294
     * doc comment annotations.
295
     *
296
     * @return void
297
     */
298 8
    public function setIgnoreAnnotations()
299
    {
300 8
        $this->ignoreAnnotations = true;
301 8
    }
302
303
    /**
304
     * Configures the maximum allowed nesting level.
305
     *
306
     * @param integer $maxNestingLevel The maximum allowed nesting level.
307
     *
308
     * @return void
309
     * @since 0.9.12
310
     */
311 2
    public function setMaxNestingLevel($maxNestingLevel)
312
    {
313 2
        $this->maxNestingLevel = $maxNestingLevel;
314 2
    }
315
316
    /**
317
     * Returns the maximum allowed nesting/recursion level.
318
     *
319
     * @return integer
320
     * @since 0.9.12
321
     */
322 2372
    protected function getMaxNestingLevel()
323
    {
324 2372
        return $this->maxNestingLevel;
325
    }
326
327
    /**
328
     * Parses the contents of the tokenizer and generates a node tree based on
329
     * the found tokens.
330
     *
331
     * @return void
332
     */
333 2374
    public function parse()
334
    {
335 2374
        $this->compilationUnit = $this->tokenizer->getSourceFile();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->tokenizer->getSourceFile() of type string is incompatible with the declared type object<PDepend\Source\AST\ASTCompilationUnit> of property $compilationUnit.

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...
336 2374
        $this->compilationUnit
0 ignored issues
show
Bug introduced by
The method setCache cannot be called on $this->compilationUnit (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
337 2374
            ->setCache($this->cache)
338 2374
            ->setId($this->idBuilder->forFile($this->compilationUnit));
0 ignored issues
show
Documentation introduced by
$this->compilationUnit is of type string, but the function expects a object<PDepend\Source\AST\ASTCompilationUnit>.

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...
339
340 2374
        if ($this->compilationUnit->getFileName() === 'php://stdin') {
0 ignored issues
show
Bug introduced by
The method getFileName cannot be called on $this->compilationUnit (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
341
            $hash = md5('php://stdin');
342
        } else {
343 2374
            $hash = md5_file($this->compilationUnit->getFileName());
0 ignored issues
show
Bug introduced by
The method getFileName cannot be called on $this->compilationUnit (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
344
        }
345
346 2374
        if ($this->cache->restore($this->compilationUnit->getId(), $hash)) {
0 ignored issues
show
Bug introduced by
The method getId cannot be called on $this->compilationUnit (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
347 2
            return;
348
        }
349
350 2372
        $this->cache->remove($this->compilationUnit->getId());
0 ignored issues
show
Bug introduced by
The method getId cannot be called on $this->compilationUnit (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
351
352 2372
        $this->setUpEnvironment();
353
354 2372
        $this->tokenStack->push();
355
356 2372
        Log::debug('Processing file ' . $this->compilationUnit);
357
358 2372
        $tokenType = $this->tokenizer->peek();
359 2372
        while ($tokenType !== Tokenizer::T_EOF) {
360
            switch ($tokenType) {
361 2372
                case Tokens::T_COMMENT:
362 8
                    $this->consumeToken(Tokens::T_COMMENT);
363 8
                    break;
364 2372
                case Tokens::T_DOC_COMMENT:
365 142
                    $comment = $this->consumeToken(Tokens::T_DOC_COMMENT)->image;
366
367 142
                    $this->packageName = $this->parsePackageAnnotation($comment);
368 142
                    $this->docComment  = $comment;
369 142
                    break;
370 2372
                case Tokens::T_USE:
371
                    // Parse a use statement. This method has no return value but it
372
                    // creates a new entry in the symbol map.
373 22
                    $this->parseUseDeclarations();
374 20
                    break;
375 2372
                case Tokens::T_NAMESPACE:
376 148
                    $this->parseNamespaceDeclaration();
377 144
                    break;
378 2372
                case Tokens::T_NO_PHP:
379 2372
                case Tokens::T_OPEN_TAG:
380 2372
                case Tokens::T_OPEN_TAG_WITH_ECHO:
381 2372
                    $this->consumeToken($tokenType);
382 2372
                    $this->reset();
383 2372
                    break;
384 2366
                case Tokens::T_CLOSE_TAG:
385 344
                    $this->parseNonePhpCode();
386 344
                    $this->reset();
387 344
                    break;
388 2366
                default:
389 2366
                    if (null === $this->parseOptionalStatement()) {
390
                        // Consume whatever token
391 54
                        $this->consumeToken($tokenType);
392 54
                    }
393 2292
                    break;
394 2366
            }
395
396 2372
            $tokenType = $this->tokenizer->peek();
397 2372
        }
398
399 2292
        $this->compilationUnit->setTokens($this->tokenStack->pop());
0 ignored issues
show
Bug introduced by
The method setTokens cannot be called on $this->compilationUnit (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
400 2292
        $this->cache->store(
401 2292
            $this->compilationUnit->getId(),
0 ignored issues
show
Bug introduced by
The method getId cannot be called on $this->compilationUnit (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
402 2292
            $this->compilationUnit,
403
            $hash
404 2292
        );
405
406 2292
        $this->tearDownEnvironment();
407 2292
    }
408
409
    /**
410
     * Initializes the parser environment.
411
     *
412
     * @return void
413
     * @since 0.9.12
414
     */
415 2372
    protected function setUpEnvironment()
416
    {
417 2372
        ini_set('xdebug.max_nesting_level', $this->getMaxNestingLevel());
418
419 2372
        $this->useSymbolTable->createScope();
420
421 2372
        $this->reset();
422 2372
    }
423
424
    /**
425
     * Restores the parser environment back.
426
     *
427
     * @throws NoActiveScopeException
428
     *
429
     * @return void
430
     * @since 0.9.12
431
     */
432 2292
    protected function tearDownEnvironment()
433
    {
434 2292
        ini_restore('xdebug.max_nesting_level');
435
436 2292
        $this->useSymbolTable->destroyScope();
437 2292
    }
438
439
    /**
440
     * Resets some object properties.
441
     *
442
     * @param integer $modifiers Optional default modifiers.
443
     *
444
     * @return void
445
     */
446 2372
    protected function reset($modifiers = 0)
447
    {
448 2372
        $this->packageName = Builder::DEFAULT_NAMESPACE;
449 2372
        $this->docComment  = null;
450 2372
        $this->modifiers   = $modifiers;
451 2372
    }
452
453
    /**
454
     * Tests if the given token type is a reserved keyword in the supported PHP
455
     * version.
456
     *
457
     * @param  $tokenType
458
     * @return boolean
459
     * @since 1.1.1
460
     */
461
    abstract protected function isKeyword($tokenType);
462
463
    /**
464
     * Parses a valid class or interface name and returns the image of the parsed
465
     * token.
466
     *
467
     * @return string
468
     * @throws \PDepend\Source\Parser\TokenStreamEndException
469
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
470
     */
471 1474 View Code Duplication
    protected function parseClassName()
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...
472
    {
473 1474
        $type = $this->tokenizer->peek();
474
475 1474
        if ($this->isClassName($type)) {
476 1474
            return $this->consumeToken($type)->image;
477 2
        } elseif ($type === Tokenizer::T_EOF) {
478
            throw new TokenStreamEndException($this->tokenizer);
479
        }
480
481 2
        $this->throwUnexpectedTokenException();
482
    }
483
484
    /**
485
     * Will return <b>true</b> if the given <b>$tokenType</b> is a valid class
486
     * name part.
487
     *
488
     * @param integer $tokenType
489
     * @return boolean
490
     * @since 0.10.6
491
     */
492
    protected function isClassName($tokenType)
493
    {
494
        switch ($tokenType) {
495
            case Tokens::T_NULL:
496
            case Tokens::T_TRUE:
497
            case Tokens::T_FALSE:
498
            case Tokens::T_TRAIT:
499
            case Tokens::T_YIELD:
500
            case Tokens::T_STRING:
501
            case Tokens::T_TRAIT_C:
502
            case Tokens::T_CALLABLE:
503
            case Tokens::T_INSTEADOF:
504
                return true;
505
        }
506
        return false;
507
    }
508
509
    /**
510
     * Parses a function name from the given tokenizer and returns the string
511
     * literal representing the function name. If no valid token exists in the
512
     * token stream, this method will throw an exception.
513
     *
514
     * @return string
515
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
516
     * @throws \PDepend\Source\Parser\TokenStreamEndException
517
     * @since 0.10.0
518
     */
519 1556 View Code Duplication
    protected function parseFunctionName()
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...
520
    {
521 1556
        $tokenType = $this->tokenizer->peek();
522
523 1556
        if ($this->isFunctionName($tokenType)) {
524 1554
            return $this->consumeToken($tokenType)->image;
525 2
        } elseif ($tokenType === Tokenizer::T_EOF) {
526
            throw new TokenStreamEndException($this->tokenizer);
527
        }
528
529 2
        $this->throwUnexpectedTokenException();
530
    }
531
532
    /**
533
     * @param integer $tokenType
534
     * @return boolean
535
     */
536
    private function isAllowedName($tokenType)
537
    {
538
        switch ($tokenType) {
539
            case Tokens::T_NULL:
540
            case Tokens::T_SELF:
541
            case Tokens::T_TRUE:
542
            case Tokens::T_FALSE:
543
            case Tokens::T_TRAIT:
544
            case Tokens::T_YIELD:
545
            case Tokens::T_PARENT:
546
            case Tokens::T_STRING:
547
            case Tokens::T_TRAIT_C:
548
            case Tokens::T_CALLABLE:
549
            case Tokens::T_INSTEADOF:
550
                return true;
551
        }
552
        return false;
553
    }
554
555
    /**
556
     * @param integer $tokenType
557
     * @return boolean
558
     */
559
    protected function isConstantName($tokenType)
560
    {
561
        return $this->isAllowedName($tokenType);
562
    }
563
564
    /**
565
     * Tests if the give token is a valid function name in the supported PHP
566
     * version.
567
     *
568
     * @param integer $tokenType
569
     * @return boolean
570
     * @since 2.3
571
     */
572
    protected function isFunctionName($tokenType)
573
    {
574
        return $this->isAllowedName($tokenType);
575
    }
576
577
    /**
578
     * @return string
579
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
580
     * @throws \PDepend\Source\Parser\TokenStreamEndException
581
     */
582 476 View Code Duplication
    protected function parseMethodName()
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...
583
    {
584 476
        $tokenType = $this->tokenizer->peek();
585
586 476
        if ($this->isMethodName($tokenType)) {
587 476
            return $this->consumeToken($tokenType)->image;
588
        } elseif ($tokenType === Tokenizer::T_EOF) {
589
            throw new TokenStreamEndException($this->tokenizer);
590
        }
591
592
        $this->throwUnexpectedTokenException();
593
    }
594
595
    /**
596
     * @param integer $tokenType
597
     * @return bool
598
     */
599
    protected function isMethodName($tokenType)
600
    {
601
        return $this->isAllowedName($tokenType);
602
    }
603
604
    /**
605
     * Parses a trait declaration.
606
     *
607
     * @return \PDepend\Source\AST\ASTTrait
608
     * @since 1.0.0
609
     */
610 110 View Code Duplication
    private function parseTraitDeclaration()
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...
611
    {
612 110
        $this->tokenStack->push();
613
614 110
        $trait = $this->parseTraitSignature();
615 110
        $trait = $this->parseTypeBody($trait);
616 110
        $trait->setTokens($this->tokenStack->pop());
617
618 110
        $this->reset();
619
620 110
        return $trait;
621
    }
622
623
    /**
624
     * Parses the signature of a trait.
625
     *
626
     * @return \PDepend\Source\AST\ASTTrait
627
     */
628 110 View Code Duplication
    private function parseTraitSignature()
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...
629
    {
630 110
        $this->consumeToken(Tokens::T_TRAIT);
631 110
        $this->consumeComments();
632
633 110
        $qualifiedName = $this->createQualifiedTypeName($this->parseClassName());
634
635 110
        $trait = $this->builder->buildTrait($qualifiedName);
636 110
        $trait->setCompilationUnit($this->compilationUnit);
637 110
        $trait->setComment($this->docComment);
638 110
        $trait->setId($this->idBuilder->forClassOrInterface($trait));
639 110
        $trait->setUserDefined();
640
641 110
        return $trait;
642
    }
643
644
    /**
645
     * Parses the dependencies in a interface signature.
646
     *
647
     * @return \PDepend\Source\AST\ASTInterface
648
     */
649 154 View Code Duplication
    private function parseInterfaceDeclaration()
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...
650
    {
651 154
        $this->tokenStack->push();
652
653 154
        $interface = $this->parseInterfaceSignature();
654 154
        $interface = $this->parseTypeBody($interface);
655 152
        $interface->setTokens($this->tokenStack->pop());
656
657 152
        $this->reset();
658
659 152
        return $interface;
660
    }
661
662
    /**
663
     * Parses the signature of an interface and finally returns a configured
664
     * interface instance.
665
     *
666
     * @return \PDepend\Source\AST\ASTInterface
667
     * @since 0.10.2
668
     */
669 154 View Code Duplication
    private function parseInterfaceSignature()
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...
670
    {
671 154
        $this->consumeToken(Tokens::T_INTERFACE);
672 154
        $this->consumeComments();
673
674 154
        $qualifiedName = $this->createQualifiedTypeName($this->parseClassName());
675
676 154
        $interface = $this->builder->buildInterface($qualifiedName);
677 154
        $interface->setCompilationUnit($this->compilationUnit);
678 154
        $interface->setComment($this->docComment);
679 154
        $interface->setId($this->idBuilder->forClassOrInterface($interface));
680 154
        $interface->setUserDefined();
681
682 154
        return $this->parseOptionalExtendsList($interface);
683
    }
684
685
    /**
686
     * Parses an optional interface list of an interface declaration.
687
     *
688
     * @param  \PDepend\Source\AST\ASTInterface $interface
689
     * @return \PDepend\Source\AST\ASTInterface
690
     * @since 0.10.2
691
     */
692 154 View Code Duplication
    private function parseOptionalExtendsList(ASTInterface $interface)
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...
693
    {
694 154
        $this->consumeComments();
695 154
        $tokenType = $this->tokenizer->peek();
696
697 154
        if ($tokenType === Tokens::T_EXTENDS) {
698 68
            $this->consumeToken(Tokens::T_EXTENDS);
699 68
            $this->parseInterfaceList($interface);
700 68
        }
701 154
        return $interface;
702
    }
703
704
    /**
705
     * Parses the dependencies in a class signature.
706
     *
707
     * @return \PDepend\Source\AST\ASTClass
708
     */
709 696 View Code Duplication
    protected function parseClassDeclaration()
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...
710
    {
711 696
        $this->tokenStack->push();
712
713 696
        $class = $this->parseClassSignature();
714 696
        $class = $this->parseTypeBody($class);
715 678
        $class->setTokens($this->tokenStack->pop());
716
717 678
        $this->reset();
718
719 678
        return $class;
720
    }
721
722
    /**
723
     * Parses the signature of a class.
724
     *
725
     * The signature of a class consists of optional class modifiers like, final
726
     * or abstract, the T_CLASS token, the class name, an optional parent class
727
     * and an optional list of implemented interfaces.
728
     *
729
     * @return \PDepend\Source\AST\ASTClass
730
     * @since 1.0.0
731
     */
732 696
    protected function parseClassSignature()
733
    {
734 696
        $this->parseClassModifiers();
735 696
        $this->consumeToken(Tokens::T_CLASS);
736 696
        $this->consumeComments();
737
738 696
        $qualifiedName = $this->createQualifiedTypeName($this->parseClassName());
739
740 696
        $class = $this->builder->buildClass($qualifiedName);
741 696
        $class->setCompilationUnit($this->compilationUnit);
742 696
        $class->setModifiers($this->modifiers);
743 696
        $class->setComment($this->docComment);
744 696
        $class->setId($this->idBuilder->forClassOrInterface($class));
745 696
        $class->setUserDefined();
746
747 696
        $this->consumeComments();
748 696
        $tokenType = $this->tokenizer->peek();
749
750 696 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...
751 154
            $class = $this->parseClassExtends($class);
752
753 154
            $this->consumeComments();
754 154
            $tokenType = $this->tokenizer->peek();
755 154
        }
756
757 696
        if ($tokenType === Tokens::T_IMPLEMENTS) {
758 72
            $this->consumeToken(Tokens::T_IMPLEMENTS);
759 72
            $this->parseInterfaceList($class);
760 72
        }
761
762 696
        return $class;
763
    }
764
765
    /**
766
     * This method parses an optional class modifier. Valid class modifiers are
767
     * <b>final</b> or <b>abstract</b>.
768
     *
769
     * @return void
770
     */
771 696
    private function parseClassModifiers()
772
    {
773 696
        $this->consumeComments();
774 696
        $tokenType = $this->tokenizer->peek();
775
776 696
        if ($tokenType === Tokens::T_ABSTRACT) {
777 40
            $this->consumeToken(Tokens::T_ABSTRACT);
778 40
            $this->modifiers |= State::IS_EXPLICIT_ABSTRACT;
779 696
        } elseif ($tokenType === Tokens::T_FINAL) {
780 6
            $this->consumeToken(Tokens::T_FINAL);
781 6
            $this->modifiers |= State::IS_FINAL;
782 6
        }
783
784 696
        $this->consumeComments();
785 696
    }
786
787
    /**
788
     * Parses a parent class declaration for the given <b>$class</b>.
789
     *
790
     * @param \PDepend\Source\AST\ASTClass $class
791
     * @return \PDepend\Source\AST\ASTClass
792
     * @since 1.0.0
793
     */
794 154
    protected function parseClassExtends(ASTClass $class)
795
    {
796 154
        $this->consumeToken(Tokens::T_EXTENDS);
797 154
        $this->tokenStack->push();
798
799 154
        $class->setParentClassReference(
800 154
            $this->setNodePositionsAndReturn(
0 ignored issues
show
Compatibility introduced by
$this->setNodePositionsA...>parseQualifiedName())) of type object<PDepend\Source\AST\ASTNode> is not a sub-type of object<PDepend\Source\AST\ASTClassReference>. It seems like you assume a concrete implementation of the interface PDepend\Source\AST\ASTNode to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
801 154
                $this->builder->buildAstClassReference(
802 154
                    $this->parseQualifiedName()
803 154
                )
804 154
            )
805 154
        );
806
807 154
        return $class;
808
    }
809
810
    /**
811
     * This method parses a list of interface names as used in the <b>extends</b>
812
     * part of a interface declaration or in the <b>implements</b> part of a
813
     * class declaration.
814
     *
815
     * @param \PDepend\Source\AST\AbstractASTClassOrInterface $abstractType
816
     * @return void
817
     */
818 130 View Code Duplication
    protected function parseInterfaceList(AbstractASTClassOrInterface $abstractType)
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...
819
    {
820 130
        while (true) {
821 130
            $this->tokenStack->push();
822
823 130
            $abstractType->addInterfaceReference(
824 130
                $this->setNodePositionsAndReturn(
0 ignored issues
show
Compatibility introduced by
$this->setNodePositionsA...>parseQualifiedName())) of type object<PDepend\Source\AST\ASTNode> is not a sub-type of object<PDepend\Source\AS...ssOrInterfaceReference>. It seems like you assume a concrete implementation of the interface PDepend\Source\AST\ASTNode to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
825 130
                    $this->builder->buildAstClassOrInterfaceReference(
826 130
                        $this->parseQualifiedName()
827 130
                    )
828 130
                )
829 130
            );
830
831 130
            $this->consumeComments();
832 130
            $tokenType = $this->tokenizer->peek();
833
834 130
            if ($tokenType === Tokens::T_CURLY_BRACE_OPEN) {
835 130
                break;
836
            }
837 40
            $this->consumeToken(Tokens::T_COMMA);
838 40
        }
839 130
    }
840
841
    /**
842
     * Parses a class/interface/trait body.
843
     *
844
     * @param \PDepend\Source\AST\AbstractASTClassOrInterface $classOrInterface
845
     * @return \PDepend\Source\AST\AbstractASTClassOrInterface
846
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
847
     * @throws \PDepend\Source\Parser\TokenStreamEndException
848
     */
849 842
    protected function parseTypeBody(AbstractASTClassOrInterface $classOrInterface)
850
    {
851 842
        $this->classOrInterface = $classOrInterface;
852
853
        // Consume comments and read opening curly brace
854 842
        $this->consumeComments();
855 842
        $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
856
857 842
        $defaultModifier = State::IS_PUBLIC;
858 842
        if ($classOrInterface instanceof ASTInterface) {
859 154
            $defaultModifier |= State::IS_ABSTRACT;
860 154
        }
861 842
        $this->reset();
862
863 842
        $tokenType = $this->tokenizer->peek();
864
865 842
        while ($tokenType !== Tokenizer::T_EOF) {
866
            switch ($tokenType) {
867 842
                case Tokens::T_ABSTRACT:
868 842
                case Tokens::T_PUBLIC:
869 842
                case Tokens::T_PRIVATE:
870 842
                case Tokens::T_PROTECTED:
871 842
                case Tokens::T_STATIC:
872 842
                case Tokens::T_FINAL:
873 842
                case Tokens::T_FUNCTION:
874 842
                case Tokens::T_VARIABLE:
875 842
                case Tokens::T_VAR:
876 524
                    $methodOrProperty = $this->parseMethodOrFieldDeclaration(
877
                        $defaultModifier
878 524
                    );
879
880 512
                    if ($methodOrProperty instanceof \PDepend\Source\AST\ASTNode) {
881 124
                        $classOrInterface->addChild($methodOrProperty);
882 124
                    }
883
884 512
                    $this->reset();
885 512
                    break;
886 830
                case Tokens::T_CONST:
887 98
                    $classOrInterface->addChild($this->parseConstantDefinition());
888 96
                    $this->reset();
889 96
                    break;
890 828
                case Tokens::T_CURLY_BRACE_CLOSE:
891 822
                    $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
892
893 822
                    $this->reset();
894
895
                    // Reset context class or interface instance
896 822
                    $this->classOrInterface = null;
897
898
                    // Stop processing
899 822
                    return $classOrInterface;
900 286
                case Tokens::T_COMMENT:
901 32
                    $token = $this->consumeToken(Tokens::T_COMMENT);
902
903 32
                    $comment = $this->builder->buildAstComment($token->image);
904 32
                    $comment->configureLinesAndColumns(
905 32
                        $token->startLine,
906 32
                        $token->endLine,
907 32
                        $token->startColumn,
908 32
                        $token->endColumn
909 32
                    );
910
911 32
                    $classOrInterface->addChild($comment);
912 32
                    break;
913 266
                case Tokens::T_DOC_COMMENT:
914 126
                    $token = $this->consumeToken(Tokens::T_DOC_COMMENT);
915
916 126
                    $comment = $this->builder->buildAstComment($token->image);
917 126
                    $comment->configureLinesAndColumns(
918 126
                        $token->startLine,
919 126
                        $token->endLine,
920 126
                        $token->startColumn,
921 126
                        $token->endColumn
922 126
                    );
923
924 126
                    $classOrInterface->addChild($comment);
925
926 126
                    $this->docComment = $token->image;
927 126
                    break;
928 140
                case Tokens::T_USE:
929 138
                    $classOrInterface->addChild($this->parseTraitUseStatement());
930 136
                    break;
931 2
                default:
932 2
                    $this->throwUnexpectedTokenException();
933 2
            }
934
935 630
            $tokenType = $this->tokenizer->peek();
936 630
        }
937
938 2
        throw new TokenStreamEndException($this->tokenizer);
939
    }
940
941
    /**
942
     * This method will parse a list of modifiers and a following property or
943
     * method.
944
     *
945
     * @param  integer $modifiers
946
     * @return \PDepend\Source\AST\ASTMethod|\PDepend\Source\AST\ASTFieldDeclaration
947
     * @since 0.9.6
948
     */
949 524
    private function parseMethodOrFieldDeclaration($modifiers = 0)
950
    {
951 524
        $this->tokenStack->push();
952
953 524
        $tokenType = $this->tokenizer->peek();
954 524
        while ($tokenType !== Tokenizer::T_EOF) {
955
            switch ($tokenType) {
956 524
                case Tokens::T_PRIVATE:
957 98
                    $modifiers |= State::IS_PRIVATE;
958 98
                    $modifiers = $modifiers & ~State::IS_PUBLIC;
959 98
                    break;
960 524
                case Tokens::T_PROTECTED:
961 78
                    $modifiers |= State::IS_PROTECTED;
962 78
                    $modifiers = $modifiers & ~State::IS_PUBLIC;
963 78
                    break;
964 524
                case Tokens::T_VAR:
965 524
                case Tokens::T_PUBLIC:
966 306
                    $modifiers |= State::IS_PUBLIC;
967 306
                    break;
968 524
                case Tokens::T_STATIC:
969 42
                    $modifiers |= State::IS_STATIC;
970 42
                    break;
971 524
                case Tokens::T_ABSTRACT:
972 38
                    $modifiers |= State::IS_ABSTRACT;
973 38
                    break;
974 524
                case Tokens::T_FINAL:
975 30
                    $modifiers |= State::IS_FINAL;
976 30
                    break;
977 524
                case Tokens::T_FUNCTION:
978 444
                    $method = $this->parseMethodDeclaration();
979 434
                    $method->setModifiers($modifiers);
980 434
                    $method->setCompilationUnit($this->compilationUnit);
981 434
                    $method->setId($this->idBuilder->forMethod($method));
982 434
                    $method->setTokens($this->tokenStack->pop());
983
984 434
                    return $method;
985 126
                case Tokens::T_VARIABLE:
986 122
                    $declaration = $this->parseFieldDeclaration();
987 122
                    $declaration->setModifiers($modifiers);
988
989 122
                    return $declaration;
990 4
                default:
991 4
                    return $this->parseUnknownDeclaration($tokenType, $modifiers);
992 4
            }
993
994 376
            $this->consumeToken($tokenType);
995 376
            $this->consumeComments();
996
997 376
            $tokenType = $this->tokenizer->peek();
998 376
        }
999
1000
        $this->throwUnexpectedTokenException();
1001
    }
1002
1003
    /**
1004
     * Override this in later PHPParserVersions as necessary
1005
     * @param integer $tokenType
1006
     * @param integer $modifiers
1007
     * @throws UnexpectedTokenException
1008
     */
1009 2
    protected function parseUnknownDeclaration($tokenType, $modifiers)
1010
    {
1011 2
        $this->throwUnexpectedTokenException();
1012
    }
1013
1014
    /**
1015
     * This method will parse a class field declaration with all it's variables.
1016
     *
1017
     * <code>
1018
     * // Simple field declaration
1019
     * class Foo {
1020
     *     protected $foo;
1021
     * }
1022
     *
1023
     * // Field declaration with multiple properties
1024
     * class Foo {
1025
     *     protected $foo = 23
1026
     *               $bar = 42,
1027
     *               $baz = null;
1028
     * }
1029
     * </code>
1030
     *
1031
     * @return \PDepend\Source\AST\ASTFieldDeclaration
1032
     * @since 0.9.6
1033
     */
1034 122
    private function parseFieldDeclaration()
1035
    {
1036 122
        $declaration = $this->builder->buildAstFieldDeclaration();
1037 122
        $declaration->setComment($this->docComment);
1038
1039 122
        $type = $this->parseFieldDeclarationType();
1040 122
        if ($type !== null) {
1041 42
            $declaration->addChild($type);
1042 42
        }
1043
1044 122
        $this->consumeComments();
1045 122
        $tokenType = $this->tokenizer->peek();
1046
1047 122
        while ($tokenType !== Tokenizer::T_EOF) {
1048 122
            $declaration->addChild($this->parseVariableDeclarator());
1049
1050 122
            $this->consumeComments();
1051 122
            $tokenType = $this->tokenizer->peek();
1052
1053 122
            if ($tokenType !== Tokens::T_COMMA) {
1054 122
                break;
1055
            }
1056 16
            $this->consumeToken(Tokens::T_COMMA);
1057
1058 16
            $this->consumeComments();
1059 16
            $tokenType = $this->tokenizer->peek();
1060 16
        }
1061
1062 122
        $this->setNodePositionsAndReturn($declaration);
1063
1064 122
        $this->consumeToken(Tokens::T_SEMICOLON);
1065
1066 122
        return $declaration;
1067
    }
1068
1069
    /**
1070
     * This method parses a simple function or a PHP 5.3 lambda function or
1071
     * closure.
1072
     *
1073
     * @return \PDepend\Source\AST\AbstractASTCallable
1074
     * @since 0.9.5
1075
     */
1076 1556
    private function parseFunctionOrClosureDeclaration()
1077
    {
1078 1556
        $this->tokenStack->push();
1079
1080 1556
        $this->consumeToken(Tokens::T_FUNCTION);
1081 1556
        $this->consumeComments();
1082
1083 1556
        $returnReference = $this->parseOptionalByReference();
1084
1085 1556
        if ($this->isNextTokenFormalParameterList()) {
1086 2
            $callable = $this->parseClosureDeclaration();
1087 2
            return $this->setNodePositionsAndReturn($callable);
1088
        } else {
1089 1556
            $callable = $this->parseFunctionDeclaration();
1090 1502
            $this->compilationUnit->addChild($callable);
1091
        }
1092
1093 1502
        $callable->setComment($this->docComment);
1094 1502
        $callable->setTokens($this->tokenStack->pop());
1095 1502
        $this->prepareCallable($callable);
1096
1097 1502
        if ($returnReference) {
1098 4
            $callable->setReturnsReference();
1099 4
        }
1100
1101 1502
        $this->reset();
1102
1103 1502
        return $callable;
1104
    }
1105
1106
    /**
1107
     * Parses an optional by reference token. The return value will be
1108
     * <b>true</b> when a reference token was found, otherwise this method will
1109
     * return <b>false</b>.
1110
     *
1111
     * @return boolean
1112
     * @since 0.9.8
1113
     */
1114 1972
    private function parseOptionalByReference()
1115
    {
1116 1972
        if ($this->isNextTokenByReference()) {
1117 40
            return $this->parseByReference();
1118
        }
1119 1966
        return false;
1120
    }
1121
1122
    /**
1123
     * Tests that the next available token is the returns by reference token.
1124
     *
1125
     * @return boolean
1126
     * @since 0.9.8
1127
     */
1128 1972
    private function isNextTokenByReference()
1129
    {
1130 1972
        return ($this->tokenizer->peek() === Tokens::T_BITWISE_AND);
1131
    }
1132
1133
    /**
1134
     * This method parses a returns by reference token and returns <b>true</b>.
1135
     *
1136
     * @return boolean
1137
     */
1138 40
    private function parseByReference()
1139
    {
1140 40
        $this->consumeToken(Tokens::T_BITWISE_AND);
1141 40
        $this->consumeComments();
1142
1143 40
        return true;
1144
    }
1145
1146
    /**
1147
     * Tests that the next available token is an opening parenthesis.
1148
     *
1149
     * @return boolean
1150
     * @since 0.9.10
1151
     */
1152 1556
    private function isNextTokenFormalParameterList()
1153
    {
1154 1556
        $this->consumeComments();
1155 1556
        return ($this->tokenizer->peek() === Tokens::T_PARENTHESIS_OPEN);
1156
    }
1157
1158
    /**
1159
     * This method parses a function declaration.
1160
     *
1161
     * @return \PDepend\Source\AST\ASTFunction
1162
     * @since 0.9.5
1163
     */
1164 1556
    private function parseFunctionDeclaration()
1165
    {
1166 1556
        $this->consumeComments();
1167
1168
        // Next token must be the function identifier
1169 1556
        $functionName = $this->parseFunctionName();
1170
1171 1554
        $function = $this->builder->buildFunction($functionName);
1172 1554
        $function->setCompilationUnit($this->compilationUnit);
1173 1554
        $function->setId($this->idBuilder->forFunction($function));
1174
1175 1554
        $this->parseCallableDeclaration($function);
1176
1177
        // First check for an existing namespace
1178 1502
        if ($this->namespaceName !== null) {
1179 54
            $namespaceName = $this->namespaceName;
1180 1502
        } elseif ($this->packageName !== Builder::DEFAULT_NAMESPACE) {
1181 32
            $namespaceName = $this->packageName;
1182 32
        } else {
1183 1446
            $namespaceName = $this->globalPackageName;
1184
        }
1185
1186 1502
        $namespace = $this->builder->buildNamespace($namespaceName);
1187 1502
        $namespace->setPackageAnnotation(null === $this->namespaceName);
1188 1502
        $namespace->addFunction($function);
1189
1190
        // Store function in source file, because we need them during the file's
1191
        // __wakeup() phase for function declarations within another function or
1192
        // method declaration.
1193 1502
        $this->compilationUnit->addChild($function);
1194
1195 1502
        return $function;
1196
    }
1197
1198
    /**
1199
     * This method parses a method declaration.
1200
     *
1201
     * @return \PDepend\Source\AST\ASTMethod
1202
     * @since 0.9.5
1203
     */
1204 444
    private function parseMethodDeclaration()
1205
    {
1206
        // Read function keyword
1207 444
        $this->consumeToken(Tokens::T_FUNCTION);
1208 444
        $this->consumeComments();
1209
1210 444
        $returnsReference = $this->parseOptionalByReference();
1211
1212 444
        $methodName = $this->parseMethodName();
1213
1214 444
        $method = $this->builder->buildMethod($methodName);
1215 444
        $method->setComment($this->docComment);
1216 444
        $method->setCompilationUnit($this->compilationUnit);
1217
1218 444
        $this->classOrInterface->addMethod($method);
1219
1220 444
        $this->parseCallableDeclaration($method);
1221 434
        $this->prepareCallable($method);
1222
1223 434
        if ($returnsReference === true) {
1224 6
            $method->setReturnsReference();
1225 6
        }
1226
1227 434
        return $method;
1228
    }
1229
1230
    /**
1231
     * This method parses a PHP 5.3 closure or lambda function.
1232
     *
1233
     * @return \PDepend\Source\AST\ASTClosure
1234
     * @since 0.9.5
1235
     */
1236 52 View Code Duplication
    private function parseClosureDeclaration()
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...
1237
    {
1238 52
        $this->tokenStack->push();
1239
1240 52
        if (Tokens::T_FUNCTION === $this->tokenizer->peek()) {
1241 50
            $this->consumeToken(Tokens::T_FUNCTION);
1242 50
        }
1243
1244 52
        $closure = $this->builder->buildAstClosure();
1245 52
        $closure->setReturnsByReference($this->parseOptionalByReference());
1246 52
        $closure->addChild($this->parseFormalParameters());
1247 52
        $closure = $this->parseOptionalBoundVariables($closure);
1248 52
        $closure = $this->parseCallableDeclarationAddition($closure);
0 ignored issues
show
Documentation introduced by
$closure is of type object<PDepend\Source\AST\ASTClosure>, but the function expects a object<PDepend\Source\AST\AbstractASTCallable>.

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...
1249 52
        $closure->addChild($this->parseScope());
1250
1251 52
        return $this->setNodePositionsAndReturn($closure);
0 ignored issues
show
Documentation introduced by
$closure is of type object<PDepend\Source\AST\AbstractASTCallable>, 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...
1252
    }
1253
1254
    /**
1255
     * Parses a function or a method and adds it to the parent context node.
1256
     *
1257
     * @param  \PDepend\Source\AST\AbstractASTCallable $callable
1258
     * @return void
1259
     */
1260 1962
    private function parseCallableDeclaration(AbstractASTCallable $callable)
1261
    {
1262 1962
        $callable->addChild($this->parseFormalParameters());
1263 1946
        $callable = $this->parseCallableDeclarationAddition($callable);
1264
1265 1946
        $this->consumeComments();
1266 1946
        if ($this->tokenizer->peek() == Tokens::T_CURLY_BRACE_OPEN) {
1267 1914
            $callable->addChild($this->parseScope());
1268 1868
        } else {
1269 64
            $this->consumeToken(Tokens::T_SEMICOLON);
1270
        }
1271 1900
    }
1272
1273
    /**
1274
     * Extension for version specific additions.
1275
     *
1276
     * @param \PDepend\Source\AST\AbstractASTCallable $callable
1277
     * @return \PDepend\Source\AST\AbstractASTCallable
1278
     */
1279
    protected function parseCallableDeclarationAddition($callable)
1280
    {
1281
        return $callable;
1282
    }
1283
1284
    /**
1285
     * Parses a trait use statement.
1286
     *
1287
     * @return \PDepend\Source\AST\ASTTraitUseStatement
1288
     * @since 1.0.0
1289
     */
1290 138 View Code Duplication
    private function parseTraitUseStatement()
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...
1291
    {
1292 138
        $this->tokenStack->push();
1293 138
        $this->consumeToken(Tokens::T_USE);
1294
1295 138
        $useStatement = $this->builder->buildAstTraitUseStatement();
1296 138
        $useStatement->addChild($this->parseTraitReference());
1297
1298 138
        $this->consumeComments();
1299 138
        while (Tokens::T_COMMA === $this->tokenizer->peek()) {
1300 40
            $this->consumeToken(Tokens::T_COMMA);
1301 40
            $useStatement->addChild($this->parseTraitReference());
1302 40
        }
1303
1304 138
        return $this->setNodePositionsAndReturn(
1305 138
            $this->parseOptionalTraitAdaptation($useStatement)
1306 136
        );
1307
    }
1308
1309
    /**
1310
     * Parses a trait reference instance.
1311
     *
1312
     * @return \PDepend\Source\AST\ASTTraitReference
1313
     * @since 1.0.0
1314
     */
1315 138
    private function parseTraitReference()
1316
    {
1317 138
        $this->consumeComments();
1318 138
        $this->tokenStack->push();
1319
1320 138
        return $this->setNodePositionsAndReturn(
1321 138
            $this->builder->buildAstTraitReference(
1322 138
                $this->parseQualifiedName()
1323 138
            )
1324 138
        );
1325
    }
1326
1327
    /**
1328
     * Parses the adaptation list of the given use statement or simply reads
1329
     * the terminating semicolon, when no adaptation list exists.
1330
     *
1331
     * @param \PDepend\Source\AST\ASTTraitUseStatement $useStatement
1332
     * @return \PDepend\Source\AST\ASTTraitUseStatement
1333
     * @since 1.0.0
1334
     */
1335 138 View Code Duplication
    private function parseOptionalTraitAdaptation(
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...
1336
        \PDepend\Source\AST\ASTTraitUseStatement $useStatement
1337
    ) {
1338 138
        $this->consumeComments();
1339 138
        if (Tokens::T_CURLY_BRACE_OPEN === $this->tokenizer->peek()) {
1340 106
            $useStatement->addChild($this->parseTraitAdaptation());
1341 104
        } else {
1342 38
            $this->consumeToken(Tokens::T_SEMICOLON);
1343
        }
1344 136
        return $useStatement;
1345
    }
1346
1347
    /**
1348
     * Parses the adaptation expression of a trait use statement.
1349
     *
1350
     * @return \PDepend\Source\AST\ASTTraitAdaptation
1351
     * @since 1.0.0
1352
     */
1353 106
    private function parseTraitAdaptation()
1354
    {
1355 106
        $this->tokenStack->push();
1356
1357 106
        $adaptation = $this->builder->buildAstTraitAdaptation();
1358
1359 106
        $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
1360
1361
        do {
1362 106
            $this->tokenStack->push();
1363
1364 106
            $reference = $this->parseTraitMethodReference();
1365 106
            $this->consumeComments();
1366
1367 106
            if (Tokens::T_AS === $this->tokenizer->peek()) {
1368 68
                $stmt = $this->parseTraitAdaptationAliasStatement($reference);
1369 68
            } else {
1370 38
                $stmt = $this->parseTraitAdaptationPrecedenceStatement($reference);
1371
            }
1372
1373 104
            $this->consumeComments();
1374 104
            $this->consumeToken(Tokens::T_SEMICOLON);
1375
1376 104
            $adaptation->addChild($this->setNodePositionsAndReturn($stmt));
1377
1378 104
            $this->consumeComments();
1379 104
        } while (Tokens::T_CURLY_BRACE_CLOSE !== $this->tokenizer->peek());
1380
1381 104
        $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
1382
1383 104
        return $this->setNodePositionsAndReturn($adaptation);
1384
    }
1385
1386
    /**
1387
     * Parses a trait method reference and returns the found reference as an
1388
     * <b>array</b>.
1389
     *
1390
     * The returned array with contain only one element, when the referenced
1391
     * method is specified by the method's name, without the declaring trait.
1392
     * When the method reference contains the declaring trait the returned
1393
     * <b>array</b> will contain two elements. The first element is the plain
1394
     * method name and the second element is an instance of the
1395
     * {@link \PDepend\Source\AST\ASTTraitReference} class that represents the
1396
     * declaring trait.
1397
     *
1398
     * @return array
1399
     * @since 1.0.0
1400
     */
1401 106
    private function parseTraitMethodReference()
1402
    {
1403 106
        $this->tokenStack->push();
1404
1405 106
        $qualifiedName = $this->parseQualifiedName();
1406
1407 106
        $this->consumeComments();
1408 106
        if (Tokens::T_DOUBLE_COLON === $this->tokenizer->peek()) {
1409 48
            $traitReference = $this->setNodePositionsAndReturn(
1410 48
                $this->builder->buildAstTraitReference($qualifiedName)
1411 48
            );
1412
1413 48
            $this->consumeToken(Tokens::T_DOUBLE_COLON);
1414 48
            $this->consumeComments();
1415
1416 48
            return array($this->parseMethodName(), $traitReference);
1417
        }
1418 60
        $this->tokenStack->pop();
1419
1420 60
        return array($qualifiedName);
1421
    }
1422
1423
    /**
1424
     * Parses a trait adaptation alias statement.
1425
     *
1426
     * @param array $reference Parsed method reference array.
1427
     *
1428
     * @return \PDepend\Source\AST\ASTTraitAdaptationAlias
1429
     * @since 1.0.0
1430
     */
1431 68
    private function parseTraitAdaptationAliasStatement(array $reference)
1432
    {
1433 68
        $stmt = $this->builder->buildAstTraitAdaptationAlias($reference[0]);
1434
1435 68
        if (2 === count($reference)) {
1436 12
            $stmt->addChild($reference[1]);
1437 12
        }
1438
1439 68
        $this->consumeToken(Tokens::T_AS);
1440 68
        $this->consumeComments();
1441
1442 68
        switch ($this->tokenizer->peek()) {
1443 68
            case Tokens::T_PUBLIC:
1444 14
                $stmt->setNewModifier(State::IS_PUBLIC);
1445 14
                $this->consumeToken(Tokens::T_PUBLIC);
1446 14
                $this->consumeComments();
1447 14
                break;
1448 54
            case Tokens::T_PROTECTED:
1449 14
                $stmt->setNewModifier(State::IS_PROTECTED);
1450 14
                $this->consumeToken(Tokens::T_PROTECTED);
1451 14
                $this->consumeComments();
1452 14
                break;
1453 40
            case Tokens::T_PRIVATE:
1454 12
                $stmt->setNewModifier(State::IS_PRIVATE);
1455 12
                $this->consumeToken(Tokens::T_PRIVATE);
1456 12
                $this->consumeComments();
1457 12
                break;
1458 68
        }
1459
1460 68
        if (Tokens::T_SEMICOLON !== $this->tokenizer->peek()) {
1461 30
            $stmt->setNewName($this->parseMethodName());
1462 30
        }
1463 68
        return $stmt;
1464
    }
1465
1466
    /**
1467
     * Parses a trait adaptation precedence statement.
1468
     *
1469
     * @param  array $reference Parsed method reference array.
1470
     * @return \PDepend\Source\AST\ASTTraitAdaptationPrecedence
1471
     * @throws \PDepend\Source\Parser\InvalidStateException
1472
     * @since 1.0.0
1473
     */
1474 38
    private function parseTraitAdaptationPrecedenceStatement(array $reference)
1475
    {
1476 38
        if (count($reference) < 2) {
1477 2
            throw new InvalidStateException(
1478 2
                $this->tokenizer->next()->startLine,
1479 2
                $this->compilationUnit->getFileName(),
1480
                'Expecting full qualified trait method name.'
1481 2
            );
1482
        }
1483
1484 36
        $stmt = $this->builder->buildAstTraitAdaptationPrecedence($reference[0]);
1485 36
        $stmt->addChild($reference[1]);
1486
1487 36
        $this->consumeToken(Tokens::T_INSTEADOF);
1488 36
        $this->consumeComments();
1489
1490 36
        $stmt->addChild($this->parseTraitReference());
1491
1492 36
        $this->consumeComments();
1493 36
        while (Tokens::T_COMMA === $this->tokenizer->peek()) {
1494 6
            $this->consumeToken(Tokens::T_COMMA);
1495 6
            $stmt->addChild($this->parseTraitReference());
1496 6
            $this->consumeComments();
1497 6
        }
1498
1499 36
        return $stmt;
1500
    }
1501
1502
    /**
1503
     * Parses an allocation expression.
1504
     *
1505
     * <code>
1506
     * function foo()
1507
     * {
1508
     * //  -------------
1509
     *     new bar\Baz();
1510
     * //  -------------
1511
     *
1512
     * //  ---------
1513
     *     new Foo();
1514
     * //  ---------
1515
     * }
1516
     * </code>
1517
     *
1518
     * @return \PDepend\Source\AST\ASTAllocationExpression
1519
     * @since 0.9.6
1520
     */
1521 166
    private function parseAllocationExpression()
1522
    {
1523 166
        $this->tokenStack->push();
1524
1525 166
        $token = $this->consumeToken(Tokens::T_NEW);
1526
1527 166
        $allocation = $this->builder->buildAstAllocationExpression($token->image);
1528 166
        $allocation = $this->parseAllocationExpressionTypeReference($allocation);
1529
1530 158
        if ($this->isNextTokenArguments()) {
1531 102
            $allocation->addChild($this->parseArguments());
1532 102
        }
1533 158
        return $this->setNodePositionsAndReturn($allocation);
1534
    }
1535
1536
    /**
1537
     * Parse the type reference used in an allocation expression.
1538
     *
1539
     * @param \PDepend\Source\AST\ASTAllocationExpression $allocation
1540
     * @return \PDepend\Source\AST\ASTNode
1541
     * @since 2.3
1542
     */
1543 160
    protected function parseAllocationExpressionTypeReference(ASTAllocationExpression $allocation)
1544
    {
1545 160
        return $this->parseExpressionTypeReference($allocation, true);
1546
    }
1547
1548
    /**
1549
     * Parses a eval-expression node.
1550
     *
1551
     * @return \PDepend\Source\AST\ASTEvalExpression
1552
     * @since 0.9.12
1553
     */
1554 8
    private function parseEvalExpression()
1555
    {
1556 8
        $this->tokenStack->push();
1557 8
        $token = $this->consumeToken(Tokens::T_EVAL);
1558
1559 8
        $expr = $this->builder->buildAstEvalExpression($token->image);
1560 8
        $expr->addChild($this->parseParenthesisExpression());
1561
1562 8
        return $this->setNodePositionsAndReturn($expr);
1563
    }
1564
1565
    /**
1566
     * This method parses an exit-expression.
1567
     *
1568
     * @return \PDepend\Source\AST\ASTExitExpression
1569
     * @since 0.9.12
1570
     */
1571 6 View Code Duplication
    private function parseExitExpression()
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...
1572
    {
1573 6
        $this->tokenStack->push();
1574 6
        $token = $this->consumeToken(Tokens::T_EXIT);
1575
1576 6
        $expr = $this->builder->buildAstExitExpression($token->image);
1577
1578 6
        $this->consumeComments();
1579 6
        if ($this->tokenizer->peek() === Tokens::T_PARENTHESIS_OPEN) {
1580 4
            $expr->addChild($this->parseParenthesisExpression());
1581 4
        }
1582 6
        return $this->setNodePositionsAndReturn($expr);
1583
    }
1584
1585
    /**
1586
     * Parses a clone-expression node.
1587
     *
1588
     * @return \PDepend\Source\AST\ASTCloneExpression
1589
     * @since 0.9.12
1590
     */
1591 8
    private function parseCloneExpression()
1592
    {
1593 8
        $this->tokenStack->push();
1594 8
        $token = $this->consumeToken(Tokens::T_CLONE);
1595
1596 8
        $expr = $this->builder->buildAstCloneExpression($token->image);
1597 8
        $expr->addChild($this->parseExpression());
1598
1599 8
        return $this->setNodePositionsAndReturn($expr);
1600
    }
1601
1602
    /**
1603
     * This method parses a single list-statement node.
1604
     *
1605
     * @return \PDepend\Source\AST\ASTListExpression
1606
     * @since 0.9.12
1607
     */
1608 26
    protected function parseListExpression()
1609
    {
1610 26
        $this->tokenStack->push();
1611
1612 26
        $token = $this->consumeToken(Tokens::T_LIST);
1613 26
1614
        $this->consumeComments();
1615 26
1616
        $list = $this->builder->buildAstListExpression($token->image);
1617 26
1618 26
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
1619
        $this->consumeComments();
1620 26
1621
        while (($tokenType = $this->tokenizer->peek()) !== Tokenizer::T_EOF) {
1622
            // The variable is optional:
1623
            //   list(, , , , $something) = ...;
1624
            // is valid.
1625 26
            switch ($tokenType) {
1626 22
                case Tokens::T_COMMA:
1627 22
                    $this->consumeToken(Tokens::T_COMMA);
1628 22
                    $this->consumeComments();
1629 26
                    break;
1630 26
                case Tokens::T_PARENTHESIS_CLOSE:
1631 24
                    break 2;
1632 2
                case Tokens::T_LIST:
1633 2
                    $list->addChild($this->parseListExpression());
1634 2
                    $this->consumeComments();
1635 24
                    break;
1636 24
                default:
1637 24
                    $list->addChild($this->parseVariableOrConstantOrPrimaryPrefix());
1638 24
                    $this->consumeComments();
1639 24
                    break;
1640 24
            }
1641
        }
1642 26
1643
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
1644 26
1645
        return $this->setNodePositionsAndReturn($list);
1646
    }
1647
1648
    /**
1649
     * Parses a include-expression node.
1650
     *
1651
     * @return \PDepend\Source\AST\ASTIncludeExpression
1652
     * @since 0.9.12
1653 16
     */
1654
    private function parseIncludeExpression()
1655 16
    {
1656
        $expr = $this->builder->buildAstIncludeExpression();
1657 16
1658
        return $this->parseRequireOrIncludeExpression($expr, Tokens::T_INCLUDE);
1659
    }
1660
1661
    /**
1662
     * Parses a include_once-expression node.
1663
     *
1664
     * @return \PDepend\Source\AST\ASTIncludeExpression
1665
     * @since 0.9.12
1666 2
     */
1667
    private function parseIncludeOnceExpression()
1668 2
    {
1669 2
        $expr = $this->builder->buildAstIncludeExpression();
1670
        $expr->setOnce();
1671 2
1672
        return $this->parseRequireOrIncludeExpression($expr, Tokens::T_INCLUDE_ONCE);
1673
    }
1674
1675
    /**
1676
     * Parses a require-expression node.
1677
     *
1678
     * @return \PDepend\Source\AST\ASTRequireExpression
1679
     * @since 0.9.12
1680 4
     */
1681
    private function parseRequireExpression()
1682 4
    {
1683
        $expr = $this->builder->buildAstRequireExpression();
1684 4
1685
        return $this->parseRequireOrIncludeExpression($expr, Tokens::T_REQUIRE);
1686
    }
1687
1688
    /**
1689
     * Parses a require_once-expression node.
1690
     *
1691
     * @return \PDepend\Source\AST\ASTRequireExpression
1692
     * @since 0.9.12
1693 2
     */
1694
    private function parseRequireOnceExpression()
1695 2
    {
1696 2
        $expr = $this->builder->buildAstRequireExpression();
1697
        $expr->setOnce();
1698 2
1699
        return $this->parseRequireOrIncludeExpression($expr, Tokens::T_REQUIRE_ONCE);
1700
    }
1701
1702
    /**
1703
     * Parses a <b>require_once</b>-, <b>require</b>-, <b>include_once</b>- or
1704
     * <b>include</b>-expression node.
1705
     *
1706
     * @param  \PDepend\Source\AST\ASTExpression $expr
1707
     * @param  integer                           $type
1708
     * @return \PDepend\Source\AST\ASTExpression
1709
     * @since 0.9.12
1710 24
     */
1711 View Code Duplication
    private function parseRequireOrIncludeExpression(ASTExpression $expr, $type)
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...
1712 24
    {
1713
        $this->tokenStack->push();
1714 24
1715 24
        $this->consumeToken($type);
1716
        $this->consumeComments();
1717 24
1718 10
        if ($this->tokenizer->peek() === Tokens::T_PARENTHESIS_OPEN) {
1719 10
            $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
1720 10
            $expr->addChild($this->parseOptionalExpression());
0 ignored issues
show
Bug introduced by
It seems like $this->parseOptionalExpression() can be null; however, addChild() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1721 10
            $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
1722 14
        } else {
1723
            $expr->addChild($this->parseOptionalExpression());
0 ignored issues
show
Bug introduced by
It seems like $this->parseOptionalExpression() can be null; however, addChild() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1724
        }
1725 24
1726
        return $this->setNodePositionsAndReturn($expr);
1727
    }
1728
1729
    /**
1730
     * Parses a cast-expression node.
1731
     *
1732
     * @return \PDepend\Source\AST\ASTCaseExpression
1733
     * @since 0.10.0
1734 38
     */
1735 View Code Duplication
    protected function parseCastExpression()
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...
1736 38
    {
1737
        $token = $this->consumeToken($this->tokenizer->peek());
1738 38
1739 38
        $expr = $this->builder->buildAstCastExpression($token->image);
1740 38
        $expr->configureLinesAndColumns(
1741 38
            $token->startLine,
1742 38
            $token->endLine,
1743 38
            $token->startColumn,
1744 38
            $token->endColumn
1745
        );
1746 38
1747
        return $expr;
1748
    }
1749
1750
    /**
1751
     * This method will parse an increment-expression. Depending on the previous
1752
     * node this can be a {@link \PDepend\Source\AST\ASTPostIncrementExpression} or
1753
     * {@link \PDepend\Source\AST\ASTPostfixExpression}.
1754
     *
1755
     * @param  array &$expressions List of previous parsed expression nodes.
1756
     * @return \PDepend\Source\AST\ASTExpression
1757
     * @since 0.10.0
1758 136
     */
1759
    private function parseIncrementExpression(array &$expressions)
1760 136
    {
1761 28
        if ($this->isReadWriteVariable(end($expressions))) {
1762
            return $this->parsePostIncrementExpression(array_pop($expressions));
1763 116
        }
1764
        return $this->parsePreIncrementExpression();
1765
    }
1766
1767
    /**
1768
     * Parses a post increment-expression and adds the given child to that node.
1769
     *
1770
     * @param \PDepend\Source\AST\ASTNode $child The child expression node.
1771
     *
1772
     * @return \PDepend\Source\AST\ASTPostfixExpression
1773
     * @since 0.10.0
1774 28
     */
1775 View Code Duplication
    private function parsePostIncrementExpression(ASTNode $child)
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...
1776 28
    {
1777
        $token = $this->consumeToken(Tokens::T_INC);
1778 28
1779 28
        $expr = $this->builder->buildAstPostfixExpression($token->image);
1780 28
        $expr->addChild($child);
1781 28
        $expr->configureLinesAndColumns(
1782 28
            $child->getStartLine(),
1783 28
            $token->endLine,
1784 28
            $child->getStartColumn(),
1785 28
            $token->endColumn
1786
        );
1787 28
1788
        return $expr;
1789
    }
1790
1791
    /**
1792
     * Parses a pre increment-expression and adds the given child to that node.
1793
     *
1794
     * @return \PDepend\Source\AST\ASTPreIncrementExpression
1795
     * @since 0.10.0
1796 116
     */
1797 View Code Duplication
    private function parsePreIncrementExpression()
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...
1798 116
    {
1799
        $token = $this->consumeToken(Tokens::T_INC);
1800 116
1801 116
        $expr = $this->builder->buildAstPreIncrementExpression();
1802 116
        $expr->configureLinesAndColumns(
1803 116
            $token->startLine,
1804 116
            $token->endLine,
1805 116
            $token->startColumn,
1806 116
            $token->endColumn
1807
        );
1808 116
1809
        return $expr;
1810
    }
1811
1812
    /**
1813
     * This method will parse an decrement-expression. Depending on the previous
1814
     * node this can be a {@link \PDepend\Source\AST\ASTPostDecrementExpression} or
1815
     * {@link \PDepend\Source\AST\ASTPostfixExpression}.
1816
     *
1817
     * @param array &$expressions List of previous parsed expression nodes.
1818
     *
1819
     * @return \PDepend\Source\AST\ASTExpression
1820
     * @since 0.10.0
1821 66
     */
1822
    private function parseDecrementExpression(array &$expressions)
1823 66
    {
1824 10
        if ($this->isReadWriteVariable(end($expressions))) {
1825
            return $this->parsePostDecrementExpression(array_pop($expressions));
1826 56
        }
1827
        return $this->parsePreDecrementExpression();
1828
    }
1829
1830
    /**
1831
     * Parses a post decrement-expression and adds the given child to that node.
1832
     *
1833
     * @param \PDepend\Source\AST\ASTNode $child The child expression node.
1834
     *
1835
     * @return \PDepend\Source\AST\ASTPostfixExpression
1836
     * @since 0.10.0
1837 10
     */
1838 View Code Duplication
    private function parsePostDecrementExpression(ASTNode $child)
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...
1839 10
    {
1840
        $token = $this->consumeToken(Tokens::T_DEC);
1841 10
1842 10
        $expr = $this->builder->buildAstPostfixExpression($token->image);
1843 10
        $expr->addChild($child);
1844 10
        $expr->configureLinesAndColumns(
1845 10
            $child->getStartLine(),
1846 10
            $token->endLine,
1847 10
            $child->getStartColumn(),
1848 10
            $token->endColumn
1849
        );
1850 10
1851
        return $expr;
1852
    }
1853
1854
    /**
1855
     * Parses a pre decrement-expression and adds the given child to that node.
1856
     *
1857
     * @return \PDepend\Source\AST\ASTPreDecrementExpression
1858
     * @since 0.10.0
1859 56
     */
1860 View Code Duplication
    private function parsePreDecrementExpression()
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...
1861 56
    {
1862
        $token = $this->consumeToken(Tokens::T_DEC);
1863 56
1864 56
        $expr = $this->builder->buildAstPreDecrementExpression();
1865 56
        $expr->configureLinesAndColumns(
1866 56
            $token->startLine,
1867 56
            $token->endLine,
1868 56
            $token->startColumn,
1869 56
            $token->endColumn
1870
        );
1871 56
1872
        return $expr;
1873
    }
1874
1875
    /**
1876
     * Parses one or more optional php <b>array</b> or <b>string</b> expressions.
1877
     *
1878
     * <code>
1879
     * ---------
1880
     * $array[0];
1881
     * ---------
1882
     *
1883
     * ----------------
1884
     * $array[1]['foo'];
1885
     * ----------------
1886
     *
1887
     * ----------------
1888
     * $string{1}[0]{0};
1889
     * ----------------
1890
     * </code>
1891
     *
1892
     * @param \PDepend\Source\AST\ASTNode $node The parent/context node instance.
1893
     *
1894
     * @return \PDepend\Source\AST\ASTNode
1895
     * @since 0.9.12
1896 1232
     */
1897
    protected function parseOptionalIndexExpression(ASTNode $node)
1898 1232
    {
1899
        $this->consumeComments();
1900 1232
1901 1232
        switch ($this->tokenizer->peek()) {
1902 4
            case Tokens::T_CURLY_BRACE_OPEN:
1903 1232
                return $this->parseStringIndexExpression($node);
1904 116
            case Tokens::T_SQUARED_BRACKET_OPEN:
1905 1232
                return $this->parseArrayIndexExpression($node);
1906
        }
1907 1232
1908
        return $node;
1909
    }
1910
1911
    /**
1912
     * Parses an index expression as it is valid to access elements in a php
1913
     * string or array.
1914
     *
1915
     * @param \PDepend\Source\AST\ASTNode       $node  The context source node.
1916
     * @param \PDepend\Source\AST\ASTExpression $expr  The concrete index expression.
1917
     * @param integer                           $open  The open token type.
1918
     * @param integer                           $close The close token type.
1919
     *
1920
     * @return \PDepend\Source\AST\ASTNode
1921
     * @since 0.9.12
1922 118
     */
1923
    private function parseIndexExpression(
1924
        \PDepend\Source\AST\ASTNode $node,
1925
        \PDepend\Source\AST\ASTExpression $expr,
1926
        $open,
1927
        $close
1928 118
    ) {
1929
        $this->consumeToken($open);
1930 118
1931 116
        if (($child = $this->parseOptionalExpression()) != null) {
1932 116
            $expr->addChild($child);
1933
        }
1934 118
1935
        $token = $this->consumeToken($close);
1936 118
1937 118
        $expr->configureLinesAndColumns(
1938 118
            $node->getStartLine(),
1939 118
            $token->endLine,
1940 118
            $node->getStartColumn(),
1941 118
            $token->endColumn
1942
        );
1943 118
1944
        return $this->parseOptionalIndexExpression($expr);
1945
    }
1946
1947
    /**
1948
     * Parses a mandatory array index expression.
1949
     *
1950
     * <code>
1951
     * //    ---
1952
     * $array[0];
1953
     * //    ---
1954
     * </code>
1955
     *
1956
     * @param \PDepend\Source\AST\ASTNode $node The context source node.
1957
     *
1958
     * @return \PDepend\Source\AST\ASTArrayIndexExpression
1959
     * @since 0.9.12
1960 116
     */
1961 View Code Duplication
    private function parseArrayIndexExpression(ASTNode $node)
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...
1962 116
    {
1963 116
        $expr = $this->builder->buildAstArrayIndexExpression();
1964
        $expr->addChild($node);
1965 116
1966 116
        return $this->parseIndexExpression(
1967 116
            $node,
1968 116
            $expr,
1969
            Tokens::T_SQUARED_BRACKET_OPEN,
1970 116
            Tokens::T_SQUARED_BRACKET_CLOSE
1971
        );
1972
    }
1973
1974
    /**
1975
     * Parses a mandatory array index expression.
1976
     *
1977
     * <code>
1978
     * //     ---
1979
     * $string{0};
1980
     * //     ---
1981
     * </code>
1982
     *
1983
     * @param \PDepend\Source\AST\ASTNode $node The context source node.
1984
     *
1985
     * @return \PDepend\Source\AST\ASTStringIndexExpression
1986
     * @since 0.9.12
1987 4
     */
1988 View Code Duplication
    private function parseStringIndexExpression(ASTNode $node)
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...
1989 4
    {
1990 4
        $expr = $this->builder->buildAstStringIndexExpression();
1991
        $expr->addChild($node);
1992 4
1993 4
        return $this->parseIndexExpression(
1994 4
            $node,
1995 4
            $expr,
1996
            Tokens::T_CURLY_BRACE_OPEN,
1997 4
            Tokens::T_CURLY_BRACE_CLOSE
1998
        );
1999
    }
2000
2001
    /**
2002
     * This method checks if the next available token starts an arguments node.
2003
     *
2004
     * @return boolean
2005
     * @since 0.9.8
2006 158
     */
2007
    protected function isNextTokenArguments()
2008 158
    {
2009 158
        $this->consumeComments();
2010
        return $this->tokenizer->peek() === Tokens::T_PARENTHESIS_OPEN;
2011
    }
2012
2013
    /**
2014
     * This method configures the given node with its start and end positions.
2015
     *
2016
     * @param \PDepend\Source\AST\ASTNode $node
2017
     * @param array &$tokens
2018
     * @return \PDepend\Source\AST\ASTNode
2019
     * @since 0.9.8
2020 2286
     */
2021
    protected function setNodePositionsAndReturn(ASTNode $node, array &$tokens = null)
2022 2286
    {
2023
        $tokens = $this->stripTrailingComments($this->tokenStack->pop());
2024 2286
2025 2286
        $end   = $tokens[count($tokens) - 1];
2026
        $start = $tokens[0];
2027 2286
2028 2286
        $node->configureLinesAndColumns(
2029 2286
            $start->startLine,
2030 2286
            $end->endLine,
2031 2286
            $start->startColumn,
2032 2286
            $end->endColumn
2033
        );
2034 2286
2035
        return $node;
2036
    }
2037
2038
    /**
2039
     * Strips all trailing comments from the given token stream.
2040
     *
2041
     * @param  Token[] $tokens Original token stream.
2042
     * @return Token[]
2043
     * @since 1.0.0
2044 2286
     */
2045
    private function stripTrailingComments(array $tokens)
2046 2286
    {
2047
        $comments = array(Tokens::T_COMMENT, Tokens::T_DOC_COMMENT);
2048 2286
2049 42
        while (count($tokens) > 1 && in_array(end($tokens)->type, $comments)) {
2050 42
            array_pop($tokens);
2051 2286
        }
2052
        return $tokens;
2053
    }
2054
2055
    /**
2056
     * This method parse an instance of expression with its associated class or
2057
     * interface reference.
2058
     *
2059
     * <code>
2060
     *          ----------------
2061
     * ($object instanceof Clazz);
2062
     *          ----------------
2063
     *
2064
     *          ------------------------
2065
     * ($object instanceof Clazz::$clazz);
2066
     *          ------------------------
2067
     *
2068
     *          -----------------
2069
     * ($object instanceof $clazz);
2070
     *          -----------------
2071
     *
2072
     *          -----------------------
2073
     * ($object instanceof $clazz->type);
2074
     *          -----------------------
2075
     *
2076
     *          -----------------------------
2077
     * ($object instanceof static|self|parent);
2078
     *          -----------------------------
2079
     * </code>
2080
     *
2081
     * @return \PDepend\Source\AST\ASTInstanceOfExpression
2082
     * @since 0.9.6
2083 26
     */
2084
    private function parseInstanceOfExpression()
2085
    {
2086 26
        // Consume the "instanceof" keyword and strip comments
2087
        $token = $this->consumeToken(Tokens::T_INSTANCEOF);
2088 26
2089 26
        return $this->parseExpressionTypeReference(
2090
            $this->builder->buildAstInstanceOfExpression($token->image),
2091 26
            false
2092
        );
2093
    }
2094
2095
    /**
2096
     * Parses an isset-expression node.
2097
     *
2098
     * <code>
2099
     * //  -----------
2100
     * if (isset($foo)) {
2101
     * //  -----------
2102
     * }
2103
     *
2104
     * //  -----------------------
2105
     * if (isset($foo, $bar, $baz)) {
2106
     * //  -----------------------
2107
     * }
2108
     * </code>
2109
     *
2110
     * @return \PDepend\Source\AST\ASTIssetExpression
2111
     * @since 0.9.12
2112 18
     */
2113
    private function parseIssetExpression()
2114 18
    {
2115 18
        $startToken = $this->consumeToken(Tokens::T_ISSET);
2116 18
        $this->consumeComments();
2117
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
2118 18
2119 18
        $expr = $this->builder->buildAstIssetExpression();
2120
        $expr = $this->parseVariableList($expr);
2121 18
2122
        $stopToken = $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
2123 18
2124 18
        $expr->configureLinesAndColumns(
2125 18
            $startToken->startLine,
2126 18
            $stopToken->endLine,
2127 18
            $startToken->startColumn,
2128 18
            $stopToken->endColumn
2129
        );
2130 18
2131
        return $expr;
2132
    }
2133
2134
    /**
2135
     * This method parses a type identifier as it is used in expression nodes
2136
     * like {@link \PDepend\Source\AST\ASTInstanceOfExpression} or an object
2137
     * allocation node like {@link \PDepend\Source\AST\ASTAllocationExpression}.
2138
     *
2139
     * @param \PDepend\Source\AST\ASTNode $expr
2140
     * @param boolean $classRef
2141
     * @return \PDepend\Source\AST\ASTNode
2142 186
     */
2143
    protected function parseExpressionTypeReference(ASTNode $expr, $classRef)
2144
    {
2145 186
        // Peek next token and look for a static type identifier
2146 186
        $this->consumeComments();
2147
        $tokenType = $this->tokenizer->peek();
2148
2149 186
        switch ($tokenType) {
2150 186
            case Tokens::T_DOLLAR:
2151
            case Tokens::T_VARIABLE:
2152 10
                // TODO: Parse variable or Member Primary Prefix + Property Postfix
2153 10
                $ref = $this->parseVariableOrFunctionPostfixOrMemberPrimaryPrefix();
2154 176
                break;
2155 8
            case Tokens::T_SELF:
2156 6
                $ref = $this->parseSelfReference($this->consumeToken(Tokens::T_SELF));
2157 168
                break;
2158 14
            case Tokens::T_PARENT:
2159 12
                $ref = $this->parseParentReference($this->consumeToken(Tokens::T_PARENT));
2160 154
                break;
2161 10
            case Tokens::T_STATIC:
2162 8
                $ref = $this->parseStaticReference($this->consumeToken(Tokens::T_STATIC));
2163 144
                break;
2164 144
            default:
2165 142
                $ref = $this->parseClassOrInterfaceReference($classRef);
2166 144
                break;
2167
        }
2168 178
2169 178
        $expr->addChild(
2170 178
            $this->parseOptionalMemberPrimaryPrefix(
2171 178
                $this->parseOptionalStaticMemberPrimaryPrefix($ref)
2172 178
            )
2173
        );
2174 178
2175
        return $expr;
2176
    }
2177
2178
    /**
2179
     * This method parses a conditional-expression.
2180
     *
2181
     * <code>
2182
     *         --------------
2183
     * $foo = ($bar ? 42 : 23);
2184
     *         --------------
2185
     * </code>
2186
     *
2187
     * @return \PDepend\Source\AST\ASTConditionalExpression
2188
     * @since 0.9.8
2189 14
     */
2190 View Code Duplication
    protected function parseConditionalExpression()
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...
2191 14
    {
2192 14
        $this->tokenStack->push();
2193
        $this->consumeToken(Tokens::T_QUESTION_MARK);
2194 14
2195 14
        $expr = $this->builder->buildAstConditionalExpression();
2196 14
        if (($child = $this->parseOptionalExpression()) != null) {
2197 14
            $expr->addChild($child);
2198
        }
2199 14
2200
        $this->consumeToken(Tokens::T_COLON);
2201 14
2202
        $expr->addChild($this->parseExpression());
2203 14
2204
        return $this->setNodePositionsAndReturn($expr);
2205
    }
2206
2207
    /**
2208
     * This method parses a shift left expression node.
2209
     *
2210
     * @return \PDepend\Source\AST\ASTShiftLeftExpression
2211
     * @since 1.0.1
2212 8
     */
2213 View Code Duplication
    protected function parseShiftLeftExpression()
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...
2214 8
    {
2215
        $token = $this->consumeToken(Tokens::T_SL);
2216 8
2217 8
        $expr = $this->builder->buildAstShiftLeftExpression();
2218 8
        $expr->configureLinesAndColumns(
2219 8
            $token->startLine,
2220 8
            $token->endLine,
2221 8
            $token->startColumn,
2222 8
            $token->endColumn
2223 8
        );
2224
        return $expr;
2225
    }
2226
2227
    /**
2228
     * This method parses a shift right expression node.
2229
     *
2230
     * @return \PDepend\Source\AST\ASTShiftRightExpression
2231
     * @since 1.0.1
2232 8
     */
2233 View Code Duplication
    protected function parseShiftRightExpression()
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...
2234 8
    {
2235
        $token = $this->consumeToken(Tokens::T_SR);
2236 8
2237 8
        $expr = $this->builder->buildAstShiftRightExpression();
2238 8
        $expr->configureLinesAndColumns(
2239 8
            $token->startLine,
2240 8
            $token->endLine,
2241 8
            $token->startColumn,
2242 8
            $token->endColumn
2243 8
        );
2244
        return $expr;
2245
    }
2246
2247
    /**
2248
     * This method parses a boolean and-expression.
2249
     *
2250
     * @return \PDepend\Source\AST\ASTBooleanAndExpression
2251
     * @since 0.9.8
2252 38
     */
2253 View Code Duplication
    protected function parseBooleanAndExpression()
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...
2254 38
    {
2255
        $token = $this->consumeToken(Tokens::T_BOOLEAN_AND);
2256 38
2257 38
        $expr = $this->builder->buildAstBooleanAndExpression();
2258 38
        $expr->configureLinesAndColumns(
2259 38
            $token->startLine,
2260 38
            $token->endLine,
2261 38
            $token->startColumn,
2262 38
            $token->endColumn
2263 38
        );
2264
        return $expr;
2265
    }
2266
2267
    /**
2268
     * This method parses a boolean or-expression.
2269
     *
2270
     * @return \PDepend\Source\AST\ASTBooleanOrExpression
2271
     * @since 0.9.8
2272 52
     */
2273 View Code Duplication
    protected function parseBooleanOrExpression()
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...
2274 52
    {
2275
        $token = $this->consumeToken(Tokens::T_BOOLEAN_OR);
2276 52
2277 52
        $expr = $this->builder->buildAstBooleanOrExpression();
2278 52
        $expr->configureLinesAndColumns(
2279 52
            $token->startLine,
2280 52
            $token->endLine,
2281 52
            $token->startColumn,
2282 52
            $token->endColumn
2283 52
        );
2284
        return $expr;
2285
    }
2286
2287
    /**
2288
     * This method parses a logical <b>and</b>-expression.
2289
     *
2290
     * @return \PDepend\Source\AST\ASTLogicalAndExpression
2291
     * @since 0.9.8
2292 8
     */
2293 View Code Duplication
    protected function parseLogicalAndExpression()
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...
2294 8
    {
2295
        $token = $this->consumeToken(Tokens::T_LOGICAL_AND);
2296 8
2297 8
        $expr = $this->builder->buildAstLogicalAndExpression();
2298 8
        $expr->configureLinesAndColumns(
2299 8
            $token->startLine,
2300 8
            $token->endLine,
2301 8
            $token->startColumn,
2302 8
            $token->endColumn
2303 8
        );
2304
        return $expr;
2305
    }
2306
2307
    /**
2308
     * This method parses a logical <b>or</b>-expression.
2309
     *
2310
     * @return \PDepend\Source\AST\ASTLogicalOrExpression
2311
     * @since 0.9.8
2312 8
     */
2313 View Code Duplication
    protected function parseLogicalOrExpression()
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...
2314 8
    {
2315
        $token = $this->consumeToken(Tokens::T_LOGICAL_OR);
2316 8
2317 8
        $expr = $this->builder->buildAstLogicalOrExpression();
2318 8
        $expr->configureLinesAndColumns(
2319 8
            $token->startLine,
2320 8
            $token->endLine,
2321 8
            $token->startColumn,
2322 8
            $token->endColumn
2323 8
        );
2324
        return $expr;
2325
    }
2326
2327
    /**
2328
     * This method parses a logical <b>xor</b>-expression.
2329
     *
2330
     * @return \PDepend\Source\AST\ASTLogicalXorExpression
2331
     * @since 0.9.8
2332 8
     */
2333 View Code Duplication
    protected function parseLogicalXorExpression()
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...
2334 8
    {
2335
        $token = $this->consumeToken(Tokens::T_LOGICAL_XOR);
2336 8
2337 8
        $expr = $this->builder->buildAstLogicalXorExpression();
2338 8
        $expr->configureLinesAndColumns(
2339 8
            $token->startLine,
2340 8
            $token->endLine,
2341 8
            $token->startColumn,
2342 8
            $token->endColumn
2343 8
        );
2344
        return $expr;
2345
    }
2346
2347
    /**
2348
     * Parses a class or interface reference node.
2349
     *
2350
     * @param boolean $classReference Force a class reference.
2351
     *
2352
     * @return \PDepend\Source\AST\ASTClassOrInterfaceReference
2353
     * @since 0.9.8
2354 144
     */
2355
    private function parseClassOrInterfaceReference($classReference)
2356 144
    {
2357
        $this->tokenStack->push();
2358 144
2359 120
        if ($classReference === true) {
2360 120
            return $this->setNodePositionsAndReturn(
2361 120
                $this->builder->buildAstClassReference(
2362 118
                    $this->parseQualifiedName()
2363 118
                )
2364
            );
2365 24
        }
2366 24
        return $this->setNodePositionsAndReturn(
2367 24
            $this->builder->buildAstClassOrInterfaceReference(
2368 24
                $this->parseQualifiedName()
2369 24
            )
2370
        );
2371
    }
2372
2373
    /**
2374
     * This method parses a brace expression and adds all parsed node instances
2375
     * to the given {@link \PDepend\Source\AST\ASTNode} object. Finally it returns
2376
     * the prepared input node.
2377
     *
2378
     * A brace expression can be a compound:
2379
     *
2380
     * <code>
2381
     * $this->{$foo ? 'foo' : 'bar'}();
2382
     * </code>
2383
     *
2384
     * or a parameter list:
2385
     *
2386
     * <code>
2387
     * $this->foo($bar, $baz);
2388
     * </code>
2389
     *
2390
     * or an array index:
2391
     *
2392
     * <code>
2393
     * $foo[$bar];
2394
     * </code>
2395
     *
2396
     * @param  \PDepend\Source\AST\ASTNode     $node
2397
     * @param  \PDepend\Source\Tokenizer\Token $start
2398
     * @param  integer                         $closeToken
2399
     * @return \PDepend\Source\AST\ASTNode
2400
     * @throws \PDepend\Source\Parser\TokenStreamEndException
2401
     * @since 0.9.6
2402 506
     */
2403
    private function parseBraceExpression(
2404
        ASTNode $node,
2405
        Token $start,
2406
        $closeToken
2407 506
    ) {
2408 502
        if (is_object($expr = $this->parseOptionalExpression())) {
2409 502
            $node->addChild($expr);
2410
        }
2411 506
2412
        $end = $this->consumeToken($closeToken);
2413 506
2414 506
        $node->configureLinesAndColumns(
2415 506
            $start->startLine,
2416 506
            $end->endLine,
2417 506
            $start->startColumn,
2418 506
            $end->endColumn
2419 506
        );
2420
        return $node;
2421
    }
2422
2423
    /**
2424
     * Parses the body of the given statement instance and adds all parsed nodes
2425
     * to that statement.
2426
     *
2427
     * @param \PDepend\Source\AST\ASTStatement $stmt The owning statement.
2428
     *
2429
     * @return \PDepend\Source\AST\ASTStatement
2430
     * @since 0.9.12
2431 396
     */
2432
    protected function parseStatementBody(\PDepend\Source\AST\ASTStatement $stmt)
2433 396
    {
2434 396
        $this->consumeComments();
2435
        $tokenType = $this->tokenizer->peek();
2436 396
2437 308
        if ($tokenType === Tokens::T_CURLY_BRACE_OPEN) {
2438 396
            $stmt->addChild($this->parseRegularScope());
2439 70
        } elseif ($tokenType === Tokens::T_COLON) {
2440 70
            $stmt->addChild($this->parseAlternativeScope());
2441 28
        } else {
2442
            $stmt->addChild($this->parseStatement());
2443 394
        }
2444
        return $stmt;
2445
    }
2446
2447
    /**
2448
     * Parse a scope enclosed by curly braces.
2449
     *
2450
     * @return \PDepend\Source\AST\ASTScope
2451
     * @since 0.9.12
2452 368
     */
2453
    private function parseRegularScope()
2454 368
    {
2455
        $this->tokenStack->push();
2456 368
2457 368
        $this->consumeComments();
2458
        $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
2459 368
2460
        $scope = $this->parseScopeStatements();
2461 368
2462 368
        $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
2463
        return $this->setNodePositionsAndReturn($scope);
2464
    }
2465
2466
    /**
2467
     * Parses the scope of a statement that is surrounded with PHP's alternative
2468
     * syntax for statements.
2469
     *
2470
     * @return \PDepend\Source\AST\ASTScopeStatement
2471
     * @since 0.10.0
2472 70
     */
2473
    private function parseAlternativeScope()
2474 70
    {
2475 70
        $this->tokenStack->push();
2476
        $this->consumeToken(Tokens::T_COLON);
2477 70
2478
        $scope = $this->parseScopeStatements();
2479 70
2480 70
        $this->parseOptionalAlternativeScopeTermination();
2481
        return $this->setNodePositionsAndReturn($scope);
2482
    }
2483
2484
    /**
2485
     * Parses all statements that exist in a scope an adds them to a scope
2486
     * instance.
2487
     *
2488
     * @return \PDepend\Source\AST\ASTScopeStatement
2489
     * @since 0.10.0
2490 430
     */
2491
    private function parseScopeStatements()
2492 430
    {
2493 430
        $scope = $this->builder->buildAstScopeStatement();
2494 246
        while (($child = $this->parseOptionalStatement()) != null) {
2495 240
            if ($child instanceof \PDepend\Source\AST\ASTNode) {
2496 240
                $scope->addChild($child);
2497 246
            }
2498 430
        }
2499
        return $scope;
2500
    }
2501
2502
    /**
2503
     * Parses the termination of a scope statement that uses PHP's laternative
2504
     * syntax format.
2505
     *
2506
     * @return void
2507
     * @since 0.10.0
2508 70
     */
2509
    private function parseOptionalAlternativeScopeTermination()
2510 70
    {
2511 70
        $tokenType = $this->tokenizer->peek();
2512 70
        if ($this->isAlternativeScopeTermination($tokenType)) {
2513 70
            $this->parseAlternativeScopeTermination($tokenType);
2514 70
        }
2515
    }
2516
2517
2518
    /**
2519
     * This method returns <b>true</b> when the given token identifier represents
2520
     * the end token of a alternative scope termination symbol. Otherwise this
2521
     * method will return <b>false</b>.
2522
     *
2523
     * @param integer $tokenType The token type identifier.
2524
     *
2525
     * @return boolean
2526
     * @since 0.10.0
2527 70
     */
2528
    private function isAlternativeScopeTermination($tokenType)
2529 70
    {
2530 70
        return in_array(
2531
            $tokenType,
2532 70
            array(
2533 70
                Tokens::T_ENDDECLARE,
2534 70
                Tokens::T_ENDFOR,
2535 70
                Tokens::T_ENDFOREACH,
2536 70
                Tokens::T_ENDIF,
2537
                Tokens::T_ENDSWITCH,
2538 70
                Tokens::T_ENDWHILE
2539 70
            )
2540
        );
2541
    }
2542
2543
    /**
2544
     * Parses a series of tokens that represent an alternative scope termination.
2545
     *
2546
     * @param integer $tokenType The token type identifier.
2547
     *
2548
     * @return void
2549
     * @since 0.10.0
2550 78
     */
2551 View Code Duplication
    private function parseAlternativeScopeTermination($tokenType)
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...
2552 78
    {
2553 78
        $this->consumeToken($tokenType);
2554
        $this->consumeComments();
2555 78
2556 68
        if ($this->tokenizer->peek() === Tokens::T_SEMICOLON) {
2557 68
            $this->consumeToken(Tokens::T_SEMICOLON);
2558 10
        } else {
2559
            $this->parseNonePhpCode();
2560 78
        }
2561
    }
2562
2563
    /**
2564
     * This method parses multiple expressions and adds them as children to the
2565
     * given <b>$exprList</b> node.
2566
     *
2567
     * @param \PDepend\Source\AST\ASTNode
2568
     * @return \PDepend\Source\AST\ASTNode
2569
     * @since 1.0.0
2570 266
     */
2571 View Code Duplication
    private function parseExpressionList(ASTNode $exprList)
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...
2572 266
    {
2573 266
        $this->consumeComments();
2574 266
        while ($expr = $this->parseOptionalExpression()) {
2575
            $exprList->addChild($expr);
2576 266
2577 266
            $this->consumeComments();
2578 138
            if (Tokens::T_COMMA === $this->tokenizer->peek()) {
2579 138
                $this->consumeToken(Tokens::T_COMMA);
2580 138
                $this->consumeComments();
2581 266
            } else {
2582
                break;
2583 138
            }
2584
        }
2585 266
2586
        return $exprList;
2587
    }
2588
2589
    /**
2590
     * This method parses an expression node and returns it. When no expression
2591
     * was found this method will throw an InvalidStateException.
2592
     *
2593
     * @return \PDepend\Source\AST\ASTNode
2594
     * @throws \PDepend\Source\Parser\ParserException
2595
     * @since 1.0.1
2596 324
     */
2597
    protected function parseExpression()
2598 324
    {
2599
        if (null === ($expr = $this->parseOptionalExpression())) {
2600
            $token = $this->consumeToken($this->tokenizer->peek());
2601
2602
            throw new InvalidStateException(
2603
                $token->startLine,
2604
                $this->compilationUnit->getFileName(),
2605
                'Mandatory expression expected.'
2606
            );
2607 324
        }
2608
        return $expr;
2609
    }
2610
2611
    /**
2612
     * This method optionally parses an expression node and returns it. When no
2613
     * expression was found this method will return <b>null</b>.
2614
     *
2615
     * @return \PDepend\Source\AST\ASTNode
2616
     * @throws \PDepend\Source\Parser\ParserException
2617
     * @since 0.9.6
2618 1574
     */
2619
    protected function parseOptionalExpression()
2620 1574
    {
2621
        $expressions = array();
2622 1574
2623 1574
        while (($tokenType = $this->tokenizer->peek()) != Tokenizer::T_EOF) {
2624
            $expr = null;
0 ignored issues
show
Unused Code introduced by
$expr 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...
2625
2626 1574
            switch ($tokenType) {
2627 1574
                case Tokens::T_COMMA:
2628 1574
                case Tokens::T_AS:
2629 1574
                case Tokens::T_BREAK:
2630 1574
                case Tokens::T_CLOSE_TAG:
2631 1574
                case Tokens::T_COLON:
2632 1574
                case Tokens::T_CONTINUE:
2633 1574
                case Tokens::T_CURLY_BRACE_CLOSE:
2634 1574
                case Tokens::T_DECLARE:
2635 1574
                case Tokens::T_DO:
2636 1574
                case Tokens::T_DOUBLE_ARROW:
2637 1574
                case Tokens::T_ECHO:
2638 1574
                case Tokens::T_END_HEREDOC:
2639 1574
                case Tokens::T_ENDFOREACH:
2640 1574
                case Tokens::T_FOR:
2641 1574
                case Tokens::T_FOREACH:
2642 1574
                case Tokens::T_GLOBAL:
2643 1574
                case Tokens::T_GOTO:
2644 1574
                case Tokens::T_IF:
2645 1574
                case Tokens::T_PARENTHESIS_CLOSE:
2646 1574
                case Tokens::T_RETURN:
2647 1574
                case Tokens::T_SEMICOLON:
2648 1574
                case Tokens::T_SQUARED_BRACKET_CLOSE:
2649 1574
                case Tokens::T_SWITCH:
2650 1574
                case Tokens::T_THROW:
2651 1574
                case Tokens::T_TRY:
2652 1574
                case Tokens::T_UNSET:
2653 1548
                case Tokens::T_WHILE:
2654 1558
                    break 2;
2655 1558
                case Tokens::T_SELF:
2656 1558
                case Tokens::T_STRING:
2657 1558
                case Tokens::T_PARENT:
2658 1558
                case Tokens::T_STATIC:
2659 1558
                case Tokens::T_DOLLAR:
2660 1558
                case Tokens::T_VARIABLE:
2661 1558
                case Tokens::T_BACKSLASH:
2662 1230
                case Tokens::T_NAMESPACE:
2663 1216
                    $expressions[] = $this->parseVariableOrConstantOrPrimaryPrefix();
2664 1232
                    break;
2665 146
                case ($this->isArrayStartDelimiter()):
2666 146
                    $expressions[] = $this->doParseArray();
2667 1212
                    break;
2668 1212
                case Tokens::T_NULL:
2669 1212
                case Tokens::T_TRUE:
2670 1212
                case Tokens::T_FALSE:
2671 1212
                case Tokens::T_LNUMBER:
2672 1212
                case Tokens::T_DNUMBER:
2673 1212
                case Tokens::T_BACKTICK:
2674 1212
                case Tokens::T_DOUBLE_QUOTE:
2675 934
                case Tokens::T_CONSTANT_ENCAPSED_STRING:
2676 932
                    $expressions[] = $this->parseLiteralOrString();
2677 896
                    break;
2678 166
                case Tokens::T_NEW:
2679 158
                    $expressions[] = $this->parseAllocationExpression();
2680 798
                    break;
2681 8
                case Tokens::T_EVAL:
2682 8
                    $expressions[] = $this->parseEvalExpression();
2683 798
                    break;
2684 8
                case Tokens::T_CLONE:
2685 8
                    $expressions[] = $this->parseCloneExpression();
2686 790
                    break;
2687 26
                case Tokens::T_INSTANCEOF:
2688 26
                    $expressions[] = $this->parseInstanceOfExpression();
2689 786
                    break;
2690 18
                case Tokens::T_ISSET:
2691 18
                    $expressions[] = $this->parseIssetExpression();
2692 772
                    break;
2693 22
                case Tokens::T_LIST:
2694 22
                    $expressions[] = $this->parseListExpression();
2695 772
                    break;
2696 12
                case Tokens::T_QUESTION_MARK:
2697 12
                    $expressions[] = $this->parseConditionalExpression();
2698 772
                    break;
2699 38
                case Tokens::T_BOOLEAN_AND:
2700 38
                    $expressions[] = $this->parseBooleanAndExpression();
2701 772
                    break;
2702 52
                case Tokens::T_BOOLEAN_OR:
2703 52
                    $expressions[] = $this->parseBooleanOrExpression();
2704 742
                    break;
2705 8
                case Tokens::T_LOGICAL_AND:
2706 8
                    $expressions[] = $this->parseLogicalAndExpression();
2707 742
                    break;
2708 8
                case Tokens::T_LOGICAL_OR:
2709 8
                    $expressions[] = $this->parseLogicalOrExpression();
2710 742
                    break;
2711 8
                case Tokens::T_LOGICAL_XOR:
2712 8
                    $expressions[] = $this->parseLogicalXorExpression();
2713 742
                    break;
2714 36
                case Tokens::T_FUNCTION:
2715 36
                    $expressions[] = $this->parseClosureDeclaration();
2716 728
                    break;
2717 148
                case Tokens::T_PARENTHESIS_OPEN:
2718 148
                    $expressions[] = $this->parseParenthesisExpressionOrPrimaryPrefix();
2719 646
                    break;
2720 6
                case Tokens::T_EXIT:
2721 6
                    $expressions[] = $this->parseExitExpression();
2722 640
                    break;
2723 12
                case Tokens::T_START_HEREDOC:
2724 12
                    $expressions[] = $this->parseHeredoc();
2725 640
                    break;
2726 2
                case Tokens::T_CURLY_BRACE_OPEN:
2727 2
                    $expressions[] = $this->parseBraceExpression(
2728 2
                        $this->builder->buildAstExpression(),
2729
                        $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN),
2730 2
                        Tokens::T_CURLY_BRACE_CLOSE
2731 2
                    );
2732 638
                    break;
2733 16
                case Tokens::T_INCLUDE:
2734 16
                    $expressions[] = $this->parseIncludeExpression();
2735 622
                    break;
2736 2
                case Tokens::T_INCLUDE_ONCE:
2737 2
                    $expressions[] = $this->parseIncludeOnceExpression();
2738 620
                    break;
2739 4
                case Tokens::T_REQUIRE:
2740 4
                    $expressions[] = $this->parseRequireExpression();
2741 616
                    break;
2742 2
                case Tokens::T_REQUIRE_ONCE:
2743 2
                    $expressions[] = $this->parseRequireOnceExpression();
2744 614
                    break;
2745 66
                case Tokens::T_DEC:
2746 66
                    $expressions[] = $this->parseDecrementExpression($expressions);
2747 598
                    break;
2748 136
                case Tokens::T_INC:
2749 136
                    $expressions[] = $this->parseIncrementExpression($expressions);
2750 568
                    break;
2751 2
                case Tokens::T_SL:
2752 2
                    $expressions[] = $this->parseShiftLeftExpression();
2753 566
                    break;
2754 2
                case Tokens::T_SR:
2755 2
                    $expressions[] = $this->parseShiftRightExpression();
2756 564
                    break;
2757 564
                case Tokens::T_DIR:
2758 564
                case Tokens::T_FILE:
2759 564
                case Tokens::T_LINE:
2760 564
                case Tokens::T_NS_C:
2761 564
                case Tokens::T_FUNC_C:
2762 564
                case Tokens::T_CLASS_C:
2763 44
                case Tokens::T_METHOD_C:
2764 44
                    $expressions[] = $this->parseConstant();
2765 550
                    break;
2766 550
                case Tokens::T_INT_CAST:
2767 550
                case Tokens::T_BOOL_CAST:
2768 550
                case Tokens::T_ARRAY_CAST:
2769 550
                case Tokens::T_UNSET_CAST:
2770 550
                case Tokens::T_OBJECT_CAST:
2771 550
                case Tokens::T_DOUBLE_CAST:
2772 38
                case Tokens::T_STRING_CAST:
2773 38
                    $expressions[] = $this->parseCastExpression();
2774 522
                    break;
2775 522
                case Tokens::T_EQUAL:
2776 522
                case Tokens::T_OR_EQUAL:
2777 522
                case Tokens::T_SL_EQUAL:
2778 522
                case Tokens::T_SR_EQUAL:
2779 522
                case Tokens::T_AND_EQUAL:
2780 522
                case Tokens::T_DIV_EQUAL:
2781 522
                case Tokens::T_MOD_EQUAL:
2782 522
                case Tokens::T_MUL_EQUAL:
2783 522
                case Tokens::T_XOR_EQUAL:
2784 522
                case Tokens::T_PLUS_EQUAL:
2785 522
                case Tokens::T_MINUS_EQUAL:
2786 320
                case Tokens::T_CONCAT_EQUAL:
2787 320
                    $expressions[] = $this->parseAssignmentExpression(
2788 320
                        array_pop($expressions)
2789 316
                    );
2790
                    break;
2791 350
                // TODO: Handle comments here
2792 350
                case Tokens::T_COMMENT:
2793 4
                case Tokens::T_DOC_COMMENT:
2794 4
                    $this->consumeToken($tokenType);
2795 346
                    break;
2796 2
                case Tokens::T_PRINT: // TODO: Implement print expression
2797
                    $token = $this->consumeToken($tokenType);
2798 2
2799 2
                    $expr = $this->builder->buildAstPrintExpression();
2800 2
                    $expr->configureLinesAndColumns(
2801 2
                        $token->startLine,
2802 2
                        $token->endLine,
2803 2
                        $token->startColumn,
2804 2
                        $token->endColumn
2805
                    );
2806 2
2807 2
                    $expressions[] = $expr;
2808 344
                    break;
2809 344
                case Tokens::T_STRING_VARNAME: // TODO: Implement this
2810 344
                case Tokens::T_PLUS: // TODO: Make this a arithmetic expression
2811 344
                case Tokens::T_MINUS:
2812 344
                case Tokens::T_MUL:
2813 344
                case Tokens::T_DIV:
2814 344
                case Tokens::T_MOD:
2815 344
                case Tokens::T_IS_EQUAL: // TODO: Implement compare expressions
2816 344
                case Tokens::T_IS_NOT_EQUAL:
2817 344
                case Tokens::T_IS_IDENTICAL:
2818 344
                case Tokens::T_IS_NOT_IDENTICAL:
2819 344
                case Tokens::T_IS_GREATER_OR_EQUAL:
2820 344
                case Tokens::T_IS_SMALLER_OR_EQUAL:
2821 344
                case Tokens::T_ANGLE_BRACKET_OPEN:
2822 344
                case Tokens::T_ANGLE_BRACKET_CLOSE:
2823 344
                case Tokens::T_EMPTY:
2824 344
                case Tokens::T_CONCAT:
2825 344
                case Tokens::T_BITWISE_OR:
2826 344
                case Tokens::T_BITWISE_AND:
2827 344
                case Tokens::T_BITWISE_NOT:
2828 326
                case Tokens::T_BITWISE_XOR:
2829
                    $token = $this->consumeToken($tokenType);
2830 326
2831 326
                    $expr = $this->builder->buildAstExpression($token->image);
2832 326
                    $expr->configureLinesAndColumns(
2833 326
                        $token->startLine,
2834 326
                        $token->endLine,
2835 326
                        $token->startColumn,
2836 326
                        $token->endColumn
2837
                    );
2838 326
2839 326
                    $expressions[] = $expr;
2840 24
                    break;
2841 24
                case Tokens::T_AT:
2842 8
                case Tokens::T_EXCLAMATION_MARK:
2843
                    $token = $this->consumeToken($tokenType);
2844 8
2845 8
                    $expr = $this->builder->buildAstUnaryExpression($token->image);
2846 8
                    $expr->configureLinesAndColumns(
2847 8
                        $token->startLine,
2848 8
                        $token->endLine,
2849 8
                        $token->startColumn,
2850 8
                        $token->endColumn
2851
                    );
2852 8
2853 8
                    $expressions[] = $expr;
2854 16
                    break;
2855 4
                case Tokens::T_YIELD:
2856 4
                    $expressions[] = $this->parseYield();
2857 12
                    break;
2858 12
                default:
2859 10
                    $expressions[] = $this->parseOptionalExpressionForVersion();
2860 12
                    break;
2861 1536
            }
2862
        }
2863 1548
2864
        $expressions = $this->reduce($expressions);
2865 1548
2866 1548
        $count = count($expressions);
2867 400
        if ($count == 0) {
2868 1532
            return null;
2869 1470
        } elseif ($count == 1) {
2870
            return $expressions[0];
2871
        }
2872 444
2873 444
        $expr = $this->builder->buildAstExpression();
2874 444
        foreach ($expressions as $node) {
2875 444
            $expr->addChild($node);
2876 444
        }
2877 444
        $expr->configureLinesAndColumns(
2878 444
            $expressions[0]->getStartLine(),
2879 444
            $expressions[$count - 1]->getEndLine(),
2880 444
            $expressions[0]->getStartColumn(),
2881 444
            $expressions[$count - 1]->getEndColumn()
2882
        );
2883 444
2884
        return $expr;
2885
    }
2886
2887
    /**
2888
     * This method will be called when the base parser cannot handle an expression
2889
     * in the base version. In this method you can implement version specific
2890
     * expressions.
2891
     *
2892
     * @return \PDepend\Source\AST\ASTNode
2893
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
2894
     * @since 2.2
2895 2
     */
2896
    protected function parseOptionalExpressionForVersion()
2897 2
    {
2898
        $this->throwUnexpectedTokenException();
2899
    }
2900
2901
    /**
2902
     * Applies all reduce rules against the given expression list.
2903
     *
2904
     * @param \PDepend\Source\AST\ASTExpression[] $expressions Unprepared input
2905
     *        array with parsed expression nodes found in the source tree.
2906
     *
2907
     * @return \PDepend\Source\AST\ASTExpression[]
2908
     * @since 0.10.0
2909 1562
     */
2910
    protected function reduce(array $expressions)
2911 1562
    {
2912
        return $this->reduceUnaryExpression($expressions);
2913
    }
2914
2915
    /**
2916
     * Reduces all unary-expressions in the given expression list.
2917
     *
2918
     * @param \PDepend\Source\AST\ASTExpression[] $expressions Unprepared input
2919
     *        array with parsed expression nodes found in the source tree.
2920
     *
2921
     * @return \PDepend\Source\AST\ASTExpression[]
2922
     * @since 0.10.0
2923 1562
     */
2924
    private function reduceUnaryExpression(array $expressions)
2925 1562
    {
2926 526
        for ($i = count($expressions) - 2; $i >= 0; --$i) {
2927 526
            $expr = $expressions[$i];
2928 178
            if ($expr instanceof \PDepend\Source\AST\ASTUnaryExpression) {
2929
                $child = $expressions[$i + 1];
2930 178
2931
                $expr->addChild($child);
2932 178
2933 178
                $expr->configureLinesAndColumns(
2934 178
                    $expr->getStartLine(),
2935 178
                    $child->getEndLine(),
2936 178
                    $expr->getStartColumn(),
2937 178
                    $child->getEndColumn()
2938
                );
2939 178
2940 178
                unset($expressions[$i + 1]);
2941 526
            }
2942 1562
        }
2943
        return array_values($expressions);
2944
    }
2945
2946
    /**
2947
     * This method parses a switch statement.
2948
     *
2949
     * @return \PDepend\Source\AST\ASTSwitchStatement
2950
     * @since 0.9.8
2951 44
     */
2952
    private function parseSwitchStatement()
2953 44
    {
2954 44
        $this->tokenStack->push();
2955
        $this->consumeToken(Tokens::T_SWITCH);
2956 44
2957 44
        $switch = $this->builder->buildAstSwitchStatement();
2958 44
        $switch->addChild($this->parseParenthesisExpression());
2959
        $this->parseSwitchStatementBody($switch);
2960 40
2961
        return $this->setNodePositionsAndReturn($switch);
2962
    }
2963
2964
    /**
2965
     * Parses the body of a switch statement.
2966
     *
2967
     * @param \PDepend\Source\AST\ASTSwitchStatement $switch The parent switch stmt.
2968
     *
2969
     * @return \PDepend\Source\AST\ASTSwitchStatement
2970
     * @since 0.9.8
2971 44
     */
2972
    private function parseSwitchStatementBody(ASTSwitchStatement $switch)
2973 44
    {
2974 44
        $this->consumeComments();
2975 34
        if ($this->tokenizer->peek() === Tokens::T_CURLY_BRACE_OPEN) {
2976 34
            $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
2977 10
        } else {
2978
            $this->consumeToken(Tokens::T_COLON);
2979
        }
2980 44
2981
        while (($tokenType = $this->tokenizer->peek()) !== Tokenizer::T_EOF) {
2982 44
            switch ($tokenType) {
2983 6
                case Tokens::T_CLOSE_TAG:
2984 6
                    $this->parseNonePhpCode();
2985 44
                    break;
2986 10
                case Tokens::T_ENDSWITCH:
2987 10
                    $this->parseAlternativeScopeTermination(Tokens::T_ENDSWITCH);
2988 44
                    return $switch;
2989 30
                case Tokens::T_CURLY_BRACE_CLOSE:
2990 30
                    $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
2991 44
                    return $switch;
2992 36
                case Tokens::T_CASE:
2993 34
                    $switch->addChild($this->parseSwitchLabel());
2994 32
                    break;
2995 28
                case Tokens::T_DEFAULT:
2996 28
                    $switch->addChild($this->parseSwitchLabelDefault());
2997 4
                    break;
2998 4
                case Tokens::T_COMMENT:
2999 2
                case Tokens::T_DOC_COMMENT:
3000 2
                    $this->consumeToken($tokenType);
3001 2
                    break;
3002 2
                default:
3003 2
                    break 2;
3004 40
            }
3005
        }
3006 2
3007
        $this->throwUnexpectedTokenException();
3008
    }
3009
3010
    /**
3011
     * This method parses a case label of a switch statement.
3012
     *
3013
     * @return \PDepend\Source\AST\ASTSwitchLabel
3014
     * @since 0.9.8
3015 36
     */
3016
    private function parseSwitchLabel()
3017 36
    {
3018 36
        $this->tokenStack->push();
3019
        $token = $this->consumeToken(Tokens::T_CASE);
3020 36
3021 36
        $label = $this->builder->buildAstSwitchLabel($token->image);
3022
        $label->addChild($this->parseExpression());
3023 36
3024 34
        if ($this->tokenizer->peek() === Tokens::T_COLON) {
3025 34
            $this->consumeToken(Tokens::T_COLON);
3026 2
        } else {
3027
            $this->consumeToken(Tokens::T_SEMICOLON);
3028
        }
3029 36
3030
        $this->parseSwitchLabelBody($label);
3031 34
3032
        return $this->setNodePositionsAndReturn($label);
3033
    }
3034
3035
    /**
3036
     * This method parses the default label of a switch statement.
3037
     *
3038
     * @return \PDepend\Source\AST\ASTSwitchLabel
3039
     * @since 0.9.8
3040 28
     */
3041
    private function parseSwitchLabelDefault()
3042 28
    {
3043 28
        $this->tokenStack->push();
3044
        $token = $this->consumeToken(Tokens::T_DEFAULT);
3045 28
3046 28
        $this->consumeComments();
3047 26
        if ($this->tokenizer->peek() === Tokens::T_COLON) {
3048 26
            $this->consumeToken(Tokens::T_COLON);
3049 2
        } else {
3050
            $this->consumeToken(Tokens::T_SEMICOLON);
3051
        }
3052 28
3053 28
        $label = $this->builder->buildAstSwitchLabel($token->image);
3054
        $label->setDefault();
3055 28
3056
        $this->parseSwitchLabelBody($label);
3057 28
3058
        return $this->setNodePositionsAndReturn($label);
3059
    }
3060
3061
    /**
3062
     * Parses the body of an switch label node.
3063
     *
3064
     * @param  \PDepend\Source\AST\ASTSwitchLabel $label The context switch label.
3065
     * @return \PDepend\Source\AST\ASTSwitchLabel
3066 42
     */
3067
    private function parseSwitchLabelBody(\PDepend\Source\AST\ASTSwitchLabel $label)
3068 42
    {
3069
        $curlyBraceCount = 0;
3070 42
3071 42
        $tokenType = $this->tokenizer->peek();
3072
        while ($tokenType !== Tokenizer::T_EOF) {
3073 42
            switch ($tokenType) {
3074 2
                case Tokens::T_CURLY_BRACE_OPEN:
3075 2
                    $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
3076 2
                    ++$curlyBraceCount;
3077 42
                    break;
3078 30
                case Tokens::T_CURLY_BRACE_CLOSE:
3079 30
                    if ($curlyBraceCount === 0) {
3080
                        return $label;
3081 2
                    }
3082 2
                    $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
3083 2
                    --$curlyBraceCount;
3084 42
                    break;
3085 6
                case Tokens::T_CLOSE_TAG:
3086 6
                    $this->parseNonePhpCode();
3087 42
                    break;
3088 42
                case Tokens::T_CASE:
3089 42
                case Tokens::T_DEFAULT:
3090 30
                case Tokens::T_ENDSWITCH:
3091 42
                    return $label;
3092 42
                default:
3093 42
                    $statement = $this->parseOptionalStatement();
3094
                    if ($statement === null) {
3095 42
                        $this->consumeToken($tokenType);
3096 42
                    } elseif ($statement instanceof ASTNode) {
3097 42
                        $label->addChild($statement);
3098
                    }
3099
                    // TODO: Change the <else if> into and <else> when the ast
3100 42
                    //       implementation is finished.
3101 42
                    break;
3102 42
            }
3103 42
            $tokenType = $this->tokenizer->peek();
3104 2
        }
3105
        throw new TokenStreamEndException($this->tokenizer);
3106
    }
3107
3108
    /**
3109
     * Parses the termination token for a statement. This termination token can
3110
     * be a semicolon or a closing php tag.
3111
     *
3112
     * @return void
3113
     * @since 0.9.12
3114 1352
     */
3115 View Code Duplication
    private function parseStatementTermination()
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...
3116 1352
    {
3117 1352
        $this->consumeComments();
3118 1346
        if ($this->tokenizer->peek() === Tokens::T_SEMICOLON) {
3119 1346
            $this->consumeToken(Tokens::T_SEMICOLON);
3120 10
        } else {
3121
            $this->parseNonePhpCode();
3122 1348
        }
3123
    }
3124
3125
    /**
3126
     * This method parses a try-statement + associated catch-statements.
3127
     *
3128
     * @return \PDepend\Source\AST\ASTTryStatement
3129
     * @since 0.9.12
3130 58
     */
3131
    private function parseTryStatement()
3132 58
    {
3133 58
        $this->tokenStack->push();
3134
        $token = $this->consumeToken(Tokens::T_TRY);
3135 58
3136 58
        $stmt = $this->builder->buildAstTryStatement($token->image);
3137
        $stmt->addChild($this->parseRegularScope());
3138 58
3139
        $this->consumeComments();
3140 58
3141 2
        if (false === in_array($this->tokenizer->peek(), array(Tokens::T_CATCH, Tokens::T_FINALLY))) {
3142
            $this->throwUnexpectedTokenException();
3143
        }
3144 56
3145 48
        while ($this->tokenizer->peek() === Tokens::T_CATCH) {
3146 48
            $stmt->addChild($this->parseCatchStatement());
3147 48
            $this->consumeComments();
3148
        }
3149 56
3150 18
        while ($this->tokenizer->peek() === Tokens::T_FINALLY) {
3151 18
            $stmt->addChild($this->parseFinallyStatement());
3152 18
            $this->consumeComments();
3153
        }
3154 56
3155
        return $this->setNodePositionsAndReturn($stmt);
3156
    }
3157
3158
    /**
3159
     * This method parses a throw-statement.
3160
     *
3161
     * @return \PDepend\Source\AST\ASTThrowStatement
3162
     * @since 0.9.12
3163 30
     */
3164
    private function parseThrowStatement()
3165 30
    {
3166 30
        $this->tokenStack->push();
3167
        $token = $this->consumeToken(Tokens::T_THROW);
3168 30
3169 30
        $stmt = $this->builder->buildAstThrowStatement($token->image);
3170
        $stmt->addChild($this->parseExpression());
3171 30
3172
        $this->parseStatementTermination();
3173 30
3174
        return $this->setNodePositionsAndReturn($stmt);
3175
    }
3176
3177
    /**
3178
     * This method parses a goto-statement.
3179
     *
3180
     * @return \PDepend\Source\AST\ASTGotoStatement
3181
     * @since 0.9.12
3182 16
     */
3183
    private function parseGotoStatement()
3184 16
    {
3185
        $this->tokenStack->push();
3186 16
3187 16
        $this->consumeToken(Tokens::T_GOTO);
3188
        $this->consumeComments();
3189 16
3190
        $token = $this->consumeToken(Tokens::T_STRING);
3191 16
3192
        $this->parseStatementTermination();
3193 16
3194 16
        $stmt = $this->builder->buildAstGotoStatement($token->image);
3195
        return $this->setNodePositionsAndReturn($stmt);
3196
    }
3197
3198
    /**
3199
     * This method parses a label-statement.
3200
     *
3201
     * @return \PDepend\Source\AST\ASTLabelStatement
3202
     * @since 0.9.12
3203 16
     */
3204 View Code Duplication
    private function parseLabelStatement()
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...
3205 16
    {
3206
        $this->tokenStack->push();
3207 16
3208 16
        $token = $this->consumeToken(Tokens::T_STRING);
3209 16
        $this->consumeComments();
3210
        $this->consumeToken(Tokens::T_COLON);
3211 16
3212 16
        return $this->setNodePositionsAndReturn(
3213 16
            $this->builder->buildAstLabelStatement($token->image)
3214
        );
3215
    }
3216
3217
    /**
3218
     * This method parses a global-statement.
3219
     *
3220
     * @return \PDepend\Source\AST\ASTGlobalStatement
3221
     * @since 0.9.12
3222 8
     */
3223
    private function parseGlobalStatement()
3224 8
    {
3225 8
        $this->tokenStack->push();
3226
        $this->consumeToken(Tokens::T_GLOBAL);
3227 8
3228 8
        $stmt = $this->builder->buildAstGlobalStatement();
3229
        $stmt = $this->parseVariableList($stmt);
3230 8
3231
        $this->parseStatementTermination();
3232 8
3233
        return $this->setNodePositionsAndReturn($stmt);
3234
    }
3235
3236
    /**
3237
     * This method parses a unset-statement.
3238
     *
3239
     * @return \PDepend\Source\AST\ASTUnsetStatement
3240
     * @since 0.9.12
3241 2
     */
3242 View Code Duplication
    private function parseUnsetStatement()
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...
3243 2
    {
3244
        $this->tokenStack->push();
3245 2
3246 2
        $this->consumeToken(Tokens::T_UNSET);
3247 2
        $this->consumeComments();
3248
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
3249 2
3250 2
        $stmt = $this->builder->buildAstUnsetStatement();
3251
        $stmt = $this->parseVariableList($stmt);
3252 2
3253
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
3254 2
3255
        $this->parseStatementTermination();
3256 2
3257
        return $this->setNodePositionsAndReturn($stmt);
3258
    }
3259
3260
    /**
3261
     * This method parses a catch-statement.
3262
     *
3263
     * @return \PDepend\Source\AST\ASTCatchStatement
3264
     * @since 0.9.8
3265 48
     */
3266
    private function parseCatchStatement()
3267 48
    {
3268 48
        $this->tokenStack->push();
3269
        $this->consumeComments();
3270 48
3271
        $token = $this->consumeToken(Tokens::T_CATCH);
3272 48
3273
        $catch = $this->builder->buildAstCatchStatement($token->image);
3274 48
3275 48
        $this->consumeComments();
3276
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
3277 48
3278
        $this->parseCatchExceptionClass($catch);
3279 48
3280 48
        $this->consumeComments();
3281
        $catch->addChild($this->parseVariable());
3282 48
3283 48
        $this->consumeComments();
3284
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
3285 48
3286
        $catch->addChild($this->parseRegularScope());
3287 48
3288
        return $this->setNodePositionsAndReturn($catch);
3289
    }
3290
3291
    /**
3292
     * This method parses class references in catch statement.
3293
     * 
3294
     * @param \PDepend\Source\AST\ASTCatchStatement $stmt The owning catch statement.
3295 48
     */
3296
    protected function parseCatchExceptionClass(ASTCatchStatement $stmt)
3297 48
    {
3298 48
        $stmt->addChild(
3299 48
            $this->builder->buildAstClassOrInterfaceReference(
3300 48
                $this->parseQualifiedName()
3301 48
            )
3302 48
        );
3303
    }
3304
    
3305
    /**
3306
     * This method parses a finally-statement.
3307
     *
3308
     * @return \PDepend\Source\AST\ASTFinallyStatement
3309
     * @since 2.0.0
3310 18
     */
3311
    private function parseFinallyStatement()
3312 18
    {
3313 18
        $this->tokenStack->push();
3314
        $this->consumeComments();
3315 18
3316
        $token = $this->consumeToken(Tokens::T_FINALLY);
3317 18
3318 18
        $finally = $this->builder->buildAstFinallyStatement($token->image);
0 ignored issues
show
Unused Code introduced by
The call to Builder::buildAstFinallyStatement() has too many arguments starting with $token->image.

This check compares calls to functions or methods with their respective definitions. If the call has more 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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3319
        $finally->addChild($this->parseRegularScope());
3320 18
3321
        return $this->setNodePositionsAndReturn($finally);
3322
    }
3323
3324
    /**
3325
     * This method parses a single if-statement node.
3326
     *
3327
     * @return \PDepend\Source\AST\ASTIfStatement
3328
     * @since 0.9.8
3329 196
     */
3330
    private function parseIfStatement()
3331 196
    {
3332 196
        $this->tokenStack->push();
3333
        $token = $this->consumeToken(Tokens::T_IF);
3334 196
3335 196
        $stmt = $this->builder->buildAstIfStatement($token->image);
3336
        $stmt->addChild($this->parseParenthesisExpression());
3337 196
3338 194
        $this->parseStatementBody($stmt);
3339
        $this->parseOptionalElseOrElseIfStatement($stmt);
3340 194
3341
        return $this->setNodePositionsAndReturn($stmt);
3342
    }
3343
3344
    /**
3345
     * This method parses a single elseif-statement node.
3346
     *
3347
     * @return \PDepend\Source\AST\ASTElseIfStatement
3348
     * @since 0.9.8
3349 46
     */
3350
    private function parseElseIfStatement()
3351 46
    {
3352 46
        $this->tokenStack->push();
3353
        $token = $this->consumeToken(Tokens::T_ELSEIF);
3354 46
3355 46
        $stmt = $this->builder->buildAstElseIfStatement($token->image);
3356
        $stmt->addChild($this->parseParenthesisExpression());
3357 46
3358 46
        $this->parseStatementBody($stmt);
3359
        $this->parseOptionalElseOrElseIfStatement($stmt);
3360 46
3361
        return $this->setNodePositionsAndReturn($stmt);
3362
    }
3363
3364
    /**
3365
     * This method parses an optional else-, else+if- or elseif-statement.
3366
     *
3367
     * @param \PDepend\Source\AST\ASTStatement $stmt The owning if/elseif statement.
3368
     *
3369
     * @return \PDepend\Source\AST\ASTStatement
3370
     * @since 0.9.12
3371 194
     */
3372
    private function parseOptionalElseOrElseIfStatement(ASTStatement $stmt)
3373 194
    {
3374 194
        $this->consumeComments();
3375 194
        switch ($this->tokenizer->peek()) {
3376 42
            case Tokens::T_ELSE:
3377 42
                $this->consumeToken(Tokens::T_ELSE);
3378 42
                $this->consumeComments();
3379 6
                if ($this->tokenizer->peek() === Tokens::T_IF) {
3380 6
                    $stmt->addChild($this->parseIfStatement());
3381 36
                } else {
3382
                    $this->parseStatementBody($stmt);
3383 42
                }
3384 172
                break;
3385 46
            case Tokens::T_ELSEIF:
3386 46
                $stmt->addChild($this->parseElseIfStatement());
3387 194
                break;
3388
        }
3389 194
3390
        return $stmt;
3391
    }
3392
3393
    /**
3394
     * This method parses a single for-statement node.
3395
     *
3396
     * @return \PDepend\Source\AST\ASTForStatement
3397
     * @since 0.9.8
3398 98
     */
3399
    private function parseForStatement()
3400 98
    {
3401 98
        $this->tokenStack->push();
3402
        $token = $this->consumeToken(Tokens::T_FOR);
3403 98
3404 98
        $this->consumeComments();
3405
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
3406 98
3407
        $stmt = $this->builder->buildAstForStatement($token->image);
3408 98
3409 90
        if (($init = $this->parseForInit()) !== null) {
3410 90
            $stmt->addChild($init);
3411 98
        }
3412
        $this->consumeToken(Tokens::T_SEMICOLON);
3413 98
3414 92
        if (($expr = $this->parseForExpression()) !== null) {
3415 92
            $stmt->addChild($expr);
3416 98
        }
3417
        $this->consumeToken(Tokens::T_SEMICOLON);
3418 98
3419 94
        if (($update = $this->parseForUpdate()) !== null) {
3420 94
            $stmt->addChild($update);
3421 98
        }
3422
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
3423 98
3424
        return $this->setNodePositionsAndReturn($this->parseStatementBody($stmt));
3425
    }
3426
3427
    /**
3428
     * Parses the init part of a for-statement.
3429
     *
3430
     * <code>
3431
     *      ------------------------
3432
     * for ($x = 0, $y = 23, $z = 42; $x < $y; ++$x) {}
3433
     *      ------------------------
3434
     * </code>
3435
     *
3436
     * @return \PDepend\Source\AST\ASTForInit
3437
     * @since 0.9.8
3438 98
     */
3439 View Code Duplication
    private function parseForInit()
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...
3440 98
    {
3441 98
        $this->consumeComments();
3442 8
        if (Tokens::T_SEMICOLON === $this->tokenizer->peek()) {
3443
            return null;
3444
        }
3445 90
3446
        $this->tokenStack->push();
3447 90
3448 90
        $init = $this->builder->buildAstForInit();
3449
        $this->parseExpressionList($init);
3450 90
3451
        return $this->setNodePositionsAndReturn($init);
3452
    }
3453
3454
    /**
3455
     * Parses the expression part of a for-statement.
3456
     *
3457
     * @return \PDepend\Source\AST\ASTExpression
3458
     * @since 0.9.12
3459 98
     */
3460
    private function parseForExpression()
3461 98
    {
3462
        return $this->parseOptionalExpression();
3463
    }
3464
3465
    /**
3466
     * Parses the update part of a for-statement.
3467
     *
3468
     * <code>
3469
     *                                        -------------------------------
3470
     * for ($x = 0, $y = 23, $z = 42; $x < $y; ++$x, $y = $x + 1, $z = $x + 2) {}
3471
     *                                        -------------------------------
3472
     * </code>
3473
     *
3474
     * @return \PDepend\Source\AST\ASTForUpdate
3475
     * @since 0.9.12
3476 98
     */
3477 View Code Duplication
    private function parseForUpdate()
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...
3478 98
    {
3479 98
        $this->consumeComments();
3480 4
        if (Tokens::T_PARENTHESIS_CLOSE === $this->tokenizer->peek()) {
3481
            return null;
3482
        }
3483 94
3484
        $this->tokenStack->push();
3485 94
3486 94
        $update = $this->builder->buildAstForUpdate();
3487
        $this->parseExpressionList($update);
3488 94
3489
        return $this->setNodePositionsAndReturn($update);
3490
    }
3491
3492
    /**
3493
     * This method parses a single foreach-statement node.
3494
     *
3495
     * @return \PDepend\Source\AST\ASTForeachStatement
3496
     * @since 0.9.8
3497 110
     */
3498
    protected function parseForeachStatement()
3499 110
    {
3500 110
        $this->tokenStack->push();
3501
        $token = $this->consumeToken(Tokens::T_FOREACH);
3502 110
3503
        $foreach = $this->builder->buildAstForeachStatement($token->image);
3504 110
3505 110
        $this->consumeComments();
3506
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
3507 110
3508
        $foreach->addChild($this->parseExpression());
3509 110
3510 110
        $this->consumeToken(Tokens::T_AS);
3511
        $this->consumeComments();
3512 110
3513 8
        if ($this->tokenizer->peek() === Tokens::T_BITWISE_AND) {
3514 8
            $foreach->addChild($this->parseVariableOrMemberByReference());
3515 102
        } else {
3516 2
            if ($this->tokenizer->peek() === Tokens::T_LIST) {
3517 2
                $foreach->addChild($this->parseListExpression());
3518 100
            } else {
3519
                $foreach->addChild($this->parseVariableOrConstantOrPrimaryPrefix());
3520 100
3521 24
                if ($this->tokenizer->peek() === Tokens::T_DOUBLE_ARROW) {
3522
                    $this->consumeToken(Tokens::T_DOUBLE_ARROW);
3523 24
3524 2
                    if ($this->tokenizer->peek() === Tokens::T_LIST) {
3525 2
                        $foreach->addChild($this->parseListExpression());
3526 22
                    } else {
3527 22
                        $foreach->addChild($this->parseVariableOrMemberOptionalByReference());
3528 22
                    }
3529
                }
3530 24
            }
3531
        }
3532
3533
        $this->consumeComments();
3534 110
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
3535 110
3536
        return $this->setNodePositionsAndReturn(
3537 108
            $this->parseStatementBody($foreach)
3538 108
        );
3539 108
    }
3540
3541
    /**
3542
     * This method parses a single while-statement node.
3543
     *
3544
     * @return \PDepend\Source\AST\ASTWhileStatement
3545
     * @since 0.9.8
3546
     */
3547
    private function parseWhileStatement()
3548 12
    {
3549
        $this->tokenStack->push();
3550 12
        $token = $this->consumeToken(Tokens::T_WHILE);
3551 12
3552
        $stmt = $this->builder->buildAstWhileStatement($token->image);
3553 12
        $stmt->addChild($this->parseParenthesisExpression());
3554 12
3555
        return $this->setNodePositionsAndReturn(
3556 12
            $this->parseStatementBody($stmt)
3557 12
        );
3558 12
    }
3559
3560
    /**
3561
     * This method parses a do/while-statement.
3562
     *
3563
     * @return \PDepend\Source\AST\ASTDoWhileStatement
3564
     * @since 0.9.12
3565
     */
3566
    private function parseDoWhileStatement()
3567 18
    {
3568
        $this->tokenStack->push();
3569 18
        $token = $this->consumeToken(Tokens::T_DO);
3570 18
3571
        $stmt = $this->builder->buildAstDoWhileStatement($token->image);
3572 18
        $stmt = $this->parseStatementBody($stmt);
3573 18
3574
        $this->consumeComments();
3575 18
        $this->consumeToken(Tokens::T_WHILE);
3576 18
3577
        $stmt->addChild($this->parseParenthesisExpression());
3578 18
3579
        $this->parseStatementTermination();
3580 18
3581
        return $this->setNodePositionsAndReturn($stmt);
3582 18
    }
3583
3584
    /**
3585
     * This method parses a declare-statement.
3586
     *
3587
     * <code>
3588
     * -------------------------------
3589
     * declare(encoding='ISO-8859-1');
3590
     * -------------------------------
3591
     *
3592
     * -------------------
3593
     * declare(ticks=42) {
3594
     *     // ...
3595
     * }
3596
     * -
3597
     *
3598
     * ------------------
3599
     * declare(ticks=42):
3600
     *     // ...
3601
     * enddeclare;
3602
     * -----------
3603
     * </code>
3604
     *
3605
     * @return \PDepend\Source\AST\ASTDeclareStatement
3606
     * @since 0.10.0
3607
     */
3608
    private function parseDeclareStatement()
3609 28
    {
3610
        $this->tokenStack->push();
3611 28
        $this->consumeToken(Tokens::T_DECLARE);
3612 28
3613
        $stmt = $this->builder->buildAstDeclareStatement();
3614 28
        $stmt = $this->parseDeclareList($stmt);
3615 28
        $stmt = $this->parseStatementBody($stmt);
3616 28
3617
        return $this->setNodePositionsAndReturn($stmt);
3618 28
    }
3619
3620
    /**
3621
     * This method parses a list of declare values. A declare list value always
3622
     * consists of a string token and a static scalar.
3623
     *
3624
     * @param \PDepend\Source\AST\ASTDeclareStatement $stmt The declare statement that
3625
     *        is the owner of this list.
3626
     * @return \PDepend\Source\AST\ASTDeclareStatement
3627
     * @since 0.10.0
3628
     */
3629
    private function parseDeclareList(ASTDeclareStatement $stmt)
3630 28
    {
3631
        $this->consumeComments();
3632 28
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
3633 28
3634
        while (true) {
3635 28
            $this->consumeComments();
3636 28
            $name = $this->consumeToken(Tokens::T_STRING)->image;
3637 28
3638
            $this->consumeComments();
3639 28
            $this->consumeToken(Tokens::T_EQUAL);
3640 28
3641
            $this->consumeComments();
3642 28
            $value = $this->parseStaticValue();
3643 28
3644
            $stmt->addValue($name, $value);
0 ignored issues
show
Bug introduced by
It seems like $value defined by $this->parseStaticValue() on line 3642 can be null; however, PDepend\Source\AST\ASTDeclareStatement::addValue() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
3645 28
3646
            $this->consumeComments();
3647 28
            if ($this->tokenizer->peek() === Tokens::T_COMMA) {
3648 28
                $this->consumeToken(Tokens::T_COMMA);
3649 2
                continue;
3650 2
            }
3651
            break;
3652 28
        }
3653
3654
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
3655 28
        return $stmt;
3656 28
    }
3657
3658
    /**
3659
     * This method parses a single return-statement node.
3660
     *
3661
     * @return \PDepend\Source\AST\ASTReturnStatement
3662
     * @since 0.9.12
3663
     */
3664
    private function parseReturnStatement()
3665 608
    {
3666
        $this->tokenStack->push();
3667 608
        $token = $this->consumeToken(Tokens::T_RETURN);
3668 608
3669
        $stmt = $this->builder->buildAstReturnStatement($token->image);
3670 608
        if (($expr = $this->parseOptionalExpression()) != null) {
3671 608
            $stmt->addChild($expr);
3672 604
        }
3673 604
        $this->parseStatementTermination();
3674 608
3675
        return $this->setNodePositionsAndReturn($stmt);
3676 606
    }
3677
3678
    /**
3679
     * This method parses a break-statement node.
3680
     *
3681
     * @return \PDepend\Source\AST\ASTBreakStatement
3682
     * @since 0.9.12
3683
     */
3684
    private function parseBreakStatement()
3685 48
    {
3686
        $this->tokenStack->push();
3687 48
        $token = $this->consumeToken(Tokens::T_BREAK);
3688 48
3689
        $stmt = $this->builder->buildAstBreakStatement($token->image);
3690 48
        if (($expr = $this->parseOptionalExpression()) != null) {
3691 48
            $stmt->addChild($expr);
3692 10
        }
3693 10
        $this->parseStatementTermination();
3694 48
3695
        return $this->setNodePositionsAndReturn($stmt);
3696 48
    }
3697
3698
    /**
3699
     * This method parses a continue-statement node.
3700
     *
3701
     * @return \PDepend\Source\AST\ASTContinueStatement
3702
     * @since 0.9.12
3703
     */
3704
    private function parseContinueStatement()
3705 8
    {
3706
        $this->tokenStack->push();
3707 8
        $token = $this->consumeToken(Tokens::T_CONTINUE);
3708 8
3709
        $stmt = $this->builder->buildAstContinueStatement($token->image);
3710 8
        if (($expr = $this->parseOptionalExpression()) != null) {
3711 8
            $stmt->addChild($expr);
3712 8
        }
3713 8
        $this->parseStatementTermination();
3714 8
3715
        return $this->setNodePositionsAndReturn($stmt);
3716 8
    }
3717
3718
    /**
3719
     * This method parses a echo-statement node.
3720
     *
3721
     * @return \PDepend\Source\AST\ASTEchoStatement
3722
     * @since 0.9.12
3723
     */
3724
    private function parseEchoStatement()
3725 186
    {
3726
        $this->tokenStack->push();
3727 186
        $token = $this->consumeToken(Tokens::T_ECHO);
3728 186
3729
        $stmt = $this->parseExpressionList(
3730 186
            $this->builder->buildAstEchoStatement($token->image)
3731 186
        );
3732 186
3733
        $this->parseStatementTermination();
3734 186
3735
        return $this->setNodePositionsAndReturn($stmt);
3736 186
    }
3737
3738
    /**
3739
     * Parses a simple parenthesis expression or a direct object access, which
3740
     * was introduced with PHP 5.4.0:
3741
     *
3742
     * <code>
3743
     * (new MyClass())->bar();
3744
     * </code>
3745
     *
3746
     * @return \PDepend\Source\AST\ASTNode
3747
     * @since 1.0.0
3748
     */
3749
    protected function parseParenthesisExpressionOrPrimaryPrefix()
3750 150
    {
3751
        return $this->parseParenthesisExpressionOrPrimaryPrefixForVersion(
3752 150
            $this->parseParenthesisExpression()
3753 150
        );
3754 150
    }
3755
3756
    /**
3757
     * @param \PDepend\Source\AST\ASTExpression $expr
3758
     * @return \PDepend\Source\AST\ASTExpression
3759
     */
3760
    protected function parseParenthesisExpressionOrPrimaryPrefixForVersion(ASTExpression $expr)
3761
    {
3762
        $this->consumeComments();
3763
        if (Tokens::T_OBJECT_OPERATOR === $this->tokenizer->peek()) {
3764
            return $this->parseMemberPrimaryPrefix($expr->getChild(0));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->parseMembe...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...
3765
        }
3766
        return $expr;
3767
    }
3768
3769
    /**
3770
     * Parses any expression that is surrounded by an opening and a closing
3771
     * parenthesis
3772
     *
3773
     * @return \PDepend\Source\AST\ASTExpression
3774
     * @since 0.9.8
3775
     */
3776 View Code Duplication
    private function parseParenthesisExpression()
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...
3777 420
    {
3778
        $this->tokenStack->push();
3779 420
        $this->consumeComments();
3780 420
3781
        $expr = $this->builder->buildAstExpression();
3782 420
        $expr = $this->parseBraceExpression(
3783 420
            $expr,
3784 420
            $this->consumeToken(Tokens::T_PARENTHESIS_OPEN),
3785 420
            Tokens::T_PARENTHESIS_CLOSE
3786
        );
3787 420
3788
        return $this->setNodePositionsAndReturn($expr);
3789 420
    }
3790
3791
    /**
3792
     * This method parses a member primary prefix expression or a function
3793
     * postfix expression node.
3794
     *
3795
     * A member primary prefix can be a method call:
3796
     *
3797
     * <code>
3798
     * $object->foo();
3799
     *
3800
     * clazz::foo();
3801
     * </code>
3802
     *
3803
     * a property access:
3804
     *
3805
     * <code>
3806
     * $object->foo;
3807
     *
3808
     * clazz::$foo;
3809
     * </code>
3810
     *
3811
     * or a class constant access:
3812
     *
3813
     * <code>
3814
     * clazz::FOO;
3815
     * </code>
3816
     *
3817
     * A function postfix represents any kind of function call:
3818
     *
3819
     * <code>
3820
     * $function();
3821
     *
3822
     * func();
3823
     * </code>
3824
     *
3825
     * @return \PDepend\Source\AST\ASTNode
3826
     * @throws \PDepend\Source\Parser\ParserException
3827
     * @since 0.9.6
3828
     */
3829
    private function parseMemberPrefixOrFunctionPostfix()
3830 526
    {
3831
        $this->tokenStack->push();
3832 526
        $this->tokenStack->push();
3833 526
3834
        $qName = $this->parseQualifiedName();
3835 526
3836
        // Remove comments
3837
        $this->consumeComments();
3838 526
3839
        // Get next token type
3840
        $tokenType = $this->tokenizer->peek();
3841 526
3842
        switch ($tokenType) {
3843
            case Tokens::T_DOUBLE_COLON:
3844 526
                $node = $this->builder->buildAstClassOrInterfaceReference($qName);
3845 132
                $node = $this->setNodePositionsAndReturn($node);
3846 132
                $node = $this->parseStaticMemberPrimaryPrefix($node);
3847 132
                break;
3848 132
            case Tokens::T_PARENTHESIS_OPEN:
3849 408
                $node = $this->builder->buildAstIdentifier($qName);
3850 370
                $node = $this->setNodePositionsAndReturn($node);
3851 370
                $node = $this->parseFunctionPostfix($node);
3852 370
                break;
3853 368
            default:
3854 124
                $node = $this->builder->buildAstConstant($qName);
3855 124
                $node = $this->setNodePositionsAndReturn($node);
3856 124
                break;
3857 124
        }
3858 124
3859
        return $this->setNodePositionsAndReturn($node);
3860 526
    }
3861
3862
    /**
3863
     * This method will parse an optional function postfix.
3864
     *
3865
     * If the next available token is an opening parenthesis, this method will
3866
     * wrap the given <b>$node</b> with a {@link \PDepend\Source\AST\ASTFunctionPostfix}
3867
     * node.
3868
     *
3869
     * @param \PDepend\Source\AST\ASTNode $node The previously parsed node.
3870
     *
3871
     * @return \PDepend\Source\AST\ASTNode The original input node or this node
3872
     *         wrapped with a function postfix instance.
3873
     * @since 1.0.0
3874
     */
3875 View Code Duplication
    private function parseOptionalFunctionPostfix(ASTNode $node)
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...
3876 6
    {
3877
        $this->consumeComments();
3878 6
        if (Tokens::T_PARENTHESIS_OPEN === $this->tokenizer->peek()) {
3879 6
            return $this->parseFunctionPostfix($node);
3880 6
        }
3881
        return $node;
3882
    }
3883
3884
    /**
3885
     * This method parses a function postfix expression. An object of type
3886
     * {@link \PDepend\Source\AST\ASTFunctionPostfix} represents any valid php
3887
     * function call.
3888
     *
3889
     * This method will delegate the call to another method that returns a
3890
     * member primary prefix object when the function postfix expression is
3891
     * followed by an object operator.
3892
     *
3893
     * @param  \PDepend\Source\AST\ASTNode $node This node represents the function
3894
     *        identifier. An identifier can be a static string, a variable, a
3895
     *        compound variable or any other valid php function identifier.
3896
     * @return \PDepend\Source\AST\ASTNode
3897
     * @throws \PDepend\Source\Parser\ParserException
3898
     * @since 0.9.6
3899
     */
3900
    private function parseFunctionPostfix(ASTNode $node)
3901 410
    {
3902
        $image = $this->extractPostfixImage($node);
3903 410
3904
        $function = $this->builder->buildAstFunctionPostfix($image);
3905 410
        $function->addChild($node);
3906 410
        $function->addChild($this->parseArguments());
3907 410
3908
        return $this->parseOptionalMemberPrimaryPrefix(
3909 408
            $this->parseOptionalIndexExpression($function)
3910 408
        );
3911 408
    }
3912
3913
    /**
3914
     * This method parses a PHP version specific identifier for method and
3915
     * property postfix expressions.
3916
     *
3917
     * @return \PDepend\Source\AST\ASTNode
3918
     * @since 1.0.0
3919
     */
3920
    protected function parsePostfixIdentifier()
3921 88
    {
3922
        switch ($this->tokenizer->peek()) {
3923 88
            case Tokens::T_STRING:
3924 88
                $node = $this->parseLiteral();
3925
                break;
3926
            default:
3927 88
                $node = $this->parseCompoundVariableOrVariableVariableOrVariable();
3928 88
                break;
3929 88
        }
3930 88
        return $this->parseOptionalIndexExpression($node);
3931 88
    }
3932
3933
    /**
3934
     * This method parses an optional member primary expression. It will parse
3935
     * the primary expression when an object operator can be found at the actual
3936
     * token stream position. Otherwise this method simply returns the input
3937
     * {@link \PDepend\Source\AST\ASTNode} instance.
3938
     *
3939
     * @param  \PDepend\Source\AST\ASTNode $node This node represents primary prefix
3940
     *        left expression. It will be the first child of the parsed member
3941
     *        primary expression.
3942
     * @return \PDepend\Source\AST\ASTNode
3943
     * @throws \PDepend\Source\Parser\ParserException
3944
     * @since 0.9.6
3945
     */
3946 View Code Duplication
    protected function parseOptionalMemberPrimaryPrefix(ASTNode $node)
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...
3947
    {
3948
        $this->consumeComments();
3949
3950
        if ($this->tokenizer->peek() === Tokens::T_OBJECT_OPERATOR) {
3951
            return $this->parseMemberPrimaryPrefix($node);
3952
        }
3953
        return $node;
3954
    }
3955
3956
    /**
3957
     * This method parses a dynamic or object bound member primary expression.
3958
     * A member primary prefix can be a method call:
3959
     *
3960
     * <code>
3961
     * $object->foo();
3962
     * </code>
3963
     *
3964
     * or a property access:
3965
     *
3966
     * <code>
3967
     * $object->foo;
3968
     * </code>
3969
     *
3970
     * @param  \PDepend\Source\AST\ASTNode $node The left node in the parsed member
3971
     *        primary expression.
3972
     * @return \PDepend\Source\AST\ASTMemberPrimaryPrefix
3973
     * @throws \PDepend\Source\Parser\ParserException
3974
     * @since 0.9.6
3975
     */
3976
    protected function parseMemberPrimaryPrefix(ASTNode $node)
3977 274
    {
3978
        // Consume double colon and optional comments
3979
        $token = $this->consumeToken(Tokens::T_OBJECT_OPERATOR);
3980 274
3981
        $prefix = $this->builder->buildAstMemberPrimaryPrefix($token->image);
3982 274
        $prefix->addChild($node);
3983 274
3984
        $this->consumeComments();
3985 274
        $tokenType = $this->tokenizer->peek();
3986 274
3987
        switch ($tokenType) {
3988
            case ($this->isMethodName($tokenType)):
3989 274
                $child = $this->parseIdentifier($tokenType);
3990 230
                $child = $this->parseOptionalIndexExpression($child);
3991 230
3992
                // TODO: Move this in a separate method
3993
                if ($child instanceof ASTIndexExpression) {
3994 230
                    $this->consumeComments();
3995 36
                    if (Tokens::T_PARENTHESIS_OPEN === $this->tokenizer->peek()) {
3996 36
                        $prefix->addChild($this->parsePropertyPostfix($child));
3997 6
                        return $this->parseOptionalFunctionPostfix($prefix);
3998 6
                    }
3999
                }
4000 30
                break;
4001 224
            case Tokens::T_CURLY_BRACE_OPEN:
4002 44
                $child = $this->parseCompoundExpression();
4003 12
                break;
4004 12
            default:
4005 32
                $child = $this->parseCompoundVariableOrVariableVariableOrVariable();
4006 32
                break;
4007 32
        }
4008 32
4009
        $prefix->addChild(
4010 268
            $this->parseMethodOrPropertyPostfix(
4011 268
                $this->parseOptionalIndexExpression($child)
4012 268
            )
4013 268
        );
4014 268
4015
        return $this->parseOptionalMemberPrimaryPrefix(
4016 268
            $this->parseOptionalIndexExpression($prefix)
4017 268
        );
4018 268
    }
4019
4020
    /**
4021
     * This method parses an optional member primary expression. It will parse
4022
     * the primary expression when a double colon operator can be found at the
4023
     * actual token stream position. Otherwise this method simply returns the
4024
     * input {@link \PDepend\Source\AST\ASTNode} instance.
4025
     *
4026
     * @param  \PDepend\Source\AST\ASTNode $node This node represents primary prefix
4027
     *        left expression. It will be the first child of the parsed member
4028
     *        primary expression.
4029
     * @return \PDepend\Source\AST\ASTNode
4030
     * @throws \PDepend\Source\Parser\ParserException
4031
     * @since 1.0.1
4032
     */
4033 View Code Duplication
    private function parseOptionalStaticMemberPrimaryPrefix(ASTNode $node)
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...
4034 178
    {
4035
        $this->consumeComments();
4036 178
4037
        if ($this->tokenizer->peek() === Tokens::T_DOUBLE_COLON) {
4038 178
            return $this->parseStaticMemberPrimaryPrefix($node);
4039 6
        }
4040
        return $node;
4041 172
    }
4042
4043
    /**
4044
     * This method parses a static member primary expression. The given node
4045
     * contains the used static class or interface identifier. A static member
4046
     * primary prefix can represent the following code expressions:
4047
     *
4048
     * A static method class:
4049
     *
4050
     * <code>
4051
     * Foo::bar();
4052
     * </code>
4053
     *
4054
     * a static property access:
4055
     *
4056
     * <code>
4057
     * Foo::$bar;
4058
     * </code>
4059
     *
4060
     * or a static constant access:
4061
     *
4062
     * <code>
4063
     * Foo::BAR;
4064
     * </code>
4065
     *
4066
     * @param  \PDepend\Source\AST\ASTNode $node The left node in the parsed member
4067
     *        primary expression.
4068
     * @return \PDepend\Source\AST\ASTMemberPrimaryPrefix
4069
     * @throws \PDepend\Source\Parser\ParserException
4070
     * @since 0.9.6
4071
     */
4072
    protected function parseStaticMemberPrimaryPrefix(ASTNode $node)
4073 238
    {
4074
        $token = $this->consumeToken(Tokens::T_DOUBLE_COLON);
4075 238
4076
        $prefix = $this->builder->buildAstMemberPrimaryPrefix($token->image);
4077 238
        $prefix->addChild($node);
4078 238
4079
        $this->consumeComments();
4080 238
4081
        switch ($this->tokenizer->peek()) {
4082 238
            case Tokens::T_STRING:
4083 238
                $postfix = $this->parseMethodOrConstantPostfix();
4084 126
                break;
4085 126
            case Tokens::T_CLASS_FQN:
4086 122
                $postfix = $this->parseFullQualifiedClassNamePostfix();
4087 30
                break;
4088 30
            default:
4089 94
                $postfix = $this->parseMethodOrPropertyPostfix(
4090 94
                    $this->parsePostfixIdentifier()
4091 94
                );
4092 94
                break;
4093 94
        }
4094 238
4095
        $prefix->addChild($postfix);
4096 238
4097
        return $this->parseOptionalMemberPrimaryPrefix(
4098 238
            $this->parseOptionalIndexExpression($prefix)
4099 238
        );
4100 238
    }
4101
4102
    /**
4103
     * This method parses a method- or constant-postfix expression. This expression
4104
     * will contain an identifier node as nested child.
4105
     *
4106
     * @return \PDepend\Source\AST\ASTNode
4107
     * @throws \PDepend\Source\Parser\ParserException
4108
     * @since 0.9.6
4109
     */
4110
    private function parseMethodOrConstantPostfix()
4111 126
    {
4112
        $this->tokenStack->push();
4113 126
4114
        $node = $this->parseIdentifier();
4115 126
4116
        $this->consumeComments();
4117 126
        if ($this->tokenizer->peek() === Tokens::T_PARENTHESIS_OPEN) {
4118 126
            $postfix = $this->parseMethodPostfix($node);
4119 102
        } else {
4120 102
            $postfix = $this->builder->buildAstConstantPostfix($node->getImage());
4121 26
            $postfix->addChild($node);
4122 26
        }
4123
4124
        return $this->setNodePositionsAndReturn($postfix);
4125 126
    }
4126
4127
    /**
4128
     * This method parses a method- or property-postfix expression. This expression
4129
     * will contain the given node as method or property identifier.
4130
     *
4131
     * @param \PDepend\Source\AST\ASTNode $node The identifier for the parsed postfix
4132
     *        expression node. This node will be the first child of the returned
4133
     *        postfix node instance.
4134
     *
4135
     * @return \PDepend\Source\AST\ASTNode
4136
     * @throws \PDepend\Source\Parser\ParserException
4137
     * @since 0.9.6
4138
     */
4139
    private function parseMethodOrPropertyPostfix(ASTNode $node)
4140 350
    {
4141
        // Strip optional comments
4142
        $this->consumeComments();
4143 350
4144
        switch ($this->tokenizer->peek()) {
4145 350
            case Tokens::T_PARENTHESIS_OPEN:
4146 350
                $postfix = $this->parseMethodPostfix($node);
4147 170
                break;
4148 170
            default:
4149 194
                $postfix = $this->parsePropertyPostfix($node);
4150 194
                break;
4151 194
        }
4152 350
        return $this->parseOptionalMemberPrimaryPrefix($postfix);
4153 350
    }
4154
4155
    /**
4156
     * Parses/Creates a property postfix node instance.
4157
     *
4158
     * @param \PDepend\Source\AST\ASTNode $node Node that represents the image of
4159
     *        the property postfix node.
4160
     *
4161
     * @return \PDepend\Source\AST\ASTPropertyPostfix
4162
     * @since 0.10.2
4163
     */
4164
    private function parsePropertyPostfix(ASTNode $node)
4165 200
    {
4166
        $image = $this->extractPostfixImage($node);
4167 200
4168
        $postfix = $this->builder->buildAstPropertyPostfix($image);
4169 200
        $postfix->addChild($node);
4170 200
4171
        $postfix->configureLinesAndColumns(
4172 200
            $node->getStartLine(),
4173 200
            $node->getEndLine(),
4174 200
            $node->getStartColumn(),
4175 200
            $node->getEndColumn()
4176 200
        );
4177 200
4178
        return $postfix;
4179 200
    }
4180
4181
    /**
4182
     * Parses a full qualified class name postfix.
4183
     *
4184
     * @return \PDepend\Source\AST\ASTClassFqnPostfix
4185
     * @since 2.0.0
4186
     */
4187
    private function parseFullQualifiedClassNamePostfix()
4188 30
    {
4189
        $this->tokenStack->push();
4190 30
4191
        $this->consumeToken(Tokens::T_CLASS_FQN);
4192 30
4193
        return $this->setNodePositionsAndReturn(
4194 30
            $this->builder->buildAstClassFqnPostfix()
4195 30
        );
4196 30
    }
4197
4198
    /**
4199
     * This method will extract the image/name of the real property/variable
4200
     * that is wrapped by {@link \PDepend\Source\AST\ASTIndexExpression} nodes. If
4201
     * the given node is now wrapped by index expressions, this method will
4202
     * return the image of the entire <b>$node</b>.
4203
     *
4204
     * @param \PDepend\Source\AST\ASTNode $node The context node that may be wrapped
4205
     *        by multiple array or string index expressions.
4206
     *
4207
     * @return string
4208
     * @since 1.0.0
4209
     */
4210
    private function extractPostfixImage(ASTNode $node)
4211 758
    {
4212
        while ($node instanceof \PDepend\Source\AST\ASTIndexExpression) {
4213 758
            $node = $node->getChild(0);
4214 70
        }
4215 70
        return $node->getImage();
4216 758
    }
4217
4218
    /**
4219
     * Parses a method postfix node instance.
4220
     *
4221
     * @param \PDepend\Source\AST\ASTNode $node Node that represents the image of
4222
     *        the method postfix node.
4223
     *
4224
     * @return \PDepend\Source\AST\ASTMethodPostfix
4225
     * @since 1.0.0
4226
     */
4227
    private function parseMethodPostfix(ASTNode $node)
4228 226
    {
4229
        $args  = $this->parseArguments();
4230 226
        $image = $this->extractPostfixImage($node);
4231 226
4232
        $postfix = $this->builder->buildAstMethodPostfix($image);
4233 226
        $postfix->addChild($node);
4234 226
        $postfix->addChild($args);
4235 226
4236
        $postfix->configureLinesAndColumns(
4237 226
            $node->getStartLine(),
4238 226
            $args->getEndLine(),
4239 226
            $node->getStartColumn(),
4240 226
            $args->getEndColumn()
4241 226
        );
4242 226
4243
        return $this->parseOptionalMemberPrimaryPrefix($postfix);
4244 226
    }
4245
4246
    /**
4247
     * This method parses the arguments passed to a function- or method-call.
4248
     *
4249
     * @return \PDepend\Source\AST\ASTArguments
4250
     * @throws \PDepend\Source\Parser\ParserException
4251
     * @since 0.9.6
4252
     */
4253 View Code Duplication
    protected function parseArguments()
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...
4254 676
    {
4255
        $this->consumeComments();
4256 676
4257
        $this->tokenStack->push();
4258 676
4259
        $arguments = $this->builder->buildAstArguments();
4260 676
4261
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
4262 676
        $this->consumeComments();
4263 676
4264
        if (Tokens::T_PARENTHESIS_CLOSE !== $this->tokenizer->peek()) {
4265 676
            $arguments = $this->parseArgumentList($arguments);
4266 330
        }
4267 330
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
4268 676
4269
        return $this->setNodePositionsAndReturn($arguments);
4270 674
    }
4271
4272
    /**
4273
     * @param \PDepend\Source\AST\ASTArguments $arguments
4274
     * @return \PDepend\Source\AST\ASTArguments
4275
     */
4276
    protected function parseArgumentList(ASTArguments $arguments)
4277
    {
4278
        return $this->parseExpressionList($arguments);
4279
    }
4280
4281
    /**
4282
     * This method implements the parsing for various expression types like
4283
     * variables, object/static method. All these expressions are valid in
4284
     * several php language constructs like, isset, empty, unset etc.
4285
     *
4286
     * @return \PDepend\Source\AST\ASTNode
4287
     * @since 0.9.12
4288
     */
4289
    protected function parseVariableOrConstantOrPrimaryPrefix()
4290 1262
    {
4291
        $this->consumeComments();
4292 1262
        switch ($this->tokenizer->peek()) {
4293 1262
            case Tokens::T_DOLLAR:
4294 1262
            case Tokens::T_VARIABLE:
4295 1262
                $node = $this->parseVariableOrFunctionPostfixOrMemberPrimaryPrefix();
4296 930
                break;
4297 930
            case Tokens::T_SELF:
4298 628
                $node = $this->parseConstantOrSelfMemberPrimaryPrefix();
4299 42
                break;
4300 38
            case Tokens::T_PARENT:
4301 592
                $node = $this->parseConstantOrParentMemberPrimaryPrefix();
4302 28
                break;
4303 20
            case Tokens::T_STATIC:
4304 564
                $node = $this->parseStaticVariableDeclarationOrMemberPrimaryPrefix();
4305 46
                break;
4306 44
            case Tokens::T_STRING:
4307 526
            case Tokens::T_BACKSLASH:
4308 526
            case Tokens::T_NAMESPACE:
4309 526
                $node = $this->parseMemberPrefixOrFunctionPostfix();
4310 526
                break;
4311 526
            default:
4312
                $this->throwUnexpectedTokenException();
4313
        }
4314 1248
4315
        return $node;
0 ignored issues
show
Bug introduced by
The variable $node does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
4316 1248
    }
4317
4318
    /**
4319
     * This method parses any type of variable, function postfix expressions or
4320
     * any kind of member primary prefix.
4321
     *
4322
     * This method expects that the actual token represents any kind of valid
4323
     * php variable: simple variable, compound variable or variable variable.
4324
     *
4325
     * It will parse a function postfix or member primary expression when this
4326
     * variable is followed by an object operator, double colon or opening
4327
     * parenthesis.
4328
     *
4329
     * @return \PDepend\Source\AST\ASTNode
4330
     * @throws \PDepend\Source\Parser\ParserException
4331
     * @since 0.9.6
4332
     */
4333
    private function parseVariableOrFunctionPostfixOrMemberPrimaryPrefix()
4334 938
    {
4335
        $this->tokenStack->push();
4336 938
4337
        $variable = $this->parseCompoundVariableOrVariableVariableOrVariable();
4338 938
        $variable = $this->parseOptionalIndexExpression($variable);
4339 938
4340
        $this->consumeComments();
4341 938
        switch ($this->tokenizer->peek()) {
4342 938
            case Tokens::T_DOUBLE_COLON:
4343 938
                $result = $this->parseStaticMemberPrimaryPrefix($variable);
4344 42
                break;
4345 42
            case Tokens::T_OBJECT_OPERATOR:
4346 900
                $result = $this->parseMemberPrimaryPrefix($variable);
4347 240
                break;
4348 240
            case Tokens::T_PARENTHESIS_OPEN:
4349 770
                $result = $this->parseFunctionPostfix($variable);
4350 36
                break;
4351 36
            default:
4352 752
                $result = $variable;
4353 752
                break;
4354 752
        }
4355 938
        return $this->setNodePositionsAndReturn($result);
4356 938
    }
4357
4358
    /**
4359
     * Parses an assingment expression node.
4360
     *
4361
     * @param \PDepend\Source\AST\ASTNode $left The left part of the assignment
4362
     *        expression that will be parsed by this method.
4363
     *
4364
     * @return \PDepend\Source\AST\ASTAssignmentExpression
4365
     * @since 0.9.12
4366
     */
4367
    protected function parseAssignmentExpression(ASTNode $left)
4368 320
    {
4369
        $token = $this->consumeToken($this->tokenizer->peek());
4370 320
4371
        $node = $this->builder->buildAstAssignmentExpression($token->image);
4372 320
        $node->addChild($left);
4373 320
4374
        // TODO: Change this into a mandatory expression in later versions
4375
        if (($expr = $this->parseOptionalExpression()) != null) {
4376 320
            $node->addChild($expr);
4377 316
        } else {
4378 316
            $expr = $left;
4379
        }
4380
4381
        $node->configureLinesAndColumns(
4382 316
            $left->getStartLine(),
4383 316
            $expr->getEndLine(),
4384 316
            $left->getStartColumn(),
4385 316
            $expr->getEndColumn()
4386 316
        );
4387 316
4388
        return $node;
4389 316
    }
4390
4391
    /**
4392
     * This method parses a {@link \PDepend\Source\AST\ASTStaticReference} node.
4393
     *
4394
     * @param  \PDepend\Source\Tokenizer\Token $token The "static" keyword token.
4395
     * @return \PDepend\Source\AST\ASTStaticReference
4396
     * @throws \PDepend\Source\Parser\ParserException
4397
     * @throws \PDepend\Source\Parser\InvalidStateException
4398
     * @since 0.9.6
4399
     */
4400 View Code Duplication
    private function parseStaticReference(Token $token)
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...
4401 20
    {
4402
        // Strip optional comments
4403
        $this->consumeComments();
4404 20
4405
        if ($this->classOrInterface === null) {
4406 20
            throw new InvalidStateException(
4407 4
                $token->startLine,
4408 4
                (string) $this->compilationUnit,
4409 4
                'The keyword "static" was used outside of a class/method scope.'
4410
            );
4411 4
        }
4412
4413
        $ref = $this->builder->buildAstStaticReference($this->classOrInterface);
4414 16
        $ref->configureLinesAndColumns(
4415 16
            $token->startLine,
4416 16
            $token->endLine,
4417 16
            $token->startColumn,
4418 16
            $token->endColumn
4419 16
        );
4420 16
4421
        return $ref;
4422 16
    }
4423
4424
    /**
4425
     * This method parses a {@link \PDepend\Source\AST\ASTSelfReference} node.
4426
     *
4427
     * @param  \PDepend\Source\Tokenizer\Token $token The "self" keyword token.
4428
     * @return \PDepend\Source\AST\ASTSelfReference
4429
     * @throws \PDepend\Source\Parser\ParserException
4430
     * @throws \PDepend\Source\Parser\InvalidStateException
4431
     * @since 0.9.6
4432
     */
4433 View Code Duplication
    protected function parseSelfReference(Token $token)
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...
4434 52
    {
4435
        if ($this->classOrInterface === null) {
4436 52
            throw new InvalidStateException(
4437 6
                $token->startLine,
4438 6
                (string) $this->compilationUnit,
4439 6
                'The keyword "self" was used outside of a class/method scope.'
4440
            );
4441 6
        }
4442
4443
        $ref = $this->builder->buildAstSelfReference($this->classOrInterface);
4444 46
        $ref->configureLinesAndColumns(
4445 46
            $token->startLine,
4446 46
            $token->endLine,
4447 46
            $token->startColumn,
4448 46
            $token->endColumn
4449 46
        );
4450 46
4451
        return $ref;
4452 46
    }
4453
4454
    /**
4455
     * Parses a simple PHP constant use and returns a corresponding node.
4456
     *
4457
     * @return \PDepend\Source\AST\ASTNode
4458
     * @since 1.0.0
4459
     */
4460
    protected function parseConstant()
4461 44
    {
4462
        $this->tokenStack->push();
4463 44
        switch ($type = $this->tokenizer->peek()) {
4464 44
            case Tokens::T_STRING:
4465 44
                // TODO: Separate node classes for magic constants
4466
            case Tokens::T_DIR:
4467 44
            case Tokens::T_FILE:
4468 44
            case Tokens::T_LINE:
4469 44
            case Tokens::T_NS_C:
4470 44
            case Tokens::T_FUNC_C:
4471 44
            case Tokens::T_CLASS_C:
4472 44
            case Tokens::T_METHOD_C:
4473 44
            case Tokens::T_TRAIT_C:
4474 44
                $token = $this->consumeToken($type);
4475 44
4476
                return $this->setNodePositionsAndReturn(
4477 44
                    $this->builder->buildAstConstant($token->image)
4478 44
                );
4479 44
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
4480
        }
4481
    }
4482
4483
    /**
4484
     * This method parses a {@link \PDepend\Source\AST\ASTConstant} node or
4485
     * an instance of {@link \PDepend\Source\AST\ASTSelfReference} as part of
4486
     * a {@link \PDepend\Source\AST\ASTMemberPrimaryPrefix} that contains the
4487
     * self reference as its first child when the self token is followed by a
4488
     * double colon token.
4489
     *
4490
     * @return \PDepend\Source\AST\ASTNode
4491
     * @throws \PDepend\Source\Parser\ParserException
4492
     * @throws \PDepend\Source\Parser\InvalidStateException
4493
     * @since 0.9.6
4494
     */
4495 View Code Duplication
    private function parseConstantOrSelfMemberPrimaryPrefix()
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...
4496 42
    {
4497
        // Read self token and strip optional comments
4498
        $token = $this->consumeToken(Tokens::T_SELF);
4499 42
        $this->consumeComments();
4500 42
4501
        if ($this->tokenizer->peek() == Tokens::T_DOUBLE_COLON) {
4502 42
            return $this->parseStaticMemberPrimaryPrefix(
4503 40
                $this->parseSelfReference($token)
4504 40
            );
4505 36
        }
4506
        return $this->builder->buildAstConstant($token->image);
4507 2
    }
4508
4509
    /**
4510
     * This method parses a {@link \PDepend\Source\AST\ASTParentReference} node.
4511
     *
4512
     * @param  \PDepend\Source\Tokenizer\Token $token The "self" keyword token.
4513
     * @return \PDepend\Source\AST\ASTNode
4514
     * @throws \PDepend\Source\Parser\ParserException
4515
     * @throws \PDepend\Source\Parser\InvalidStateException
4516
     * @since 0.9.6
4517
     */
4518
    private function parseParentReference(Token $token)
4519 48
    {
4520 View Code Duplication
        if ($this->classOrInterface === null) {
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...
4521 48
            throw new InvalidStateException(
4522 8
                $token->startLine,
4523 8
                (string) $this->compilationUnit,
4524 8
                'The keyword "parent" was used as type hint but the parameter ' .
4525
                'declaration is not in a class scope.'
4526
            );
4527 8
        }
4528
4529
        if ($this->classOrInterface instanceof ASTTrait) {
4530 40
            $classReference = $this->builder->buildAstClassReference('__PDepend_TraitRuntimeReference');
4531 4
        } else {
4532 4
            $classReference = $this->classOrInterface->getParentClassReference();
4533 36
        }
4534
4535 View Code Duplication
        if ($classReference === null) {
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...
4536 40
            throw new InvalidStateException(
4537 6
                $token->startLine,
4538 6
                (string) $this->compilationUnit,
4539 6
                sprintf(
4540 6
                    'The keyword "parent" was used as type hint but the ' .
4541
                    'class "%s" does not declare a parent.',
4542 6
                    $this->classOrInterface->getName()
4543 6
                )
4544 6
            );
4545 6
        }
4546
4547
        $ref = $this->builder->buildAstParentReference($classReference);
4548 34
        $ref->configureLinesAndColumns(
4549 34
            $token->startLine,
4550 34
            $token->endLine,
4551 34
            $token->startColumn,
4552 34
            $token->endColumn
4553 34
        );
4554 34
4555
        return $ref;
4556 34
    }
4557
4558
    /**
4559
     * This method parses a {@link \PDepend\Source\AST\ASTConstant} node or
4560
     * an instance of {@link \PDepend\Source\AST\ASTParentReference} as part
4561
     * of a {@link \PDepend\Source\AST\ASTMemberPrimaryPrefix} that contains
4562
     * the parent reference as its first child when the self token is followed
4563
     * by a double colon token.
4564
     *
4565
     * @return \PDepend\Source\AST\ASTNode
4566
     * @throws \PDepend\Source\Parser\ParserException
4567
     * @throws \PDepend\Source\Parser\InvalidStateException
4568
     * @since 0.9.6
4569
     */
4570 View Code Duplication
    private function parseConstantOrParentMemberPrimaryPrefix()
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...
4571 28
    {
4572
        // Consume parent token and strip optional comments
4573
        $token = $this->consumeToken(Tokens::T_PARENT);
4574 28
        $this->consumeComments();
4575 28
4576
        if ($this->tokenizer->peek() == Tokens::T_DOUBLE_COLON) {
4577 28
            return $this->parseStaticMemberPrimaryPrefix(
4578 26
                $this->parseParentReference($token)
4579 26
            );
4580 18
        }
4581
        return $this->builder->buildAstConstant($token->image);
4582 2
    }
4583
4584
    /**
4585
     * Parses a variable or any other valid member expression that is optionally
4586
     * prefixed with PHP's reference operator.
4587
     *
4588
     * <code>
4589
     * //                  -----------
4590
     * foreach ( $array as &$this->foo ) {}
4591
     * //                  -----------
4592
     *
4593
     * //     ----------
4594
     * $foo = &$bar->baz;
4595
     * //     ----------
4596
     * </code>
4597
     *
4598
     * @return \PDepend\Source\AST\ASTUnaryExpression
4599
     * @since 0.9.18
4600
     */
4601
    protected function parseVariableOrMemberOptionalByReference()
4602 22
    {
4603
        $this->consumeComments();
4604 22
        if ($this->tokenizer->peek() === Tokens::T_BITWISE_AND) {
4605 22
            return $this->parseVariableOrMemberByReference();
4606 6
        }
4607
        return $this->parseVariableOrConstantOrPrimaryPrefix();
4608 16
    }
4609
4610
    /**
4611
     * Parses a variable or any other valid member expression that is prefixed
4612
     * with PHP's reference operator.
4613
     *
4614
     * <code>
4615
     * //                  -----------
4616
     * foreach ( $array as &$this->foo ) {}
4617
     * //                  -----------
4618
     *
4619
     * //     ----------
4620
     * $foo = &$bar->baz;
4621
     * //     ----------
4622
     * </code>
4623
     *
4624
     * @return \PDepend\Source\AST\ASTUnaryExpression
4625
     * @since 0.9.18
4626
     */
4627
    protected function parseVariableOrMemberByReference()
4628 14
    {
4629
        $this->tokenStack->push();
4630 14
4631
        $token = $this->consumeToken(Tokens::T_BITWISE_AND);
4632 14
        $this->consumeComments();
4633 14
4634
        $expr = $this->builder->buildAstUnaryExpression($token->image);
4635 14
        $expr->addChild($this->parseVariableOrConstantOrPrimaryPrefix());
4636 14
4637
        return $this->setNodePositionsAndReturn($expr);
4638 14
    }
4639
4640
    /**
4641
     * This method parses a simple PHP variable.
4642
     *
4643
     * @return \PDepend\Source\AST\ASTVariable
4644
     * @throws UnexpectedTokenException
4645
     * @since 0.9.6
4646
     */
4647 View Code Duplication
    private function parseVariable()
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...
4648 1050
    {
4649
        $token = $this->consumeToken(Tokens::T_VARIABLE);
4650 1050
4651
        $variable = $this->builder->buildAstVariable($token->image);
4652 1050
        $variable->configureLinesAndColumns(
4653 1050
            $token->startLine,
4654 1050
            $token->endLine,
4655 1050
            $token->startColumn,
4656 1050
            $token->endColumn
4657 1050
        );
4658 1050
4659
        return $variable;
4660 1050
    }
4661
4662
    /**
4663
     * This method parses a comma separated list of valid php variables and/or
4664
     * properties and adds them to the given node instance.
4665
     *
4666
     * @param \PDepend\Source\AST\ASTNode $node The context parent node.
4667
     *
4668
     * @return \PDepend\Source\AST\ASTNode The prepared entire node.
4669
     * @since 0.9.12
4670
     */
4671 View Code Duplication
    private function parseVariableList(ASTNode $node)
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...
4672 28
    {
4673
        $this->consumeComments();
4674 28
        while ($this->tokenizer->peek() !== Tokenizer::T_EOF) {
4675 28
            $node->addChild($this->parseVariableOrConstantOrPrimaryPrefix());
4676 28
4677
            $this->consumeComments();
4678 28
            if ($this->tokenizer->peek() === Tokens::T_COMMA) {
4679 28
                $this->consumeToken(Tokens::T_COMMA);
4680 12
                $this->consumeComments();
4681 12
            } else {
4682 12
                break;
4683 28
            }
4684
        }
4685 12
4686
        return $node;
4687 28
    }
4688
4689
    /**
4690
     * This method is a decision point between the different variable types
4691
     * availanle in PHP. It peeks the next token and then decides whether it is
4692
     * a regular variable or when the next token is of type <b>T_DOLLAR</b> a
4693
     * compound- or variable-variable.
4694
     *
4695
     * <code>
4696
     * ----
4697
     * $foo;
4698
     * ----
4699
     *
4700
     * -----
4701
     * $$foo;
4702
     * -----
4703
     *
4704
     * ------
4705
     * ${FOO};
4706
     * ------
4707
     * </code>
4708
     *
4709
     * @return \PDepend\Source\AST\ASTNode
4710
     * @throws \PDepend\Source\Parser\ParserException
4711
     * @throws UnexpectedTokenException
4712
     * @since 0.9.6
4713
     */
4714
    protected function parseCompoundVariableOrVariableVariableOrVariable()
4715 1000
    {
4716
        if ($this->tokenizer->peek() == Tokens::T_DOLLAR) {
4717 1000
            return $this->parseCompoundVariableOrVariableVariable();
4718 62
        }
4719
        return $this->parseVariable();
4720 984
    }
4721
4722
    /**
4723
     * Parses a PHP compound variable or a simple literal node.
4724
     *
4725
     * @return \PDepend\Source\AST\ASTNode
4726
     * @since 0.9.19
4727
     */
4728 View Code Duplication
    private function parseCompoundVariableOrLiteral()
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...
4729 16
    {
4730
        $this->tokenStack->push();
4731 16
4732
        // Read the dollar token
4733
        $token = $this->consumeToken(Tokens::T_DOLLAR);
4734 16
        $this->consumeComments();
4735 16
4736
        // Get next token type
4737
        $tokenType = $this->tokenizer->peek();
4738 16
4739
        switch ($tokenType) {
4740
            case Tokens::T_CURLY_BRACE_OPEN:
4741 16
                $variable = $this->builder->buildAstCompoundVariable($token->image);
4742 14
                $variable->addChild($this->parseCompoundExpression());
4743 14
                break;
4744 14
            default:
4745 2
                $variable = $this->builder->buildAstLiteral($token->image);
4746 2
                break;
4747 2
        }
4748 2
4749
        return $this->setNodePositionsAndReturn($variable);
4750 16
    }
4751
4752
    /**
4753
     * This method implements a decision point between compound-variables and
4754
     * variable-variable. It expects that the next token in the token-stream is
4755
     * of type <b>T_DOLLAR</b> and removes it from the stream. Then this method
4756
     * peeks the next available token when it is of type <b>T_CURLY_BRACE_OPEN</b>
4757
     * this is compound variable, otherwise it can be a variable-variable or a
4758
     * compound-variable.
4759
     *
4760
     * @return \PDepend\Source\AST\ASTNode
4761
     * @throws \PDepend\Source\Parser\ParserException
4762
     * @throws UnexpectedTokenException
4763
     * @since 0.9.6
4764
     */
4765 View Code Duplication
    private function parseCompoundVariableOrVariableVariable()
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...
4766 62
    {
4767
        $this->tokenStack->push();
4768 62
4769
        // Read the dollar token
4770
        $token = $this->consumeToken(Tokens::T_DOLLAR);
4771 62
        $this->consumeComments();
4772 62
4773
        // Get next token type
4774
        $tokenType = $this->tokenizer->peek();
4775 62
4776
        // T_DOLLAR|T_VARIABLE === Variable variable,
4777
        // T_CURLY_BRACE_OPEN === Compound variable
4778
        switch ($tokenType) {
4779
            case Tokens::T_DOLLAR:
4780 62
            case Tokens::T_VARIABLE:
4781 62
                $variable = $this->builder->buildAstVariableVariable($token->image);
4782 20
                $variable->addChild(
4783 20
                    $this->parseCompoundVariableOrVariableVariableOrVariable()
4784 20
                );
4785 20
                break;
4786 20
            default:
4787 44
                $variable = $this->parseCompoundVariable($token);
4788 44
                break;
4789 44
        }
4790 44
4791
        return $this->setNodePositionsAndReturn($variable);
4792 62
    }
4793
4794
    /**
4795
     * This method parses a compound variable like:
4796
     *
4797
     * <code>
4798
     * //     ----------------
4799
     * return ${'Foo' . 'Bar'};
4800
     * //     ----------------
4801
     * </code>
4802
     *
4803
     * @param  \PDepend\Source\Tokenizer\Token $token The dollar token.
4804
     * @return \PDepend\Source\AST\ASTCompoundVariable
4805
     * @since 0.10.0
4806
     */
4807
    private function parseCompoundVariable(Token $token)
4808 44
    {
4809
        return $this->parseBraceExpression(
4810 44
            $this->builder->buildAstCompoundVariable($token->image),
4811 44
            $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN),
4812 44
            Tokens::T_CURLY_BRACE_CLOSE
4813
        );
4814 44
    }
4815
4816
    /**
4817
     * This method parses a compound expression like:
4818
     *
4819
     * <code>
4820
     * //      ------  ------
4821
     * $foo = "{$bar}, {$baz}\n";
4822
     * //      ------  ------
4823
     * </code>
4824
     *
4825
     * or a simple literal token:
4826
     *
4827
     * <code>
4828
     * //      -
4829
     * $foo = "{{$bar}, {$baz}\n";
4830
     * //      -
4831
     * </code>
4832
     *
4833
     * @return \PDepend\Source\AST\ASTNode
4834
     * @since 0.9.10
4835
     */
4836
    private function parseCompoundExpressionOrLiteral()
4837 16
    {
4838
        $token = $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
4839 16
        $this->consumeComments();
4840 16
4841
        switch ($this->tokenizer->peek()) {
4842 16
            case Tokens::T_DOLLAR:
4843 16
            case Tokens::T_VARIABLE:
4844 16
                return $this->parseBraceExpression(
4845 16
                    $this->builder->buildAstCompoundExpression(),
4846 16
                    $token,
4847 16
                    Tokens::T_CURLY_BRACE_CLOSE
4848
                );
4849 16
        }
4850 2
4851
        $literal = $this->builder->buildAstLiteral($token->image);
4852 2
        $literal->configureLinesAndColumns(
4853 2
            $token->startLine,
4854 2
            $token->endLine,
4855 2
            $token->startColumn,
4856 2
            $token->endColumn
4857 2
        );
4858 2
4859
        return $literal;
4860 2
    }
4861
4862
    /**
4863
     * This method parses a compound expression node.
4864
     *
4865
     * <code>
4866
     * ------------------
4867
     * {'_' . foo . $bar}
4868
     * ------------------
4869
     * </code>
4870
     *
4871
     * @return \PDepend\Source\AST\ASTCompoundExpression
4872
     * @throws \PDepend\Source\Parser\ParserException
4873
     * @throws \PDepend\Source\Parser\ParserException
4874
     * @since 0.9.6
4875
     */
4876
    protected function parseCompoundExpression()
4877 28
    {
4878
        $this->consumeComments();
4879 28
4880
        return $this->parseBraceExpression(
4881 28
            $this->builder->buildAstCompoundExpression(),
4882 28
            $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN),
4883 28
            Tokens::T_CURLY_BRACE_CLOSE
4884
        );
4885 28
    }
4886
4887
    /**
4888
     * Parses a static identifier expression, as it is used for method and
4889
     * function names.
4890
     *
4891
     * @param integer $tokenType
4892
     * @return \PDepend\Source\AST\ASTIdentifier
4893
     * @since 0.9.12
4894
     */
4895 View Code Duplication
    protected function parseIdentifier($tokenType = Tokens::T_STRING)
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...
4896 310
    {
4897
        $token = $this->consumeToken($tokenType);
4898 310
4899
        $node = $this->builder->buildAstIdentifier($token->image);
4900 310
        $node->configureLinesAndColumns(
4901 310
            $token->startLine,
4902 310
            $token->endLine,
4903 310
            $token->startColumn,
4904 310
            $token->endColumn
4905 310
        );
4906 310
4907
        return $node;
4908 310
    }
4909
4910
    /**
4911
     * This method parses a {@link \PDepend\Source\AST\ASTLiteral} node or an
4912
     * instance of {@link \PDepend\Source\AST\ASTString} that represents a string
4913
     * in double quotes or surrounded by backticks.
4914
     *
4915
     * @return \PDepend\Source\AST\ASTNode
4916
     * @throws UnexpectedTokenException
4917
     */
4918
    protected function parseLiteralOrString()
4919 948
    {
4920
        $tokenType = $this->tokenizer->peek();
4921 948
4922
        switch ($tokenType) {
4923
            case Tokens::T_NULL:
4924 948
            case Tokens::T_TRUE:
4925 948
            case Tokens::T_FALSE:
4926 948
            case Tokens::T_DNUMBER:
4927 948
            case Tokens::T_CONSTANT_ENCAPSED_STRING:
4928 948
                $token = $this->consumeToken($tokenType);
4929 486
4930
                $literal = $this->builder->buildAstLiteral($token->image);
4931 486
                $literal->configureLinesAndColumns(
4932 486
                    $token->startLine,
4933 486
                    $token->endLine,
4934 486
                    $token->startColumn,
4935 486
                    $token->endColumn
4936 486
                );
4937 486
                return $literal;
4938 486
            case Tokens::T_LNUMBER:
4939 608
                return $this->parseIntegerNumber();
4940 556
            default:
4941 54
                return $this->parseString($tokenType);
4942 54
        }
4943 54
    }
4944
4945
    /**
4946
     * Parses an integer value.
4947
     *
4948
     * @return \PDepend\Source\AST\ASTLiteral
4949
     * @since 1.0.0
4950
     */
4951 View Code Duplication
    protected function parseIntegerNumber()
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...
4952
    {
4953
        $token = $this->consumeToken(Tokens::T_LNUMBER);
4954
4955
        $literal = $this->builder->buildAstLiteral($token->image);
4956
        $literal->configureLinesAndColumns(
4957
            $token->startLine,
4958
            $token->endLine,
4959
            $token->startColumn,
4960
            $token->endColumn
4961
        );
4962
4963
        return $literal;
4964
    }
4965
4966
    /**
4967
     * Parses an array structure.
4968
     *
4969
     * @return \PDepend\Source\AST\ASTArray
4970
     * @since 1.0.0
4971
     */
4972
    protected function doParseArray($static = false)
4973 214
    {
4974
        $this->tokenStack->push();
4975 214
4976
        return $this->setNodePositionsAndReturn(
4977 214
            $this->parseArray(
4978 214
                $this->builder->buildAstArray(),
4979 214
                $static
4980
            )
4981 214
        );
4982 212
    }
4983
4984
    /**
4985
     * Tests if the next token is a valid array start delimiter in the supported
4986
     * PHP version.
4987
     *
4988
     * @return boolean
4989
     * @since 1.0.0
4990
     */
4991
    abstract protected function isArrayStartDelimiter();
4992
4993
    /**
4994
     * Parses a php array declaration.
4995
     *
4996
     * @param \PDepend\Source\AST\ASTArray $array
4997
     * @param boolean                      $static
4998
     *
4999
     * @return \PDepend\Source\AST\ASTArray
5000
     * @since 1.0.0
5001
     */
5002
    abstract protected function parseArray(ASTArray $array, $static = false);
5003
5004
    /**
5005
     * Parses all elements in an array.
5006
     *
5007
     * @param  \PDepend\Source\AST\ASTArray $array
5008
     * @param  integer                      $endDelimiter
5009
     * @param  boolean                      $static
5010
     * @return \PDepend\Source\AST\ASTArray
5011
     * @since 1.0.0
5012
     */
5013 View Code Duplication
    protected function parseArrayElements(ASTArray $array, $endDelimiter, $static = false)
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...
5014 214
    {
5015
        $this->consumeComments();
5016 214
        while ($endDelimiter !== $this->tokenizer->peek()) {
5017 214
            $array->addChild($this->parseArrayElement($static));
5018 160
5019
            $this->consumeComments();
5020 158
            if (Tokens::T_COMMA === $this->tokenizer->peek()) {
5021 158
                $this->consumeToken(Tokens::T_COMMA);
5022 84
                $this->consumeComments();
5023 84
            }
5024 84
        }
5025 158
        return $array;
5026 212
    }
5027
5028
    /**
5029
     * Parses a single array element.
5030
     *
5031
     * An array element can have a simple value, a key/value pair, a value by
5032
     * reference or a key/value pair with a referenced value.
5033
     *
5034
     * @param  boolean $static
5035
     * @return \PDepend\Source\AST\ASTArrayElement
5036
     * @since 1.0.0
5037
     */
5038
    protected function parseArrayElement($static = false)
5039 160
    {
5040
        $this->consumeComments();
5041 160
5042
        $this->tokenStack->push();
5043 160
5044
        $element = $this->builder->buildAstArrayElement();
5045 160
        if ($this->parseOptionalByReference()) {
5046 160
            if ($static) {
5047 22
                $tokens = $this->tokenStack->pop();
5048 2
5049
                $this->throwUnexpectedTokenException(end($tokens));
5050 2
            }
5051
5052
            $element->setByReference();
5053 20
        }
5054 20
5055
        $this->consumeComments();
5056 158
        if ($this->isKeyword($this->tokenizer->peek())) {
5057 158
            $this->throwUnexpectedTokenException();
5058
        }
5059
5060
        $element->addChild($this->parseExpression());
5061 158
5062
        $this->consumeComments();
5063 158
        if (Tokens::T_DOUBLE_ARROW === $this->tokenizer->peek()) {
5064 158
            $this->consumeToken(Tokens::T_DOUBLE_ARROW);
5065 70
            $this->consumeComments();
5066 70
5067
            if ($this->parseOptionalByReference()) {
5068 70
                $element->setByReference();
5069 18
            }
5070 18
            $element->addChild($this->parseExpression());
5071 70
        }
5072 70
5073
        return $this->setNodePositionsAndReturn($element);
5074 158
    }
5075
5076
    /**
5077
     * Parses a here- or nowdoc string instance.
5078
     *
5079
     * @return \PDepend\Source\AST\ASTHeredoc
5080
     * @since 0.9.12
5081
     */
5082 View Code Duplication
    protected function parseHeredoc()
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...
5083 16
    {
5084
        $this->tokenStack->push();
5085 16
        $this->consumeToken(Tokens::T_START_HEREDOC);
5086 16
5087
        $heredoc = $this->builder->buildAstHeredoc();
5088 16
        $this->parseStringExpressions($heredoc, Tokens::T_END_HEREDOC);
5089 16
5090
        $token = $this->consumeToken(Tokens::T_END_HEREDOC);
5091 16
        $heredoc->setDelimiter($token->image);
5092 16
5093
        return $this->setNodePositionsAndReturn($heredoc);
5094 16
    }
5095
5096
    /**
5097
     * Parses a simple string sequence between two tokens of the same type.
5098
     *
5099
     * @param integer $tokenType The start/stop token type.
5100
     *
5101
     * @return string
5102
     * @since 0.9.10
5103
     */
5104
    private function parseStringSequence($tokenType)
5105
    {
5106
        $type   = $tokenType;
5107
        $string = '';
5108
5109
        do {
5110
            $string .= $this->consumeToken($type)->image;
5111
            $type    = $this->tokenizer->peek();
5112
        } while ($type != $tokenType && $type != Tokenizer::T_EOF);
5113
5114
        return $string . $this->consumeToken($tokenType)->image;
5115
    }
5116
5117
    /**
5118
     * This method parses a php string with all possible embedded expressions.
5119
     *
5120
     * <code>
5121
     * $string = "Manuel $Pichler <{$email}>";
5122
     *
5123
     * // \PDepend\Source\AST\ASTSTring
5124
     * // |-- ASTLiteral             -  "Manuel ")
5125
     * // |-- ASTVariable            -  $Pichler
5126
     * // |-- ASTLiteral             -  " <"
5127
     * // |-- ASTCompoundExpression  -  {...}
5128
     * // |   |-- ASTVariable        -  $email
5129
     * // |-- ASTLiteral             -  ">"
5130
     * </code>
5131
     *
5132
     * @param integer $delimiterType The start/stop token type.
5133
     *
5134
     * @return \PDepend\Source\AST\ASTString
5135
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
5136
     * @since 0.9.10
5137
     */
5138
    private function parseString($delimiterType)
5139 54
    {
5140
        $token = $this->consumeToken($delimiterType);
5141 54
5142
        $string = $this->builder->buildAstString();
5143 54
        $startLine = $token->startLine;
5144 54
        $startColumn = $token->startColumn;
5145 54
5146
        $this->parseStringExpressions($string, $delimiterType);
5147 54
5148
        $token = $this->consumeToken($delimiterType);
5149 54
        $endLine = $token->endLine;
5150 52
        $endColumn = $token->endColumn;
5151 52
5152
        $string->configureLinesAndColumns(
5153 52
            $startLine,
5154 52
            $endLine,
5155 52
            $startColumn,
5156 52
            $endColumn
5157
        );
5158 52
5159
        return $string;
5160 52
    }
5161
5162
    /**
5163
     * This method parses the contents of a string or here-/now-doc node. It
5164
     * will not consume the given stop token, so it is up to the calling method
5165
     * to consume the stop token. The return value of this method is the prepared
5166
     * input string node.
5167
     *
5168
     * @param  \PDepend\Source\AST\ASTNode $node
5169
     * @param  integer                     $stopToken
5170
     * @return \PDepend\Source\AST\ASTNode
5171
     * @since 0.9.12
5172
     */
5173
    private function parseStringExpressions(ASTNode $node, $stopToken)
5174 70
    {
5175
        while (($tokenType = $this->tokenizer->peek()) != Tokenizer::T_EOF) {
5176 70
            switch ($tokenType) {
5177
                case $stopToken:
5178 70
                    break 2;
5179 68
                case Tokens::T_BACKSLASH:
5180 70
                    $node->addChild($this->parseEscapedAstLiteralString());
5181
                    break;
5182
                case Tokens::T_DOLLAR:
5183 70
                    $node->addChild($this->parseCompoundVariableOrLiteral());
5184 16
                    break;
5185 16
                case Tokens::T_VARIABLE:
5186 66
                    $node->addChild($this->parseVariable());
5187 40
                    break;
5188 40
                case Tokens::T_CURLY_BRACE_OPEN:
5189 62
                    $node->addChild($this->parseCompoundExpressionOrLiteral());
5190 16
                    break;
5191 16
                default:
5192 60
                    $node->addChild($this->parseLiteral());
5193 60
                    break;
5194 60
            }
5195 60
        }
5196 70
        return $node;
5197 70
    }
5198
5199
    /**
5200
     * This method parses an escaped sequence of literal tokens.
5201
     *
5202
     * @return \PDepend\Source\AST\ASTLiteral
5203
     * @since 0.9.10
5204
     */
5205
    private function parseEscapedAstLiteralString()
5206
    {
5207
        $this->tokenStack->push();
5208
5209
        $image  = $this->consumeToken(Tokens::T_BACKSLASH)->image;
5210
        $escape = true;
5211
5212
        $tokenType = $this->tokenizer->peek();
5213
        while ($tokenType != Tokenizer::T_EOF) {
5214
            if ($tokenType === Tokens::T_BACKSLASH) {
5215
                $escape != $escape;
5216
                $image  .= $this->consumeToken(Tokens::T_BACKSLASH)->image;
5217
5218
                $tokenType = $this->tokenizer->peek();
5219
                continue;
5220
            }
5221
5222
            if ($escape) {
5223
                $image .= $this->consumeToken($tokenType)->image;
5224
                break;
5225
            }
5226
        }
5227
        return $this->setNodePositionsAndReturn(
5228
            $this->builder->buildAstLiteral($image)
5229
        );
5230
    }
5231
5232
    /**
5233
     * This method parses a simple literal and configures the position
5234
     * properties.
5235
     *
5236
     * @return \PDepend\Source\AST\ASTLiteral
5237
     * @since 0.9.10
5238
     */
5239 View Code Duplication
    protected function parseLiteral()
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...
5240 64
    {
5241
        $token = $this->consumeToken($this->tokenizer->peek());
5242 64
5243
        $node = $this->builder->buildAstLiteral($token->image);
5244 64
        $node->configureLinesAndColumns(
5245 64
            $token->startLine,
5246 64
            $token->endLine,
5247 64
            $token->startColumn,
5248 64
            $token->endColumn
5249 64
        );
5250 64
5251
        return $node;
5252 64
    }
5253
5254
    /**
5255
     * Extracts all dependencies from a callable signature.
5256
     *
5257
     * @return \PDepend\Source\AST\ASTFormalParameters
5258
     * @since 0.9.5
5259
     */
5260
    private function parseFormalParameters()
5261 1962
    {
5262
        $this->consumeComments();
5263 1962
5264
        $this->tokenStack->push();
5265 1962
5266
        $formalParameters = $this->builder->buildAstFormalParameters();
5267 1962
5268
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
5269 1962
        $this->consumeComments();
5270 1960
5271
        $tokenType = $this->tokenizer->peek();
5272 1960
5273
        // Check for function without parameters
5274
        if ($tokenType === Tokens::T_PARENTHESIS_CLOSE) {
5275 1960
            $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
5276 1332
            return $this->setNodePositionsAndReturn($formalParameters);
5277 1332
        }
5278
5279 View Code Duplication
        while ($tokenType !== Tokenizer::T_EOF) {
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...
5280 700
            $formalParameters->addChild(
5281 700
                $this->parseFormalParameterOrTypeHintOrByReference()
5282 700
            );
5283 688
5284
            $this->consumeComments();
5285 688
            $tokenType = $this->tokenizer->peek();
5286 688
5287
            // Check for following parameter
5288
            if ($tokenType !== Tokens::T_COMMA) {
5289 688
                break;
5290 688
            }
5291
            $this->consumeToken(Tokens::T_COMMA);
5292 156
        }
5293 156
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
5294 688
5295
        return $this->setNodePositionsAndReturn($formalParameters);
5296 686
    }
5297
5298
    /**
5299
     * This method parses a formal parameter in all it's variations.
5300
     *
5301
     * <code>
5302
     * //                ------------
5303
     * function traverse(Iterator $it) {}
5304
     * //                ------------
5305
     *
5306
     * //                ---------
5307
     * function traverse(array $ar) {}
5308
     * //                ---------
5309
     *
5310
     * //                ---
5311
     * function traverse(&$x) {}
5312
     * //                ---
5313
     * </code>
5314
     *
5315
     * @return \PDepend\Source\AST\ASTFormalParameter
5316
     * @since 0.9.6
5317
     */
5318
    protected function parseFormalParameterOrTypeHintOrByReference()
5319 700
    {
5320
        $this->consumeComments();
5321 700
        $tokenType = $this->tokenizer->peek();
5322 700
5323
        $this->tokenStack->push();
5324 700
5325
        switch ($tokenType) {
5326
            case Tokens::T_ARRAY:
5327 700
                $parameter = $this->parseFormalParameterAndArrayTypeHint();
5328 70
                break;
5329 68
            case ($this->isTypeHint($tokenType)):
5330 642
                $parameter = $this->parseFormalParameterAndTypeHint();
5331 108
                break;
5332 108
            case Tokens::T_SELF:
5333 580
                $parameter = $this->parseFormalParameterAndSelfTypeHint();
5334 4
                break;
5335 4
            case Tokens::T_PARENT:
5336 576
                $parameter = $this->parseFormalParameterAndParentTypeHint();
5337 8
                break;
5338 4
            case Tokens::T_BITWISE_AND:
5339 568
                $parameter = $this->parseFormalParameterAndByReference();
5340 42
                break;
5341 42
            default:
5342 532
                $parameter = $this->parseFormalParameter();
5343 532
                break;
5344 526
        }
5345 532
        return $this->setNodePositionsAndReturn($parameter);
5346 688
    }
5347
5348
    /**
5349
     * This method parses a formal parameter that has an array type hint.
5350
     *
5351
     * <code>
5352
     * //                ---------
5353
     * function traverse(array $ar) {}
5354
     * //                ---------
5355
     * </code>
5356
     *
5357
     * @return \PDepend\Source\AST\ASTFormalParameter
5358
     * @since 0.9.6
5359
     */
5360
    private function parseFormalParameterAndArrayTypeHint()
5361 70
    {
5362
        $node = $this->parseArrayType();
5363 70
5364
        $parameter = $this->parseFormalParameterOrByReference();
5365 70
        $parameter->prependChild($node);
5366 68
5367
        return $parameter;
5368 68
    }
5369
5370
    /**
5371
     * @return \PDepend\Source\AST\ASTTypeArray
5372
     */
5373 View Code Duplication
    protected function parseArrayType()
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...
5374 74
    {
5375
        $token = $this->consumeToken(Tokens::T_ARRAY);
5376 74
5377
        $type = $this->builder->buildAstTypeArray();
5378 74
        $type->configureLinesAndColumns(
5379 74
            $token->startLine,
5380 74
            $token->endLine,
5381 74
            $token->startColumn,
5382 74
            $token->endColumn
5383 74
        );
5384 74
5385
        return $type;
5386 74
    }
5387
5388
    /**
5389
     * This method parses a formal parameter that has a regular class type hint.
5390
     *
5391
     * <code>
5392
     * //                ------------
5393
     * function traverse(Iterator $it) {}
5394
     * //                ------------
5395
     * </code>
5396
     *
5397
     * @return \PDepend\Source\AST\ASTFormalParameter
5398
     * @since 0.9.6
5399
     */
5400
    private function parseFormalParameterAndTypeHint()
5401 108
    {
5402
        $this->tokenStack->push();
5403 108
5404
        $classReference = $this->setNodePositionsAndReturn(
5405 108
            $this->parseTypeHint()
5406 108
        );
5407 108
5408
        $parameter = $this->parseFormalParameterOrByReference();
5409 108
        $parameter->prependChild($classReference);
5410 108
5411
        return $parameter;
5412 108
    }
5413
5414
    /**
5415
     * This method will parse a formal parameter that has the keyword parent as
5416
     * parameter type hint.
5417
     *
5418
     * <code>
5419
     * class Foo extends Bar
5420
     * {
5421
     *     //                   ---------
5422
     *     public function test(parent $o) {}
5423
     *     //                   ---------
5424
     * }
5425
     * </code>
5426
     *
5427
     * @return \PDepend\Source\AST\ASTFormalParameter
5428
     * @throws \PDepend\Source\Parser\InvalidStateException
5429
     * @since 0.9.6
5430
     */
5431
    private function parseFormalParameterAndParentTypeHint()
5432 8
    {
5433
        $reference = $this->parseParentType();
5434 8
        $parameter = $this->parseFormalParameterOrByReference();
5435 4
        $parameter->prependChild($reference);
5436 4
5437
        return $parameter;
5438 4
    }
5439
5440
    /**
5441
     * @return \PDepend\Source\AST\ASTParentReference
5442
     */
5443
    protected function parseParentType()
5444 8
    {
5445
        return $this->parseParentReference($this->consumeToken(Tokens::T_PARENT));
5446 8
    }
5447
5448
    /**
5449
     * This method will parse a formal parameter that has the keyword self as
5450
     * parameter type hint.
5451
     *
5452
     * <code>
5453
     * class Foo
5454
     * {
5455
     *     //                   -------
5456
     *     public function test(self $o) {}
5457
     *     //                   -------
5458
     * }
5459
     * </code>
5460
     *
5461
     * @return \PDepend\Source\AST\ASTFormalParameter
5462
     * @since 0.9.6
5463
     */
5464
    private function parseFormalParameterAndSelfTypeHint()
5465 4
    {
5466
        $self = $this->parseSelfType();
5467 4
5468
        $parameter = $this->parseFormalParameterOrByReference();
5469 4
        $parameter->addChild($self);
5470 4
5471
        return $parameter;
5472 4
    }
5473
5474
    /**
5475
     * @return \PDepend\Source\AST\ASTSelfReference
5476
     */
5477
    protected function parseSelfType()
5478 4
    {
5479
        return $this->parseSelfReference($this->consumeToken(Tokens::T_SELF));
5480 4
    }
5481
5482
    /**
5483
     * This method will parse a formal parameter that can optionally be passed
5484
     * by reference.
5485
     *
5486
     * <code>
5487
     * //                 ---  -------
5488
     * function foo(array &$x, $y = 42) {}
5489
     * //                 ---  -------
5490
     * </code>
5491
     *
5492
     * @return \PDepend\Source\AST\ASTFormalParameter
5493
     * @since 0.9.6
5494
     */
5495
    protected function parseFormalParameterOrByReference()
5496 184
    {
5497
        $this->consumeComments();
5498 184
        if ($this->tokenizer->peek() === Tokens::T_BITWISE_AND) {
5499 184
            return $this->parseFormalParameterAndByReference();
5500 22
        }
5501
        return $this->parseFormalParameter();
5502 162
    }
5503
5504
    /**
5505
     * This method will parse a formal parameter that is passed by reference.
5506
     *
5507
     * <code>
5508
     * //                 ---  --------
5509
     * function foo(array &$x, &$y = 42) {}
5510
     * //                 ---  --------
5511
     * </code>
5512
     *
5513
     * @return \PDepend\Source\AST\ASTFormalParameter
5514
     * @since 0.9.6
5515
     */
5516
    private function parseFormalParameterAndByReference()
5517 64
    {
5518
        $this->consumeToken(Tokens::T_BITWISE_AND);
5519 64
        $this->consumeComments();
5520 64
5521
        $parameter = $this->parseFormalParameter();
5522 64
        $parameter->setPassedByReference();
5523 64
5524
        return $parameter;
5525 64
    }
5526
5527
    /**
5528
     * This method will parse a formal parameter. A formal parameter is at least
5529
     * a variable name, but can also contain a default parameter value.
5530
     *
5531
     * <code>
5532
     * //               --  -------
5533
     * function foo(Bar $x, $y = 42) {}
5534
     * //               --  -------
5535
     * </code>
5536
     *
5537
     * @return \PDepend\Source\AST\ASTFormalParameter
5538
     * @since 0.9.6
5539
     */
5540
    protected function parseFormalParameter()
5541
    {
5542
        $parameter = $this->builder->buildAstFormalParameter();
5543
        $parameter->addChild($this->parseVariableDeclarator());
5544
5545
        return $parameter;
5546
    }
5547
5548
    /**
5549
     * Tests if the given token type is a valid formal parameter in the supported
5550
     * PHP version.
5551
     *
5552
     * @param integer $tokenType
5553
     * @return boolean
5554
     * @since 1.0.0
5555
     */
5556 View Code Duplication
    protected function isTypeHint($tokenType)
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...
5557 638
    {
5558
        switch ($tokenType) {
5559
            case Tokens::T_STRING:
5560 638
            case Tokens::T_BACKSLASH:
5561 638
            case Tokens::T_NAMESPACE:
5562 638
                return true;
5563 104
        }
5564
        return false;
5565 580
    }
5566
5567
    /**
5568
     * Parses a type hint that is valid in the supported PHP version.
5569
     *
5570
     * @return \PDepend\Source\AST\ASTNode
5571
     * @since 1.0.0
5572
     */
5573
    protected function parseTypeHint()
5574
    {
5575
        switch ($this->tokenizer->peek()) {
5576
            case Tokens::T_STRING:
5577
            case Tokens::T_BACKSLASH:
5578
            case Tokens::T_NAMESPACE:
5579
                $type = $this->builder->buildAstClassOrInterfaceReference(
5580
                    $this->parseQualifiedName()
5581
                );
5582
                break;
5583
        }
5584
        return $type;
0 ignored issues
show
Bug introduced by
The variable $type does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
5585
    }
5586
5587
    /**
5588
     * Extracts all dependencies from a callable body.
5589
     *
5590
     * @return \PDepend\Source\AST\ASTScope
5591
     * @since 0.9.12
5592
     */
5593 View Code Duplication
    private function parseScope()
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...
5594 1914
    {
5595
        $scope = $this->builder->buildAstScope();
5596 1914
5597
        $this->tokenStack->push();
5598 1914
5599
        $this->consumeComments();
5600 1914
        $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
5601 1914
5602
        while (($stmt = $this->parseOptionalStatement()) !== null) {
5603 1914
            // TODO: Remove if-statement once, we have translated functions and
5604
            //       closures into ast-nodes
5605
            if ($stmt instanceof \PDepend\Source\AST\ASTNode) {
5606 1528
                $scope->addChild($stmt);
5607 1528
            }
5608 1528
        }
5609 1528
5610
        $this->consumeComments();
5611 1868
        $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
5612 1868
5613
        return $this->setNodePositionsAndReturn($scope);
5614 1868
    }
5615
5616
    /**
5617
     * Parse a statement.
5618
     *
5619
     * @return \PDepend\Source\AST\ASTNode
5620
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
5621
     * @throws \PDepend\Source\Parser\TokenStreamEndException
5622
     * @since 1.0.0
5623
     */
5624
    private function parseStatement()
5625 28
    {
5626
        if (is_object($stmt = $this->parseOptionalStatement())) {
5627 28
            return $stmt;
5628 26
        }
5629
        if (is_object($token = $this->tokenizer->next())) {
5630 2
            $this->throwUnexpectedTokenException($token);
5631 2
        }
5632
        throw new TokenStreamEndException($this->compilationUnit->getFileName());
0 ignored issues
show
Documentation introduced by
$this->compilationUnit->getFileName() is of type string, but the function expects a object<PDepend\Source\Tokenizer\Tokenizer>.

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...
5633
    }
5634
5635
    /**
5636
     * Parses an optional statement or returns <b>null</b>.
5637
     *
5638
     * @return \PDepend\Source\AST\ASTNode
5639
     * @since 0.9.8
5640
     */
5641
    private function parseOptionalStatement()
5642 2366
    {
5643
        $tokenType = $this->tokenizer->peek();
5644 2366
5645
        switch ($tokenType) {
5646
            case Tokens::T_ECHO:
5647 2366
                return $this->parseEchoStatement();
5648 186
            case Tokens::T_SWITCH:
5649 2364
                return $this->parseSwitchStatement();
5650 44
            case Tokens::T_TRY:
5651 2364
                return $this->parseTryStatement();
5652 58
            case Tokens::T_THROW:
5653 2364
                return $this->parseThrowStatement();
5654 30
            case Tokens::T_IF:
5655 2364
                return $this->parseIfStatement();
5656 196
            case Tokens::T_FOR:
5657 2364
                return $this->parseForStatement();
5658 98
            case Tokens::T_FOREACH:
5659 2364
                return $this->parseForeachStatement();
5660 110
            case Tokens::T_DO:
5661 2364
                return $this->parseDoWhileStatement();
5662 18
            case Tokens::T_WHILE:
5663 2364
                return $this->parseWhileStatement();
5664 12
            case Tokens::T_RETURN:
5665 2364
                return $this->parseReturnStatement();
5666 608
            case Tokens::T_BREAK:
5667 2364
                return $this->parseBreakStatement();
5668 48
            case Tokens::T_CONTINUE:
5669 2364
                return $this->parseContinueStatement();
5670 8
            case Tokens::T_GOTO:
5671 2364
                return $this->parseGotoStatement();
5672 16
            case Tokens::T_GLOBAL:
5673 2364
                return $this->parseGlobalStatement();
5674 8
            case Tokens::T_UNSET:
5675 2364
                return $this->parseUnsetStatement();
5676 2
            case Tokens::T_STRING:
5677 2364
                if ($this->tokenizer->peekNext() === Tokens::T_COLON) {
5678 322
                    return $this->parseLabelStatement();
5679 16
                }
5680
                break;
5681 306
            case Tokens::T_CONST:
5682 2364
                return $this->parseConstantDefinition();
5683 4
            case Tokens::T_FUNCTION:
5684 2362
                return $this->parseFunctionOrClosureDeclaration();
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->parseFunctionOrClosureDeclaration(); of type PDepend\Source\AST\ASTNo...\Source\AST\ASTFunction adds the type PDepend\Source\AST\ASTFunction to the return on line 5684 which is incompatible with the return type documented by PDepend\Source\Language\...:parseOptionalStatement of type PDepend\Source\AST\ASTNode|null.
Loading history...
5685 1556
            case Tokens::T_COMMENT:
5686 2340
                return $this->parseCommentWithOptionalInlineClassOrInterfaceReference();
5687 36
            case Tokens::T_DOC_COMMENT:
5688 2340
                return $this->builder->buildAstComment(
5689 8
                    $this->consumeToken(Tokens::T_DOC_COMMENT)->image
5690 8
                );
5691 8
            case Tokens::T_CURLY_BRACE_OPEN:
5692 2340
                return $this->parseRegularScope();
5693 2
            case Tokens::T_DECLARE:
5694 2340
                return $this->parseDeclareStatement();
5695 28
            case Tokens::T_ELSE:
5696 2340
            case Tokens::T_ENDIF:
5697 2340
            case Tokens::T_ELSEIF:
5698 2340
            case Tokens::T_ENDFOR:
5699 2340
            case Tokens::T_ENDWHILE:
5700 2340
            case Tokens::T_ENDSWITCH:
5701 2340
            case Tokens::T_ENDDECLARE:
5702 2340
            case Tokens::T_ENDFOREACH:
5703 2340
            case Tokens::T_CURLY_BRACE_CLOSE:
5704 2340
                return null;
5705 1910
            case Tokens::T_DECLARE:
5706 1226
                return $this->parseDeclareStatement();
5707
            case Tokens::T_CLOSE_TAG:
5708 1226
                if (($tokenType = $this->parseNonePhpCode()) === Tokenizer::T_EOF) {
5709 12
                    return null;
5710
                }
5711
5712
                return $this->parseOptionalStatement();
5713 12 View Code Duplication
            case Tokens::T_TRAIT:
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...
5714 1220
                $package = $this->getNamespaceOrPackage();
5715 110
                $package->addType($trait = $this->parseTraitDeclaration());
5716 110
5717
                $this->builder->restoreTrait($trait);
5718 110
                $this->compilationUnit->addChild($trait);
5719 110
5720
                return $trait;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $trait; (PDepend\Source\AST\ASTTrait) is incompatible with the return type documented by PDepend\Source\Language\...:parseOptionalStatement of type PDepend\Source\AST\ASTNode|null.

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...
5721 110 View Code Duplication
            case Tokens::T_INTERFACE:
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...
5722 1176
                $package = $this->getNamespaceOrPackage();
5723 154
                $package->addType($interface = $this->parseInterfaceDeclaration());
5724 154
5725
                $this->builder->restoreInterface($interface);
5726 152
                $this->compilationUnit->addChild($interface);
5727 152
5728
                return $interface;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $interface; (PDepend\Source\AST\ASTInterface) is incompatible with the return type documented by PDepend\Source\Language\...:parseOptionalStatement of type PDepend\Source\AST\ASTNode|null.

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...
5729 152
            case Tokens::T_CLASS:
5730 1084
            case Tokens::T_FINAL:
5731 1084 View Code Duplication
            case Tokens::T_ABSTRACT:
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...
5732 1084
                $package = $this->getNamespaceOrPackage();
5733 696
                $package->addType($class = $this->parseClassDeclaration());
5734 696
5735
                $this->builder->restoreClass($class);
5736 678
                $this->compilationUnit->addChild($class);
5737 678
5738
                return $class;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $class; (PDepend\Source\AST\ASTClass) is incompatible with the return type documented by PDepend\Source\Language\...:parseOptionalStatement of type PDepend\Source\AST\ASTNode|null.

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...
5739 678
            case Tokens::T_YIELD:
5740 508
                return $this->parseYield();
5741 14
        }
5742
5743
        $this->tokenStack->push();
5744 736
        $stmt = $this->builder->buildAstStatement();
5745 736
5746
        if (($expr = $this->parseOptionalExpression()) != null) {
5747 736
            $stmt->addChild($expr);
5748 686
        }
5749 686
        $this->parseStatementTermination();
5750 708
5751
        return $this->setNodePositionsAndReturn($stmt);
5752 702
    }
5753
5754
    /**
5755
     * Parses a sequence of none php code tokens and returns the token type of
5756
     * the next token.
5757
     *
5758
     * @return integer
5759
     * @since 0.9.12
5760
     */
5761
    private function parseNonePhpCode()
5762 368
    {
5763
        $this->consumeToken(Tokens::T_CLOSE_TAG);
5764 368
5765
        $this->tokenStack->push();
5766 360
        while (($tokenType = $this->tokenizer->peek()) !== Tokenizer::T_EOF) {
5767 360
            switch ($tokenType) {
5768
                case Tokens::T_OPEN_TAG:
5769 22
                case Tokens::T_OPEN_TAG_WITH_ECHO:
5770 22
                    $this->consumeToken($tokenType);
5771 22
                    $tokenType = $this->tokenizer->peek();
5772 22
                    break 2;
5773 22
                default:
5774 20
                    $this->consumeToken($tokenType);
5775 20
                    break;
5776 20
            }
5777 20
        }
5778 20
        $this->tokenStack->pop();
5779 360
5780
        return $tokenType;
5781 360
    }
5782
5783
    /**
5784
     * Parses a comment and optionally an embedded class or interface type
5785
     * annotation.
5786
     *
5787
     * @return \PDepend\Source\AST\ASTComment
5788
     * @since 0.9.8
5789
     */
5790
    private function parseCommentWithOptionalInlineClassOrInterfaceReference()
5791 36
    {
5792
        $token = $this->consumeToken(Tokens::T_COMMENT);
5793 36
5794
        $comment = $this->builder->buildAstComment($token->image);
5795 36
        if (preg_match(self::REGEXP_INLINE_TYPE, $token->image, $match)) {
5796 36
            $image = $this->useSymbolTable->lookup($match[1]) ?: $match[1];
5797 8
5798
            $comment->addChild(
5799 8
                $this->builder->buildAstClassOrInterfaceReference($image)
5800 8
            );
5801 8
        }
5802 8
5803
        $comment->configureLinesAndColumns(
5804 36
            $token->startLine,
5805 36
            $token->endLine,
5806 36
            $token->startColumn,
5807 36
            $token->endColumn
5808 36
        );
5809 36
        return $comment;
5810 36
    }
5811
5812
    /**
5813
     * Parses an optional set of bound closure variables.
5814
     *
5815
     * @param \PDepend\Source\AST\ASTClosure $closure The context closure instance.
5816
     *
5817
     * @return \PDepend\Source\AST\ASTClosure
5818
     * @since 1.0.0
5819
     */
5820
    private function parseOptionalBoundVariables(
5821 52
        \PDepend\Source\AST\ASTClosure $closure
5822
    ) {
5823
        $this->consumeComments();
5824 52
        if (Tokens::T_USE === $this->tokenizer->peek()) {
5825 52
            return $this->parseBoundVariables($closure);
5826
        }
5827
        return $closure;
5828 52
    }
5829
5830
    /**
5831
     * Parses a list of bound closure variables.
5832
     *
5833
     * @param \PDepend\Source\AST\ASTClosure $closure The parent closure instance.
5834
     *
5835
     * @return \PDepend\Source\AST\ASTClosure
5836
     * @since 0.9.5
5837
     */
5838
    private function parseBoundVariables(\PDepend\Source\AST\ASTClosure $closure)
5839
    {
5840
        $this->consumeToken(Tokens::T_USE);
5841
5842
        $this->consumeComments();
5843
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
5844
5845
        while ($this->tokenizer->peek() !== Tokenizer::T_EOF) {
5846
            $this->consumeComments();
5847
5848
            if ($this->tokenizer->peek() === Tokens::T_BITWISE_AND) {
5849
                $this->consumeToken(Tokens::T_BITWISE_AND);
5850
                $this->consumeComments();
5851
            }
5852
5853
            $this->consumeToken(Tokens::T_VARIABLE);
5854
            $this->consumeComments();
5855
5856
            if ($this->tokenizer->peek() === Tokens::T_COMMA) {
5857
                $this->consumeToken(Tokens::T_COMMA);
5858
                continue;
5859
            }
5860
            break;
5861
        }
5862
5863
        $this->consumeComments();
5864
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
5865
5866
        return $closure;
5867
    }
5868
5869
    /**
5870
     * Parses a php class/method name chain.
5871
     *
5872
     * <code>
5873
     * PDepend\Source\Parser::parse();
5874
     * </code>
5875
     *
5876
     * @throws NoActiveScopeException
5877
     *
5878
     * @return string
5879
     * @link   http://php.net/manual/en/language.namespaces.importing.php
5880
     */
5881
    protected function parseQualifiedName()
5882 1100
    {
5883
        $fragments = $this->parseQualifiedNameRaw();
5884 1100
5885
        // Check for fully qualified name
5886
        if ($fragments[0] === '\\') {
5887 1098
            return join('', $fragments);
5888 48
        } else {
5889
            switch (strtolower($fragments[0])) {
5890 1084
                case 'int':
5891 1084
                case 'bool':
5892 1084
                case 'float':
5893 1084
                case 'string':
5894 1084
                case 'callable':
5895 1084
                    return $fragments[0];
5896 30
            }
5897 1060
        }
5898
5899
        // Search for an use alias
5900
        $mapsTo = $this->useSymbolTable->lookup($fragments[0]);
5901 1060
        if ($mapsTo !== null) {
5902 1060
            // Remove alias and add real namespace
5903
            array_shift($fragments);
5904 20
            array_unshift($fragments, $mapsTo);
5905 20
        } elseif ($this->namespaceName !== null
5906 1060
            && $this->namespacePrefixReplaced === false
5907 1050
        ) {
5908 1050
            // Prepend current namespace
5909
            array_unshift($fragments, $this->namespaceName, '\\');
5910 46
        }
5911 46
        return join('', $fragments);
5912 1060
    }
5913
5914
    /**
5915
     * This method parses a qualified PHP 5.3 class, interface and namespace
5916
     * identifier and returns the collected tokens as a string array.
5917
     *
5918
     * @return array(string)
0 ignored issues
show
Documentation introduced by
The doc-type array(string) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
5919
     * @since 0.9.5
5920
     */
5921
    protected function parseQualifiedNameRaw()
5922 1104
    {
5923
        // Reset namespace prefix flag
5924
        $this->namespacePrefixReplaced = false;
5925 1104
5926
        // Consume comments and fetch first token type
5927
        $this->consumeComments();
5928 1104
        $tokenType = $this->tokenizer->peek();
5929 1104
5930
        $qualifiedName = array();
5931 1104
5932
        if ($tokenType === Tokens::T_NAMESPACE) {
5933 1104
            // Consume namespace keyword
5934
            $this->consumeToken(Tokens::T_NAMESPACE);
5935 40
            $this->consumeComments();
5936 40
5937
            // Add current namespace as first token
5938
            $qualifiedName = array((string) $this->namespaceName);
5939 40
5940
            // Set prefixed flag to true
5941
            $this->namespacePrefixReplaced = true;
5942 40
        } elseif ($this->isClassName($tokenType)) {
5943 1104
            $qualifiedName[] = $this->parseClassName();
5944 1078
5945
            $this->consumeComments();
5946 1078
            $tokenType = $this->tokenizer->peek();
5947 1078
5948
            // Stop here for simple identifier
5949
            if ($tokenType !== Tokens::T_BACKSLASH) {
5950 1078
                return $qualifiedName;
5951 1040
            }
5952
        }
5953 70
5954
        do {
5955
            // Next token must be a namespace separator
5956
            $this->consumeToken(Tokens::T_BACKSLASH);
5957 152
            $this->consumeComments();
5958 150
5959
            // Append to qualified name
5960
            $qualifiedName[] = '\\';
5961 150
5962
            if ($nextElement = $this->parseQualifiedNameElement($qualifiedName)) {
5963 150
                $qualifiedName[] = $nextElement;
5964 148
            }
5965 148
5966
            $this->consumeComments();
5967 148
5968
            // Get next token type
5969
            $tokenType = $this->tokenizer->peek();
5970 148
        } while ($tokenType === Tokens::T_BACKSLASH);
5971 148
5972
        return $qualifiedName;
5973 146
    }
5974
5975
    /**
5976
     * @param array $previousElements
5977
     * @return string
5978
     */
5979
    protected function parseQualifiedNameElement(array $previousElements)
5980 148
    {
5981
        return $this->parseClassName();
5982 148
    }
5983
5984
    /**
5985
     * This method parses a PHP 5.3 namespace declaration.
5986
     *
5987
     * @throws NoActiveScopeException
5988
     *
5989
     * @return void
5990
     * @since 0.9.5
5991
     */
5992
    private function parseNamespaceDeclaration()
5993 148
    {
5994
        // Consume namespace keyword and strip optional comments
5995
        $this->consumeToken(Tokens::T_NAMESPACE);
5996 148
        $this->consumeComments();
5997 148
5998
        $tokenType = $this->tokenizer->peek();
5999 148
6000
        // Search for a namespace identifier
6001
        if ($this->isClassName($tokenType)) {
6002 148
            // Reset namespace property
6003
            $this->namespaceName = null;
6004 138
6005
            $qualifiedName = $this->parseQualifiedName();
6006 138
6007
            $this->consumeComments();
6008 138
            if ($this->tokenizer->peek() === Tokens::T_CURLY_BRACE_OPEN) {
6009 138
                $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
6010 48
            } else {
6011 48
                $this->consumeToken(Tokens::T_SEMICOLON);
6012 90
            }
6013
6014
            // Create a package for this namespace
6015
            $this->namespaceName = $qualifiedName;
6016 138
6017
            $this->useSymbolTable->resetScope();
6018 138
6019
        } elseif ($tokenType === Tokens::T_BACKSLASH) {
6020 148
            // Same namespace reference, something like:
6021
            //   new namespace\Foo();
6022
            // or:
6023
            //   $x = namespace\foo::bar();
6024
6025
            // Now parse a qualified name
6026
            $this->parseQualifiedNameRaw();
6027 2
        } else {
6028
            // Consume opening curly brace
6029
            $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
6030 14
6031
            // Create a package for this namespace
6032
            $this->namespaceName = '';
6033 12
6034
            $this->useSymbolTable->resetScope();
6035 12
        }
6036
6037
        $this->reset();
6038 144
    }
6039 144
6040
    /**
6041
     * This method parses a list of PHP 5.3 use declarations and adds a mapping
6042
     * between short name and full qualified name to the use symbol table.
6043
     *
6044
     * <code>
6045
     * use \foo\bar as fb,
6046
     *     \foobar\Bar;
6047
     * </code>
6048
     *
6049
     * @return void
6050
     * @since 0.9.5
6051
     */
6052
    protected function parseUseDeclarations()
6053
    {
6054
        // Consume use keyword
6055
        $this->consumeToken(Tokens::T_USE);
6056
        $this->consumeComments();
6057
6058
        // Parse all use declarations
6059
        $this->parseUseDeclaration();
6060
        $this->consumeComments();
6061
6062
        // Consume closing semicolon
6063
        $this->consumeToken(Tokens::T_SEMICOLON);
6064
6065
        // Reset any previous state
6066
        $this->reset();
6067
    }
6068
6069
    /**
6070
     * This method parses a single use declaration and adds a mapping between
6071
     * short name and full qualified name to the use symbol table.
6072
     *
6073
     * @throws NoActiveScopeException
6074
     *
6075
     * @return void
6076
     * @since 0.9.5
6077
     */
6078
    protected function parseUseDeclaration()
6079 22
    {
6080
        $fragments = $this->parseQualifiedNameRaw();
6081 22
        $this->consumeComments();
6082 20
6083
        // Add leading backslash, because aliases must be full qualified
6084
        // http://php.net/manual/en/language.namespaces.importing.php
6085
        if ($fragments[0] !== '\\') {
6086 20
            array_unshift($fragments, '\\');
6087 14
        }
6088 14
6089
        $this->parseUseDeclarationForVersion($fragments);
6090 20
6091
        // Check for a following use declaration
6092
        if ($this->tokenizer->peek() === Tokens::T_COMMA) {
6093 20
            // Consume comma token and comments
6094
            $this->consumeToken(Tokens::T_COMMA);
6095 10
            $this->consumeComments();
6096 10
6097
            $this->parseUseDeclaration();
6098 10
        }
6099 10
    }
6100 20
6101
    /**
6102
     * @param array $fragments
6103
     * @return void
6104
     */
6105
    protected function parseUseDeclarationForVersion(array $fragments)
6106 18
    {
6107
        $image = $this->parseNamespaceImage($fragments);
6108 18
6109
        // Add mapping between image and qualified name to symbol table
6110
        $this->useSymbolTable->add($image, join('', $fragments));
6111 18
    }
6112 18
6113
    /**
6114
     * @param array $fragments
6115
     * @return string
6116
     */
6117 View Code Duplication
    protected function parseNamespaceImage(array $fragments)
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...
6118 20
    {
6119
        if ($this->tokenizer->peek() === Tokens::T_AS) {
6120 20
            $this->consumeToken(Tokens::T_AS);
6121 14
            $this->consumeComments();
6122 14
6123
            $image = $this->consumeToken(Tokens::T_STRING)->image;
6124 14
            $this->consumeComments();
6125 14
        } else {
6126 14
            $image = end($fragments);
6127 16
        }
6128
        return $image;
6129 20
    }
6130
6131
    /**
6132
     * Parses a single constant definition with one or more constant declarators.
6133
     *
6134
     * <code>
6135
     * class Foo
6136
     * {
6137
     * //  ------------------------
6138
     *     const FOO = 42, BAR = 23;
6139
     * //  ------------------------
6140
     * }
6141
     * </code>
6142
     *
6143
     * @return \PDepend\Source\AST\ASTConstantDefinition
6144
     * @since 0.9.6
6145
     */
6146
    protected function parseConstantDefinition()
6147 104
    {
6148
        $this->tokenStack->push();
6149 104
6150
        $token = $this->consumeToken(Tokens::T_CONST);
6151 104
6152
        $definition = $this->builder->buildAstConstantDefinition($token->image);
6153 104
        $definition->setComment($this->docComment);
6154 104
6155 View Code Duplication
        do {
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...
6156
            $definition->addChild($this->parseConstantDeclarator());
6157 104
6158
            $this->consumeComments();
6159 102
            $tokenType = $this->tokenizer->peek();
6160 102
6161
            if ($tokenType === Tokens::T_SEMICOLON) {
6162 102
                break;
6163 102
            }
6164
            $this->consumeToken(Tokens::T_COMMA);
6165 12
        } while ($tokenType !== Tokenizer::T_EOF);
6166 12
6167
6168
        $definition = $this->setNodePositionsAndReturn($definition);
6169 102
6170
        $this->consumeToken(Tokens::T_SEMICOLON);
6171 102
6172
        return $definition;
6173 102
    }
6174
6175
    /**
6176
     * Parses a single constant declarator.
6177
     *
6178
     * <code>
6179
     * class Foo
6180
     * {
6181
     *     //    --------
6182
     *     const BAR = 42;
6183
     *     //    --------
6184
     * }
6185
     * </code>
6186
     *
6187
     * Or in a comma separated constant defintion:
6188
     *
6189
     * <code>
6190
     * class Foo
6191
     * {
6192
     *     //    --------
6193
     *     const BAR = 42,
6194
     *     //    --------
6195
     *
6196
     *     //    --------------
6197
     *     const BAZ = 'Foobar',
6198
     *     //    --------------
6199
     *
6200
     *     //    ----------
6201
     *     const FOO = 3.14;
6202
     *     //    ----------
6203
     * }
6204
     * </code>
6205
     *
6206
     * @return \PDepend\Source\AST\ASTConstantDeclarator
6207
     * @since 0.9.6
6208
     */
6209 View Code Duplication
    protected function parseConstantDeclarator()
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...
6210 104
    {
6211
        // Remove leading comments and create a new token stack
6212
        $this->consumeComments();
6213 104
        $this->tokenStack->push();
6214 104
6215
        $tokenType = $this->tokenizer->peek();
6216 104
        if (false === $this->isConstantName($tokenType)) {
6217 104
            $this->throwUnexpectedTokenException();
6218
        }
6219
6220
        $token = $this->consumeToken($tokenType);
6221 104
6222
        $this->consumeComments();
6223 104
        $this->consumeToken(Tokens::T_EQUAL);
6224 104
6225
        $declarator = $this->builder->buildAstConstantDeclarator($token->image);
6226 104
        $declarator->setValue($this->parseConstantDeclaratorValue());
0 ignored issues
show
Bug introduced by
It seems like $this->parseConstantDeclaratorValue() can be null; however, setValue() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
6227 104
6228
        return $this->setNodePositionsAndReturn($declarator);
6229 102
    }
6230
6231
    /**
6232
     * Parses the value of a php constant. By default this can be only static
6233
     * values that were allowed in the oldest supported PHP version.
6234
     *
6235
     * @return \PDepend\Source\AST\ASTValue
6236
     * @since 2.2.x
6237
     */
6238
    protected function parseConstantDeclaratorValue()
6239
    {
6240
        return $this->parseStaticValue();
6241
    }
6242
6243
    /**
6244
     * This method parses a static variable declaration list, a member primary
6245
     * prefix invoked in the static context of a class or it parses a static
6246
     * closure declaration.
6247
     *
6248
     * Static variable:
6249
     * <code>
6250
     * function foo() {
6251
     * //  ------------------------------
6252
     *     static $foo, $bar, $baz = null;
6253
     * //  ------------------------------
6254
     * }
6255
     * </code>
6256
     *
6257
     * Static method invocation:
6258
     * <code>
6259
     * class Foo {
6260
     *     public function baz() {
6261
     * //      ----------------
6262
     *         static::foobar();
6263
     * //      ----------------
6264
     *     }
6265
     *     public function foobar() {}
6266
     * }
6267
     *
6268
     * class Bar extends Foo {
6269
     *     public function foobar() {}
6270
     * }
6271
     * </code>
6272
     *
6273
     * Static closure declaration:
6274
     * <code>
6275
     * $closure = static function($x, $y) {
6276
     *     return ($x * $y);
6277
     * };
6278
     * </code>
6279
     *
6280
     * @return \PDepend\Source\AST\ASTConstant
6281
     * @throws \PDepend\Source\Parser\ParserException
6282
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
6283
     * @since 0.9.6
6284
     */
6285
    private function parseStaticVariableDeclarationOrMemberPrimaryPrefix()
6286 46
    {
6287
        $this->tokenStack->push();
6288 46
6289
        // Consume static token and strip optional comments
6290
        $token = $this->consumeToken(Tokens::T_STATIC);
6291 46
        $this->consumeComments();
6292 46
6293
        // Fetch next token type
6294
        $tokenType = $this->tokenizer->peek();
6295 46
6296
        if ($tokenType === Tokens::T_PARENTHESIS_OPEN
6297
            || $tokenType === Tokens::T_DOUBLE_COLON
6298 46
        ) {
6299 46
            $static = $this->parseStaticReference($token);
6300 10
6301
            $prefix = $this->parseStaticMemberPrimaryPrefix($static);
6302 8
            return $this->setNodePositionsAndReturn($prefix);
6303 8
        } elseif ($tokenType === Tokens::T_FUNCTION) {
6304 36
            $closure = $this->parseClosureDeclaration();
6305 14
            $closure->setStatic(true);
6306 14
6307
            return $this->setNodePositionsAndReturn($closure);
6308 14
        }
6309
6310
        $declaration = $this->parseStaticVariableDeclaration($token);
6311 22
        return $this->setNodePositionsAndReturn($declaration);
6312 22
6313
    }
6314
6315
    /**
6316
     * This method will parse a static variable declaration.
6317
     *
6318
     * <code>
6319
     * function foo()
6320
     * {
6321
     *     // First declaration
6322
     *     static $foo;
6323
     *     // Second declaration
6324
     *     static $bar = array();
6325
     *     // Third declaration
6326
     *     static $baz    = array(),
6327
     *            $foobar = null,
6328
     *            $barbaz;
6329
     * }
6330
     * </code>
6331
     *
6332
     * @param  \PDepend\Source\Tokenizer\Token $token Token with the "static" keyword.
6333
     * @return \PDepend\Source\AST\ASTStaticVariableDeclaration
6334
     * @since 0.9.6
6335
     */
6336
    private function parseStaticVariableDeclaration(Token $token)
6337 22
    {
6338
        $staticDeclaration = $this->builder->buildAstStaticVariableDeclaration(
6339 22
            $token->image
6340 22
        );
6341 22
6342
        // Strip optional comments
6343
        $this->consumeComments();
6344 22
6345
        // Fetch next token type
6346
        $tokenType = $this->tokenizer->peek();
6347 22
6348 View Code Duplication
        while ($tokenType !== Tokenizer::T_EOF) {
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...
6349 22
            $staticDeclaration->addChild($this->parseVariableDeclarator());
6350 22
6351
            $this->consumeComments();
6352 22
6353
            // Semicolon terminates static declaration
6354
            $tokenType = $this->tokenizer->peek();
6355 22
            if ($tokenType === Tokens::T_SEMICOLON) {
6356 22
                break;
6357 22
            }
6358
            // We are here, so there must be a next declarator
6359
            $this->consumeToken(Tokens::T_COMMA);
6360 14
        }
6361 14
6362
        return $staticDeclaration;
6363 22
    }
6364
6365
    /**
6366
     * This method will parse a variable declarator.
6367
     *
6368
     * <code>
6369
     * // Parameter declarator
6370
     * function foo($x = 23) {
6371
     * }
6372
     * // Property declarator
6373
     * class Foo{
6374
     *     protected $bar = 42;
6375
     * }
6376
     * // Static declarator
6377
     * function baz() {
6378
     *     static $foo;
6379
     * }
6380
     * </code>
6381
     *
6382
     * @return \PDepend\Source\AST\ASTVariableDeclarator
6383
     * @since 0.9.6
6384
     */
6385 View Code Duplication
    protected function parseVariableDeclarator()
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...
6386 806
    {
6387
        $this->tokenStack->push();
6388 806
6389
        $name = $this->consumeToken(Tokens::T_VARIABLE)->image;
6390 806
        $this->consumeComments();
6391 804
6392
        $declarator = $this->builder->buildAstVariableDeclarator($name);
6393 804
6394
        if ($this->tokenizer->peek() === Tokens::T_EQUAL) {
6395 804
            $this->consumeToken(Tokens::T_EQUAL);
6396 190
            $declarator->setValue($this->parseStaticValueOrStaticArray());
0 ignored issues
show
Bug introduced by
It seems like $this->parseStaticValueOrStaticArray() can be null; however, setValue() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
6397 190
        }
6398 184
        return $this->setNodePositionsAndReturn($declarator);
6399 798
    }
6400
6401
    /**
6402
     * This method will parse a static value or a static array as it is
6403
     * used as default value for a parameter or property declaration.
6404
     *
6405
     * @return \PDepend\Source\AST\ASTValue
6406
     * @since 0.9.6
6407
     */
6408
    protected function parseStaticValueOrStaticArray()
6409 266
    {
6410
        $this->consumeComments();
6411 266
        if ($this->isArrayStartDelimiter()) {
6412 266
            // TODO: Use default value as value!
6413
            $defaultValue = $this->doParseArray(true);
0 ignored issues
show
Unused Code introduced by
$defaultValue 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...
6414 70
6415
            $value = new ASTValue();
6416 68
            $value->setValue(array());
6417 68
6418
            return $value;
6419 68
        }
6420
        return $this->parseStaticValue();
6421 236
    }
6422
6423
    /**
6424
     * This method will parse a static default value as it is used for a
6425
     * parameter, property or constant declaration.
6426
     *
6427
     * @return \PDepend\Source\AST\ASTValue
6428
     * @since 0.9.5
6429
     */
6430
    protected function parseStaticValue()
6431 264
    {
6432
        $defaultValue = new ASTValue();
6433 264
6434
        $this->consumeComments();
6435 264
6436
        // By default all parameters positive signed
6437
        $signed = 1;
6438 264
6439
        $tokenType = $this->tokenizer->peek();
6440 264
        while ($tokenType !== Tokenizer::T_EOF) {
6441 264
            switch ($tokenType) {
6442
                case Tokens::T_COMMA:
6443 262
                case Tokens::T_SEMICOLON:
6444 262
                case Tokens::T_PARENTHESIS_CLOSE:
6445 262
                    if ($defaultValue->isValueAvailable() === true) {
6446 248
                        return $defaultValue;
6447 244
                    }
6448
                    throw new MissingValueException($this->tokenizer);
6449 4
                case Tokens::T_NULL:
6450 258
                    $this->consumeToken(Tokens::T_NULL);
6451 68
                    $defaultValue->setValue(null);
6452 68
                    break;
6453 68
                case Tokens::T_TRUE:
6454 248
                    $this->consumeToken(Tokens::T_TRUE);
6455 10
                    $defaultValue->setValue(true);
6456 10
                    break;
6457 10
                case Tokens::T_FALSE:
6458 240
                    $this->consumeToken(Tokens::T_FALSE);
6459 8
                    $defaultValue->setValue(false);
6460 8
                    break;
6461 8 View Code Duplication
                case Tokens::T_LNUMBER:
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...
6462 234
                    $token = $this->consumeToken(Tokens::T_LNUMBER);
6463 206
                    $defaultValue->setValue($signed * (int) $token->image);
6464 206
                    break;
6465 206 View Code Duplication
                case Tokens::T_DNUMBER:
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...
6466 78
                    $token = $this->consumeToken(Tokens::T_DNUMBER);
6467 6
                    $defaultValue->setValue($signed * (double) $token->image);
6468 6
                    break;
6469 6
                case Tokens::T_CONSTANT_ENCAPSED_STRING:
6470 72
                    $token = $this->consumeToken(Tokens::T_CONSTANT_ENCAPSED_STRING);
6471 46
                    $defaultValue->setValue(substr($token->image, 1, -1));
6472 46
                    break;
6473 46
                case Tokens::T_DOUBLE_COLON:
6474 30
                    $this->consumeToken(Tokens::T_DOUBLE_COLON);
6475 6
                    break;
6476 6
                case Tokens::T_CLASS_FQN:
6477 30
                    $this->consumeToken(Tokens::T_CLASS_FQN);
6478
                    break;
6479
                case Tokens::T_PLUS:
6480 30
                    $this->consumeToken(Tokens::T_PLUS);
6481 4
                    break;
6482 4
                case Tokens::T_MINUS:
6483 30
                    $this->consumeToken(Tokens::T_MINUS);
6484 4
                    $signed *= -1;
6485 4
                    break;
6486 4
                case Tokens::T_DOUBLE_QUOTE:
6487 30
                    $defaultValue->setValue($this->parseStringSequence($tokenType));
6488
                    break;
6489
                case Tokens::T_DIR:
6490 30
                case Tokens::T_FILE:
6491 30
                case Tokens::T_LINE:
6492 30
                case Tokens::T_SELF:
6493 30
                case Tokens::T_NS_C:
6494 30
                case Tokens::T_FUNC_C:
6495 30
                case Tokens::T_PARENT:
6496 30
                case Tokens::T_STRING:
6497 30
                case Tokens::T_STATIC:
6498 30
                case Tokens::T_CLASS_C:
6499 30
                case Tokens::T_METHOD_C:
6500 30
                case Tokens::T_BACKSLASH:
6501 30
                case Tokens::T_SQUARED_BRACKET_OPEN:
6502 30
                case Tokens::T_SQUARED_BRACKET_CLOSE:
6503 30
                    // There is a default value but we don't handle it at the moment.
6504
                    $defaultValue->setValue(null);
6505 12
                    $this->consumeToken($tokenType);
6506 12
                    break;
6507 12
                case Tokens::T_START_HEREDOC:
6508 18
                    $defaultValue->setValue(
6509 4
                        $this->parseHeredoc()->getChild(0)->getImage()
6510 4
                    );
6511 4
                    break;
6512 4
                default:
6513 16
                    return $this->parseStaticValueVersionSpecific($defaultValue);
6514 16
            }
6515 16
6516
            $this->consumeComments();
6517 258
6518
            $tokenType = $this->tokenizer->peek();
6519 258
        }
6520 258
6521
        // We should never reach this, so throw an exception
6522
        throw new TokenStreamEndException($this->tokenizer);
6523 2
    }
6524
6525
    /**
6526
     * Parses additional static values that are valid in the supported php version.
6527
     *
6528
     * @param  \PDepend\Source\AST\ASTValue $value
6529
     * @return \PDepend\Source\AST\ASTValue
6530
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
6531
     */
6532
    protected function parseStaticValueVersionSpecific(ASTValue $value)
6533
    {
6534
        $this->throwUnexpectedTokenException();
6535
    }
6536
6537
    /**
6538
     * Checks if the given expression is a read/write variable as defined in
6539
     * the PHP zend_language_parser.y definition.
6540
     *
6541
     * @param \PDepend\Source\AST\ASTNode $expr The context node instance.
6542
     *
6543
     * @return boolean
6544
     * @since 0.10.0
6545
     */
6546
    private function isReadWriteVariable($expr)
6547 164
    {
6548
        return ($expr instanceof \PDepend\Source\AST\ASTVariable
6549
            || $expr instanceof \PDepend\Source\AST\ASTFunctionPostfix
6550 164
            || $expr instanceof \PDepend\Source\AST\ASTVariableVariable
6551 156
            || $expr instanceof \PDepend\Source\AST\ASTCompoundVariable
6552 154
            || $expr instanceof \PDepend\Source\AST\ASTMemberPrimaryPrefix);
6553 164
    }
6554
6555
    /**
6556
     * This method creates a qualified class or interface name based on the
6557
     * current parser state. By default method uses the current namespace scope
6558
     * as prefix for the given local name. And it will fallback to a previously
6559
     * parsed package annotation, when no namespace declaration was parsed.
6560
     *
6561
     * @param string $localName The local class or interface name.
6562
     *
6563
     * @return string
6564
     */
6565
    private function createQualifiedTypeName($localName)
6566 834
    {
6567
        return ltrim($this->getNamespaceOrPackageName() . '\\' . $localName, '\\');
6568 834
    }
6569
6570
    /**
6571
     * Returns the name of a declared names. When the parsed code is not namespaced
6572
     * this method will return the name from the @package annotation.
6573
     *
6574
     * @return string
6575
     * @since 0.9.8
6576
     */
6577
    private function getNamespaceOrPackageName()
6578 834
    {
6579
        if ($this->namespaceName === null) {
6580 834
            return $this->packageName;
6581 746
        }
6582
        return $this->namespaceName;
6583 88
    }
6584
6585
    /**
6586
     * Returns the currently active package or namespace.
6587
     *
6588
     * @return \PDepend\Source\AST\ASTNamespace
6589
     * @since 1.0.0
6590
     */
6591
    private function getNamespaceOrPackage()
6592 834
    {
6593
        $namespace = $this->builder->buildNamespace($this->getNamespaceOrPackageName());
6594 834
        $namespace->setPackageAnnotation(null === $this->namespaceName);
6595 834
6596
        return $namespace;
6597 834
    }
6598
6599
    /**
6600
     * Extracts the @package information from the given comment.
6601
     *
6602
     * @param string $comment
6603
     * @return string
6604
     */
6605
    private function parsePackageAnnotation($comment)
6606 142
    {
6607
        if (getenv('DISMISS_PACKAGES')) {
6608 142
            $this->packageName = null;
6609
            $this->globalPackageName = null;
6610
6611
            return;
6612
        }
6613
6614
        $package = Builder::DEFAULT_NAMESPACE;
6615 142
        if (preg_match('#\*\s*@package\s+(\S+)#', $comment, $match)) {
6616 142
            $package = trim($match[1]);
6617 90
            if (preg_match('#\*\s*@subpackage\s+(\S+)#', $comment, $match)) {
6618 90
                $package .= '\\' . trim($match[1]);
6619 8
            }
6620 8
        }
6621 90
6622
        // Check for doc level comment
6623
        if ($this->globalPackageName === Builder::DEFAULT_NAMESPACE
6624 142
            && $this->isFileComment() === true
6625 142
        ) {
6626 142
            $this->globalPackageName = $package;
6627 12
6628
            $this->compilationUnit->setComment($comment);
6629 12
        }
6630 12
        return $package;
6631 142
    }
6632
6633
    /**
6634
     * Checks that the current token could be used as file comment.
6635
     *
6636
     * This method checks that the previous token is an open tag and the following
6637
     * token is not a class, a interface, final, abstract or a function.
6638
     *
6639
     * @return boolean
6640
     */
6641
    protected function isFileComment()
6642 142
    {
6643
        if ($this->tokenizer->prev() !== Tokens::T_OPEN_TAG) {
6644 142
            return false;
6645 48
        }
6646
6647
        $notExpectedTags = array(
6648
            Tokens::T_CLASS,
6649 136
            Tokens::T_FINAL,
6650 136
            Tokens::T_TRAIT,
6651 136
            Tokens::T_ABSTRACT,
6652 136
            Tokens::T_FUNCTION,
6653 136
            Tokens::T_INTERFACE
6654
        );
6655 136
6656
        return !in_array($this->tokenizer->peek(), $notExpectedTags, true);
6657 136
    }
6658
6659
    /**
6660
     * Returns the class names of all <b>throws</b> annotations with in the
6661
     * given comment block.
6662
     *
6663
     * @param string $comment The context doc comment block.
6664
     *
6665
     * @return array
6666
     */
6667
    private function parseThrowsAnnotations($comment)
6668 1896
    {
6669
        $throws = array();
6670 1896
        if (preg_match_all(self::REGEXP_THROWS_TYPE, $comment, $matches) > 0) {
6671 1896
            foreach ($matches[1] as $match) {
6672 18
                $throws[] = $this->useSymbolTable->lookup($match) ?: $match;
6673 18
            }
6674 18
        }
6675 18
        return $throws;
6676 1896
    }
6677
6678
    /**
6679
     * This method parses the given doc comment text for a return annotation and
6680
     * it returns the found return type.
6681
     *
6682
     * @param string $comment A doc comment text.
6683
     *
6684
     * @return string
6685
     */
6686
    private function parseReturnAnnotation($comment)
6687 1894
    {
6688
        if (0 === preg_match(self::REGEXP_RETURN_TYPE, $comment, $match)) {
6689 1894
            return null;
6690 1842
        }
6691
6692
        foreach (explode('|', end($match)) as $image) {
6693 84
            $image = $this->useSymbolTable->lookup($image) ?: $image;
6694 84
6695
            if (Type::isScalarType($image)) {
6696 84
                continue;
6697 62
            }
6698
6699
            return $image;
6700 32
        }
6701 58
        return null;
6702 58
    }
6703
6704
    /**
6705
     * This method parses the given doc comment text for a var annotation and
6706
     * it returns the found property types.
6707
     *
6708
     * @param  string $comment A doc comment text.
6709
     * @return array(string)
0 ignored issues
show
Documentation introduced by
The doc-type array(string) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
6710
     */
6711
    private function parseVarAnnotation($comment)
6712 118
    {
6713
        if (preg_match(self::REGEXP_VAR_TYPE, $comment, $match) > 0) {
6714 118
            $useSymbolTable = $this->useSymbolTable;
6715 42
6716
            return array_map(
6717 42
                function ($image) use ($useSymbolTable) {
6718 42
                    return $useSymbolTable->lookup($image) ?: $image;
6719 42
                },
6720 42
                array_map('trim', explode('|', end($match)))
6721 42
            );
6722 42
        }
6723
        return array();
6724 76
    }
6725
6726
    /**
6727
     * This method will extract the type information of a property from it's
6728
     * doc comment information. The returned value will be <b>null</b> when no
6729
     * type information exists.
6730
     *
6731
     * @return \PDepend\Source\AST\ASTType
6732
     * @since 0.9.6
6733
     */
6734
    private function parseFieldDeclarationType()
6735 122
    {
6736
        // Skip, if ignore annotations is set
6737
        if ($this->ignoreAnnotations === true) {
6738 122
            return null;
6739 4
        }
6740
6741
        $reference = $this->parseFieldDeclarationClassOrInterfaceReference();
6742 118
        if ($reference !== null) {
6743 118
            return $reference;
6744 20
        }
6745
6746
        $annotations = $this->parseVarAnnotation($this->docComment);
6747 102
        foreach ($annotations as $annotation) {
6748 102
            if (Type::isPrimitiveType($annotation) === true) {
6749 26
                return $this->builder->buildAstScalarType(
6750 22
                    Type::getPrimitiveType($annotation)
6751 22
                );
6752 22
            } elseif (Type::isArrayType($annotation) === true) {
6753 4
                return $this->builder->buildAstTypeArray();
6754 4
            }
6755
        }
6756 76
        return null;
6757 76
    }
6758
6759
    /**
6760
     * Extracts non scalar types from a field doc comment and creates a
6761
     * matching type instance.
6762
     *
6763
     * @return \PDepend\Source\AST\ASTClassOrInterfaceReference
6764
     * @since 0.9.6
6765
     */
6766
    private function parseFieldDeclarationClassOrInterfaceReference()
6767 118
    {
6768
        $annotations = $this->parseVarAnnotation($this->docComment);
6769 118
        foreach ($annotations as $annotation) {
6770 118
            if (Type::isScalarType($annotation) === false) {
6771 42
                return $this->builder->buildAstClassOrInterfaceReference(
6772 20
                    $annotation
6773
                );
6774 20
            }
6775
        }
6776 104
        return null;
6777 102
    }
6778
6779
    /**
6780
     * This method parses a yield-statement node.
6781
     *
6782
     * @return \PDepend\Source\AST\ASTYieldStatmenet
6783
     */
6784
    private function parseYield()
6785 18
    {
6786
        $this->tokenStack->push();
6787 18
6788
        $token = $this->consumeToken(Tokens::T_YIELD);
6789 18
        $this->consumeComments();
6790 18
6791
        $yield = $this->builder->buildAstYieldStatement($token->image);
6792 18
6793
        $node = $this->parseOptionalExpression();
6794 18
        if ($node) {
6795 18
            $yield->addChild($node);
6796 18
6797
            if ($this->tokenizer->peek() === Tokens::T_DOUBLE_ARROW) {
6798 18
                $this->consumeToken(Tokens::T_DOUBLE_ARROW);
6799 8
6800
                $yield->addChild($this->parseOptionalExpression());
6801 8
            }
6802 8
        }
6803 18
6804
        $this->consumeComments();
6805 18
        if (Tokens::T_PARENTHESIS_CLOSE === $this->tokenizer->peek()) {
6806 18
            return $this->setNodePositionsAndReturn($yield);
6807 4
        }
6808
6809
        $this->parseStatementTermination();
6810 14
        return $this->setNodePositionsAndReturn($yield);
6811 14
    }
6812
6813
    /**
6814
     * Extracts documented <b>throws</b> and <b>return</b> types and sets them
6815
     * to the given <b>$callable</b> instance.
6816
     *
6817
     * @param  \PDepend\Source\AST\AbstractASTCallable $callable
6818
     * @return void
6819
     */
6820
    private function prepareCallable(AbstractASTCallable $callable)
6821 1900
    {
6822
        // Skip, if ignore annotations is set
6823
        if ($this->ignoreAnnotations === true) {
6824 1900
            return;
6825 4
        }
6826
6827
        // Get all @throws Types
6828
        $throws = $this->parseThrowsAnnotations($callable->getComment());
6829 1896
        foreach ($throws as $qualifiedName) {
6830 1896
            $callable->addExceptionClassReference(
6831 18
                $this->builder->buildAstClassOrInterfaceReference($qualifiedName)
6832 18
            );
6833 18
        }
6834 1896
6835
        // Stop here if return class already exists.
6836
        if ($callable->hasReturnClass()) {
6837 1896
            return;
6838 2
        }
6839
6840
        // Get return annotation
6841
        $qualifiedName = $this->parseReturnAnnotation($callable->getComment());
6842 1894
        if ($qualifiedName !== null) {
6843 1894
            $callable->setReturnClassReference(
6844 32
                $this->builder->buildAstClassOrInterfaceReference($qualifiedName)
6845 32
            );
6846 32
        }
6847 32
    }
6848 1894
6849
    /**
6850
     * This method will consume the next token in the token stream. It will
6851
     * throw an exception if the type of this token is not identical with
6852
     * <b>$tokenType</b>.
6853
     *
6854
     * @param  integer $tokenType The next expected token type.
6855
     * @return \PDepend\Source\Tokenizer\Token
6856
     * @throws \PDepend\Source\Parser\TokenStreamEndException
6857
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
6858
     */
6859
    protected function consumeToken($tokenType)
6860 2372
    {
6861
        switch ($this->tokenizer->peek()) {
6862 2372
            case $tokenType:
6863 2372
                return $this->tokenStack->add($this->tokenizer->next());
6864 2372
            case Tokenizer::T_EOF:
6865 24
                throw new TokenStreamEndException($this->tokenizer);
6866 8
        }
6867 16
        $this->throwUnexpectedTokenException();
6868 16
    }
6869
6870
    /**
6871
     * This method will consume all comment tokens from the token stream.
6872
     *
6873
     * @return void
6874
     */
6875
    protected function consumeComments()
6876 2372
    {
6877
        $type = $this->tokenizer->peek();
6878 2372
        while ($type == Tokens::T_COMMENT || $type == Tokens::T_DOC_COMMENT) {
6879 2372
            $token = $this->consumeToken($type);
6880 72
            $type  = $this->tokenizer->peek();
6881 72
6882
            if (Tokens::T_COMMENT === $token->type) {
6883 72
                continue;
6884 68
            }
6885
6886
            $this->docComment = $token->image;
6887 6
            if (preg_match('(\s+@package\s+[^\s]+\s+)', $token->image)) {
6888 6
                $this->packageName = $this->parsePackageAnnotation($token->image);
6889
            }
6890
        }
6891 6
    }
6892 2372
6893
    /**
6894
     * Throws an UnexpectedTokenException
6895
     *
6896
     * @param \PDepend\Source\Tokenizer\Token $token
6897
     * @return void
6898
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
6899
     * @since 2.2.5
6900
     */
6901
    protected function throwUnexpectedTokenException(Token $token = null)
6902 36
    {
6903
        throw new UnexpectedTokenException(
6904 36
            (null === $token) ? $this->tokenizer->next() : $token,
6905 36
            $this->tokenizer->getSourceFile()
6906 36
        );
6907 36
    }
6908
}
6909