Completed
Push — master ( 8418b3...91ec6e )
by
unknown
13s queued 11s
created

AbstractPHPParser::getUnexpectedTokenException()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2.1481

Importance

Changes 0
Metric Value
cc 2
nc 1
nop 1
dl 0
loc 7
ccs 2
cts 3
cp 0.6667
crap 2.1481
rs 10
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\Parser\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 2377
    public function __construct(Tokenizer $tokenizer, Builder $builder, CacheDriver $cache)
280
    {
281 2377
        $this->tokenizer = $tokenizer;
282 2377
        $this->builder   = $builder;
283 2377
        $this->cache     = $cache;
284
285 2377
        $this->idBuilder    = new \PDepend\Util\IdBuilder();
286 2377
        $this->tokenStack     = new \PDepend\Source\Parser\TokenStack();
287 2377
        $this->useSymbolTable = new \PDepend\Source\Parser\SymbolTable();
288
289 2377
        $this->builder->setCache($this->cache);
290 2377
    }
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 2375
    protected function getMaxNestingLevel()
323
    {
324 2375
        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 2377
    public function parse()
334
    {
335 2377
        $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 2377
        $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 2377
            ->setCache($this->cache)
338 2377
            ->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 2377
        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 2377
            $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 2377
        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 2375
        $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 2375
        $this->setUpEnvironment();
353
354 2375
        $this->tokenStack->push();
355
356 2375
        Log::debug('Processing file ' . $this->compilationUnit);
357
358 2375
        $tokenType = $this->tokenizer->peek();
359 2375
        while ($tokenType !== Tokenizer::T_EOF) {
360
            switch ($tokenType) {
361 2375
                case Tokens::T_COMMENT:
362 8
                    $this->consumeToken(Tokens::T_COMMENT);
363 8
                    break;
364 2375
                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 2375
                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 2375
                case Tokens::T_NAMESPACE:
376 148
                    $this->parseNamespaceDeclaration();
377 144
                    break;
378 2375
                case Tokens::T_NO_PHP:
379 2375
                case Tokens::T_OPEN_TAG:
380 2375
                case Tokens::T_OPEN_TAG_WITH_ECHO:
381 2375
                    $this->consumeToken($tokenType);
382 2375
                    $this->reset();
383 2375
                    break;
384 2369
                case Tokens::T_CLOSE_TAG:
385 344
                    $this->parseNonePhpCode();
386 344
                    $this->reset();
387 344
                    break;
388 2369
                default:
389 2369
                    if (null === $this->parseOptionalStatement()) {
390
                        // Consume whatever token
391 54
                        $this->consumeToken($tokenType);
392 54
                    }
393 2294
                    break;
394 2369
            }
395
396 2375
            $tokenType = $this->tokenizer->peek();
397 2375
        }
398
399 2294
        $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 2294
        $this->cache->store(
401 2294
            $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 2294
            $this->compilationUnit,
403
            $hash
404 2294
        );
405
406 2294
        $this->tearDownEnvironment();
407 2294
    }
408
409
    /**
410
     * Initializes the parser environment.
411
     *
412
     * @return void
413
     * @since 0.9.12
414
     */
415 2375
    protected function setUpEnvironment()
416
    {
417 2375
        ini_set('xdebug.max_nesting_level', $this->getMaxNestingLevel());
418
419 2375
        $this->useSymbolTable->createScope();
420
421 2375
        $this->reset();
422 2375
    }
423
424
    /**
425
     * Restores the parser environment back.
426
     *
427
     * @throws NoActiveScopeException
428
     *
429
     * @return void
430
     * @since 0.9.12
431
     */
432 2294
    protected function tearDownEnvironment()
433
    {
434 2294
        ini_restore('xdebug.max_nesting_level');
435
436 2294
        $this->useSymbolTable->destroyScope();
437 2294
    }
438
439
    /**
440
     * Resets some object properties.
441
     *
442
     * @param integer $modifiers Optional default modifiers.
443
     *
444
     * @return void
445
     */
446 2375
    protected function reset($modifiers = 0)
447
    {
448 2375
        $this->packageName = Builder::DEFAULT_NAMESPACE;
449 2375
        $this->docComment  = null;
450 2375
        $this->modifiers   = $modifiers;
451 2375
    }
452
453
    /**
454
     * Tests if the given token type is a reserved keyword in the supported PHP
455
     * version.
456
     *
457
     * @param  integer $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 1476 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 1476
        $type = $this->tokenizer->peek();
474
475 1476
        if ($this->isClassName($type)) {
476 1476
            return $this->consumeToken($type)->image;
477 2
        } elseif ($type === Tokenizer::T_EOF) {
478
            throw new TokenStreamEndException($this->tokenizer);
479
        }
480
481 2
        throw $this->getUnexpectedTokenException();
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 1557 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 1557
        $tokenType = $this->tokenizer->peek();
522
523 1557
        if ($this->isFunctionName($tokenType)) {
524 1555
            return $this->consumeToken($tokenType)->image;
525 2
        } elseif ($tokenType === Tokenizer::T_EOF) {
526
            throw new TokenStreamEndException($this->tokenizer);
527
        }
528
529 2
        throw $this->getUnexpectedTokenException();
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 478 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 478
        $tokenType = $this->tokenizer->peek();
585
586 478
        if ($this->isMethodName($tokenType)) {
587 478
            return $this->consumeToken($tokenType)->image;
588
        } elseif ($tokenType === Tokenizer::T_EOF) {
589
            throw new TokenStreamEndException($this->tokenizer);
590
        }
591
592
        throw $this->getUnexpectedTokenException();
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 698 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 698
        $this->tokenStack->push();
712
713 698
        $class = $this->parseClassSignature();
714 698
        $class = $this->parseTypeBody($class);
715 680
        $class->setTokens($this->tokenStack->pop());
716
717 680
        $this->reset();
718
719 680
        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 698
    protected function parseClassSignature()
733
    {
734 698
        $this->parseClassModifiers();
735 698
        $this->consumeToken(Tokens::T_CLASS);
736 698
        $this->consumeComments();
737
738 698
        $qualifiedName = $this->createQualifiedTypeName($this->parseClassName());
739
740 698
        $class = $this->builder->buildClass($qualifiedName);
741 698
        $class->setCompilationUnit($this->compilationUnit);
742 698
        $class->setModifiers($this->modifiers);
743 698
        $class->setComment($this->docComment);
744 698
        $class->setId($this->idBuilder->forClassOrInterface($class));
745 698
        $class->setUserDefined();
746
747 698
        $this->consumeComments();
748 698
        $tokenType = $this->tokenizer->peek();
749
750 698 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 698
        if ($tokenType === Tokens::T_IMPLEMENTS) {
758 72
            $this->consumeToken(Tokens::T_IMPLEMENTS);
759 72
            $this->parseInterfaceList($class);
760 72
        }
761
762 698
        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 698
    private function parseClassModifiers()
772
    {
773 698
        $this->consumeComments();
774 698
        $tokenType = $this->tokenizer->peek();
775
776 698
        if ($tokenType === Tokens::T_ABSTRACT) {
777 40
            $this->consumeToken(Tokens::T_ABSTRACT);
778 40
            $this->modifiers |= State::IS_EXPLICIT_ABSTRACT;
779 698
        } elseif ($tokenType === Tokens::T_FINAL) {
780 6
            $this->consumeToken(Tokens::T_FINAL);
781 6
            $this->modifiers |= State::IS_FINAL;
782 6
        }
783
784 698
        $this->consumeComments();
785 698
    }
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 844
    protected function parseTypeBody(AbstractASTClassOrInterface $classOrInterface)
850
    {
851 844
        $this->classOrInterface = $classOrInterface;
852
853
        // Consume comments and read opening curly brace
854 844
        $this->consumeComments();
855 844
        $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
856
857 844
        $defaultModifier = State::IS_PUBLIC;
858 844
        if ($classOrInterface instanceof ASTInterface) {
859 154
            $defaultModifier |= State::IS_ABSTRACT;
860 154
        }
861 844
        $this->reset();
862
863 844
        $tokenType = $this->tokenizer->peek();
864
865 844
        while ($tokenType !== Tokenizer::T_EOF) {
866
            switch ($tokenType) {
867 844
                case Tokens::T_ABSTRACT:
868 844
                case Tokens::T_PUBLIC:
869 844
                case Tokens::T_PRIVATE:
870 844
                case Tokens::T_PROTECTED:
871 844
                case Tokens::T_STATIC:
872 844
                case Tokens::T_FINAL:
873 844
                case Tokens::T_FUNCTION:
874 844
                case Tokens::T_VARIABLE:
875 844
                case Tokens::T_VAR:
876 526
                    $methodOrProperty = $this->parseMethodOrFieldDeclaration(
877
                        $defaultModifier
878 526
                    );
879
880 514
                    if ($methodOrProperty instanceof \PDepend\Source\AST\ASTNode) {
881 124
                        $classOrInterface->addChild($methodOrProperty);
882 124
                    }
883
884 514
                    $this->reset();
885 514
                    break;
886 832
                case Tokens::T_CONST:
887 98
                    $classOrInterface->addChild($this->parseConstantDefinition());
888 96
                    $this->reset();
889 96
                    break;
890 830
                case Tokens::T_CURLY_BRACE_CLOSE:
891 824
                    $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
892
893 824
                    $this->reset();
894
895
                    // Reset context class or interface instance
896 824
                    $this->classOrInterface = null;
897
898
                    // Stop processing
899 824
                    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
                    throw $this->getUnexpectedTokenException();
933 2
            }
934
935 632
            $tokenType = $this->tokenizer->peek();
936 632
        }
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 526
    private function parseMethodOrFieldDeclaration($modifiers = 0)
950
    {
951 526
        $this->tokenStack->push();
952
953 526
        $tokenType = $this->tokenizer->peek();
954 526
        while ($tokenType !== Tokenizer::T_EOF) {
955
            switch ($tokenType) {
956 526
                case Tokens::T_PRIVATE:
957 98
                    $modifiers |= State::IS_PRIVATE;
958 98
                    $modifiers = $modifiers & ~State::IS_PUBLIC;
959 98
                    break;
960 526
                case Tokens::T_PROTECTED:
961 78
                    $modifiers |= State::IS_PROTECTED;
962 78
                    $modifiers = $modifiers & ~State::IS_PUBLIC;
963 78
                    break;
964 526
                case Tokens::T_VAR:
965 526
                case Tokens::T_PUBLIC:
966 308
                    $modifiers |= State::IS_PUBLIC;
967 308
                    break;
968 526
                case Tokens::T_STATIC:
969 42
                    $modifiers |= State::IS_STATIC;
970 42
                    break;
971 526
                case Tokens::T_ABSTRACT:
972 38
                    $modifiers |= State::IS_ABSTRACT;
973 38
                    break;
974 526
                case Tokens::T_FINAL:
975 30
                    $modifiers |= State::IS_FINAL;
976 30
                    break;
977 526
                case Tokens::T_FUNCTION:
978 446
                    $method = $this->parseMethodDeclaration();
979 436
                    $method->setModifiers($modifiers);
980 436
                    $method->setCompilationUnit($this->compilationUnit);
981 436
                    $method->setId($this->idBuilder->forMethod($method));
982 436
                    $method->setTokens($this->tokenStack->pop());
983
984 436
                    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 378
            $this->consumeToken($tokenType);
995 378
            $this->consumeComments();
996
997 378
            $tokenType = $this->tokenizer->peek();
998 378
        }
999
1000
        throw $this->getUnexpectedTokenException();
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
        throw $this->getUnexpectedTokenException();
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 1557
    private function parseFunctionOrClosureDeclaration()
1077
    {
1078 1557
        $this->tokenStack->push();
1079
1080 1557
        $this->consumeToken(Tokens::T_FUNCTION);
1081 1557
        $this->consumeComments();
1082
1083 1557
        $returnReference = $this->parseOptionalByReference();
1084
1085 1557
        if ($this->isNextTokenFormalParameterList()) {
1086 2
            $callable = $this->parseClosureDeclaration();
1087 2
            return $this->setNodePositionsAndReturn($callable);
1088
        } else {
1089 1557
            $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 1975
    private function parseOptionalByReference()
1115
    {
1116 1975
        if ($this->isNextTokenByReference()) {
1117 40
            return $this->parseByReference();
1118
        }
1119 1969
        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 1975
    private function isNextTokenByReference()
1129
    {
1130 1975
        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 1557
    private function isNextTokenFormalParameterList()
1153
    {
1154 1557
        $this->consumeComments();
1155 1557
        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 1557
    private function parseFunctionDeclaration()
1165
    {
1166 1557
        $this->consumeComments();
1167
1168
        // Next token must be the function identifier
1169 1557
        $functionName = $this->parseFunctionName();
1170
1171 1555
        $function = $this->builder->buildFunction($functionName);
1172 1555
        $function->setCompilationUnit($this->compilationUnit);
1173 1555
        $function->setId($this->idBuilder->forFunction($function));
1174
1175 1555
        $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 446
    private function parseMethodDeclaration()
1205
    {
1206
        // Read function keyword
1207 446
        $this->consumeToken(Tokens::T_FUNCTION);
1208 446
        $this->consumeComments();
1209
1210 446
        $returnsReference = $this->parseOptionalByReference();
1211
1212 446
        $methodName = $this->parseMethodName();
1213
1214 446
        $method = $this->builder->buildMethod($methodName);
1215 446
        $method->setComment($this->docComment);
1216 446
        $method->setCompilationUnit($this->compilationUnit);
1217
1218 446
        $this->classOrInterface->addMethod($method);
1219
1220 446
        $this->parseCallableDeclaration($method);
1221 436
        $this->prepareCallable($method);
1222
1223 436
        if ($returnsReference === true) {
1224 6
            $method->setReturnsReference();
1225 6
        }
1226
1227 436
        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 1965
    private function parseCallableDeclaration(AbstractASTCallable $callable)
1261
    {
1262 1965
        $callable->addChild($this->parseFormalParameters());
1263 1949
        $callable = $this->parseCallableDeclarationAddition($callable);
1264
1265 1949
        $this->consumeComments();
1266 1949
        if ($this->tokenizer->peek() == Tokens::T_CURLY_BRACE_OPEN) {
1267 1917
            $callable->addChild($this->parseScope());
1268 1870
        } else {
1269 64
            $this->consumeToken(Tokens::T_SEMICOLON);
1270
        }
1271 1902
    }
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 168
    private function parseAllocationExpression()
1522
    {
1523 168
        $this->tokenStack->push();
1524
1525 168
        $token = $this->consumeToken(Tokens::T_NEW);
1526
1527 168
        $allocation = $this->builder->buildAstAllocationExpression($token->image);
1528 168
        $allocation = $this->parseAllocationExpressionTypeReference($allocation);
1529
1530 160
        if ($this->isNextTokenArguments()) {
1531 104
            $allocation->addChild($this->parseArguments());
1532 104
        }
1533 160
        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 162
    protected function parseAllocationExpressionTypeReference(ASTAllocationExpression $allocation)
1544
    {
1545 162
        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
    private function parseListExpression()
1609
    {
1610 26
        $this->tokenStack->push();
1611
1612 26
        $token = $this->consumeToken(Tokens::T_LIST);
1613 26
        $this->consumeComments();
1614
1615 26
        $list = $this->builder->buildAstListExpression($token->image);
1616
1617 26
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
1618 26
        $this->consumeComments();
1619
1620 26
        while (($tokenType = $this->tokenizer->peek()) !== Tokenizer::T_EOF) {
1621
            // The variable is optional:
1622
            //   list(, , , , $something) = ...;
1623
            // is valid.
1624
            switch ($tokenType) {
1625 26
                case Tokens::T_COMMA:
1626 22
                    $this->consumeToken(Tokens::T_COMMA);
1627 22
                    $this->consumeComments();
1628 22
                    break;
1629 26
                case Tokens::T_PARENTHESIS_CLOSE:
1630 26
                    break 2;
1631 24
                case Tokens::T_LIST:
1632 2
                    $list->addChild($this->parseListExpression());
1633 2
                    $this->consumeComments();
1634 2
                    break;
1635 24
                default:
1636 24
                    $list->addChild($this->parseVariableOrConstantOrPrimaryPrefix());
1637 24
                    $this->consumeComments();
1638 24
                    break;
1639 24
            }
1640 24
        }
1641
1642 26
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
1643
1644 26
        return $this->setNodePositionsAndReturn($list);
1645
    }
1646
1647
    /**
1648
     * Parses a include-expression node.
1649
     *
1650
     * @return \PDepend\Source\AST\ASTIncludeExpression
1651
     * @since 0.9.12
1652
     */
1653 16
    private function parseIncludeExpression()
1654
    {
1655 16
        $expr = $this->builder->buildAstIncludeExpression();
1656
1657 16
        return $this->parseRequireOrIncludeExpression($expr, Tokens::T_INCLUDE);
1658
    }
1659
1660
    /**
1661
     * Parses a include_once-expression node.
1662
     *
1663
     * @return \PDepend\Source\AST\ASTIncludeExpression
1664
     * @since 0.9.12
1665
     */
1666 2
    private function parseIncludeOnceExpression()
1667
    {
1668 2
        $expr = $this->builder->buildAstIncludeExpression();
1669 2
        $expr->setOnce();
1670
1671 2
        return $this->parseRequireOrIncludeExpression($expr, Tokens::T_INCLUDE_ONCE);
1672
    }
1673
1674
    /**
1675
     * Parses a require-expression node.
1676
     *
1677
     * @return \PDepend\Source\AST\ASTRequireExpression
1678
     * @since 0.9.12
1679
     */
1680 4
    private function parseRequireExpression()
1681
    {
1682 4
        $expr = $this->builder->buildAstRequireExpression();
1683
1684 4
        return $this->parseRequireOrIncludeExpression($expr, Tokens::T_REQUIRE);
1685
    }
1686
1687
    /**
1688
     * Parses a require_once-expression node.
1689
     *
1690
     * @return \PDepend\Source\AST\ASTRequireExpression
1691
     * @since 0.9.12
1692
     */
1693 2
    private function parseRequireOnceExpression()
1694
    {
1695 2
        $expr = $this->builder->buildAstRequireExpression();
1696 2
        $expr->setOnce();
1697
1698 2
        return $this->parseRequireOrIncludeExpression($expr, Tokens::T_REQUIRE_ONCE);
1699
    }
1700
1701
    /**
1702
     * Parses a <b>require_once</b>-, <b>require</b>-, <b>include_once</b>- or
1703
     * <b>include</b>-expression node.
1704
     *
1705
     * @param  \PDepend\Source\AST\ASTExpression $expr
1706
     * @param  integer                           $type
1707
     * @return \PDepend\Source\AST\ASTExpression
1708
     * @since 0.9.12
1709
     */
1710 24 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...
1711
    {
1712 24
        $this->tokenStack->push();
1713
1714 24
        $this->consumeToken($type);
1715 24
        $this->consumeComments();
1716
1717 24
        if ($this->tokenizer->peek() === Tokens::T_PARENTHESIS_OPEN) {
1718 10
            $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
1719 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...
1720 10
            $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
1721 10
        } else {
1722 14
            $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...
1723
        }
1724
1725 24
        return $this->setNodePositionsAndReturn($expr);
1726
    }
1727
1728
    /**
1729
     * Parses a cast-expression node.
1730
     *
1731
     * @return \PDepend\Source\AST\ASTCastExpression
1732
     * @since 0.10.0
1733
     */
1734 38 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...
1735
    {
1736 38
        $token = $this->consumeToken($this->tokenizer->peek());
1737
1738 38
        $expr = $this->builder->buildAstCastExpression($token->image);
1739 38
        $expr->configureLinesAndColumns(
1740 38
            $token->startLine,
1741 38
            $token->endLine,
1742 38
            $token->startColumn,
1743 38
            $token->endColumn
1744 38
        );
1745
1746 38
        return $expr;
1747
    }
1748
1749
    /**
1750
     * This method will parse an increment-expression. Depending on the previous
1751
     * node this can be a {@link \PDepend\Source\AST\ASTPostIncrementExpression} or
1752
     * {@link \PDepend\Source\AST\ASTPostfixExpression}.
1753
     *
1754
     * @param  array $expressions List of previous parsed expression nodes.
1755
     * @return \PDepend\Source\AST\ASTExpression
1756
     * @since 0.10.0
1757
     */
1758 136
    private function parseIncrementExpression(array &$expressions)
1759
    {
1760 136
        if ($this->isReadWriteVariable(end($expressions))) {
1761 28
            return $this->parsePostIncrementExpression(array_pop($expressions));
1762
        }
1763 116
        return $this->parsePreIncrementExpression();
1764
    }
1765
1766
    /**
1767
     * Parses a post increment-expression and adds the given child to that node.
1768
     *
1769
     * @param \PDepend\Source\AST\ASTNode $child The child expression node.
1770
     *
1771
     * @return \PDepend\Source\AST\ASTPostfixExpression
1772
     * @since 0.10.0
1773
     */
1774 28 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...
1775
    {
1776 28
        $token = $this->consumeToken(Tokens::T_INC);
1777
1778 28
        $expr = $this->builder->buildAstPostfixExpression($token->image);
1779 28
        $expr->addChild($child);
1780 28
        $expr->configureLinesAndColumns(
1781 28
            $child->getStartLine(),
1782 28
            $token->endLine,
1783 28
            $child->getStartColumn(),
1784 28
            $token->endColumn
1785 28
        );
1786
1787 28
        return $expr;
1788
    }
1789
1790
    /**
1791
     * Parses a pre increment-expression and adds the given child to that node.
1792
     *
1793
     * @return \PDepend\Source\AST\ASTPreIncrementExpression
1794
     * @since 0.10.0
1795
     */
1796 116 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...
1797
    {
1798 116
        $token = $this->consumeToken(Tokens::T_INC);
1799
1800 116
        $expr = $this->builder->buildAstPreIncrementExpression();
1801 116
        $expr->configureLinesAndColumns(
1802 116
            $token->startLine,
1803 116
            $token->endLine,
1804 116
            $token->startColumn,
1805 116
            $token->endColumn
1806 116
        );
1807
1808 116
        return $expr;
1809
    }
1810
1811
    /**
1812
     * This method will parse an decrement-expression. Depending on the previous
1813
     * node this can be a {@link \PDepend\Source\AST\ASTPostDecrementExpression} or
1814
     * {@link \PDepend\Source\AST\ASTPostfixExpression}.
1815
     *
1816
     * @param array $expressions List of previous parsed expression nodes.
1817
     *
1818
     * @return \PDepend\Source\AST\ASTExpression
1819
     * @since 0.10.0
1820
     */
1821 66
    private function parseDecrementExpression(array &$expressions)
1822
    {
1823 66
        if ($this->isReadWriteVariable(end($expressions))) {
1824 10
            return $this->parsePostDecrementExpression(array_pop($expressions));
1825
        }
1826 56
        return $this->parsePreDecrementExpression();
1827
    }
1828
1829
    /**
1830
     * Parses a post decrement-expression and adds the given child to that node.
1831
     *
1832
     * @param \PDepend\Source\AST\ASTNode $child The child expression node.
1833
     *
1834
     * @return \PDepend\Source\AST\ASTPostfixExpression
1835
     * @since 0.10.0
1836
     */
1837 10 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...
1838
    {
1839 10
        $token = $this->consumeToken(Tokens::T_DEC);
1840
1841 10
        $expr = $this->builder->buildAstPostfixExpression($token->image);
1842 10
        $expr->addChild($child);
1843 10
        $expr->configureLinesAndColumns(
1844 10
            $child->getStartLine(),
1845 10
            $token->endLine,
1846 10
            $child->getStartColumn(),
1847 10
            $token->endColumn
1848 10
        );
1849
1850 10
        return $expr;
1851
    }
1852
1853
    /**
1854
     * Parses a pre decrement-expression and adds the given child to that node.
1855
     *
1856
     * @return \PDepend\Source\AST\ASTPreDecrementExpression
1857
     * @since 0.10.0
1858
     */
1859 56 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...
1860
    {
1861 56
        $token = $this->consumeToken(Tokens::T_DEC);
1862
1863 56
        $expr = $this->builder->buildAstPreDecrementExpression();
1864 56
        $expr->configureLinesAndColumns(
1865 56
            $token->startLine,
1866 56
            $token->endLine,
1867 56
            $token->startColumn,
1868 56
            $token->endColumn
1869 56
        );
1870
1871 56
        return $expr;
1872
    }
1873
1874
    /**
1875
     * Parses one or more optional php <b>array</b> or <b>string</b> expressions.
1876
     *
1877
     * <code>
1878
     * ---------
1879
     * $array[0];
1880
     * ---------
1881
     *
1882
     * ----------------
1883
     * $array[1]['foo'];
1884
     * ----------------
1885
     *
1886
     * ----------------
1887
     * $string{1}[0]{0};
1888
     * ----------------
1889
     * </code>
1890
     *
1891
     * @param \PDepend\Source\AST\ASTNode $node The parent/context node instance.
1892
     *
1893
     * @return \PDepend\Source\AST\ASTNode
1894
     * @since 0.9.12
1895
     */
1896 1234
    protected function parseOptionalIndexExpression(ASTNode $node)
1897
    {
1898 1234
        $this->consumeComments();
1899
1900 1234
        switch ($this->tokenizer->peek()) {
1901 1234
            case Tokens::T_CURLY_BRACE_OPEN:
1902 4
                return $this->parseStringIndexExpression($node);
1903 1234
            case Tokens::T_SQUARED_BRACKET_OPEN:
1904 116
                return $this->parseArrayIndexExpression($node);
1905 1234
        }
1906
1907 1234
        return $node;
1908
    }
1909
1910
    /**
1911
     * Parses an index expression as it is valid to access elements in a php
1912
     * string or array.
1913
     *
1914
     * @param \PDepend\Source\AST\ASTNode       $node  The context source node.
1915
     * @param \PDepend\Source\AST\ASTExpression $expr  The concrete index expression.
1916
     * @param integer                           $open  The open token type.
1917
     * @param integer                           $close The close token type.
1918
     *
1919
     * @return \PDepend\Source\AST\ASTNode
1920
     * @since 0.9.12
1921
     */
1922 118
    private function parseIndexExpression(
1923
        \PDepend\Source\AST\ASTNode $node,
1924
        \PDepend\Source\AST\ASTExpression $expr,
1925
        $open,
1926
        $close
1927
    ) {
1928 118
        $this->consumeToken($open);
1929
1930 118
        if (($child = $this->parseOptionalExpression()) != null) {
1931 116
            $expr->addChild($child);
1932 116
        }
1933
1934 118
        $token = $this->consumeToken($close);
1935
1936 118
        $expr->configureLinesAndColumns(
1937 118
            $node->getStartLine(),
1938 118
            $token->endLine,
1939 118
            $node->getStartColumn(),
1940 118
            $token->endColumn
1941 118
        );
1942
1943 118
        return $this->parseOptionalIndexExpression($expr);
1944
    }
1945
1946
    /**
1947
     * Parses a mandatory array index expression.
1948
     *
1949
     * <code>
1950
     * //    ---
1951
     * $array[0];
1952
     * //    ---
1953
     * </code>
1954
     *
1955
     * @param \PDepend\Source\AST\ASTNode $node The context source node.
1956
     *
1957
     * @return \PDepend\Source\AST\ASTArrayIndexExpression
1958
     * @since 0.9.12
1959
     */
1960 116 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...
1961
    {
1962 116
        $expr = $this->builder->buildAstArrayIndexExpression();
1963 116
        $expr->addChild($node);
1964
1965 116
        return $this->parseIndexExpression(
1966 116
            $node,
1967 116
            $expr,
1968 116
            Tokens::T_SQUARED_BRACKET_OPEN,
1969
            Tokens::T_SQUARED_BRACKET_CLOSE
1970 116
        );
1971
    }
1972
1973
    /**
1974
     * Parses a mandatory array index expression.
1975
     *
1976
     * <code>
1977
     * //     ---
1978
     * $string{0};
1979
     * //     ---
1980
     * </code>
1981
     *
1982
     * @param \PDepend\Source\AST\ASTNode $node The context source node.
1983
     *
1984
     * @return \PDepend\Source\AST\ASTStringIndexExpression
1985
     * @since 0.9.12
1986
     */
1987 4 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...
1988
    {
1989 4
        $expr = $this->builder->buildAstStringIndexExpression();
1990 4
        $expr->addChild($node);
1991
1992 4
        return $this->parseIndexExpression(
1993 4
            $node,
1994 4
            $expr,
1995 4
            Tokens::T_CURLY_BRACE_OPEN,
1996
            Tokens::T_CURLY_BRACE_CLOSE
1997 4
        );
1998
    }
1999
2000
    /**
2001
     * This method checks if the next available token starts an arguments node.
2002
     *
2003
     * @return boolean
2004
     * @since 0.9.8
2005
     */
2006 160
    protected function isNextTokenArguments()
2007
    {
2008 160
        $this->consumeComments();
2009 160
        return $this->tokenizer->peek() === Tokens::T_PARENTHESIS_OPEN;
2010
    }
2011
2012
    /**
2013
     * This method configures the given node with its start and end positions.
2014
     *
2015
     * @param \PDepend\Source\AST\ASTNode $node
2016
     * @param array|null $tokens
2017
     * @return \PDepend\Source\AST\ASTNode
2018
     * @since 0.9.8
2019
     */
2020 2289
    protected function setNodePositionsAndReturn(ASTNode $node, array &$tokens = null)
2021
    {
2022 2289
        $tokens = $this->stripTrailingComments($this->tokenStack->pop());
2023
2024 2289
        $end   = $tokens[count($tokens) - 1];
2025 2289
        $start = $tokens[0];
2026
2027 2289
        $node->configureLinesAndColumns(
2028 2289
            $start->startLine,
2029 2289
            $end->endLine,
2030 2289
            $start->startColumn,
2031 2289
            $end->endColumn
2032 2289
        );
2033
2034 2289
        return $node;
2035
    }
2036
2037
    /**
2038
     * Strips all trailing comments from the given token stream.
2039
     *
2040
     * @param  Token[] $tokens Original token stream.
2041
     * @return Token[]
2042
     * @since 1.0.0
2043
     */
2044 2289
    private function stripTrailingComments(array $tokens)
2045
    {
2046 2289
        $comments = array(Tokens::T_COMMENT, Tokens::T_DOC_COMMENT);
2047
2048 2289
        while (count($tokens) > 1 && in_array(end($tokens)->type, $comments)) {
2049 42
            array_pop($tokens);
2050 42
        }
2051 2289
        return $tokens;
2052
    }
2053
2054
    /**
2055
     * This method parse an instance of expression with its associated class or
2056
     * interface reference.
2057
     *
2058
     * <code>
2059
     *          ----------------
2060
     * ($object instanceof Clazz);
2061
     *          ----------------
2062
     *
2063
     *          ------------------------
2064
     * ($object instanceof Clazz::$clazz);
2065
     *          ------------------------
2066
     *
2067
     *          -----------------
2068
     * ($object instanceof $clazz);
2069
     *          -----------------
2070
     *
2071
     *          -----------------------
2072
     * ($object instanceof $clazz->type);
2073
     *          -----------------------
2074
     *
2075
     *          -----------------------------
2076
     * ($object instanceof static|self|parent);
2077
     *          -----------------------------
2078
     * </code>
2079
     *
2080
     * @return \PDepend\Source\AST\ASTInstanceOfExpression
2081
     * @since 0.9.6
2082
     */
2083 26
    private function parseInstanceOfExpression()
2084
    {
2085
        // Consume the "instanceof" keyword and strip comments
2086 26
        $token = $this->consumeToken(Tokens::T_INSTANCEOF);
2087
2088 26
        return $this->parseExpressionTypeReference(
2089 26
            $this->builder->buildAstInstanceOfExpression($token->image),
2090
            false
2091 26
        );
2092
    }
2093
2094
    /**
2095
     * Parses an isset-expression node.
2096
     *
2097
     * <code>
2098
     * //  -----------
2099
     * if (isset($foo)) {
2100
     * //  -----------
2101
     * }
2102
     *
2103
     * //  -----------------------
2104
     * if (isset($foo, $bar, $baz)) {
2105
     * //  -----------------------
2106
     * }
2107
     * </code>
2108
     *
2109
     * @return \PDepend\Source\AST\ASTIssetExpression
2110
     * @since 0.9.12
2111
     */
2112 18
    private function parseIssetExpression()
2113
    {
2114 18
        $startToken = $this->consumeToken(Tokens::T_ISSET);
2115 18
        $this->consumeComments();
2116 18
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
2117
2118 18
        $expr = $this->builder->buildAstIssetExpression();
2119 18
        $expr = $this->parseVariableList($expr);
2120
2121 18
        $stopToken = $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
2122
2123 18
        $expr->configureLinesAndColumns(
2124 18
            $startToken->startLine,
2125 18
            $stopToken->endLine,
2126 18
            $startToken->startColumn,
2127 18
            $stopToken->endColumn
2128 18
        );
2129
2130 18
        return $expr;
2131
    }
2132
2133
    /**
2134
     * This method parses a type identifier as it is used in expression nodes
2135
     * like {@link \PDepend\Source\AST\ASTInstanceOfExpression} or an object
2136
     * allocation node like {@link \PDepend\Source\AST\ASTAllocationExpression}.
2137
     *
2138
     * @param \PDepend\Source\AST\ASTNode $expr
2139
     * @param boolean $classRef
2140
     * @return \PDepend\Source\AST\ASTNode
2141
     */
2142 188
    protected function parseExpressionTypeReference(ASTNode $expr, $classRef)
2143
    {
2144
        // Peek next token and look for a static type identifier
2145 188
        $this->consumeComments();
2146 188
        $tokenType = $this->tokenizer->peek();
2147
2148
        switch ($tokenType) {
2149 188
            case Tokens::T_DOLLAR:
2150 188
            case Tokens::T_VARIABLE:
2151
                // TODO: Parse variable or Member Primary Prefix + Property Postfix
2152 10
                $ref = $this->parseVariableOrFunctionPostfixOrMemberPrimaryPrefix();
2153 10
                break;
2154 178
            case Tokens::T_SELF:
2155 8
                $ref = $this->parseSelfReference($this->consumeToken(Tokens::T_SELF));
2156 6
                break;
2157 170
            case Tokens::T_PARENT:
2158 14
                $ref = $this->parseParentReference($this->consumeToken(Tokens::T_PARENT));
2159 12
                break;
2160 156
            case Tokens::T_STATIC:
2161 10
                $ref = $this->parseStaticReference($this->consumeToken(Tokens::T_STATIC));
2162 8
                break;
2163 146
            default:
2164 146
                $ref = $this->parseClassOrInterfaceReference($classRef);
2165 144
                break;
2166 146
        }
2167
2168 180
        $expr->addChild(
2169 180
            $this->parseOptionalMemberPrimaryPrefix(
2170 180
                $this->parseOptionalStaticMemberPrimaryPrefix($ref)
2171 180
            )
2172 180
        );
2173
2174 180
        return $expr;
2175
    }
2176
2177
    /**
2178
     * This method parses a conditional-expression.
2179
     *
2180
     * <code>
2181
     *         --------------
2182
     * $foo = ($bar ? 42 : 23);
2183
     *         --------------
2184
     * </code>
2185
     *
2186
     * @return \PDepend\Source\AST\ASTConditionalExpression
2187
     * @since 0.9.8
2188
     */
2189 14 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...
2190
    {
2191 14
        $this->tokenStack->push();
2192 14
        $this->consumeToken(Tokens::T_QUESTION_MARK);
2193
2194 14
        $expr = $this->builder->buildAstConditionalExpression();
2195 14
        if (($child = $this->parseOptionalExpression()) != null) {
2196 14
            $expr->addChild($child);
2197 14
        }
2198
2199 14
        $this->consumeToken(Tokens::T_COLON);
2200
2201 14
        $expr->addChild($this->parseExpression());
2202
2203 14
        return $this->setNodePositionsAndReturn($expr);
2204
    }
2205
2206
    /**
2207
     * This method parses a shift left expression node.
2208
     *
2209
     * @return \PDepend\Source\AST\ASTShiftLeftExpression
2210
     * @since 1.0.1
2211
     */
2212 8 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...
2213
    {
2214 8
        $token = $this->consumeToken(Tokens::T_SL);
2215
2216 8
        $expr = $this->builder->buildAstShiftLeftExpression();
2217 8
        $expr->configureLinesAndColumns(
2218 8
            $token->startLine,
2219 8
            $token->endLine,
2220 8
            $token->startColumn,
2221 8
            $token->endColumn
2222 8
        );
2223 8
        return $expr;
2224
    }
2225
2226
    /**
2227
     * This method parses a shift right expression node.
2228
     *
2229
     * @return \PDepend\Source\AST\ASTShiftRightExpression
2230
     * @since 1.0.1
2231
     */
2232 8 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...
2233
    {
2234 8
        $token = $this->consumeToken(Tokens::T_SR);
2235
2236 8
        $expr = $this->builder->buildAstShiftRightExpression();
2237 8
        $expr->configureLinesAndColumns(
2238 8
            $token->startLine,
2239 8
            $token->endLine,
2240 8
            $token->startColumn,
2241 8
            $token->endColumn
2242 8
        );
2243 8
        return $expr;
2244
    }
2245
2246
    /**
2247
     * This method parses a boolean and-expression.
2248
     *
2249
     * @return \PDepend\Source\AST\ASTBooleanAndExpression
2250
     * @since 0.9.8
2251
     */
2252 38 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...
2253
    {
2254 38
        $token = $this->consumeToken(Tokens::T_BOOLEAN_AND);
2255
2256 38
        $expr = $this->builder->buildAstBooleanAndExpression();
2257 38
        $expr->configureLinesAndColumns(
2258 38
            $token->startLine,
2259 38
            $token->endLine,
2260 38
            $token->startColumn,
2261 38
            $token->endColumn
2262 38
        );
2263 38
        return $expr;
2264
    }
2265
2266
    /**
2267
     * This method parses a boolean or-expression.
2268
     *
2269
     * @return \PDepend\Source\AST\ASTBooleanOrExpression
2270
     * @since 0.9.8
2271
     */
2272 52 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...
2273
    {
2274 52
        $token = $this->consumeToken(Tokens::T_BOOLEAN_OR);
2275
2276 52
        $expr = $this->builder->buildAstBooleanOrExpression();
2277 52
        $expr->configureLinesAndColumns(
2278 52
            $token->startLine,
2279 52
            $token->endLine,
2280 52
            $token->startColumn,
2281 52
            $token->endColumn
2282 52
        );
2283 52
        return $expr;
2284
    }
2285
2286
    /**
2287
     * This method parses a logical <b>and</b>-expression.
2288
     *
2289
     * @return \PDepend\Source\AST\ASTLogicalAndExpression
2290
     * @since 0.9.8
2291
     */
2292 8 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...
2293
    {
2294 8
        $token = $this->consumeToken(Tokens::T_LOGICAL_AND);
2295
2296 8
        $expr = $this->builder->buildAstLogicalAndExpression();
2297 8
        $expr->configureLinesAndColumns(
2298 8
            $token->startLine,
2299 8
            $token->endLine,
2300 8
            $token->startColumn,
2301 8
            $token->endColumn
2302 8
        );
2303 8
        return $expr;
2304
    }
2305
2306
    /**
2307
     * This method parses a logical <b>or</b>-expression.
2308
     *
2309
     * @return \PDepend\Source\AST\ASTLogicalOrExpression
2310
     * @since 0.9.8
2311
     */
2312 8 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...
2313
    {
2314 8
        $token = $this->consumeToken(Tokens::T_LOGICAL_OR);
2315
2316 8
        $expr = $this->builder->buildAstLogicalOrExpression();
2317 8
        $expr->configureLinesAndColumns(
2318 8
            $token->startLine,
2319 8
            $token->endLine,
2320 8
            $token->startColumn,
2321 8
            $token->endColumn
2322 8
        );
2323 8
        return $expr;
2324
    }
2325
2326
    /**
2327
     * This method parses a logical <b>xor</b>-expression.
2328
     *
2329
     * @return \PDepend\Source\AST\ASTLogicalXorExpression
2330
     * @since 0.9.8
2331
     */
2332 8 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...
2333
    {
2334 8
        $token = $this->consumeToken(Tokens::T_LOGICAL_XOR);
2335
2336 8
        $expr = $this->builder->buildAstLogicalXorExpression();
2337 8
        $expr->configureLinesAndColumns(
2338 8
            $token->startLine,
2339 8
            $token->endLine,
2340 8
            $token->startColumn,
2341 8
            $token->endColumn
2342 8
        );
2343 8
        return $expr;
2344
    }
2345
2346
    /**
2347
     * Parses a class or interface reference node.
2348
     *
2349
     * @param boolean $classReference Force a class reference.
2350
     *
2351
     * @return \PDepend\Source\AST\ASTClassOrInterfaceReference
2352
     * @since 0.9.8
2353
     */
2354 146
    private function parseClassOrInterfaceReference($classReference)
2355
    {
2356 146
        $this->tokenStack->push();
2357
2358 146
        if ($classReference === true) {
2359 122
            return $this->setNodePositionsAndReturn(
2360 122
                $this->builder->buildAstClassReference(
2361 122
                    $this->parseQualifiedName()
2362 120
                )
2363 120
            );
2364
        }
2365 24
        return $this->setNodePositionsAndReturn(
2366 24
            $this->builder->buildAstClassOrInterfaceReference(
2367 24
                $this->parseQualifiedName()
2368 24
            )
2369 24
        );
2370
    }
2371
2372
    /**
2373
     * This method parses a brace expression and adds all parsed node instances
2374
     * to the given {@link \PDepend\Source\AST\ASTNode} object. Finally it returns
2375
     * the prepared input node.
2376
     *
2377
     * A brace expression can be a compound:
2378
     *
2379
     * <code>
2380
     * $this->{$foo ? 'foo' : 'bar'}();
2381
     * </code>
2382
     *
2383
     * or a parameter list:
2384
     *
2385
     * <code>
2386
     * $this->foo($bar, $baz);
2387
     * </code>
2388
     *
2389
     * or an array index:
2390
     *
2391
     * <code>
2392
     * $foo[$bar];
2393
     * </code>
2394
     *
2395
     * @param  \PDepend\Source\AST\ASTNode     $node
2396
     * @param  \PDepend\Source\Tokenizer\Token $start
2397
     * @param  integer                         $closeToken
2398
     * @return \PDepend\Source\AST\ASTNode
2399
     * @throws \PDepend\Source\Parser\TokenStreamEndException
2400
     * @since 0.9.6
2401
     */
2402 508
    protected function parseBraceExpression(
2403
        ASTNode $node,
2404
        Token $start,
2405
        $closeToken
2406
    ) {
2407 508
        if (is_object($expr = $this->parseOptionalExpression())) {
2408 504
            $node->addChild($expr);
2409 504
        }
2410
2411 508
        $end = $this->consumeToken($closeToken);
2412
2413 508
        $node->configureLinesAndColumns(
2414 508
            $start->startLine,
2415 508
            $end->endLine,
2416 508
            $start->startColumn,
2417 508
            $end->endColumn
2418 508
        );
2419 508
        return $node;
2420
    }
2421
2422
    /**
2423
     * Parses the body of the given statement instance and adds all parsed nodes
2424
     * to that statement.
2425
     *
2426
     * @param \PDepend\Source\AST\ASTStatement $stmt The owning statement.
2427
     *
2428
     * @return \PDepend\Source\AST\ASTStatement
2429
     * @since 0.9.12
2430
     */
2431 396
    private function parseStatementBody(\PDepend\Source\AST\ASTStatement $stmt)
2432
    {
2433 396
        $this->consumeComments();
2434 396
        $tokenType = $this->tokenizer->peek();
2435
2436 396
        if ($tokenType === Tokens::T_CURLY_BRACE_OPEN) {
2437 308
            $stmt->addChild($this->parseRegularScope());
2438 396
        } elseif ($tokenType === Tokens::T_COLON) {
2439 70
            $stmt->addChild($this->parseAlternativeScope());
2440 70
        } else {
2441 28
            $stmt->addChild($this->parseStatement());
2442
        }
2443 394
        return $stmt;
2444
    }
2445
2446
    /**
2447
     * Parse a scope enclosed by curly braces.
2448
     *
2449
     * @return \PDepend\Source\AST\ASTScope
2450
     * @since 0.9.12
2451
     */
2452 368
    private function parseRegularScope()
2453
    {
2454 368
        $this->tokenStack->push();
2455
2456 368
        $this->consumeComments();
2457 368
        $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
2458
2459 368
        $scope = $this->parseScopeStatements();
2460
2461 368
        $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
2462 368
        return $this->setNodePositionsAndReturn($scope);
2463
    }
2464
2465
    /**
2466
     * Parses the scope of a statement that is surrounded with PHP's alternative
2467
     * syntax for statements.
2468
     *
2469
     * @return \PDepend\Source\AST\ASTScopeStatement
2470
     * @since 0.10.0
2471
     */
2472 70
    private function parseAlternativeScope()
2473
    {
2474 70
        $this->tokenStack->push();
2475 70
        $this->consumeToken(Tokens::T_COLON);
2476
2477 70
        $scope = $this->parseScopeStatements();
2478
2479 70
        $this->parseOptionalAlternativeScopeTermination();
2480 70
        return $this->setNodePositionsAndReturn($scope);
2481
    }
2482
2483
    /**
2484
     * Parses all statements that exist in a scope an adds them to a scope
2485
     * instance.
2486
     *
2487
     * @return \PDepend\Source\AST\ASTScopeStatement
2488
     * @since 0.10.0
2489
     */
2490 430
    private function parseScopeStatements()
2491
    {
2492 430
        $scope = $this->builder->buildAstScopeStatement();
2493 430
        while (($child = $this->parseOptionalStatement()) != null) {
2494 246
            if ($child instanceof \PDepend\Source\AST\ASTNode) {
2495 240
                $scope->addChild($child);
2496 240
            }
2497 246
        }
2498 430
        return $scope;
2499
    }
2500
2501
    /**
2502
     * Parses the termination of a scope statement that uses PHP's laternative
2503
     * syntax format.
2504
     *
2505
     * @return void
2506
     * @since 0.10.0
2507
     */
2508 70
    private function parseOptionalAlternativeScopeTermination()
2509
    {
2510 70
        $tokenType = $this->tokenizer->peek();
2511 70
        if ($this->isAlternativeScopeTermination($tokenType)) {
2512 70
            $this->parseAlternativeScopeTermination($tokenType);
2513 70
        }
2514 70
    }
2515
2516
2517
    /**
2518
     * This method returns <b>true</b> when the given token identifier represents
2519
     * the end token of a alternative scope termination symbol. Otherwise this
2520
     * method will return <b>false</b>.
2521
     *
2522
     * @param integer $tokenType The token type identifier.
2523
     *
2524
     * @return boolean
2525
     * @since 0.10.0
2526
     */
2527 70
    private function isAlternativeScopeTermination($tokenType)
2528
    {
2529 70
        return in_array(
2530 70
            $tokenType,
2531
            array(
2532 70
                Tokens::T_ENDDECLARE,
2533 70
                Tokens::T_ENDFOR,
2534 70
                Tokens::T_ENDFOREACH,
2535 70
                Tokens::T_ENDIF,
2536 70
                Tokens::T_ENDSWITCH,
2537
                Tokens::T_ENDWHILE
2538 70
            )
2539 70
        );
2540
    }
2541
2542
    /**
2543
     * Parses a series of tokens that represent an alternative scope termination.
2544
     *
2545
     * @param integer $tokenType The token type identifier.
2546
     *
2547
     * @return void
2548
     * @since 0.10.0
2549
     */
2550 78 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...
2551
    {
2552 78
        $this->consumeToken($tokenType);
2553 78
        $this->consumeComments();
2554
2555 78
        if ($this->tokenizer->peek() === Tokens::T_SEMICOLON) {
2556 68
            $this->consumeToken(Tokens::T_SEMICOLON);
2557 68
        } else {
2558 10
            $this->parseNonePhpCode();
2559
        }
2560 78
    }
2561
2562
    /**
2563
     * This method parses multiple expressions and adds them as children to the
2564
     * given <b>$exprList</b> node.
2565
     *
2566
     * @param \PDepend\Source\AST\ASTNode $exprList
2567
     * @return \PDepend\Source\AST\ASTNode
2568
     * @since 1.0.0
2569
     */
2570 268 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...
2571
    {
2572 268
        $this->consumeComments();
2573 268
        while ($expr = $this->parseOptionalExpression()) {
2574 268
            $exprList->addChild($expr);
2575
2576 268
            $this->consumeComments();
2577 268
            if (Tokens::T_COMMA === $this->tokenizer->peek()) {
2578 138
                $this->consumeToken(Tokens::T_COMMA);
2579 138
                $this->consumeComments();
2580 138
            } else {
2581 268
                break;
2582
            }
2583 138
        }
2584
2585 268
        return $exprList;
2586
    }
2587
2588
    /**
2589
     * This method parses an expression node and returns it. When no expression
2590
     * was found this method will throw an InvalidStateException.
2591
     *
2592
     * @return \PDepend\Source\AST\ASTNode
2593
     * @throws \PDepend\Source\Parser\ParserException
2594
     * @since 1.0.1
2595
     */
2596 324
    private function parseExpression()
2597
    {
2598 324
        if (null === ($expr = $this->parseOptionalExpression())) {
2599
            $token = $this->consumeToken($this->tokenizer->peek());
2600
2601
            throw new InvalidStateException(
2602
                $token->startLine,
2603
                $this->compilationUnit->getFileName(),
2604
                'Mandatory expression expected.'
2605
            );
2606
        }
2607 324
        return $expr;
2608
    }
2609
2610
    /**
2611
     * This method optionally parses an expression node and returns it. When no
2612
     * expression was found this method will return <b>null</b>.
2613
     *
2614
     * @return \PDepend\Source\AST\ASTNode|null
2615
     * @throws \PDepend\Source\Parser\ParserException
2616
     * @since 0.9.6
2617
     */
2618 1577
    protected function parseOptionalExpression()
2619
    {
2620 1577
        $expressions = array();
2621
2622 1577
        while (($tokenType = $this->tokenizer->peek()) != Tokenizer::T_EOF) {
2623 1577
            $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...
2624
2625
            switch ($tokenType) {
2626 1577
                case Tokens::T_COMMA:
2627 1577
                case Tokens::T_AS:
2628 1577
                case Tokens::T_BREAK:
2629 1577
                case Tokens::T_CLOSE_TAG:
2630 1577
                case Tokens::T_COLON:
2631 1577
                case Tokens::T_CONTINUE:
2632 1577
                case Tokens::T_CURLY_BRACE_CLOSE:
2633 1577
                case Tokens::T_DECLARE:
2634 1577
                case Tokens::T_DO:
2635 1577
                case Tokens::T_DOUBLE_ARROW:
2636 1577
                case Tokens::T_ECHO:
2637 1577
                case Tokens::T_END_HEREDOC:
2638 1577
                case Tokens::T_ENDFOREACH:
2639 1577
                case Tokens::T_FOR:
2640 1577
                case Tokens::T_FOREACH:
2641 1577
                case Tokens::T_GLOBAL:
2642 1577
                case Tokens::T_GOTO:
2643 1577
                case Tokens::T_IF:
2644 1577
                case Tokens::T_PARENTHESIS_CLOSE:
2645 1577
                case Tokens::T_RETURN:
2646 1577
                case Tokens::T_SEMICOLON:
2647 1577
                case Tokens::T_SQUARED_BRACKET_CLOSE:
2648 1577
                case Tokens::T_SWITCH:
2649 1577
                case Tokens::T_THROW:
2650 1577
                case Tokens::T_TRY:
2651 1577
                case Tokens::T_UNSET:
2652 1577
                case Tokens::T_WHILE:
2653 1550
                    break 2;
2654 1561
                case Tokens::T_SELF:
2655 1561
                case Tokens::T_STRING:
2656 1561
                case Tokens::T_PARENT:
2657 1561
                case Tokens::T_STATIC:
2658 1561
                case Tokens::T_DOLLAR:
2659 1561
                case Tokens::T_VARIABLE:
2660 1561
                case Tokens::T_BACKSLASH:
2661 1561
                case Tokens::T_NAMESPACE:
2662 1230
                    $expressions[] = $this->parseVariableOrConstantOrPrimaryPrefix();
2663 1216
                    break;
2664 1235
                case ($this->isArrayStartDelimiter()):
2665 146
                    $expressions[] = $this->doParseArray();
2666 146
                    break;
2667 1215
                case Tokens::T_NULL:
2668 1215
                case Tokens::T_TRUE:
2669 1215
                case Tokens::T_FALSE:
2670 1215
                case Tokens::T_LNUMBER:
2671 1215
                case Tokens::T_DNUMBER:
2672 1215
                case Tokens::T_BACKTICK:
2673 1215
                case Tokens::T_DOUBLE_QUOTE:
2674 1215
                case Tokens::T_CONSTANT_ENCAPSED_STRING:
2675 937
                    $expressions[] = $this->parseLiteralOrString();
2676 934
                    break;
2677 898
                case Tokens::T_NEW:
2678 168
                    $expressions[] = $this->parseAllocationExpression();
2679 160
                    break;
2680 800
                case Tokens::T_EVAL:
2681 8
                    $expressions[] = $this->parseEvalExpression();
2682 8
                    break;
2683 800
                case Tokens::T_CLONE:
2684 8
                    $expressions[] = $this->parseCloneExpression();
2685 8
                    break;
2686 792
                case Tokens::T_INSTANCEOF:
2687 26
                    $expressions[] = $this->parseInstanceOfExpression();
2688 26
                    break;
2689 788
                case Tokens::T_ISSET:
2690 18
                    $expressions[] = $this->parseIssetExpression();
2691 18
                    break;
2692 774
                case Tokens::T_LIST:
2693 22
                    $expressions[] = $this->parseListExpression();
2694 22
                    break;
2695 774
                case Tokens::T_QUESTION_MARK:
2696 12
                    $expressions[] = $this->parseConditionalExpression();
2697 12
                    break;
2698 774
                case Tokens::T_BOOLEAN_AND:
2699 38
                    $expressions[] = $this->parseBooleanAndExpression();
2700 38
                    break;
2701 774
                case Tokens::T_BOOLEAN_OR:
2702 52
                    $expressions[] = $this->parseBooleanOrExpression();
2703 52
                    break;
2704 744
                case Tokens::T_LOGICAL_AND:
2705 8
                    $expressions[] = $this->parseLogicalAndExpression();
2706 8
                    break;
2707 744
                case Tokens::T_LOGICAL_OR:
2708 8
                    $expressions[] = $this->parseLogicalOrExpression();
2709 8
                    break;
2710 744
                case Tokens::T_LOGICAL_XOR:
2711 8
                    $expressions[] = $this->parseLogicalXorExpression();
2712 8
                    break;
2713 744
                case Tokens::T_FUNCTION:
2714 36
                    $expressions[] = $this->parseClosureDeclaration();
2715 36
                    break;
2716 730
                case Tokens::T_PARENTHESIS_OPEN:
2717 150
                    $expressions[] = $this->parseParenthesisExpressionOrPrimaryPrefix();
2718 150
                    break;
2719 646
                case Tokens::T_EXIT:
2720 6
                    $expressions[] = $this->parseExitExpression();
2721 6
                    break;
2722 640
                case Tokens::T_START_HEREDOC:
2723 12
                    $expressions[] = $this->parseHeredoc();
2724 12
                    break;
2725 640
                case Tokens::T_CURLY_BRACE_OPEN:
2726 2
                    $expressions[] = $this->parseBraceExpression(
2727 2
                        $this->builder->buildAstExpression(),
2728 2
                        $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN),
2729
                        Tokens::T_CURLY_BRACE_CLOSE
2730 2
                    );
2731 2
                    break;
2732 638
                case Tokens::T_INCLUDE:
2733 16
                    $expressions[] = $this->parseIncludeExpression();
2734 16
                    break;
2735 622
                case Tokens::T_INCLUDE_ONCE:
2736 2
                    $expressions[] = $this->parseIncludeOnceExpression();
2737 2
                    break;
2738 620
                case Tokens::T_REQUIRE:
2739 4
                    $expressions[] = $this->parseRequireExpression();
2740 4
                    break;
2741 616
                case Tokens::T_REQUIRE_ONCE:
2742 2
                    $expressions[] = $this->parseRequireOnceExpression();
2743 2
                    break;
2744 614
                case Tokens::T_DEC:
2745 66
                    $expressions[] = $this->parseDecrementExpression($expressions);
2746 66
                    break;
2747 598
                case Tokens::T_INC:
2748 136
                    $expressions[] = $this->parseIncrementExpression($expressions);
2749 136
                    break;
2750 568
                case Tokens::T_SL:
2751 2
                    $expressions[] = $this->parseShiftLeftExpression();
2752 2
                    break;
2753 566
                case Tokens::T_SR:
2754 2
                    $expressions[] = $this->parseShiftRightExpression();
2755 2
                    break;
2756 564
                case Tokens::T_DIR:
2757 564
                case Tokens::T_FILE:
2758 564
                case Tokens::T_LINE:
2759 564
                case Tokens::T_NS_C:
2760 564
                case Tokens::T_FUNC_C:
2761 564
                case Tokens::T_CLASS_C:
2762 564
                case Tokens::T_METHOD_C:
2763 44
                    $expressions[] = $this->parseConstant();
2764 44
                    break;
2765 550
                case Tokens::T_INT_CAST:
2766 550
                case Tokens::T_BOOL_CAST:
2767 550
                case Tokens::T_ARRAY_CAST:
2768 550
                case Tokens::T_UNSET_CAST:
2769 550
                case Tokens::T_OBJECT_CAST:
2770 550
                case Tokens::T_DOUBLE_CAST:
2771 550
                case Tokens::T_STRING_CAST:
2772 38
                    $expressions[] = $this->parseCastExpression();
2773 38
                    break;
2774 522
                case Tokens::T_EQUAL:
2775 522
                case Tokens::T_OR_EQUAL:
2776 522
                case Tokens::T_SL_EQUAL:
2777 522
                case Tokens::T_SR_EQUAL:
2778 522
                case Tokens::T_AND_EQUAL:
2779 522
                case Tokens::T_DIV_EQUAL:
2780 522
                case Tokens::T_MOD_EQUAL:
2781 522
                case Tokens::T_MUL_EQUAL:
2782 522
                case Tokens::T_XOR_EQUAL:
2783 522
                case Tokens::T_PLUS_EQUAL:
2784 522
                case Tokens::T_MINUS_EQUAL:
2785 522
                case Tokens::T_CONCAT_EQUAL:
2786 320
                    $expressions[] = $this->parseAssignmentExpression(
2787 320
                        array_pop($expressions)
2788 320
                    );
2789 316
                    break;
2790
                // TODO: Handle comments here
2791 350
                case Tokens::T_COMMENT:
2792 350
                case Tokens::T_DOC_COMMENT:
2793 4
                    $this->consumeToken($tokenType);
2794 4
                    break;
2795 346
                case Tokens::T_PRINT: // TODO: Implement print expression
2796 2
                    $token = $this->consumeToken($tokenType);
2797
2798 2
                    $expr = $this->builder->buildAstPrintExpression();
2799 2
                    $expr->configureLinesAndColumns(
2800 2
                        $token->startLine,
2801 2
                        $token->endLine,
2802 2
                        $token->startColumn,
2803 2
                        $token->endColumn
2804 2
                    );
2805
2806 2
                    $expressions[] = $expr;
2807 2
                    break;
2808 344
                case Tokens::T_STRING_VARNAME: // TODO: Implement this
2809 344
                case Tokens::T_PLUS: // TODO: Make this a arithmetic expression
2810 344
                case Tokens::T_MINUS:
2811 344
                case Tokens::T_MUL:
2812 344
                case Tokens::T_DIV:
2813 344
                case Tokens::T_MOD:
2814 344
                case Tokens::T_IS_EQUAL: // TODO: Implement compare expressions
2815 344
                case Tokens::T_IS_NOT_EQUAL:
2816 344
                case Tokens::T_IS_IDENTICAL:
2817 344
                case Tokens::T_IS_NOT_IDENTICAL:
2818 344
                case Tokens::T_IS_GREATER_OR_EQUAL:
2819 344
                case Tokens::T_IS_SMALLER_OR_EQUAL:
2820 344
                case Tokens::T_ANGLE_BRACKET_OPEN:
2821 344
                case Tokens::T_ANGLE_BRACKET_CLOSE:
2822 344
                case Tokens::T_EMPTY:
2823 344
                case Tokens::T_CONCAT:
2824 344
                case Tokens::T_BITWISE_OR:
2825 344
                case Tokens::T_BITWISE_AND:
2826 344
                case Tokens::T_BITWISE_NOT:
2827 344
                case Tokens::T_BITWISE_XOR:
2828 326
                    $token = $this->consumeToken($tokenType);
2829
2830 326
                    $expr = $this->builder->buildAstExpression($token->image);
2831 326
                    $expr->configureLinesAndColumns(
2832 326
                        $token->startLine,
2833 326
                        $token->endLine,
2834 326
                        $token->startColumn,
2835 326
                        $token->endColumn
2836 326
                    );
2837
2838 326
                    $expressions[] = $expr;
2839 326
                    break;
2840 24
                case Tokens::T_AT:
2841 24
                case Tokens::T_EXCLAMATION_MARK:
2842 8
                    $token = $this->consumeToken($tokenType);
2843
2844 8
                    $expr = $this->builder->buildAstUnaryExpression($token->image);
2845 8
                    $expr->configureLinesAndColumns(
2846 8
                        $token->startLine,
2847 8
                        $token->endLine,
2848 8
                        $token->startColumn,
2849 8
                        $token->endColumn
2850 8
                    );
2851
2852 8
                    $expressions[] = $expr;
2853 8
                    break;
2854 16
                case Tokens::T_YIELD:
2855 4
                    $expressions[] = $this->parseYield();
2856 4
                    break;
2857 12
                default:
2858 12
                    $expressions[] = $this->parseOptionalExpressionForVersion();
2859 10
                    break;
2860 12
            }
2861 1538
        }
2862
2863 1550
        $expressions = $this->reduce($expressions);
2864
2865 1550
        $count = count($expressions);
2866 1550
        if ($count == 0) {
2867 400
            return null;
2868 1534
        } elseif ($count == 1) {
2869 1472
            return $expressions[0];
2870
        }
2871
2872 444
        $expr = $this->builder->buildAstExpression();
2873 444
        foreach ($expressions as $node) {
2874 444
            $expr->addChild($node);
2875 444
        }
2876 444
        $expr->configureLinesAndColumns(
2877 444
            $expressions[0]->getStartLine(),
2878 444
            $expressions[$count - 1]->getEndLine(),
2879 444
            $expressions[0]->getStartColumn(),
2880 444
            $expressions[$count - 1]->getEndColumn()
2881 444
        );
2882
2883 444
        return $expr;
2884
    }
2885
2886
    /**
2887
     * This method will be called when the base parser cannot handle an expression
2888
     * in the base version. In this method you can implement version specific
2889
     * expressions.
2890
     *
2891
     * @return \PDepend\Source\AST\ASTNode
2892
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
2893
     * @since 2.2
2894
     */
2895 2
    protected function parseOptionalExpressionForVersion()
2896
    {
2897 2
        throw $this->getUnexpectedTokenException();
2898
    }
2899
2900
    /**
2901
     * Applies all reduce rules against the given expression list.
2902
     *
2903
     * @param \PDepend\Source\AST\ASTExpression[] $expressions Unprepared input
2904
     *        array with parsed expression nodes found in the source tree.
2905
     *
2906
     * @return \PDepend\Source\AST\ASTExpression[]
2907
     * @since 0.10.0
2908
     */
2909 1564
    protected function reduce(array $expressions)
2910
    {
2911 1564
        return $this->reduceUnaryExpression($expressions);
2912
    }
2913
2914
    /**
2915
     * Reduces all unary-expressions in the given expression list.
2916
     *
2917
     * @param \PDepend\Source\AST\ASTExpression[] $expressions Unprepared input
2918
     *        array with parsed expression nodes found in the source tree.
2919
     *
2920
     * @return \PDepend\Source\AST\ASTExpression[]
2921
     * @since 0.10.0
2922
     */
2923 1564
    private function reduceUnaryExpression(array $expressions)
2924
    {
2925 1564
        for ($i = count($expressions) - 2; $i >= 0; --$i) {
2926 526
            $expr = $expressions[$i];
2927 526
            if ($expr instanceof \PDepend\Source\AST\ASTUnaryExpression) {
2928 178
                $child = $expressions[$i + 1];
2929
2930 178
                $expr->addChild($child);
2931
2932 178
                $expr->configureLinesAndColumns(
2933 178
                    $expr->getStartLine(),
2934 178
                    $child->getEndLine(),
2935 178
                    $expr->getStartColumn(),
2936 178
                    $child->getEndColumn()
2937 178
                );
2938
2939 178
                unset($expressions[$i + 1]);
2940 178
            }
2941 526
        }
2942 1564
        return array_values($expressions);
2943
    }
2944
2945
    /**
2946
     * This method parses a switch statement.
2947
     *
2948
     * @return \PDepend\Source\AST\ASTSwitchStatement
2949
     * @since 0.9.8
2950
     */
2951 44
    private function parseSwitchStatement()
2952
    {
2953 44
        $this->tokenStack->push();
2954 44
        $this->consumeToken(Tokens::T_SWITCH);
2955
2956 44
        $switch = $this->builder->buildAstSwitchStatement();
2957 44
        $switch->addChild($this->parseParenthesisExpression());
2958 44
        $this->parseSwitchStatementBody($switch);
2959
2960 40
        return $this->setNodePositionsAndReturn($switch);
2961
    }
2962
2963
    /**
2964
     * Parses the body of a switch statement.
2965
     *
2966
     * @param \PDepend\Source\AST\ASTSwitchStatement $switch The parent switch stmt.
2967
     *
2968
     * @return \PDepend\Source\AST\ASTSwitchStatement
2969
     * @since 0.9.8
2970
     */
2971 44
    private function parseSwitchStatementBody(ASTSwitchStatement $switch)
2972
    {
2973 44
        $this->consumeComments();
2974 44
        if ($this->tokenizer->peek() === Tokens::T_CURLY_BRACE_OPEN) {
2975 34
            $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
2976 34
        } else {
2977 10
            $this->consumeToken(Tokens::T_COLON);
2978
        }
2979
2980 44
        while (($tokenType = $this->tokenizer->peek()) !== Tokenizer::T_EOF) {
2981
            switch ($tokenType) {
2982 44
                case Tokens::T_CLOSE_TAG:
2983 6
                    $this->parseNonePhpCode();
2984 6
                    break;
2985 44
                case Tokens::T_ENDSWITCH:
2986 10
                    $this->parseAlternativeScopeTermination(Tokens::T_ENDSWITCH);
2987 10
                    return $switch;
2988 44
                case Tokens::T_CURLY_BRACE_CLOSE:
2989 30
                    $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
2990 30
                    return $switch;
2991 44
                case Tokens::T_CASE:
2992 36
                    $switch->addChild($this->parseSwitchLabel());
2993 34
                    break;
2994 32
                case Tokens::T_DEFAULT:
2995 28
                    $switch->addChild($this->parseSwitchLabelDefault());
2996 28
                    break;
2997 4
                case Tokens::T_COMMENT:
2998 4
                case Tokens::T_DOC_COMMENT:
2999 2
                    $this->consumeToken($tokenType);
3000 2
                    break;
3001 2
                default:
3002 2
                    break 2;
3003 2
            }
3004 40
        }
3005
3006 2
        throw $this->getUnexpectedTokenException();
3007
    }
3008
3009
    /**
3010
     * This method parses a case label of a switch statement.
3011
     *
3012
     * @return \PDepend\Source\AST\ASTSwitchLabel
3013
     * @since 0.9.8
3014
     */
3015 36
    private function parseSwitchLabel()
3016
    {
3017 36
        $this->tokenStack->push();
3018 36
        $token = $this->consumeToken(Tokens::T_CASE);
3019
3020 36
        $label = $this->builder->buildAstSwitchLabel($token->image);
3021 36
        $label->addChild($this->parseExpression());
3022
3023 36
        if ($this->tokenizer->peek() === Tokens::T_COLON) {
3024 34
            $this->consumeToken(Tokens::T_COLON);
3025 34
        } else {
3026 2
            $this->consumeToken(Tokens::T_SEMICOLON);
3027
        }
3028
3029 36
        $this->parseSwitchLabelBody($label);
3030
3031 34
        return $this->setNodePositionsAndReturn($label);
3032
    }
3033
3034
    /**
3035
     * This method parses the default label of a switch statement.
3036
     *
3037
     * @return \PDepend\Source\AST\ASTSwitchLabel
3038
     * @since 0.9.8
3039
     */
3040 28
    private function parseSwitchLabelDefault()
3041
    {
3042 28
        $this->tokenStack->push();
3043 28
        $token = $this->consumeToken(Tokens::T_DEFAULT);
3044
3045 28
        $this->consumeComments();
3046 28
        if ($this->tokenizer->peek() === Tokens::T_COLON) {
3047 26
            $this->consumeToken(Tokens::T_COLON);
3048 26
        } else {
3049 2
            $this->consumeToken(Tokens::T_SEMICOLON);
3050
        }
3051
3052 28
        $label = $this->builder->buildAstSwitchLabel($token->image);
3053 28
        $label->setDefault();
3054
3055 28
        $this->parseSwitchLabelBody($label);
3056
3057 28
        return $this->setNodePositionsAndReturn($label);
3058
    }
3059
3060
    /**
3061
     * Parses the body of an switch label node.
3062
     *
3063
     * @param  \PDepend\Source\AST\ASTSwitchLabel $label The context switch label.
3064
     * @return \PDepend\Source\AST\ASTSwitchLabel
3065
     */
3066 42
    private function parseSwitchLabelBody(\PDepend\Source\AST\ASTSwitchLabel $label)
3067
    {
3068 42
        $curlyBraceCount = 0;
3069
3070 42
        $tokenType = $this->tokenizer->peek();
3071 42
        while ($tokenType !== Tokenizer::T_EOF) {
3072
            switch ($tokenType) {
3073 42
                case Tokens::T_CURLY_BRACE_OPEN:
3074 2
                    $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
3075 2
                    ++$curlyBraceCount;
3076 2
                    break;
3077 42
                case Tokens::T_CURLY_BRACE_CLOSE:
3078 30
                    if ($curlyBraceCount === 0) {
3079 30
                        return $label;
3080
                    }
3081 2
                    $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
3082 2
                    --$curlyBraceCount;
3083 2
                    break;
3084 42
                case Tokens::T_CLOSE_TAG:
3085 6
                    $this->parseNonePhpCode();
3086 6
                    break;
3087 42
                case Tokens::T_CASE:
3088 42
                case Tokens::T_DEFAULT:
3089 42
                case Tokens::T_ENDSWITCH:
3090 30
                    return $label;
3091 42
                default:
3092 42
                    $statement = $this->parseOptionalStatement();
3093 42
                    if ($statement === null) {
3094
                        $this->consumeToken($tokenType);
3095 42
                    } elseif ($statement instanceof ASTNode) {
3096 42
                        $label->addChild($statement);
3097 42
                    }
3098
                    // TODO: Change the <else if> into and <else> when the ast
3099
                    //       implementation is finished.
3100 42
                    break;
3101 42
            }
3102 42
            $tokenType = $this->tokenizer->peek();
3103 42
        }
3104 2
        throw new TokenStreamEndException($this->tokenizer);
3105
    }
3106
3107
    /**
3108
     * Parses the termination token for a statement. This termination token can
3109
     * be a semicolon or a closing php tag.
3110
     *
3111
     * @return void
3112
     * @since 0.9.12
3113
     */
3114 1354 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...
3115
    {
3116 1354
        $this->consumeComments();
3117 1354
        if ($this->tokenizer->peek() === Tokens::T_SEMICOLON) {
3118 1348
            $this->consumeToken(Tokens::T_SEMICOLON);
3119 1348
        } else {
3120 10
            $this->parseNonePhpCode();
3121
        }
3122 1350
    }
3123
3124
    /**
3125
     * This method parses a try-statement + associated catch-statements.
3126
     *
3127
     * @return \PDepend\Source\AST\ASTTryStatement
3128
     * @since 0.9.12
3129
     */
3130 58
    private function parseTryStatement()
3131
    {
3132 58
        $this->tokenStack->push();
3133 58
        $token = $this->consumeToken(Tokens::T_TRY);
3134
3135 58
        $stmt = $this->builder->buildAstTryStatement($token->image);
3136 58
        $stmt->addChild($this->parseRegularScope());
3137
3138 58
        $this->consumeComments();
3139
3140 58
        if (false === in_array($this->tokenizer->peek(), array(Tokens::T_CATCH, Tokens::T_FINALLY))) {
3141 2
            throw $this->getUnexpectedTokenException();
3142
        }
3143
3144 56
        while ($this->tokenizer->peek() === Tokens::T_CATCH) {
3145 48
            $stmt->addChild($this->parseCatchStatement());
3146 48
            $this->consumeComments();
3147 48
        }
3148
3149 56
        while ($this->tokenizer->peek() === Tokens::T_FINALLY) {
3150 18
            $stmt->addChild($this->parseFinallyStatement());
3151 18
            $this->consumeComments();
3152 18
        }
3153
3154 56
        return $this->setNodePositionsAndReturn($stmt);
3155
    }
3156
3157
    /**
3158
     * This method parses a throw-statement.
3159
     *
3160
     * @return \PDepend\Source\AST\ASTThrowStatement
3161
     * @since 0.9.12
3162
     */
3163 30
    private function parseThrowStatement()
3164
    {
3165 30
        $this->tokenStack->push();
3166 30
        $token = $this->consumeToken(Tokens::T_THROW);
3167
3168 30
        $stmt = $this->builder->buildAstThrowStatement($token->image);
3169 30
        $stmt->addChild($this->parseExpression());
3170
3171 30
        $this->parseStatementTermination();
3172
3173 30
        return $this->setNodePositionsAndReturn($stmt);
3174
    }
3175
3176
    /**
3177
     * This method parses a goto-statement.
3178
     *
3179
     * @return \PDepend\Source\AST\ASTGotoStatement
3180
     * @since 0.9.12
3181
     */
3182 16
    private function parseGotoStatement()
3183
    {
3184 16
        $this->tokenStack->push();
3185
3186 16
        $this->consumeToken(Tokens::T_GOTO);
3187 16
        $this->consumeComments();
3188
3189 16
        $token = $this->consumeToken(Tokens::T_STRING);
3190
3191 16
        $this->parseStatementTermination();
3192
3193 16
        $stmt = $this->builder->buildAstGotoStatement($token->image);
3194 16
        return $this->setNodePositionsAndReturn($stmt);
3195
    }
3196
3197
    /**
3198
     * This method parses a label-statement.
3199
     *
3200
     * @return \PDepend\Source\AST\ASTLabelStatement
3201
     * @since 0.9.12
3202
     */
3203 16 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...
3204
    {
3205 16
        $this->tokenStack->push();
3206
3207 16
        $token = $this->consumeToken(Tokens::T_STRING);
3208 16
        $this->consumeComments();
3209 16
        $this->consumeToken(Tokens::T_COLON);
3210
3211 16
        return $this->setNodePositionsAndReturn(
3212 16
            $this->builder->buildAstLabelStatement($token->image)
3213 16
        );
3214
    }
3215
3216
    /**
3217
     * This method parses a global-statement.
3218
     *
3219
     * @return \PDepend\Source\AST\ASTGlobalStatement
3220
     * @since 0.9.12
3221
     */
3222 8
    private function parseGlobalStatement()
3223
    {
3224 8
        $this->tokenStack->push();
3225 8
        $this->consumeToken(Tokens::T_GLOBAL);
3226
3227 8
        $stmt = $this->builder->buildAstGlobalStatement();
3228 8
        $stmt = $this->parseVariableList($stmt);
3229
3230 8
        $this->parseStatementTermination();
3231
3232 8
        return $this->setNodePositionsAndReturn($stmt);
3233
    }
3234
3235
    /**
3236
     * This method parses a unset-statement.
3237
     *
3238
     * @return \PDepend\Source\AST\ASTUnsetStatement
3239
     * @since 0.9.12
3240
     */
3241 2 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...
3242
    {
3243 2
        $this->tokenStack->push();
3244
3245 2
        $this->consumeToken(Tokens::T_UNSET);
3246 2
        $this->consumeComments();
3247 2
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
3248
3249 2
        $stmt = $this->builder->buildAstUnsetStatement();
3250 2
        $stmt = $this->parseVariableList($stmt);
3251
3252 2
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
3253
3254 2
        $this->parseStatementTermination();
3255
3256 2
        return $this->setNodePositionsAndReturn($stmt);
3257
    }
3258
3259
    /**
3260
     * This method parses a catch-statement.
3261
     *
3262
     * @return \PDepend\Source\AST\ASTCatchStatement
3263
     * @since 0.9.8
3264
     */
3265 48
    private function parseCatchStatement()
3266
    {
3267 48
        $this->tokenStack->push();
3268 48
        $this->consumeComments();
3269
3270 48
        $token = $this->consumeToken(Tokens::T_CATCH);
3271
3272 48
        $catch = $this->builder->buildAstCatchStatement($token->image);
3273
3274 48
        $this->consumeComments();
3275 48
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
3276
3277 48
        $this->parseCatchExceptionClass($catch);
3278
3279 48
        $this->consumeComments();
3280 48
        $catch->addChild($this->parseVariable());
3281
3282 48
        $this->consumeComments();
3283 48
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
3284
3285 48
        $catch->addChild($this->parseRegularScope());
3286
3287 48
        return $this->setNodePositionsAndReturn($catch);
3288
    }
3289
3290
    /**
3291
     * This method parses class references in catch statement.
3292
     * 
3293
     * @param \PDepend\Source\AST\ASTCatchStatement $stmt The owning catch statement.
3294
     */
3295 48
    protected function parseCatchExceptionClass(ASTCatchStatement $stmt)
3296
    {
3297 48
        $stmt->addChild(
3298 48
            $this->builder->buildAstClassOrInterfaceReference(
3299 48
                $this->parseQualifiedName()
3300 48
            )
3301 48
        );
3302 48
    }
3303
    
3304
    /**
3305
     * This method parses a finally-statement.
3306
     *
3307
     * @return \PDepend\Source\AST\ASTFinallyStatement
3308
     * @since 2.0.0
3309
     */
3310 18 View Code Duplication
    private function parseFinallyStatement()
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...
3311
    {
3312 18
        $this->tokenStack->push();
3313 18
        $this->consumeComments();
3314
3315 18
        $token = $this->consumeToken(Tokens::T_FINALLY);
0 ignored issues
show
Unused Code introduced by
$token 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...
3316
3317 18
        $finally = $this->builder->buildAstFinallyStatement();
3318 18
        $finally->addChild($this->parseRegularScope());
3319
3320 18
        return $this->setNodePositionsAndReturn($finally);
3321
    }
3322
3323
    /**
3324
     * This method parses a single if-statement node.
3325
     *
3326
     * @return \PDepend\Source\AST\ASTIfStatement
3327
     * @since 0.9.8
3328
     */
3329 196
    private function parseIfStatement()
3330
    {
3331 196
        $this->tokenStack->push();
3332 196
        $token = $this->consumeToken(Tokens::T_IF);
3333
3334 196
        $stmt = $this->builder->buildAstIfStatement($token->image);
3335 196
        $stmt->addChild($this->parseParenthesisExpression());
3336
3337 196
        $this->parseStatementBody($stmt);
3338 194
        $this->parseOptionalElseOrElseIfStatement($stmt);
3339
3340 194
        return $this->setNodePositionsAndReturn($stmt);
3341
    }
3342
3343
    /**
3344
     * This method parses a single elseif-statement node.
3345
     *
3346
     * @return \PDepend\Source\AST\ASTElseIfStatement
3347
     * @since 0.9.8
3348
     */
3349 46
    private function parseElseIfStatement()
3350
    {
3351 46
        $this->tokenStack->push();
3352 46
        $token = $this->consumeToken(Tokens::T_ELSEIF);
3353
3354 46
        $stmt = $this->builder->buildAstElseIfStatement($token->image);
3355 46
        $stmt->addChild($this->parseParenthesisExpression());
3356
3357 46
        $this->parseStatementBody($stmt);
3358 46
        $this->parseOptionalElseOrElseIfStatement($stmt);
3359
3360 46
        return $this->setNodePositionsAndReturn($stmt);
3361
    }
3362
3363
    /**
3364
     * This method parses an optional else-, else+if- or elseif-statement.
3365
     *
3366
     * @param \PDepend\Source\AST\ASTStatement $stmt The owning if/elseif statement.
3367
     *
3368
     * @return \PDepend\Source\AST\ASTStatement
3369
     * @since 0.9.12
3370
     */
3371 194
    private function parseOptionalElseOrElseIfStatement(ASTStatement $stmt)
3372
    {
3373 194
        $this->consumeComments();
3374 194
        switch ($this->tokenizer->peek()) {
3375 194
            case Tokens::T_ELSE:
3376 42
                $this->consumeToken(Tokens::T_ELSE);
3377 42
                $this->consumeComments();
3378 42
                if ($this->tokenizer->peek() === Tokens::T_IF) {
3379 6
                    $stmt->addChild($this->parseIfStatement());
3380 6
                } else {
3381 36
                    $this->parseStatementBody($stmt);
3382
                }
3383 42
                break;
3384 172
            case Tokens::T_ELSEIF:
3385 46
                $stmt->addChild($this->parseElseIfStatement());
3386 46
                break;
3387 194
        }
3388
3389 194
        return $stmt;
3390
    }
3391
3392
    /**
3393
     * This method parses a single for-statement node.
3394
     *
3395
     * @return \PDepend\Source\AST\ASTForStatement
3396
     * @since 0.9.8
3397
     */
3398 98
    private function parseForStatement()
3399
    {
3400 98
        $this->tokenStack->push();
3401 98
        $token = $this->consumeToken(Tokens::T_FOR);
3402
3403 98
        $this->consumeComments();
3404 98
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
3405
3406 98
        $stmt = $this->builder->buildAstForStatement($token->image);
3407
3408 98
        if (($init = $this->parseForInit()) !== null) {
3409 90
            $stmt->addChild($init);
3410 90
        }
3411 98
        $this->consumeToken(Tokens::T_SEMICOLON);
3412
3413 98
        if (($expr = $this->parseForExpression()) !== null) {
3414 92
            $stmt->addChild($expr);
3415 92
        }
3416 98
        $this->consumeToken(Tokens::T_SEMICOLON);
3417
3418 98
        if (($update = $this->parseForUpdate()) !== null) {
3419 94
            $stmt->addChild($update);
3420 94
        }
3421 98
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
3422
3423 98
        return $this->setNodePositionsAndReturn($this->parseStatementBody($stmt));
3424
    }
3425
3426
    /**
3427
     * Parses the init part of a for-statement.
3428
     *
3429
     * <code>
3430
     *      ------------------------
3431
     * for ($x = 0, $y = 23, $z = 42; $x < $y; ++$x) {}
3432
     *      ------------------------
3433
     * </code>
3434
     *
3435
     * @return \PDepend\Source\AST\ASTForInit|null
3436
     * @since 0.9.8
3437
     */
3438 98 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...
3439
    {
3440 98
        $this->consumeComments();
3441 98
        if (Tokens::T_SEMICOLON === $this->tokenizer->peek()) {
3442 8
            return null;
3443
        }
3444
3445 90
        $this->tokenStack->push();
3446
3447 90
        $init = $this->builder->buildAstForInit();
3448 90
        $this->parseExpressionList($init);
3449
3450 90
        return $this->setNodePositionsAndReturn($init);
3451
    }
3452
3453
    /**
3454
     * Parses the expression part of a for-statement.
3455
     *
3456
     * @return \PDepend\Source\AST\ASTExpression
3457
     * @since 0.9.12
3458
     */
3459 98
    private function parseForExpression()
3460
    {
3461 98
        return $this->parseOptionalExpression();
3462
    }
3463
3464
    /**
3465
     * Parses the update part of a for-statement.
3466
     *
3467
     * <code>
3468
     *                                        -------------------------------
3469
     * for ($x = 0, $y = 23, $z = 42; $x < $y; ++$x, $y = $x + 1, $z = $x + 2) {}
3470
     *                                        -------------------------------
3471
     * </code>
3472
     *
3473
     * @return \PDepend\Source\AST\ASTForUpdate|null
3474
     * @since 0.9.12
3475
     */
3476 98 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...
3477
    {
3478 98
        $this->consumeComments();
3479 98
        if (Tokens::T_PARENTHESIS_CLOSE === $this->tokenizer->peek()) {
3480 4
            return null;
3481
        }
3482
3483 94
        $this->tokenStack->push();
3484
3485 94
        $update = $this->builder->buildAstForUpdate();
3486 94
        $this->parseExpressionList($update);
3487
3488 94
        return $this->setNodePositionsAndReturn($update);
3489
    }
3490
3491
    /**
3492
     * This method parses a single foreach-statement node.
3493
     *
3494
     * @return \PDepend\Source\AST\ASTForeachStatement
3495
     * @since 0.9.8
3496
     */
3497 110
    private function parseForeachStatement()
3498
    {
3499 110
        $this->tokenStack->push();
3500 110
        $token = $this->consumeToken(Tokens::T_FOREACH);
3501
3502 110
        $foreach = $this->builder->buildAstForeachStatement($token->image);
3503
3504 110
        $this->consumeComments();
3505 110
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
3506
3507 110
        $foreach->addChild($this->parseExpression());
3508
3509 110
        $this->consumeToken(Tokens::T_AS);
3510 110
        $this->consumeComments();
3511
3512 110
        if ($this->tokenizer->peek() === Tokens::T_BITWISE_AND) {
3513 8
            $foreach->addChild($this->parseVariableOrMemberByReference());
3514 8
        } else {
3515 102
            if ($this->tokenizer->peek() == Tokens::T_LIST) {
3516 2
                $foreach->addChild($this->parseListExpression());
3517 2
            } else {
3518 100
                $foreach->addChild($this->parseVariableOrConstantOrPrimaryPrefix());
3519
3520 100
                if ($this->tokenizer->peek() === Tokens::T_DOUBLE_ARROW) {
3521 24
                    $this->consumeToken(Tokens::T_DOUBLE_ARROW);
3522
3523 24
                    if ($this->tokenizer->peek() == Tokens::T_LIST) {
3524 2
                        $foreach->addChild($this->parseListExpression());
3525 2
                    } else {
3526 22
                        $foreach->addChild(
3527 22
                            $this->parseVariableOrMemberOptionalByReference()
3528 22
                        );
3529
                    }
3530 24
                }
3531
            }
3532
        }
3533
3534 110
        $this->consumeComments();
3535 110
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
3536
3537 108
        return $this->setNodePositionsAndReturn(
3538 108
            $this->parseStatementBody($foreach)
3539 108
        );
3540
    }
3541
3542
    /**
3543
     * This method parses a single while-statement node.
3544
     *
3545
     * @return \PDepend\Source\AST\ASTWhileStatement
3546
     * @since 0.9.8
3547
     */
3548 12
    private function parseWhileStatement()
3549
    {
3550 12
        $this->tokenStack->push();
3551 12
        $token = $this->consumeToken(Tokens::T_WHILE);
3552
3553 12
        $stmt = $this->builder->buildAstWhileStatement($token->image);
3554 12
        $stmt->addChild($this->parseParenthesisExpression());
3555
3556 12
        return $this->setNodePositionsAndReturn(
3557 12
            $this->parseStatementBody($stmt)
3558 12
        );
3559
    }
3560
3561
    /**
3562
     * This method parses a do/while-statement.
3563
     *
3564
     * @return \PDepend\Source\AST\ASTDoWhileStatement
3565
     * @since 0.9.12
3566
     */
3567 18
    private function parseDoWhileStatement()
3568
    {
3569 18
        $this->tokenStack->push();
3570 18
        $token = $this->consumeToken(Tokens::T_DO);
3571
3572 18
        $stmt = $this->builder->buildAstDoWhileStatement($token->image);
3573 18
        $stmt = $this->parseStatementBody($stmt);
3574
3575 18
        $this->consumeComments();
3576 18
        $this->consumeToken(Tokens::T_WHILE);
3577
3578 18
        $stmt->addChild($this->parseParenthesisExpression());
3579
3580 18
        $this->parseStatementTermination();
3581
3582 18
        return $this->setNodePositionsAndReturn($stmt);
3583
    }
3584
3585
    /**
3586
     * This method parses a declare-statement.
3587
     *
3588
     * <code>
3589
     * -------------------------------
3590
     * declare(encoding='ISO-8859-1');
3591
     * -------------------------------
3592
     *
3593
     * -------------------
3594
     * declare(ticks=42) {
3595
     *     // ...
3596
     * }
3597
     * -
3598
     *
3599
     * ------------------
3600
     * declare(ticks=42):
3601
     *     // ...
3602
     * enddeclare;
3603
     * -----------
3604
     * </code>
3605
     *
3606
     * @return \PDepend\Source\AST\ASTDeclareStatement
3607
     * @since 0.10.0
3608
     */
3609 28
    private function parseDeclareStatement()
3610
    {
3611 28
        $this->tokenStack->push();
3612 28
        $this->consumeToken(Tokens::T_DECLARE);
3613
3614 28
        $stmt = $this->builder->buildAstDeclareStatement();
3615 28
        $stmt = $this->parseDeclareList($stmt);
3616 28
        $stmt = $this->parseStatementBody($stmt);
3617
3618 28
        return $this->setNodePositionsAndReturn($stmt);
3619
    }
3620
3621
    /**
3622
     * This method parses a list of declare values. A declare list value always
3623
     * consists of a string token and a static scalar.
3624
     *
3625
     * @param \PDepend\Source\AST\ASTDeclareStatement $stmt The declare statement that
3626
     *        is the owner of this list.
3627
     * @return \PDepend\Source\AST\ASTDeclareStatement
3628
     * @since 0.10.0
3629
     */
3630 28
    private function parseDeclareList(ASTDeclareStatement $stmt)
3631
    {
3632 28
        $this->consumeComments();
3633 28
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
3634
3635 28
        while (true) {
3636 28
            $this->consumeComments();
3637 28
            $name = $this->consumeToken(Tokens::T_STRING)->image;
3638
3639 28
            $this->consumeComments();
3640 28
            $this->consumeToken(Tokens::T_EQUAL);
3641
3642 28
            $this->consumeComments();
3643 28
            $value = $this->parseStaticValue();
3644
3645 28
            $stmt->addValue($name, $value);
3646
3647 28
            $this->consumeComments();
3648 28
            if ($this->tokenizer->peek() === Tokens::T_COMMA) {
3649 2
                $this->consumeToken(Tokens::T_COMMA);
3650 2
                continue;
3651
            }
3652 28
            break;
3653
        }
3654
3655 28
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
3656 28
        return $stmt;
3657
    }
3658
3659
    /**
3660
     * This method parses a single return-statement node.
3661
     *
3662
     * @return \PDepend\Source\AST\ASTReturnStatement
3663
     * @since 0.9.12
3664
     */
3665 611
    private function parseReturnStatement()
3666
    {
3667 611
        $this->tokenStack->push();
3668 611
        $token = $this->consumeToken(Tokens::T_RETURN);
3669
3670 611
        $stmt = $this->builder->buildAstReturnStatement($token->image);
3671 611
        if (($expr = $this->parseOptionalExpression()) != null) {
3672 606
            $stmt->addChild($expr);
3673 606
        }
3674 610
        $this->parseStatementTermination();
3675
3676 608
        return $this->setNodePositionsAndReturn($stmt);
3677
    }
3678
3679
    /**
3680
     * This method parses a break-statement node.
3681
     *
3682
     * @return \PDepend\Source\AST\ASTBreakStatement
3683
     * @since 0.9.12
3684
     */
3685 48
    private function parseBreakStatement()
3686
    {
3687 48
        $this->tokenStack->push();
3688 48
        $token = $this->consumeToken(Tokens::T_BREAK);
3689
3690 48
        $stmt = $this->builder->buildAstBreakStatement($token->image);
3691 48
        if (($expr = $this->parseOptionalExpression()) != null) {
3692 10
            $stmt->addChild($expr);
3693 10
        }
3694 48
        $this->parseStatementTermination();
3695
3696 48
        return $this->setNodePositionsAndReturn($stmt);
3697
    }
3698
3699
    /**
3700
     * This method parses a continue-statement node.
3701
     *
3702
     * @return \PDepend\Source\AST\ASTContinueStatement
3703
     * @since 0.9.12
3704
     */
3705 8
    private function parseContinueStatement()
3706
    {
3707 8
        $this->tokenStack->push();
3708 8
        $token = $this->consumeToken(Tokens::T_CONTINUE);
3709
3710 8
        $stmt = $this->builder->buildAstContinueStatement($token->image);
3711 8
        if (($expr = $this->parseOptionalExpression()) != null) {
3712 8
            $stmt->addChild($expr);
3713 8
        }
3714 8
        $this->parseStatementTermination();
3715
3716 8
        return $this->setNodePositionsAndReturn($stmt);
3717
    }
3718
3719
    /**
3720
     * This method parses a echo-statement node.
3721
     *
3722
     * @return \PDepend\Source\AST\ASTEchoStatement
3723
     * @since 0.9.12
3724
     */
3725 188
    private function parseEchoStatement()
3726
    {
3727 188
        $this->tokenStack->push();
3728 188
        $token = $this->consumeToken(Tokens::T_ECHO);
3729
3730 188
        $stmt = $this->parseExpressionList(
3731 188
            $this->builder->buildAstEchoStatement($token->image)
3732 188
        );
3733
3734 188
        $this->parseStatementTermination();
3735
3736 188
        return $this->setNodePositionsAndReturn($stmt);
3737
    }
3738
3739
    /**
3740
     * Parses a simple parenthesis expression or a direct object access, which
3741
     * was introduced with PHP 5.4.0:
3742
     *
3743
     * <code>
3744
     * (new MyClass())->bar();
3745
     * </code>
3746
     *
3747
     * @return \PDepend\Source\AST\ASTNode
3748
     * @since 1.0.0
3749
     */
3750 152
    protected function parseParenthesisExpressionOrPrimaryPrefix()
3751
    {
3752 152
        return $this->parseParenthesisExpressionOrPrimaryPrefixForVersion(
3753 152
            $this->parseParenthesisExpression()
3754 152
        );
3755
    }
3756
3757
    /**
3758
     * @param \PDepend\Source\AST\ASTExpression $expr
3759
     * @return \PDepend\Source\AST\ASTExpression
3760
     */
3761
    protected function parseParenthesisExpressionOrPrimaryPrefixForVersion(ASTExpression $expr)
3762
    {
3763
        $this->consumeComments();
3764
        if (Tokens::T_OBJECT_OPERATOR === $this->tokenizer->peek()) {
3765
            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...
3766
        }
3767
        return $expr;
3768
    }
3769
3770
    /**
3771
     * Parses any expression that is surrounded by an opening and a closing
3772
     * parenthesis
3773
     *
3774
     * @return \PDepend\Source\AST\ASTExpression
3775
     * @since 0.9.8
3776
     */
3777 View Code Duplication
    protected 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...
3778
    {
3779
        $this->tokenStack->push();
3780
        $this->consumeComments();
3781
3782
        $expr = $this->builder->buildAstExpression();
3783
        $expr = $this->parseBraceExpression(
3784
            $expr,
3785
            $this->consumeToken(Tokens::T_PARENTHESIS_OPEN),
3786
            Tokens::T_PARENTHESIS_CLOSE
3787
        );
3788
3789
        return $this->setNodePositionsAndReturn($expr);
3790
    }
3791
3792
    /**
3793
     * This method parses a member primary prefix expression or a function
3794
     * postfix expression node.
3795
     *
3796
     * A member primary prefix can be a method call:
3797
     *
3798
     * <code>
3799
     * $object->foo();
3800
     *
3801
     * clazz::foo();
3802
     * </code>
3803
     *
3804
     * a property access:
3805
     *
3806
     * <code>
3807
     * $object->foo;
3808
     *
3809
     * clazz::$foo;
3810
     * </code>
3811
     *
3812
     * or a class constant access:
3813
     *
3814
     * <code>
3815
     * clazz::FOO;
3816
     * </code>
3817
     *
3818
     * A function postfix represents any kind of function call:
3819
     *
3820
     * <code>
3821
     * $function();
3822
     *
3823
     * func();
3824
     * </code>
3825
     *
3826
     * @return \PDepend\Source\AST\ASTNode
3827
     * @throws \PDepend\Source\Parser\ParserException
3828
     * @since 0.9.6
3829
     */
3830 526
    private function parseMemberPrefixOrFunctionPostfix()
3831
    {
3832 526
        $this->tokenStack->push();
3833 526
        $this->tokenStack->push();
3834
3835 526
        $qName = $this->parseQualifiedName();
3836
3837
        // Remove comments
3838 526
        $this->consumeComments();
3839
3840
        // Get next token type
3841 526
        $tokenType = $this->tokenizer->peek();
3842
3843
        switch ($tokenType) {
3844 526
            case Tokens::T_DOUBLE_COLON:
3845 132
                $node = $this->builder->buildAstClassOrInterfaceReference($qName);
3846 132
                $node = $this->setNodePositionsAndReturn($node);
3847 132
                $node = $this->parseStaticMemberPrimaryPrefix($node);
3848 132
                break;
3849 408
            case Tokens::T_PARENTHESIS_OPEN:
3850 370
                $node = $this->builder->buildAstIdentifier($qName);
3851 370
                $node = $this->setNodePositionsAndReturn($node);
3852 370
                $node = $this->parseFunctionPostfix($node);
3853 368
                break;
3854 124
            default:
3855 124
                $node = $this->builder->buildAstConstant($qName);
3856 124
                $node = $this->setNodePositionsAndReturn($node);
3857 124
                break;
3858 124
        }
3859
3860 526
        return $this->setNodePositionsAndReturn($node);
3861
    }
3862
3863
    /**
3864
     * This method will parse an optional function postfix.
3865
     *
3866
     * If the next available token is an opening parenthesis, this method will
3867
     * wrap the given <b>$node</b> with a {@link \PDepend\Source\AST\ASTFunctionPostfix}
3868
     * node.
3869
     *
3870
     * @param \PDepend\Source\AST\ASTNode $node The previously parsed node.
3871
     *
3872
     * @return \PDepend\Source\AST\ASTNode The original input node or this node
3873
     *         wrapped with a function postfix instance.
3874
     * @since 1.0.0
3875
     */
3876 6 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...
3877
    {
3878 6
        $this->consumeComments();
3879 6
        if (Tokens::T_PARENTHESIS_OPEN === $this->tokenizer->peek()) {
3880 6
            return $this->parseFunctionPostfix($node);
3881
        }
3882
        return $node;
3883
    }
3884
3885
    /**
3886
     * This method parses a function postfix expression. An object of type
3887
     * {@link \PDepend\Source\AST\ASTFunctionPostfix} represents any valid php
3888
     * function call.
3889
     *
3890
     * This method will delegate the call to another method that returns a
3891
     * member primary prefix object when the function postfix expression is
3892
     * followed by an object operator.
3893
     *
3894
     * @param  \PDepend\Source\AST\ASTNode $node This node represents the function
3895
     *        identifier. An identifier can be a static string, a variable, a
3896
     *        compound variable or any other valid php function identifier.
3897
     * @return \PDepend\Source\AST\ASTNode
3898
     * @throws \PDepend\Source\Parser\ParserException
3899
     * @since 0.9.6
3900
     */
3901 410
    private function parseFunctionPostfix(ASTNode $node)
3902
    {
3903 410
        $image = $this->extractPostfixImage($node);
3904
3905 410
        $function = $this->builder->buildAstFunctionPostfix($image);
3906 410
        $function->addChild($node);
3907 410
        $function->addChild($this->parseArguments());
3908
3909 408
        return $this->parseOptionalMemberPrimaryPrefix(
3910 408
            $this->parseOptionalIndexExpression($function)
3911 408
        );
3912
    }
3913
3914
    /**
3915
     * This method parses a PHP version specific identifier for method and
3916
     * property postfix expressions.
3917
     *
3918
     * @return \PDepend\Source\AST\ASTNode
3919
     * @since 1.0.0
3920
     */
3921 88
    protected function parsePostfixIdentifier()
3922
    {
3923 88
        switch ($this->tokenizer->peek()) {
3924 88
            case Tokens::T_STRING:
3925
                $node = $this->parseLiteral();
3926
                break;
3927 88
            default:
3928 88
                $node = $this->parseCompoundVariableOrVariableVariableOrVariable();
3929 88
                break;
3930 88
        }
3931 88
        return $this->parseOptionalIndexExpression($node);
3932
    }
3933
3934
    /**
3935
     * This method parses an optional member primary expression. It will parse
3936
     * the primary expression when an object operator can be found at the actual
3937
     * token stream position. Otherwise this method simply returns the input
3938
     * {@link \PDepend\Source\AST\ASTNode} instance.
3939
     *
3940
     * @param  \PDepend\Source\AST\ASTNode $node This node represents primary prefix
3941
     *        left expression. It will be the first child of the parsed member
3942
     *        primary expression.
3943
     * @return \PDepend\Source\AST\ASTNode
3944
     * @throws \PDepend\Source\Parser\ParserException
3945
     * @since 0.9.6
3946
     */
3947 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...
3948
    {
3949
        $this->consumeComments();
3950
3951
        if ($this->tokenizer->peek() === Tokens::T_OBJECT_OPERATOR) {
3952
            return $this->parseMemberPrimaryPrefix($node);
3953
        }
3954
        return $node;
3955
    }
3956
3957
    /**
3958
     * This method parses a dynamic or object bound member primary expression.
3959
     * A member primary prefix can be a method call:
3960
     *
3961
     * <code>
3962
     * $object->foo();
3963
     * </code>
3964
     *
3965
     * or a property access:
3966
     *
3967
     * <code>
3968
     * $object->foo;
3969
     * </code>
3970
     *
3971
     * @param  \PDepend\Source\AST\ASTNode $node The left node in the parsed member
3972
     *        primary expression.
3973
     * @return \PDepend\Source\AST\ASTMemberPrimaryPrefix
3974
     * @throws \PDepend\Source\Parser\ParserException
3975
     * @since 0.9.6
3976
     */
3977 276
    protected function parseMemberPrimaryPrefix(ASTNode $node)
3978
    {
3979
        // Consume double colon and optional comments
3980 276
        $token = $this->consumeToken(Tokens::T_OBJECT_OPERATOR);
3981
3982 276
        $prefix = $this->builder->buildAstMemberPrimaryPrefix($token->image);
3983 276
        $prefix->addChild($node);
3984
3985 276
        $this->consumeComments();
3986 276
        $tokenType = $this->tokenizer->peek();
3987
3988
        switch ($tokenType) {
3989 276
            case ($this->isMethodName($tokenType)):
3990 232
                $child = $this->parseIdentifier($tokenType);
3991 232
                $child = $this->parseOptionalIndexExpression($child);
3992
3993
                // TODO: Move this in a separate method
3994 232
                if ($child instanceof ASTIndexExpression) {
3995 36
                    $this->consumeComments();
3996 36
                    if (Tokens::T_PARENTHESIS_OPEN === $this->tokenizer->peek()) {
3997 6
                        $prefix->addChild($this->parsePropertyPostfix($child));
3998 6
                        return $this->parseOptionalFunctionPostfix($prefix);
3999
                    }
4000 30
                }
4001 226
                break;
4002 44
            case Tokens::T_CURLY_BRACE_OPEN:
4003 12
                $child = $this->parseCompoundExpression();
4004 12
                break;
4005 32
            default:
4006 32
                $child = $this->parseCompoundVariableOrVariableVariableOrVariable();
4007 32
                break;
4008 32
        }
4009
4010 270
        $prefix->addChild(
4011 270
            $this->parseMethodOrPropertyPostfix(
4012 270
                $this->parseOptionalIndexExpression($child)
4013 270
            )
4014 270
        );
4015
4016 270
        return $this->parseOptionalMemberPrimaryPrefix(
4017 270
            $this->parseOptionalIndexExpression($prefix)
4018 270
        );
4019
    }
4020
4021
    /**
4022
     * This method parses an optional member primary expression. It will parse
4023
     * the primary expression when a double colon operator can be found at the
4024
     * actual token stream position. Otherwise this method simply returns the
4025
     * input {@link \PDepend\Source\AST\ASTNode} instance.
4026
     *
4027
     * @param  \PDepend\Source\AST\ASTNode $node This node represents primary prefix
4028
     *        left expression. It will be the first child of the parsed member
4029
     *        primary expression.
4030
     * @return \PDepend\Source\AST\ASTNode
4031
     * @throws \PDepend\Source\Parser\ParserException
4032
     * @since 1.0.1
4033
     */
4034 180 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...
4035
    {
4036 180
        $this->consumeComments();
4037
4038 180
        if ($this->tokenizer->peek() === Tokens::T_DOUBLE_COLON) {
4039 6
            return $this->parseStaticMemberPrimaryPrefix($node);
4040
        }
4041 174
        return $node;
4042
    }
4043
4044
    /**
4045
     * This method parses a static member primary expression. The given node
4046
     * contains the used static class or interface identifier. A static member
4047
     * primary prefix can represent the following code expressions:
4048
     *
4049
     * A static method class:
4050
     *
4051
     * <code>
4052
     * Foo::bar();
4053
     * </code>
4054
     *
4055
     * a static property access:
4056
     *
4057
     * <code>
4058
     * Foo::$bar;
4059
     * </code>
4060
     *
4061
     * or a static constant access:
4062
     *
4063
     * <code>
4064
     * Foo::BAR;
4065
     * </code>
4066
     *
4067
     * @param  \PDepend\Source\AST\ASTNode $node The left node in the parsed member
4068
     *        primary expression.
4069
     * @return \PDepend\Source\AST\ASTMemberPrimaryPrefix
4070
     * @throws \PDepend\Source\Parser\ParserException
4071
     * @since 0.9.6
4072
     */
4073 238
    protected function parseStaticMemberPrimaryPrefix(ASTNode $node)
4074
    {
4075 238
        $token = $this->consumeToken(Tokens::T_DOUBLE_COLON);
4076
4077 238
        $prefix = $this->builder->buildAstMemberPrimaryPrefix($token->image);
4078 238
        $prefix->addChild($node);
4079
4080 238
        $this->consumeComments();
4081
4082 238
        switch ($this->tokenizer->peek()) {
4083 238
            case Tokens::T_STRING:
4084 126
                $postfix = $this->parseMethodOrConstantPostfix();
4085 126
                break;
4086 122
            case Tokens::T_CLASS_FQN:
4087 30
                $postfix = $this->parseFullQualifiedClassNamePostfix();
4088 30
                break;
4089 94
            default:
4090 94
                $postfix = $this->parseMethodOrPropertyPostfix(
4091 94
                    $this->parsePostfixIdentifier()
4092 94
                );
4093 94
                break;
4094 238
        }
4095
4096 238
        $prefix->addChild($postfix);
4097
4098 238
        return $this->parseOptionalMemberPrimaryPrefix(
4099 238
            $this->parseOptionalIndexExpression($prefix)
4100 238
        );
4101
    }
4102
4103
    /**
4104
     * This method parses a method- or constant-postfix expression. This expression
4105
     * will contain an identifier node as nested child.
4106
     *
4107
     * @return \PDepend\Source\AST\ASTNode
4108
     * @throws \PDepend\Source\Parser\ParserException
4109
     * @since 0.9.6
4110
     */
4111 126
    private function parseMethodOrConstantPostfix()
4112
    {
4113 126
        $this->tokenStack->push();
4114
4115 126
        $node = $this->parseIdentifier();
4116
4117 126
        $this->consumeComments();
4118 126
        if ($this->tokenizer->peek() === Tokens::T_PARENTHESIS_OPEN) {
4119 102
            $postfix = $this->parseMethodPostfix($node);
4120 102
        } else {
4121 26
            $postfix = $this->builder->buildAstConstantPostfix($node->getImage());
4122 26
            $postfix->addChild($node);
4123
        }
4124
4125 126
        return $this->setNodePositionsAndReturn($postfix);
4126
    }
4127
4128
    /**
4129
     * This method parses a method- or property-postfix expression. This expression
4130
     * will contain the given node as method or property identifier.
4131
     *
4132
     * @param \PDepend\Source\AST\ASTNode $node The identifier for the parsed postfix
4133
     *        expression node. This node will be the first child of the returned
4134
     *        postfix node instance.
4135
     *
4136
     * @return \PDepend\Source\AST\ASTNode
4137
     * @throws \PDepend\Source\Parser\ParserException
4138
     * @since 0.9.6
4139
     */
4140 352
    private function parseMethodOrPropertyPostfix(ASTNode $node)
4141
    {
4142
        // Strip optional comments
4143 352
        $this->consumeComments();
4144
4145 352
        switch ($this->tokenizer->peek()) {
4146 352
            case Tokens::T_PARENTHESIS_OPEN:
4147 172
                $postfix = $this->parseMethodPostfix($node);
4148 172
                break;
4149 194
            default:
4150 194
                $postfix = $this->parsePropertyPostfix($node);
4151 194
                break;
4152 352
        }
4153 352
        return $this->parseOptionalMemberPrimaryPrefix($postfix);
4154
    }
4155
4156
    /**
4157
     * Parses/Creates a property postfix node instance.
4158
     *
4159
     * @param \PDepend\Source\AST\ASTNode $node Node that represents the image of
4160
     *        the property postfix node.
4161
     *
4162
     * @return \PDepend\Source\AST\ASTPropertyPostfix
4163
     * @since 0.10.2
4164
     */
4165 200
    private function parsePropertyPostfix(ASTNode $node)
4166
    {
4167 200
        $image = $this->extractPostfixImage($node);
4168
4169 200
        $postfix = $this->builder->buildAstPropertyPostfix($image);
4170 200
        $postfix->addChild($node);
4171
4172 200
        $postfix->configureLinesAndColumns(
4173 200
            $node->getStartLine(),
4174 200
            $node->getEndLine(),
4175 200
            $node->getStartColumn(),
4176 200
            $node->getEndColumn()
4177 200
        );
4178
4179 200
        return $postfix;
4180
    }
4181
4182
    /**
4183
     * Parses a full qualified class name postfix.
4184
     *
4185
     * @return \PDepend\Source\AST\ASTClassFqnPostfix
4186
     * @since 2.0.0
4187
     */
4188 30
    private function parseFullQualifiedClassNamePostfix()
4189
    {
4190 30
        $this->tokenStack->push();
4191
4192 30
        $this->consumeToken(Tokens::T_CLASS_FQN);
4193
4194 30
        return $this->setNodePositionsAndReturn(
4195 30
            $this->builder->buildAstClassFqnPostfix()
4196 30
        );
4197
    }
4198
4199
    /**
4200
     * This method will extract the image/name of the real property/variable
4201
     * that is wrapped by {@link \PDepend\Source\AST\ASTIndexExpression} nodes. If
4202
     * the given node is now wrapped by index expressions, this method will
4203
     * return the image of the entire <b>$node</b>.
4204
     *
4205
     * @param \PDepend\Source\AST\ASTNode $node The context node that may be wrapped
4206
     *        by multiple array or string index expressions.
4207
     *
4208
     * @return string
4209
     * @since 1.0.0
4210
     */
4211 760
    private function extractPostfixImage(ASTNode $node)
4212
    {
4213 760
        while ($node instanceof \PDepend\Source\AST\ASTIndexExpression) {
4214 70
            $node = $node->getChild(0);
4215 70
        }
4216 760
        return $node->getImage();
4217
    }
4218
4219
    /**
4220
     * Parses a method postfix node instance.
4221
     *
4222
     * @param \PDepend\Source\AST\ASTNode $node Node that represents the image of
4223
     *        the method postfix node.
4224
     *
4225
     * @return \PDepend\Source\AST\ASTMethodPostfix
4226
     * @since 1.0.0
4227
     */
4228 228
    private function parseMethodPostfix(ASTNode $node)
4229
    {
4230 228
        $args  = $this->parseArguments();
4231 228
        $image = $this->extractPostfixImage($node);
4232
4233 228
        $postfix = $this->builder->buildAstMethodPostfix($image);
4234 228
        $postfix->addChild($node);
4235 228
        $postfix->addChild($args);
4236
4237 228
        $postfix->configureLinesAndColumns(
4238 228
            $node->getStartLine(),
4239 228
            $args->getEndLine(),
4240 228
            $node->getStartColumn(),
4241 228
            $args->getEndColumn()
4242 228
        );
4243
4244 228
        return $this->parseOptionalMemberPrimaryPrefix($postfix);
4245
    }
4246
4247
    /**
4248
     * This method parses the arguments passed to a function- or method-call.
4249
     *
4250
     * @return \PDepend\Source\AST\ASTArguments
4251
     * @throws \PDepend\Source\Parser\ParserException
4252
     * @since 0.9.6
4253
     */
4254 678 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...
4255
    {
4256 678
        $this->consumeComments();
4257
4258 678
        $this->tokenStack->push();
4259
4260 678
        $arguments = $this->builder->buildAstArguments();
4261
4262 678
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
4263 678
        $this->consumeComments();
4264
4265 678
        if (Tokens::T_PARENTHESIS_CLOSE !== $this->tokenizer->peek()) {
4266 330
            $arguments = $this->parseArgumentList($arguments);
4267 330
        }
4268 678
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
4269
4270 676
        return $this->setNodePositionsAndReturn($arguments);
4271
    }
4272
4273
    /**
4274
     * @param \PDepend\Source\AST\ASTArguments $arguments
4275
     * @return \PDepend\Source\AST\ASTArguments
4276
     */
4277
    protected function parseArgumentList(ASTArguments $arguments)
4278
    {
4279
        return $this->parseExpressionList($arguments);
4280
    }
4281
4282
    /**
4283
     * This method implements the parsing for various expression types like
4284
     * variables, object/static method. All these expressions are valid in
4285
     * several php language constructs like, isset, empty, unset etc.
4286
     *
4287
     * @return \PDepend\Source\AST\ASTNode
4288
     * @since 0.9.12
4289
     */
4290 1262
    protected function parseVariableOrConstantOrPrimaryPrefix()
4291
    {
4292 1262
        $this->consumeComments();
4293 1262
        switch ($this->tokenizer->peek()) {
4294 1262
            case Tokens::T_DOLLAR:
4295 1262
            case Tokens::T_VARIABLE:
4296 930
                return $this->parseVariableOrFunctionPostfixOrMemberPrimaryPrefix();
4297 930
            case Tokens::T_SELF:
4298 628
                return $this->parseConstantOrSelfMemberPrimaryPrefix();
4299 42
            case Tokens::T_PARENT:
4300 38
                return $this->parseConstantOrParentMemberPrimaryPrefix();
4301 592
            case Tokens::T_STATIC:
4302 28
                return $this->parseStaticVariableDeclarationOrMemberPrimaryPrefix();
4303 20
            case Tokens::T_STRING:
4304 564
            case Tokens::T_BACKSLASH:
4305 46
            case Tokens::T_NAMESPACE:
4306 44
                return $this->parseMemberPrefixOrFunctionPostfix();
4307 526
        }
4308 526
4309 526
        throw $this->getUnexpectedTokenException();
4310 526
    }
4311 526
4312
    /**
4313
     * This method parses any type of variable, function postfix expressions or
4314 1248
     * any kind of member primary prefix.
4315
     *
4316 1248
     * This method expects that the actual token represents any kind of valid
4317
     * php variable: simple variable, compound variable or variable variable.
4318
     *
4319
     * It will parse a function postfix or member primary expression when this
4320
     * variable is followed by an object operator, double colon or opening
4321
     * parenthesis.
4322
     *
4323
     * @return \PDepend\Source\AST\ASTNode
4324
     * @throws \PDepend\Source\Parser\ParserException
4325
     * @since 0.9.6
4326
     */
4327
    private function parseVariableOrFunctionPostfixOrMemberPrimaryPrefix()
4328
    {
4329
        $this->tokenStack->push();
4330
4331
        $variable = $this->parseCompoundVariableOrVariableVariableOrVariable();
4332
        $variable = $this->parseOptionalIndexExpression($variable);
4333
4334 938
        $this->consumeComments();
4335
        switch ($this->tokenizer->peek()) {
4336 938
            case Tokens::T_DOUBLE_COLON:
4337
                $result = $this->parseStaticMemberPrimaryPrefix($variable);
4338 938
                break;
4339 938
            case Tokens::T_OBJECT_OPERATOR:
4340
                $result = $this->parseMemberPrimaryPrefix($variable);
4341 938
                break;
4342 938
            case Tokens::T_PARENTHESIS_OPEN:
4343 938
                $result = $this->parseFunctionPostfix($variable);
4344 42
                break;
4345 42
            default:
4346 900
                $result = $variable;
4347 240
                break;
4348 240
        }
4349 770
        return $this->setNodePositionsAndReturn($result);
4350 36
    }
4351 36
4352 752
    /**
4353 752
     * Parses an assingment expression node.
4354 752
     *
4355 938
     * @param \PDepend\Source\AST\ASTNode $left The left part of the assignment
4356 938
     *        expression that will be parsed by this method.
4357
     *
4358
     * @return \PDepend\Source\AST\ASTAssignmentExpression
4359
     * @since 0.9.12
4360
     */
4361
    protected function parseAssignmentExpression(ASTNode $left)
4362
    {
4363
        $token = $this->consumeToken($this->tokenizer->peek());
4364
4365
        $node = $this->builder->buildAstAssignmentExpression($token->image);
4366
        $node->addChild($left);
4367
4368 320
        // TODO: Change this into a mandatory expression in later versions
4369
        if (($expr = $this->parseOptionalExpression()) != null) {
4370 320
            $node->addChild($expr);
4371
        } else {
4372 320
            $expr = $left;
4373 320
        }
4374
4375
        $node->configureLinesAndColumns(
4376 320
            $left->getStartLine(),
4377 316
            $expr->getEndLine(),
4378 316
            $left->getStartColumn(),
4379
            $expr->getEndColumn()
4380
        );
4381
4382 316
        return $node;
4383 316
    }
4384 316
4385 316
    /**
4386 316
     * This method parses a {@link \PDepend\Source\AST\ASTStaticReference} node.
4387 316
     *
4388
     * @param  \PDepend\Source\Tokenizer\Token $token The "static" keyword token.
4389 316
     * @return \PDepend\Source\AST\ASTStaticReference
4390
     * @throws \PDepend\Source\Parser\ParserException
4391
     * @throws \PDepend\Source\Parser\InvalidStateException
4392
     * @since 0.9.6
4393
     */
4394 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...
4395
    {
4396
        // Strip optional comments
4397
        $this->consumeComments();
4398
4399
        if ($this->classOrInterface === null) {
4400
            throw new InvalidStateException(
4401 20
                $token->startLine,
4402
                (string) $this->compilationUnit,
4403
                'The keyword "static" was used outside of a class/method scope.'
4404 20
            );
4405
        }
4406 20
4407 4
        $ref = $this->builder->buildAstStaticReference($this->classOrInterface);
4408 4
        $ref->configureLinesAndColumns(
4409 4
            $token->startLine,
4410
            $token->endLine,
4411 4
            $token->startColumn,
4412
            $token->endColumn
4413
        );
4414 16
4415 16
        return $ref;
4416 16
    }
4417 16
4418 16
    /**
4419 16
     * This method parses a {@link \PDepend\Source\AST\ASTSelfReference} node.
4420 16
     *
4421
     * @param  \PDepend\Source\Tokenizer\Token $token The "self" keyword token.
4422 16
     * @return \PDepend\Source\AST\ASTSelfReference
4423
     * @throws \PDepend\Source\Parser\ParserException
4424
     * @throws \PDepend\Source\Parser\InvalidStateException
4425
     * @since 0.9.6
4426
     */
4427 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...
4428
    {
4429
        if ($this->classOrInterface === null) {
4430
            throw new InvalidStateException(
4431
                $token->startLine,
4432
                (string) $this->compilationUnit,
4433
                'The keyword "self" was used outside of a class/method scope.'
4434 52
            );
4435
        }
4436 52
4437 6
        $ref = $this->builder->buildAstSelfReference($this->classOrInterface);
4438 6
        $ref->configureLinesAndColumns(
4439 6
            $token->startLine,
4440
            $token->endLine,
4441 6
            $token->startColumn,
4442
            $token->endColumn
4443
        );
4444 46
4445 46
        return $ref;
4446 46
    }
4447 46
4448 46
    /**
4449 46
     * Parses a simple PHP constant use and returns a corresponding node.
4450 46
     *
4451
     * @return \PDepend\Source\AST\ASTNode
4452 46
     * @since 1.0.0
4453
     */
4454
    protected function parseConstant()
4455
    {
4456
        $this->tokenStack->push();
4457
        switch ($type = $this->tokenizer->peek()) {
4458
            case Tokens::T_STRING:
4459
                // TODO: Separate node classes for magic constants
4460
            case Tokens::T_DIR:
4461 44
            case Tokens::T_FILE:
4462
            case Tokens::T_LINE:
4463 44
            case Tokens::T_NS_C:
4464 44
            case Tokens::T_FUNC_C:
4465 44
            case Tokens::T_CLASS_C:
4466
            case Tokens::T_METHOD_C:
4467 44
            case Tokens::T_TRAIT_C:
4468 44
                $token = $this->consumeToken($type);
4469 44
4470 44
                return $this->setNodePositionsAndReturn(
4471 44
                    $this->builder->buildAstConstant($token->image)
4472 44
                );
4473 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...
4474 44
        }
4475 44
    }
4476
4477 44
    /**
4478 44
     * This method parses a {@link \PDepend\Source\AST\ASTConstant} node or
4479 44
     * an instance of {@link \PDepend\Source\AST\ASTSelfReference} as part of
4480
     * a {@link \PDepend\Source\AST\ASTMemberPrimaryPrefix} that contains the
4481
     * self reference as its first child when the self token is followed by a
4482
     * double colon token.
4483
     *
4484
     * @return \PDepend\Source\AST\ASTNode
4485
     * @throws \PDepend\Source\Parser\ParserException
4486
     * @throws \PDepend\Source\Parser\InvalidStateException
4487
     * @since 0.9.6
4488
     */
4489 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...
4490
    {
4491
        // Read self token and strip optional comments
4492
        $token = $this->consumeToken(Tokens::T_SELF);
4493
        $this->consumeComments();
4494
4495
        if ($this->tokenizer->peek() == Tokens::T_DOUBLE_COLON) {
4496 42
            return $this->parseStaticMemberPrimaryPrefix(
4497
                $this->parseSelfReference($token)
4498
            );
4499 42
        }
4500 42
        return $this->builder->buildAstConstant($token->image);
4501
    }
4502 42
4503 40
    /**
4504 40
     * This method parses a {@link \PDepend\Source\AST\ASTParentReference} node.
4505 36
     *
4506
     * @param  \PDepend\Source\Tokenizer\Token $token The "self" keyword token.
4507 2
     * @return \PDepend\Source\AST\ASTNode
4508
     * @throws \PDepend\Source\Parser\ParserException
4509
     * @throws \PDepend\Source\Parser\InvalidStateException
4510
     * @since 0.9.6
4511
     */
4512
    private function parseParentReference(Token $token)
4513
    {
4514 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...
4515
            throw new InvalidStateException(
4516
                $token->startLine,
4517
                (string) $this->compilationUnit,
4518
                'The keyword "parent" was used as type hint but the parameter ' .
4519 48
                'declaration is not in a class scope.'
4520
            );
4521 48
        }
4522 8
4523 8
        if ($this->classOrInterface instanceof ASTTrait) {
4524 8
            $classReference = $this->builder->buildAstClassReference('__PDepend_TraitRuntimeReference');
4525
        } else {
4526
            $classReference = $this->classOrInterface->getParentClassReference();
4527 8
        }
4528
4529 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...
4530 40
            throw new InvalidStateException(
4531 4
                $token->startLine,
4532 4
                (string) $this->compilationUnit,
4533 36
                sprintf(
4534
                    'The keyword "parent" was used as type hint but the ' .
4535
                    'class "%s" does not declare a parent.',
4536 40
                    $this->classOrInterface->getName()
4537 6
                )
4538 6
            );
4539 6
        }
4540 6
4541
        $ref = $this->builder->buildAstParentReference($classReference);
4542 6
        $ref->configureLinesAndColumns(
4543 6
            $token->startLine,
4544 6
            $token->endLine,
4545 6
            $token->startColumn,
4546
            $token->endColumn
4547
        );
4548 34
4549 34
        return $ref;
4550 34
    }
4551 34
4552 34
    /**
4553 34
     * This method parses a {@link \PDepend\Source\AST\ASTConstant} node or
4554 34
     * an instance of {@link \PDepend\Source\AST\ASTParentReference} as part
4555
     * of a {@link \PDepend\Source\AST\ASTMemberPrimaryPrefix} that contains
4556 34
     * the parent reference as its first child when the self token is followed
4557
     * by a double colon token.
4558
     *
4559
     * @return \PDepend\Source\AST\ASTNode
4560
     * @throws \PDepend\Source\Parser\ParserException
4561
     * @throws \PDepend\Source\Parser\InvalidStateException
4562
     * @since 0.9.6
4563
     */
4564 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...
4565
    {
4566
        // Consume parent token and strip optional comments
4567
        $token = $this->consumeToken(Tokens::T_PARENT);
4568
        $this->consumeComments();
4569
4570
        if ($this->tokenizer->peek() == Tokens::T_DOUBLE_COLON) {
4571 28
            return $this->parseStaticMemberPrimaryPrefix(
4572
                $this->parseParentReference($token)
4573
            );
4574 28
        }
4575 28
        return $this->builder->buildAstConstant($token->image);
4576
    }
4577 28
4578 26
    /**
4579 26
     * Parses a variable or any other valid member expression that is optionally
4580 18
     * prefixed with PHP's reference operator.
4581
     *
4582 2
     * <code>
4583
     * //                  -----------
4584
     * foreach ( $array as &$this->foo ) {}
4585
     * //                  -----------
4586
     *
4587
     * //     ----------
4588
     * $foo = &$bar->baz;
4589
     * //     ----------
4590
     * </code>
4591
     *
4592
     * @return \PDepend\Source\AST\ASTUnaryExpression
4593
     * @since 0.9.18
4594
     */
4595
    private function parseVariableOrMemberOptionalByReference()
4596
    {
4597
        $this->consumeComments();
4598
        if ($this->tokenizer->peek() === Tokens::T_BITWISE_AND) {
4599
            return $this->parseVariableOrMemberByReference();
4600
        }
4601
        return $this->parseVariableOrConstantOrPrimaryPrefix();
4602 22
    }
4603
4604 22
    /**
4605 22
     * Parses a variable or any other valid member expression that is prefixed
4606 6
     * with PHP's reference operator.
4607
     *
4608 16
     * <code>
4609
     * //                  -----------
4610
     * foreach ( $array as &$this->foo ) {}
4611
     * //                  -----------
4612
     *
4613
     * //     ----------
4614
     * $foo = &$bar->baz;
4615
     * //     ----------
4616
     * </code>
4617
     *
4618
     * @return \PDepend\Source\AST\ASTUnaryExpression
4619
     * @since 0.9.18
4620
     */
4621
    private function parseVariableOrMemberByReference()
4622
    {
4623
        $this->tokenStack->push();
4624
4625
        $token = $this->consumeToken(Tokens::T_BITWISE_AND);
4626
        $this->consumeComments();
4627
4628 14
        $expr = $this->builder->buildAstUnaryExpression($token->image);
4629
        $expr->addChild($this->parseVariableOrConstantOrPrimaryPrefix());
4630 14
4631
        return $this->setNodePositionsAndReturn($expr);
4632 14
    }
4633 14
4634
    /**
4635 14
     * This method parses a simple PHP variable.
4636 14
     *
4637
     * @return \PDepend\Source\AST\ASTVariable
4638 14
     * @throws UnexpectedTokenException
4639
     * @since 0.9.6
4640
     */
4641 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...
4642
    {
4643
        $token = $this->consumeToken(Tokens::T_VARIABLE);
4644
4645
        $variable = $this->builder->buildAstVariable($token->image);
4646
        $variable->configureLinesAndColumns(
4647
            $token->startLine,
4648 1050
            $token->endLine,
4649
            $token->startColumn,
4650 1050
            $token->endColumn
4651
        );
4652 1050
4653 1050
        return $variable;
4654 1050
    }
4655 1050
4656 1050
    /**
4657 1050
     * This method parses a comma separated list of valid php variables and/or
4658 1050
     * properties and adds them to the given node instance.
4659
     *
4660 1050
     * @param \PDepend\Source\AST\ASTNode $node The context parent node.
4661
     *
4662
     * @return \PDepend\Source\AST\ASTNode The prepared entire node.
4663
     * @since 0.9.12
4664
     */
4665 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...
4666
    {
4667
        $this->consumeComments();
4668
        while ($this->tokenizer->peek() !== Tokenizer::T_EOF) {
4669
            $node->addChild($this->parseVariableOrConstantOrPrimaryPrefix());
4670
4671
            $this->consumeComments();
4672 28
            if ($this->tokenizer->peek() === Tokens::T_COMMA) {
4673
                $this->consumeToken(Tokens::T_COMMA);
4674 28
                $this->consumeComments();
4675 28
            } else {
4676 28
                break;
4677
            }
4678 28
        }
4679 28
4680 12
        return $node;
4681 12
    }
4682 12
4683 28
    /**
4684
     * This method is a decision point between the different variable types
4685 12
     * availanle in PHP. It peeks the next token and then decides whether it is
4686
     * a regular variable or when the next token is of type <b>T_DOLLAR</b> a
4687 28
     * compound- or variable-variable.
4688
     *
4689
     * <code>
4690
     * ----
4691
     * $foo;
4692
     * ----
4693
     *
4694
     * -----
4695
     * $$foo;
4696
     * -----
4697
     *
4698
     * ------
4699
     * ${FOO};
4700
     * ------
4701
     * </code>
4702
     *
4703
     * @return \PDepend\Source\AST\ASTNode
4704
     * @throws \PDepend\Source\Parser\ParserException
4705
     * @throws UnexpectedTokenException
4706
     * @since 0.9.6
4707
     */
4708
    protected function parseCompoundVariableOrVariableVariableOrVariable()
4709
    {
4710
        if ($this->tokenizer->peek() == Tokens::T_DOLLAR) {
4711
            return $this->parseCompoundVariableOrVariableVariable();
4712
        }
4713
        return $this->parseVariable();
4714
    }
4715 1000
4716
    /**
4717 1000
     * Parses a PHP compound variable or a simple literal node.
4718 62
     *
4719
     * @return \PDepend\Source\AST\ASTNode
4720 984
     * @since 0.9.19
4721
     */
4722 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...
4723
    {
4724
        $this->tokenStack->push();
4725
4726
        // Read the dollar token
4727
        $token = $this->consumeToken(Tokens::T_DOLLAR);
4728
        $this->consumeComments();
4729 16
4730
        // Get next token type
4731 16
        $tokenType = $this->tokenizer->peek();
4732
4733
        switch ($tokenType) {
4734 16
            case Tokens::T_CURLY_BRACE_OPEN:
4735 16
                $variable = $this->builder->buildAstCompoundVariable($token->image);
4736
                $variable->addChild($this->parseCompoundExpression());
4737
                break;
4738 16
            default:
4739
                $variable = $this->builder->buildAstLiteral($token->image);
4740
                break;
4741 16
        }
4742 14
4743 14
        return $this->setNodePositionsAndReturn($variable);
4744 14
    }
4745 2
4746 2
    /**
4747 2
     * This method implements a decision point between compound-variables and
4748 2
     * variable-variable. It expects that the next token in the token-stream is
4749
     * of type <b>T_DOLLAR</b> and removes it from the stream. Then this method
4750 16
     * peeks the next available token when it is of type <b>T_CURLY_BRACE_OPEN</b>
4751
     * this is compound variable, otherwise it can be a variable-variable or a
4752
     * compound-variable.
4753
     *
4754
     * @return \PDepend\Source\AST\ASTNode
4755
     * @throws \PDepend\Source\Parser\ParserException
4756
     * @throws UnexpectedTokenException
4757
     * @since 0.9.6
4758
     */
4759 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...
4760
    {
4761
        $this->tokenStack->push();
4762
4763
        // Read the dollar token
4764
        $token = $this->consumeToken(Tokens::T_DOLLAR);
4765
        $this->consumeComments();
4766 62
4767
        // Get next token type
4768 62
        $tokenType = $this->tokenizer->peek();
4769
4770
        // T_DOLLAR|T_VARIABLE === Variable variable,
4771 62
        // T_CURLY_BRACE_OPEN === Compound variable
4772 62
        switch ($tokenType) {
4773
            case Tokens::T_DOLLAR:
4774
            case Tokens::T_VARIABLE:
4775 62
                $variable = $this->builder->buildAstVariableVariable($token->image);
4776
                $variable->addChild(
4777
                    $this->parseCompoundVariableOrVariableVariableOrVariable()
4778
                );
4779
                break;
4780 62
            default:
4781 62
                $variable = $this->parseCompoundVariable($token);
4782 20
                break;
4783 20
        }
4784 20
4785 20
        return $this->setNodePositionsAndReturn($variable);
4786 20
    }
4787 44
4788 44
    /**
4789 44
     * This method parses a compound variable like:
4790 44
     *
4791
     * <code>
4792 62
     * //     ----------------
4793
     * return ${'Foo' . 'Bar'};
4794
     * //     ----------------
4795
     * </code>
4796
     *
4797
     * @param  \PDepend\Source\Tokenizer\Token $token The dollar token.
4798
     * @return \PDepend\Source\AST\ASTCompoundVariable
4799
     * @since 0.10.0
4800
     */
4801
    private function parseCompoundVariable(Token $token)
4802
    {
4803
        return $this->parseBraceExpression(
4804
            $this->builder->buildAstCompoundVariable($token->image),
4805
            $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN),
4806
            Tokens::T_CURLY_BRACE_CLOSE
4807
        );
4808 44
    }
4809
4810 44
    /**
4811 44
     * This method parses a compound expression like:
4812 44
     *
4813
     * <code>
4814 44
     * //      ------  ------
4815
     * $foo = "{$bar}, {$baz}\n";
4816
     * //      ------  ------
4817
     * </code>
4818
     *
4819
     * or a simple literal token:
4820
     *
4821
     * <code>
4822
     * //      -
4823
     * $foo = "{{$bar}, {$baz}\n";
4824
     * //      -
4825
     * </code>
4826
     *
4827
     * @return \PDepend\Source\AST\ASTNode
4828
     * @since 0.9.10
4829
     */
4830
    private function parseCompoundExpressionOrLiteral()
4831
    {
4832
        $token = $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
4833
        $this->consumeComments();
4834
4835
        switch ($this->tokenizer->peek()) {
4836
            case Tokens::T_DOLLAR:
4837 16
            case Tokens::T_VARIABLE:
4838
                return $this->parseBraceExpression(
4839 16
                    $this->builder->buildAstCompoundExpression(),
4840 16
                    $token,
4841
                    Tokens::T_CURLY_BRACE_CLOSE
4842 16
                );
4843 16
        }
4844 16
4845 16
        $literal = $this->builder->buildAstLiteral($token->image);
4846 16
        $literal->configureLinesAndColumns(
4847 16
            $token->startLine,
4848
            $token->endLine,
4849 16
            $token->startColumn,
4850 2
            $token->endColumn
4851
        );
4852 2
4853 2
        return $literal;
4854 2
    }
4855 2
4856 2
    /**
4857 2
     * This method parses a compound expression node.
4858 2
     *
4859
     * <code>
4860 2
     * ------------------
4861
     * {'_' . foo . $bar}
4862
     * ------------------
4863
     * </code>
4864
     *
4865
     * @return \PDepend\Source\AST\ASTCompoundExpression
4866
     * @throws \PDepend\Source\Parser\ParserException
4867
     * @throws \PDepend\Source\Parser\ParserException
4868
     * @since 0.9.6
4869
     */
4870
    protected function parseCompoundExpression()
4871
    {
4872
        $this->consumeComments();
4873
4874
        return $this->parseBraceExpression(
4875
            $this->builder->buildAstCompoundExpression(),
4876
            $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN),
4877 28
            Tokens::T_CURLY_BRACE_CLOSE
4878
        );
4879 28
    }
4880
4881 28
    /**
4882 28
     * Parses a static identifier expression, as it is used for method and
4883 28
     * function names.
4884
     *
4885 28
     * @param integer $tokenType
4886
     * @return \PDepend\Source\AST\ASTIdentifier
4887
     * @since 0.9.12
4888
     */
4889 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...
4890
    {
4891
        $token = $this->consumeToken($tokenType);
4892
4893
        $node = $this->builder->buildAstIdentifier($token->image);
4894
        $node->configureLinesAndColumns(
4895
            $token->startLine,
4896 312
            $token->endLine,
4897
            $token->startColumn,
4898 312
            $token->endColumn
4899
        );
4900 312
4901 312
        return $node;
4902 312
    }
4903 312
4904 312
    /**
4905 312
     * This method parses a {@link \PDepend\Source\AST\ASTLiteral} node or an
4906 312
     * instance of {@link \PDepend\Source\AST\ASTString} that represents a string
4907
     * in double quotes or surrounded by backticks.
4908 312
     *
4909
     * @return \PDepend\Source\AST\ASTNode
4910
     * @throws UnexpectedTokenException
4911
     */
4912
    protected function parseLiteralOrString()
4913
    {
4914
        $tokenType = $this->tokenizer->peek();
4915
4916
        switch ($tokenType) {
4917
            case Tokens::T_NULL:
4918
            case Tokens::T_TRUE:
4919 951
            case Tokens::T_FALSE:
4920
            case Tokens::T_DNUMBER:
4921 951
            case Tokens::T_CONSTANT_ENCAPSED_STRING:
4922
                $token = $this->consumeToken($tokenType);
4923
4924 951
                $literal = $this->builder->buildAstLiteral($token->image);
4925 951
                $literal->configureLinesAndColumns(
4926 951
                    $token->startLine,
4927 951
                    $token->endLine,
4928 951
                    $token->startColumn,
4929 488
                    $token->endColumn
4930
                );
4931 488
                return $literal;
4932 488
            case Tokens::T_LNUMBER:
4933 488
                return $this->parseIntegerNumber();
4934 488
            default:
4935 488
                return $this->parseString($tokenType);
4936 488
        }
4937 488
    }
4938 488
4939 609
    /**
4940 557
     * Parses an integer value.
4941 54
     *
4942 54
     * @return \PDepend\Source\AST\ASTLiteral
4943 54
     * @since 1.0.0
4944
     */
4945 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...
4946
    {
4947
        $token = $this->consumeToken(Tokens::T_LNUMBER);
4948
4949
        $literal = $this->builder->buildAstLiteral($token->image);
4950
        $literal->configureLinesAndColumns(
4951
            $token->startLine,
4952
            $token->endLine,
4953
            $token->startColumn,
4954
            $token->endColumn
4955
        );
4956
4957
        return $literal;
4958
    }
4959
4960
    /**
4961
     * Parses an array structure.
4962
     *
4963
     * @return \PDepend\Source\AST\ASTArray
4964
     * @since 1.0.0
4965
     */
4966
    protected function doParseArray($static = false)
4967
    {
4968
        $this->tokenStack->push();
4969
4970
        return $this->setNodePositionsAndReturn(
4971
            $this->parseArray(
4972
                $this->builder->buildAstArray(),
4973 214
                $static
4974
            )
4975 214
        );
4976
    }
4977 214
4978 214
    /**
4979 214
     * Tests if the next token is a valid array start delimiter in the supported
4980
     * PHP version.
4981 214
     *
4982 212
     * @return boolean
4983
     * @since 1.0.0
4984
     */
4985
    abstract protected function isArrayStartDelimiter();
4986
4987
    /**
4988
     * Parses a php array declaration.
4989
     *
4990
     * @param \PDepend\Source\AST\ASTArray $array
4991
     * @param boolean                      $static
4992
     *
4993
     * @return \PDepend\Source\AST\ASTArray
4994
     * @since 1.0.0
4995
     */
4996
    abstract protected function parseArray(ASTArray $array, $static = false);
4997
4998
    /**
4999
     * Parses all elements in an array.
5000
     *
5001
     * @param  \PDepend\Source\AST\ASTArray $array
5002
     * @param  integer                      $endDelimiter
5003
     * @param  boolean                      $static
5004
     * @return \PDepend\Source\AST\ASTArray
5005
     * @since 1.0.0
5006
     */
5007 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...
5008
    {
5009
        $this->consumeComments();
5010
        while ($endDelimiter !== $this->tokenizer->peek()) {
5011
            $array->addChild($this->parseArrayElement($static));
5012
5013
            $this->consumeComments();
5014 214
            if (Tokens::T_COMMA === $this->tokenizer->peek()) {
5015
                $this->consumeToken(Tokens::T_COMMA);
5016 214
                $this->consumeComments();
5017 214
            }
5018 160
        }
5019
        return $array;
5020 158
    }
5021 158
5022 84
    /**
5023 84
     * Parses a single array element.
5024 84
     *
5025 158
     * An array element can have a simple value, a key/value pair, a value by
5026 212
     * reference or a key/value pair with a referenced value.
5027
     *
5028
     * @param  boolean $static
5029
     * @return \PDepend\Source\AST\ASTArrayElement
5030
     * @since 1.0.0
5031
     */
5032
    protected function parseArrayElement($static = false)
5033
    {
5034
        $this->consumeComments();
5035
5036
        $this->tokenStack->push();
5037
5038
        $element = $this->builder->buildAstArrayElement();
5039 160
        if ($this->parseOptionalByReference()) {
5040
            if ($static) {
5041 160
                $tokens = $this->tokenStack->pop();
5042
5043 160
                throw $this->getUnexpectedTokenException(end($tokens));
0 ignored issues
show
Security Bug introduced by
It seems like end($tokens) targeting end() can also be of type false; however, PDepend\Source\Language\...xpectedTokenException() does only seem to accept null|object<PDepend\Source\Tokenizer\Token>, did you maybe forget to handle an error condition?
Loading history...
5044
            }
5045 160
5046 160
            $element->setByReference();
5047 22
        }
5048 2
5049
        $this->consumeComments();
5050 2
        if ($this->isKeyword($this->tokenizer->peek())) {
5051
            throw $this->getUnexpectedTokenException();
5052
        }
5053 20
5054 20
        $element->addChild($this->parseExpression());
5055
5056 158
        $this->consumeComments();
5057 158
        if (Tokens::T_DOUBLE_ARROW === $this->tokenizer->peek()) {
5058
            $this->consumeToken(Tokens::T_DOUBLE_ARROW);
5059
            $this->consumeComments();
5060
5061 158
            if ($this->parseOptionalByReference()) {
5062
                $element->setByReference();
5063 158
            }
5064 158
            $element->addChild($this->parseExpression());
5065 70
        }
5066 70
5067
        return $this->setNodePositionsAndReturn($element);
5068 70
    }
5069 18
5070 18
    /**
5071 70
     * Parses a here- or nowdoc string instance.
5072 70
     *
5073
     * @return \PDepend\Source\AST\ASTHeredoc
5074 158
     * @since 0.9.12
5075
     */
5076 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...
5077
    {
5078
        $this->tokenStack->push();
5079
        $this->consumeToken(Tokens::T_START_HEREDOC);
5080
5081
        $heredoc = $this->builder->buildAstHeredoc();
5082
        $this->parseStringExpressions($heredoc, Tokens::T_END_HEREDOC);
5083 16
5084
        $token = $this->consumeToken(Tokens::T_END_HEREDOC);
5085 16
        $heredoc->setDelimiter($token->image);
5086 16
5087
        return $this->setNodePositionsAndReturn($heredoc);
5088 16
    }
5089 16
5090
    /**
5091 16
     * Parses a simple string sequence between two tokens of the same type.
5092 16
     *
5093
     * @param integer $tokenType The start/stop token type.
5094 16
     *
5095
     * @return string
5096
     * @since 0.9.10
5097
     */
5098
    private function parseStringSequence($tokenType)
5099
    {
5100
        $type   = $tokenType;
5101
        $string = '';
5102
5103
        do {
5104
            $string .= $this->consumeToken($type)->image;
5105
            $type    = $this->tokenizer->peek();
5106
        } while ($type != $tokenType && $type != Tokenizer::T_EOF);
5107
5108
        return $string . $this->consumeToken($tokenType)->image;
5109
    }
5110
5111
    /**
5112
     * This method parses a php string with all possible embedded expressions.
5113
     *
5114
     * <code>
5115
     * $string = "Manuel $Pichler <{$email}>";
5116
     *
5117
     * // \PDepend\Source\AST\ASTSTring
5118
     * // |-- ASTLiteral             -  "Manuel ")
5119
     * // |-- ASTVariable            -  $Pichler
5120
     * // |-- ASTLiteral             -  " <"
5121
     * // |-- ASTCompoundExpression  -  {...}
5122
     * // |   |-- ASTVariable        -  $email
5123
     * // |-- ASTLiteral             -  ">"
5124
     * </code>
5125
     *
5126
     * @param integer $delimiterType The start/stop token type.
5127
     *
5128
     * @return \PDepend\Source\AST\ASTString
5129
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
5130
     * @since 0.9.10
5131
     */
5132
    private function parseString($delimiterType)
5133
    {
5134
        $token = $this->consumeToken($delimiterType);
5135
5136
        $string = $this->builder->buildAstString();
5137
        $startLine = $token->startLine;
5138
        $startColumn = $token->startColumn;
5139 54
5140
        $this->parseStringExpressions($string, $delimiterType);
5141 54
5142
        $token = $this->consumeToken($delimiterType);
5143 54
        $endLine = $token->endLine;
5144 54
        $endColumn = $token->endColumn;
5145 54
5146
        $string->configureLinesAndColumns(
5147 54
            $startLine,
5148
            $endLine,
5149 54
            $startColumn,
5150 52
            $endColumn
5151 52
        );
5152
5153 52
        return $string;
5154 52
    }
5155 52
5156 52
    /**
5157
     * This method parses the contents of a string or here-/now-doc node. It
5158 52
     * will not consume the given stop token, so it is up to the calling method
5159
     * to consume the stop token. The return value of this method is the prepared
5160 52
     * input string node.
5161
     *
5162
     * @param  \PDepend\Source\AST\ASTNode $node
5163
     * @param  integer                     $stopToken
5164
     * @return \PDepend\Source\AST\ASTNode
5165
     * @since 0.9.12
5166
     */
5167
    private function parseStringExpressions(ASTNode $node, $stopToken)
5168
    {
5169
        while (($tokenType = $this->tokenizer->peek()) != Tokenizer::T_EOF) {
5170
            switch ($tokenType) {
5171
                case $stopToken:
5172
                    break 2;
5173
                case Tokens::T_BACKSLASH:
5174 70
                    $node->addChild($this->parseEscapedAstLiteralString());
5175
                    break;
5176 70
                case Tokens::T_DOLLAR:
5177
                    $node->addChild($this->parseCompoundVariableOrLiteral());
5178 70
                    break;
5179 68
                case Tokens::T_VARIABLE:
5180 70
                    $node->addChild($this->parseVariable());
5181
                    break;
5182
                case Tokens::T_CURLY_BRACE_OPEN:
5183 70
                    $node->addChild($this->parseCompoundExpressionOrLiteral());
5184 16
                    break;
5185 16
                default:
5186 66
                    $node->addChild($this->parseLiteral());
5187 40
                    break;
5188 40
            }
5189 62
        }
5190 16
        return $node;
5191 16
    }
5192 60
5193 60
    /**
5194 60
     * This method parses an escaped sequence of literal tokens.
5195 60
     *
5196 70
     * @return \PDepend\Source\AST\ASTLiteral
5197 70
     * @since 0.9.10
5198
     */
5199
    private function parseEscapedAstLiteralString()
5200
    {
5201
        $this->tokenStack->push();
5202
5203
        $image  = $this->consumeToken(Tokens::T_BACKSLASH)->image;
5204
        $escape = true;
5205
5206
        $tokenType = $this->tokenizer->peek();
5207
        while ($tokenType != Tokenizer::T_EOF) {
5208
            if ($tokenType === Tokens::T_BACKSLASH) {
5209
                $escape != $escape;
5210
                $image  .= $this->consumeToken(Tokens::T_BACKSLASH)->image;
5211
5212
                $tokenType = $this->tokenizer->peek();
5213
                continue;
5214
            }
5215
5216
            if ($escape) {
5217
                $image .= $this->consumeToken($tokenType)->image;
5218
                break;
5219
            }
5220
        }
5221
        return $this->setNodePositionsAndReturn(
5222
            $this->builder->buildAstLiteral($image)
5223
        );
5224
    }
5225
5226
    /**
5227
     * This method parses a simple literal and configures the position
5228
     * properties.
5229
     *
5230
     * @return \PDepend\Source\AST\ASTLiteral
5231
     * @since 0.9.10
5232
     */
5233 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...
5234
    {
5235
        $token = $this->consumeToken($this->tokenizer->peek());
5236
5237
        $node = $this->builder->buildAstLiteral($token->image);
5238
        $node->configureLinesAndColumns(
5239
            $token->startLine,
5240 64
            $token->endLine,
5241
            $token->startColumn,
5242 64
            $token->endColumn
5243
        );
5244 64
5245 64
        return $node;
5246 64
    }
5247 64
5248 64
    /**
5249 64
     * Extracts all dependencies from a callable signature.
5250 64
     *
5251
     * @return \PDepend\Source\AST\ASTFormalParameters
5252 64
     * @since 0.9.5
5253
     */
5254
    private function parseFormalParameters()
5255
    {
5256
        $this->consumeComments();
5257
5258
        $this->tokenStack->push();
5259
5260
        $formalParameters = $this->builder->buildAstFormalParameters();
5261 1965
5262
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
5263 1965
        $this->consumeComments();
5264
5265 1965
        $tokenType = $this->tokenizer->peek();
5266
5267 1965
        // Check for function without parameters
5268
        if ($tokenType === Tokens::T_PARENTHESIS_CLOSE) {
5269 1965
            $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
5270 1963
            return $this->setNodePositionsAndReturn($formalParameters);
5271
        }
5272 1963
5273 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...
5274
            $formalParameters->addChild(
5275 1963
                $this->parseFormalParameterOrTypeHintOrByReference()
5276 1335
            );
5277 1335
5278
            $this->consumeComments();
5279
            $tokenType = $this->tokenizer->peek();
5280 700
5281 700
            // Check for following parameter
5282 700
            if ($tokenType !== Tokens::T_COMMA) {
5283 688
                break;
5284
            }
5285 688
            $this->consumeToken(Tokens::T_COMMA);
5286 688
        }
5287
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
5288
5289 688
        return $this->setNodePositionsAndReturn($formalParameters);
5290 688
    }
5291
5292 156
    /**
5293 156
     * This method parses a formal parameter in all it's variations.
5294 688
     *
5295
     * <code>
5296 686
     * //                ------------
5297
     * function traverse(Iterator $it) {}
5298
     * //                ------------
5299
     *
5300
     * //                ---------
5301
     * function traverse(array $ar) {}
5302
     * //                ---------
5303
     *
5304
     * //                ---
5305
     * function traverse(&$x) {}
5306
     * //                ---
5307
     * </code>
5308
     *
5309
     * @return \PDepend\Source\AST\ASTFormalParameter
5310
     * @since 0.9.6
5311
     */
5312
    protected function parseFormalParameterOrTypeHintOrByReference()
5313
    {
5314
        $this->consumeComments();
5315
        $tokenType = $this->tokenizer->peek();
5316
5317
        $this->tokenStack->push();
5318
5319 700
        switch ($tokenType) {
5320
            case Tokens::T_ARRAY:
5321 700
                $parameter = $this->parseFormalParameterAndArrayTypeHint();
5322 700
                break;
5323
            case ($this->isTypeHint($tokenType)):
5324 700
                $parameter = $this->parseFormalParameterAndTypeHint();
5325
                break;
5326
            case Tokens::T_SELF:
5327 700
                $parameter = $this->parseFormalParameterAndSelfTypeHint();
5328 70
                break;
5329 68
            case Tokens::T_PARENT:
5330 642
                $parameter = $this->parseFormalParameterAndParentTypeHint();
5331 108
                break;
5332 108
            case Tokens::T_BITWISE_AND:
5333 580
                $parameter = $this->parseFormalParameterAndByReference();
5334 4
                break;
5335 4
            default:
5336 576
                $parameter = $this->parseFormalParameter();
5337 8
                break;
5338 4
        }
5339 568
        return $this->setNodePositionsAndReturn($parameter);
5340 42
    }
5341 42
5342 532
    /**
5343 532
     * This method parses a formal parameter that has an array type hint.
5344 526
     *
5345 532
     * <code>
5346 688
     * //                ---------
5347
     * function traverse(array $ar) {}
5348
     * //                ---------
5349
     * </code>
5350
     *
5351
     * @return \PDepend\Source\AST\ASTFormalParameter
5352
     * @since 0.9.6
5353
     */
5354
    private function parseFormalParameterAndArrayTypeHint()
5355
    {
5356
        $node = $this->parseArrayType();
5357
5358
        $parameter = $this->parseFormalParameterOrByReference();
5359
        $parameter->prependChild($node);
5360
5361 70
        return $parameter;
5362
    }
5363 70
5364
    /**
5365 70
     * @return \PDepend\Source\AST\ASTTypeArray
5366 68
     */
5367 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...
5368 68
    {
5369
        $token = $this->consumeToken(Tokens::T_ARRAY);
5370
5371
        $type = $this->builder->buildAstTypeArray();
5372
        $type->configureLinesAndColumns(
5373
            $token->startLine,
5374 74
            $token->endLine,
5375
            $token->startColumn,
5376 74
            $token->endColumn
5377
        );
5378 74
5379 74
        return $type;
5380 74
    }
5381 74
5382 74
    /**
5383 74
     * This method parses a formal parameter that has a regular class type hint.
5384 74
     *
5385
     * <code>
5386 74
     * //                ------------
5387
     * function traverse(Iterator $it) {}
5388
     * //                ------------
5389
     * </code>
5390
     *
5391
     * @return \PDepend\Source\AST\ASTFormalParameter
5392
     * @since 0.9.6
5393
     */
5394
    private function parseFormalParameterAndTypeHint()
5395
    {
5396
        $this->tokenStack->push();
5397
5398
        $classReference = $this->setNodePositionsAndReturn(
5399
            $this->parseTypeHint()
0 ignored issues
show
Bug introduced by
It seems like $this->parseTypeHint() can be null; however, setNodePositionsAndReturn() 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...
5400
        );
5401 108
5402
        $parameter = $this->parseFormalParameterOrByReference();
5403 108
        $parameter->prependChild($classReference);
5404
5405 108
        return $parameter;
5406 108
    }
5407 108
5408
    /**
5409 108
     * This method will parse a formal parameter that has the keyword parent as
5410 108
     * parameter type hint.
5411
     *
5412 108
     * <code>
5413
     * class Foo extends Bar
5414
     * {
5415
     *     //                   ---------
5416
     *     public function test(parent $o) {}
5417
     *     //                   ---------
5418
     * }
5419
     * </code>
5420
     *
5421
     * @return \PDepend\Source\AST\ASTFormalParameter
5422
     * @throws \PDepend\Source\Parser\InvalidStateException
5423
     * @since 0.9.6
5424
     */
5425
    private function parseFormalParameterAndParentTypeHint()
5426
    {
5427
        $reference = $this->parseParentType();
5428
        $parameter = $this->parseFormalParameterOrByReference();
5429
        $parameter->prependChild($reference);
5430
5431
        return $parameter;
5432 8
    }
5433
5434 8
    /**
5435 4
     * @return \PDepend\Source\AST\ASTParentReference
5436 4
     */
5437
    protected function parseParentType()
5438 4
    {
5439
        return $this->parseParentReference($this->consumeToken(Tokens::T_PARENT));
5440
    }
5441
5442
    /**
5443
     * This method will parse a formal parameter that has the keyword self as
5444 8
     * parameter type hint.
5445
     *
5446 8
     * <code>
5447
     * class Foo
5448
     * {
5449
     *     //                   -------
5450
     *     public function test(self $o) {}
5451
     *     //                   -------
5452
     * }
5453
     * </code>
5454
     *
5455
     * @return \PDepend\Source\AST\ASTFormalParameter
5456
     * @since 0.9.6
5457
     */
5458
    private function parseFormalParameterAndSelfTypeHint()
5459
    {
5460
        $self = $this->parseSelfType();
5461
5462
        $parameter = $this->parseFormalParameterOrByReference();
5463
        $parameter->addChild($self);
5464
5465 4
        return $parameter;
5466
    }
5467 4
5468
    /**
5469 4
     * @return \PDepend\Source\AST\ASTSelfReference
5470 4
     */
5471
    protected function parseSelfType()
5472 4
    {
5473
        return $this->parseSelfReference($this->consumeToken(Tokens::T_SELF));
5474
    }
5475
5476
    /**
5477
     * This method will parse a formal parameter that can optionally be passed
5478 4
     * by reference.
5479
     *
5480 4
     * <code>
5481
     * //                 ---  -------
5482
     * function foo(array &$x, $y = 42) {}
5483
     * //                 ---  -------
5484
     * </code>
5485
     *
5486
     * @return \PDepend\Source\AST\ASTFormalParameter
5487
     * @since 0.9.6
5488
     */
5489
    protected function parseFormalParameterOrByReference()
5490
    {
5491
        $this->consumeComments();
5492
        if ($this->tokenizer->peek() === Tokens::T_BITWISE_AND) {
5493
            return $this->parseFormalParameterAndByReference();
5494
        }
5495
        return $this->parseFormalParameter();
5496 184
    }
5497
5498 184
    /**
5499 184
     * This method will parse a formal parameter that is passed by reference.
5500 22
     *
5501
     * <code>
5502 162
     * //                 ---  --------
5503
     * function foo(array &$x, &$y = 42) {}
5504
     * //                 ---  --------
5505
     * </code>
5506
     *
5507
     * @return \PDepend\Source\AST\ASTFormalParameter
5508
     * @since 0.9.6
5509
     */
5510
    private function parseFormalParameterAndByReference()
5511
    {
5512
        $this->consumeToken(Tokens::T_BITWISE_AND);
5513
        $this->consumeComments();
5514
5515
        $parameter = $this->parseFormalParameter();
5516
        $parameter->setPassedByReference();
5517 64
5518
        return $parameter;
5519 64
    }
5520 64
5521
    /**
5522 64
     * This method will parse a formal parameter. A formal parameter is at least
5523 64
     * a variable name, but can also contain a default parameter value.
5524
     *
5525 64
     * <code>
5526
     * //               --  -------
5527
     * function foo(Bar $x, $y = 42) {}
5528
     * //               --  -------
5529
     * </code>
5530
     *
5531
     * @return \PDepend\Source\AST\ASTFormalParameter
5532
     * @since 0.9.6
5533
     */
5534
    protected function parseFormalParameter()
5535
    {
5536
        $parameter = $this->builder->buildAstFormalParameter();
5537
        $parameter->addChild($this->parseVariableDeclarator());
5538
5539
        return $parameter;
5540
    }
5541
5542
    /**
5543
     * Tests if the given token type is a valid formal parameter in the supported
5544
     * PHP version.
5545
     *
5546
     * @param integer $tokenType
5547
     * @return boolean
5548
     * @since 1.0.0
5549
     */
5550 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...
5551
    {
5552
        switch ($tokenType) {
5553
            case Tokens::T_STRING:
5554
            case Tokens::T_BACKSLASH:
5555
            case Tokens::T_NAMESPACE:
5556
                return true;
5557 638
        }
5558
        return false;
5559
    }
5560 638
5561 638
    /**
5562 638
     * Parses a type hint that is valid in the supported PHP version.
5563 104
     *
5564
     * @return \PDepend\Source\AST\ASTNode|null
5565 580
     * @since 1.0.0
5566
     */
5567
    protected function parseTypeHint()
5568
    {
5569
        switch ($this->tokenizer->peek()) {
5570
            case Tokens::T_STRING:
5571
            case Tokens::T_BACKSLASH:
5572
            case Tokens::T_NAMESPACE:
5573
                return $this->builder->buildAstClassOrInterfaceReference(
5574
                    $this->parseQualifiedName()
5575
                );
5576
        }
5577
5578
        return null;
5579
    }
5580
5581
    /**
5582
     * Extracts all dependencies from a callable body.
5583
     *
5584
     * @return \PDepend\Source\AST\ASTScope
5585
     * @since 0.9.12
5586
     */
5587 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...
5588
    {
5589
        $scope = $this->builder->buildAstScope();
5590
5591
        $this->tokenStack->push();
5592
5593
        $this->consumeComments();
5594 1917
        $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
5595
5596 1917
        while (($stmt = $this->parseOptionalStatement()) !== null) {
5597
            // TODO: Remove if-statement once, we have translated functions and
5598 1917
            //       closures into ast-nodes
5599
            if ($stmt instanceof \PDepend\Source\AST\ASTNode) {
5600 1917
                $scope->addChild($stmt);
5601 1917
            }
5602
        }
5603 1917
5604
        $this->consumeComments();
5605
        $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
5606 1530
5607 1530
        return $this->setNodePositionsAndReturn($scope);
5608 1530
    }
5609 1530
5610
    /**
5611 1870
     * Parse a statement.
5612 1870
     *
5613
     * @return \PDepend\Source\AST\ASTNode
5614 1870
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
5615
     * @throws \PDepend\Source\Parser\TokenStreamEndException
5616
     * @since 1.0.0
5617
     */
5618
    private function parseStatement()
5619
    {
5620
        if (is_object($stmt = $this->parseOptionalStatement())) {
5621
            return $stmt;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $stmt; (PDepend\Source\AST\ASTNo...end\Source\AST\ASTClass) is incompatible with the return type documented by PDepend\Source\Language\...PParser::parseStatement of type PDepend\Source\AST\ASTNode.

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...
5622
        }
5623
        if (is_object($token = $this->tokenizer->next())) {
5624
            throw $this->getUnexpectedTokenException($token);
5625 28
        }
5626
        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...
5627 28
    }
5628 26
5629
    /**
5630 2
     * Parses an optional statement or returns <b>null</b>.
5631 2
     *
5632
     * @return \PDepend\Source\AST\ASTNode|null
5633
     * @since 0.9.8
5634
     */
5635
    private function parseOptionalStatement()
5636
    {
5637
        $tokenType = $this->tokenizer->peek();
5638
5639
        switch ($tokenType) {
5640
            case Tokens::T_ECHO:
5641
                return $this->parseEchoStatement();
5642 2369
            case Tokens::T_SWITCH:
5643
                return $this->parseSwitchStatement();
5644 2369
            case Tokens::T_TRY:
5645
                return $this->parseTryStatement();
5646
            case Tokens::T_THROW:
5647 2369
                return $this->parseThrowStatement();
5648 188
            case Tokens::T_IF:
5649 2367
                return $this->parseIfStatement();
5650 44
            case Tokens::T_FOR:
5651 2367
                return $this->parseForStatement();
5652 58
            case Tokens::T_FOREACH:
5653 2367
                return $this->parseForeachStatement();
5654 30
            case Tokens::T_DO:
5655 2367
                return $this->parseDoWhileStatement();
5656 196
            case Tokens::T_WHILE:
5657 2367
                return $this->parseWhileStatement();
5658 98
            case Tokens::T_RETURN:
5659 2367
                return $this->parseReturnStatement();
5660 110
            case Tokens::T_BREAK:
5661 2367
                return $this->parseBreakStatement();
5662 18
            case Tokens::T_CONTINUE:
5663 2367
                return $this->parseContinueStatement();
5664 12
            case Tokens::T_GOTO:
5665 2367
                return $this->parseGotoStatement();
5666 611
            case Tokens::T_GLOBAL:
5667 2367
                return $this->parseGlobalStatement();
5668 48
            case Tokens::T_UNSET:
5669 2367
                return $this->parseUnsetStatement();
5670 8
            case Tokens::T_STRING:
5671 2367
                if ($this->tokenizer->peekNext() === Tokens::T_COLON) {
5672 16
                    return $this->parseLabelStatement();
5673 2367
                }
5674 8
                break;
5675 2367
            case Tokens::T_CONST:
5676 2
                return $this->parseConstantDefinition();
5677 2367
            case Tokens::T_FUNCTION:
5678 322
                return $this->parseFunctionOrClosureDeclaration();
5679 16
            case Tokens::T_COMMENT:
5680
                return $this->parseCommentWithOptionalInlineClassOrInterfaceReference();
5681 306
            case Tokens::T_DOC_COMMENT:
5682 2367
                return $this->builder->buildAstComment(
5683 4
                    $this->consumeToken(Tokens::T_DOC_COMMENT)->image
5684 2365
                );
5685 1557
            case Tokens::T_CURLY_BRACE_OPEN:
5686 2342
                return $this->parseRegularScope();
5687 36
            case Tokens::T_DECLARE:
5688 2342
                return $this->parseDeclareStatement();
5689 8
            case Tokens::T_ELSE:
5690 8
            case Tokens::T_ENDIF:
5691 8
            case Tokens::T_ELSEIF:
5692 2342
            case Tokens::T_ENDFOR:
5693 2
            case Tokens::T_ENDWHILE:
5694 2342
            case Tokens::T_ENDSWITCH:
5695 28
            case Tokens::T_ENDDECLARE:
5696 2342
            case Tokens::T_ENDFOREACH:
5697 2342
            case Tokens::T_CURLY_BRACE_CLOSE:
5698 2342
                return null;
5699 2342
            case Tokens::T_DECLARE:
5700 2342
                return $this->parseDeclareStatement();
5701 2342
            case Tokens::T_CLOSE_TAG:
5702 2342
                if (($tokenType = $this->parseNonePhpCode()) === Tokenizer::T_EOF) {
5703 2342
                    return null;
5704 2342
                }
5705 1912
5706 1228
                return $this->parseOptionalStatement();
5707 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...
5708 1228
                $package = $this->getNamespaceOrPackage();
5709 12
                $package->addType($trait = $this->parseTraitDeclaration());
5710
5711
                $this->builder->restoreTrait($trait);
5712
                $this->compilationUnit->addChild($trait);
5713 12
5714 1222
                return $trait;
5715 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...
5716 110
                $package = $this->getNamespaceOrPackage();
5717
                $package->addType($interface = $this->parseInterfaceDeclaration());
5718 110
5719 110
                $this->builder->restoreInterface($interface);
5720
                $this->compilationUnit->addChild($interface);
5721 110
5722 1178
                return $interface;
5723 154
            case Tokens::T_CLASS:
5724 154
            case Tokens::T_FINAL:
5725 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...
5726 152
                $package = $this->getNamespaceOrPackage();
5727 152
                $package->addType($class = $this->parseClassDeclaration());
5728
5729 152
                $this->builder->restoreClass($class);
5730 1086
                $this->compilationUnit->addChild($class);
5731 1086
5732 1086
                return $class;
5733 698
            case Tokens::T_YIELD:
5734 698
                return $this->parseYield();
5735
        }
5736 680
5737 680
        $this->tokenStack->push();
5738
        $stmt = $this->builder->buildAstStatement();
5739 680
5740 510
        if (($expr = $this->parseOptionalExpression()) != null) {
5741 14
            $stmt->addChild($expr);
5742
        }
5743
        $this->parseStatementTermination();
5744 738
5745 738
        return $this->setNodePositionsAndReturn($stmt);
5746
    }
5747 738
5748 688
    /**
5749 688
     * Parses a sequence of none php code tokens and returns the token type of
5750 710
     * the next token.
5751
     *
5752 704
     * @return integer
5753
     * @since 0.9.12
5754
     */
5755
    private function parseNonePhpCode()
5756
    {
5757
        $this->consumeToken(Tokens::T_CLOSE_TAG);
5758
5759
        $this->tokenStack->push();
5760
        while (($tokenType = $this->tokenizer->peek()) !== Tokenizer::T_EOF) {
5761
            switch ($tokenType) {
5762 368
                case Tokens::T_OPEN_TAG:
5763
                case Tokens::T_OPEN_TAG_WITH_ECHO:
5764 368
                    $this->consumeToken($tokenType);
5765
                    $tokenType = $this->tokenizer->peek();
5766 360
                    break 2;
5767 360
                default:
5768
                    $this->consumeToken($tokenType);
5769 22
                    break;
5770 22
            }
5771 22
        }
5772 22
        $this->tokenStack->pop();
5773 22
5774 20
        return $tokenType;
5775 20
    }
5776 20
5777 20
    /**
5778 20
     * Parses a comment and optionally an embedded class or interface type
5779 360
     * annotation.
5780
     *
5781 360
     * @return \PDepend\Source\AST\ASTComment
5782
     * @since 0.9.8
5783
     */
5784
    private function parseCommentWithOptionalInlineClassOrInterfaceReference()
5785
    {
5786
        $token = $this->consumeToken(Tokens::T_COMMENT);
5787
5788
        $comment = $this->builder->buildAstComment($token->image);
5789
        if (preg_match(self::REGEXP_INLINE_TYPE, $token->image, $match)) {
5790
            $image = $this->useSymbolTable->lookup($match[1]) ?: $match[1];
5791 36
5792
            $comment->addChild(
5793 36
                $this->builder->buildAstClassOrInterfaceReference($image)
5794
            );
5795 36
        }
5796 36
5797 8
        $comment->configureLinesAndColumns(
5798
            $token->startLine,
5799 8
            $token->endLine,
5800 8
            $token->startColumn,
5801 8
            $token->endColumn
5802 8
        );
5803
        return $comment;
5804 36
    }
5805 36
5806 36
    /**
5807 36
     * Parses an optional set of bound closure variables.
5808 36
     *
5809 36
     * @param \PDepend\Source\AST\ASTClosure $closure The context closure instance.
5810 36
     *
5811
     * @return \PDepend\Source\AST\ASTClosure
5812
     * @since 1.0.0
5813
     */
5814
    private function parseOptionalBoundVariables(
5815
        \PDepend\Source\AST\ASTClosure $closure
5816
    ) {
5817
        $this->consumeComments();
5818
        if (Tokens::T_USE === $this->tokenizer->peek()) {
5819
            return $this->parseBoundVariables($closure);
5820
        }
5821 52
        return $closure;
5822
    }
5823
5824 52
    /**
5825 52
     * Parses a list of bound closure variables.
5826
     *
5827
     * @param \PDepend\Source\AST\ASTClosure $closure The parent closure instance.
5828 52
     *
5829
     * @return \PDepend\Source\AST\ASTClosure
5830
     * @since 0.9.5
5831
     */
5832
    private function parseBoundVariables(\PDepend\Source\AST\ASTClosure $closure)
5833
    {
5834
        $this->consumeToken(Tokens::T_USE);
5835
5836
        $this->consumeComments();
5837
        $this->consumeToken(Tokens::T_PARENTHESIS_OPEN);
5838
5839
        while ($this->tokenizer->peek() !== Tokenizer::T_EOF) {
5840
            $this->consumeComments();
5841
5842
            if ($this->tokenizer->peek() === Tokens::T_BITWISE_AND) {
5843
                $this->consumeToken(Tokens::T_BITWISE_AND);
5844
                $this->consumeComments();
5845
            }
5846
5847
            $this->consumeToken(Tokens::T_VARIABLE);
5848
            $this->consumeComments();
5849
5850
            if ($this->tokenizer->peek() === Tokens::T_COMMA) {
5851
                $this->consumeToken(Tokens::T_COMMA);
5852
                continue;
5853
            }
5854
            break;
5855
        }
5856
5857
        $this->consumeComments();
5858
        $this->consumeToken(Tokens::T_PARENTHESIS_CLOSE);
5859
5860
        return $closure;
5861
    }
5862
5863
    /**
5864
     * Parses a php class/method name chain.
5865
     *
5866
     * <code>
5867
     * PDepend\Source\Parser::parse();
5868
     * </code>
5869
     *
5870
     * @throws NoActiveScopeException
5871
     *
5872
     * @return string
5873
     * @link   http://php.net/manual/en/language.namespaces.importing.php
5874
     */
5875
    protected function parseQualifiedName()
5876
    {
5877
        $fragments = $this->parseQualifiedNameRaw();
5878
5879
        // Check for fully qualified name
5880
        if ($fragments[0] === '\\') {
5881
            return join('', $fragments);
5882 1102
        } elseif ($this->isScalarOrCallableTypeHint($fragments[0])) {
5883
            return $fragments[0];
5884 1102
        }
5885
5886
        // Search for an use alias
5887 1100
        $mapsTo = $this->useSymbolTable->lookup($fragments[0]);
5888 48
        if ($mapsTo !== null) {
5889 1086
            // Remove alias and add real namespace
5890 32
            array_shift($fragments);
5891
            array_unshift($fragments, $mapsTo);
5892
        } elseif ($this->namespaceName !== null
5893
            && $this->namespacePrefixReplaced === false
5894 1060
        ) {
5895 1060
            // Prepend current namespace
5896
            array_unshift($fragments, $this->namespaceName, '\\');
5897 20
        }
5898 20
        return join('', $fragments);
5899 1060
    }
5900 1050
5901 1050
    /**
5902
     * This method parses a qualified PHP 5.3 class, interface and namespace
5903 46
     * identifier and returns the collected tokens as a string array.
5904 46
     *
5905 1060
     * @return array<string>
5906
     * @since 0.9.5
5907
     */
5908
    protected function parseQualifiedNameRaw()
5909
    {
5910
        // Reset namespace prefix flag
5911
        $this->namespacePrefixReplaced = false;
5912
5913
        // Consume comments and fetch first token type
5914
        $this->consumeComments();
5915 1106
        $tokenType = $this->tokenizer->peek();
5916
5917
        $qualifiedName = array();
5918 1106
5919
        if ($tokenType === Tokens::T_NAMESPACE) {
5920
            // Consume namespace keyword
5921 1106
            $this->consumeToken(Tokens::T_NAMESPACE);
5922 1106
            $this->consumeComments();
5923
5924 1106
            // Add current namespace as first token
5925
            $qualifiedName = array((string) $this->namespaceName);
5926 1106
5927
            // Set prefixed flag to true
5928 40
            $this->namespacePrefixReplaced = true;
5929 40
        } elseif ($this->isClassName($tokenType)) {
5930
            $qualifiedName[] = $this->parseClassName();
5931
5932 40
            $this->consumeComments();
5933
            $tokenType = $this->tokenizer->peek();
5934
5935 40
            // Stop here for simple identifier
5936 1106
            if ($tokenType !== Tokens::T_BACKSLASH) {
5937 1080
                return $qualifiedName;
5938
            }
5939 1080
        }
5940 1080
5941
        do {
5942
            // Next token must be a namespace separator
5943 1080
            $this->consumeToken(Tokens::T_BACKSLASH);
5944 1042
            $this->consumeComments();
5945
5946 70
            // Append to qualified name
5947
            $qualifiedName[] = '\\';
5948
5949
            if ($nextElement = $this->parseQualifiedNameElement($qualifiedName)) {
5950 152
                $qualifiedName[] = $nextElement;
5951 150
            }
5952
5953
            $this->consumeComments();
5954 150
5955
            // Get next token type
5956 150
            $tokenType = $this->tokenizer->peek();
5957 148
        } while ($tokenType === Tokens::T_BACKSLASH);
5958 148
5959
        return $qualifiedName;
5960 148
    }
5961
5962
    /**
5963 148
     * Determines if the given image is a PHP 7 type hint.
5964 148
     *
5965
     * @param string $image
5966 146
     * @return boolean
5967
     */
5968
    protected function isScalarOrCallableTypeHint($image)
5969
    {
5970
        // Scalar & callable type hints were not present in PHP 5
5971
        return false;
5972
    }
5973
5974
    /**
5975
     * @param array $previousElements
5976
     * @return string
5977
     */
5978
    protected function parseQualifiedNameElement(array $previousElements)
5979
    {
5980
        return $this->parseClassName();
5981
    }
5982
5983
    /**
5984
     * This method parses a PHP 5.3 namespace declaration.
5985 148
     *
5986
     * @throws NoActiveScopeException
5987 148
     *
5988
     * @return void
5989
     * @since 0.9.5
5990
     */
5991
    private function parseNamespaceDeclaration()
5992
    {
5993
        // Consume namespace keyword and strip optional comments
5994
        $this->consumeToken(Tokens::T_NAMESPACE);
5995
        $this->consumeComments();
5996
5997
        $tokenType = $this->tokenizer->peek();
5998 148
5999
        // Search for a namespace identifier
6000
        if ($this->isClassName($tokenType)) {
6001 148
            // Reset namespace property
6002 148
            $this->namespaceName = null;
6003
6004 148
            $qualifiedName = $this->parseQualifiedName();
6005
6006
            $this->consumeComments();
6007 148
            if ($this->tokenizer->peek() === Tokens::T_CURLY_BRACE_OPEN) {
6008
                $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
6009 138
            } else {
6010
                $this->consumeToken(Tokens::T_SEMICOLON);
6011 138
            }
6012
6013 138
            // Create a package for this namespace
6014 138
            $this->namespaceName = $qualifiedName;
6015 48
6016 48
            $this->useSymbolTable->resetScope();
6017 90
        } elseif ($tokenType === Tokens::T_BACKSLASH) {
6018
            // Same namespace reference, something like:
6019
            //   new namespace\Foo();
6020
            // or:
6021 138
            //   $x = namespace\foo::bar();
6022
6023 138
            // Now parse a qualified name
6024 148
            $this->parseQualifiedNameRaw();
6025
        } else {
6026
            // Consume opening curly brace
6027
            $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
6028
6029
            // Create a package for this namespace
6030
            $this->namespaceName = '';
6031 2
6032
            $this->useSymbolTable->resetScope();
6033
        }
6034 14
6035
        $this->reset();
6036
    }
6037 12
6038
    /**
6039 12
     * This method parses a list of PHP 5.3 use declarations and adds a mapping
6040
     * between short name and full qualified name to the use symbol table.
6041
     *
6042 144
     * <code>
6043 144
     * use \foo\bar as fb,
6044
     *     \foobar\Bar;
6045
     * </code>
6046
     *
6047
     * @return void
6048
     * @since 0.9.5
6049
     */
6050
    protected function parseUseDeclarations()
6051
    {
6052
        // Consume use keyword
6053
        $this->consumeToken(Tokens::T_USE);
6054
        $this->consumeComments();
6055
6056
        // Parse all use declarations
6057
        $this->parseUseDeclaration();
6058
        $this->consumeComments();
6059
6060
        // Consume closing semicolon
6061
        $this->consumeToken(Tokens::T_SEMICOLON);
6062
6063
        // Reset any previous state
6064
        $this->reset();
6065
    }
6066
6067
    /**
6068
     * This method parses a single use declaration and adds a mapping between
6069
     * short name and full qualified name to the use symbol table.
6070
     *
6071
     * @throws NoActiveScopeException
6072
     *
6073
     * @return void
6074
     * @since 0.9.5
6075
     */
6076
    protected function parseUseDeclaration()
6077
    {
6078
        $fragments = $this->parseQualifiedNameRaw();
6079
        $this->consumeComments();
6080
6081
        // Add leading backslash, because aliases must be full qualified
6082
        // http://php.net/manual/en/language.namespaces.importing.php
6083 22
        if ($fragments[0] !== '\\') {
6084
            array_unshift($fragments, '\\');
6085 22
        }
6086 20
6087
        $this->parseUseDeclarationForVersion($fragments);
6088
6089
        // Check for a following use declaration
6090 20
        if ($this->tokenizer->peek() === Tokens::T_COMMA) {
6091 14
            // Consume comma token and comments
6092 14
            $this->consumeToken(Tokens::T_COMMA);
6093
            $this->consumeComments();
6094 20
6095
            $this->parseUseDeclaration();
6096
        }
6097 20
    }
6098
6099 10
    /**
6100 10
     * @param array $fragments
6101
     * @return void
6102 10
     */
6103 10
    protected function parseUseDeclarationForVersion(array $fragments)
6104 20
    {
6105
        $image = $this->parseNamespaceImage($fragments);
6106
6107
        // Add mapping between image and qualified name to symbol table
6108
        $this->useSymbolTable->add($image, join('', $fragments));
6109
    }
6110 18
6111
    /**
6112 18
     * @param array $fragments
6113
     * @return string
6114
     */
6115 18 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...
6116 18
    {
6117
        if ($this->tokenizer->peek() === Tokens::T_AS) {
6118
            $this->consumeToken(Tokens::T_AS);
6119
            $this->consumeComments();
6120
6121
            $image = $this->consumeToken(Tokens::T_STRING)->image;
6122 20
            $this->consumeComments();
6123
        } else {
6124 20
            $image = end($fragments);
6125 14
        }
6126 14
        return $image;
6127
    }
6128 14
6129 14
    /**
6130 14
     * Parses a single constant definition with one or more constant declarators.
6131 16
     *
6132
     * <code>
6133 20
     * class Foo
6134
     * {
6135
     * //  ------------------------
6136
     *     const FOO = 42, BAR = 23;
6137
     * //  ------------------------
6138
     * }
6139
     * </code>
6140
     *
6141
     * @return \PDepend\Source\AST\ASTConstantDefinition
6142
     * @since 0.9.6
6143
     */
6144
    protected function parseConstantDefinition()
6145
    {
6146
        $this->tokenStack->push();
6147
6148
        $token = $this->consumeToken(Tokens::T_CONST);
6149
6150
        $definition = $this->builder->buildAstConstantDefinition($token->image);
6151 104
        $definition->setComment($this->docComment);
6152
6153 104 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...
6154
            $definition->addChild($this->parseConstantDeclarator());
6155 104
6156
            $this->consumeComments();
6157 104
            $tokenType = $this->tokenizer->peek();
6158 104
6159
            if ($tokenType === Tokens::T_SEMICOLON) {
6160
                break;
6161 104
            }
6162
            $this->consumeToken(Tokens::T_COMMA);
6163 102
        } while ($tokenType !== Tokenizer::T_EOF);
6164 102
6165
6166 102
        $definition = $this->setNodePositionsAndReturn($definition);
6167 102
6168
        $this->consumeToken(Tokens::T_SEMICOLON);
6169 12
6170 12
        return $definition;
6171
    }
6172
6173 102
    /**
6174
     * Parses a single constant declarator.
6175 102
     *
6176
     * <code>
6177 102
     * class Foo
6178
     * {
6179
     *     //    --------
6180
     *     const BAR = 42;
6181
     *     //    --------
6182
     * }
6183
     * </code>
6184
     *
6185
     * Or in a comma separated constant defintion:
6186
     *
6187
     * <code>
6188
     * class Foo
6189
     * {
6190
     *     //    --------
6191
     *     const BAR = 42,
6192
     *     //    --------
6193
     *
6194
     *     //    --------------
6195
     *     const BAZ = 'Foobar',
6196
     *     //    --------------
6197
     *
6198
     *     //    ----------
6199
     *     const FOO = 3.14;
6200
     *     //    ----------
6201
     * }
6202
     * </code>
6203
     *
6204
     * @return \PDepend\Source\AST\ASTConstantDeclarator
6205
     * @since 0.9.6
6206
     */
6207 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...
6208
    {
6209
        // Remove leading comments and create a new token stack
6210
        $this->consumeComments();
6211
        $this->tokenStack->push();
6212
6213
        $tokenType = $this->tokenizer->peek();
6214 104
        if (false === $this->isConstantName($tokenType)) {
6215
            throw $this->getUnexpectedTokenException();
6216
        }
6217 104
6218 104
        $token = $this->consumeToken($tokenType);
6219
6220 104
        $this->consumeComments();
6221 104
        $this->consumeToken(Tokens::T_EQUAL);
6222
6223
        $declarator = $this->builder->buildAstConstantDeclarator($token->image);
6224
        $declarator->setValue($this->parseConstantDeclaratorValue());
6225 104
6226
        return $this->setNodePositionsAndReturn($declarator);
6227 104
    }
6228 104
6229
    /**
6230 104
     * Parses the value of a php constant. By default this can be only static
6231 104
     * values that were allowed in the oldest supported PHP version.
6232
     *
6233 102
     * @return \PDepend\Source\AST\ASTValue
6234
     * @since 2.2.x
6235
     */
6236
    protected function parseConstantDeclaratorValue()
6237
    {
6238
        return $this->parseStaticValue();
6239
    }
6240
6241
    /**
6242
     * This method parses a static variable declaration list, a member primary
6243
     * prefix invoked in the static context of a class or it parses a static
6244
     * closure declaration.
6245
     *
6246
     * Static variable:
6247
     * <code>
6248
     * function foo() {
6249
     * //  ------------------------------
6250
     *     static $foo, $bar, $baz = null;
6251
     * //  ------------------------------
6252
     * }
6253
     * </code>
6254
     *
6255
     * Static method invocation:
6256
     * <code>
6257
     * class Foo {
6258
     *     public function baz() {
6259
     * //      ----------------
6260
     *         static::foobar();
6261
     * //      ----------------
6262
     *     }
6263
     *     public function foobar() {}
6264
     * }
6265
     *
6266
     * class Bar extends Foo {
6267
     *     public function foobar() {}
6268
     * }
6269
     * </code>
6270
     *
6271
     * Static closure declaration:
6272
     * <code>
6273
     * $closure = static function($x, $y) {
6274
     *     return ($x * $y);
6275
     * };
6276
     * </code>
6277
     *
6278
     * @return \PDepend\Source\AST\ASTConstant
6279
     * @throws \PDepend\Source\Parser\ParserException
6280
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
6281
     * @since 0.9.6
6282
     */
6283
    private function parseStaticVariableDeclarationOrMemberPrimaryPrefix()
6284
    {
6285
        $this->tokenStack->push();
6286
6287
        // Consume static token and strip optional comments
6288
        $token = $this->consumeToken(Tokens::T_STATIC);
6289
        $this->consumeComments();
6290 46
6291
        // Fetch next token type
6292 46
        $tokenType = $this->tokenizer->peek();
6293
6294
        if ($tokenType === Tokens::T_PARENTHESIS_OPEN
6295 46
            || $tokenType === Tokens::T_DOUBLE_COLON
6296 46
        ) {
6297
            $static = $this->parseStaticReference($token);
6298
6299 46
            $prefix = $this->parseStaticMemberPrimaryPrefix($static);
6300
            return $this->setNodePositionsAndReturn($prefix);
6301
        } elseif ($tokenType === Tokens::T_FUNCTION) {
6302 46
            $closure = $this->parseClosureDeclaration();
6303 46
            $closure->setStatic(true);
6304 10
6305
            return $this->setNodePositionsAndReturn($closure);
6306 8
        }
6307 8
6308 36
        $declaration = $this->parseStaticVariableDeclaration($token);
6309 14
        return $this->setNodePositionsAndReturn($declaration);
6310 14
    }
6311
6312 14
    /**
6313
     * This method will parse a static variable declaration.
6314
     *
6315 22
     * <code>
6316 22
     * function foo()
6317
     * {
6318
     *     // First declaration
6319
     *     static $foo;
6320
     *     // Second declaration
6321
     *     static $bar = array();
6322
     *     // Third declaration
6323
     *     static $baz    = array(),
6324
     *            $foobar = null,
6325
     *            $barbaz;
6326
     * }
6327
     * </code>
6328
     *
6329
     * @param  \PDepend\Source\Tokenizer\Token $token Token with the "static" keyword.
6330
     * @return \PDepend\Source\AST\ASTStaticVariableDeclaration
6331
     * @since 0.9.6
6332
     */
6333
    private function parseStaticVariableDeclaration(Token $token)
6334
    {
6335
        $staticDeclaration = $this->builder->buildAstStaticVariableDeclaration(
6336
            $token->image
6337
        );
6338
6339
        // Strip optional comments
6340 22
        $this->consumeComments();
6341
6342 22
        // Fetch next token type
6343 22
        $tokenType = $this->tokenizer->peek();
6344 22
6345 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...
6346
            $staticDeclaration->addChild($this->parseVariableDeclarator());
6347 22
6348
            $this->consumeComments();
6349
6350 22
            // Semicolon terminates static declaration
6351
            $tokenType = $this->tokenizer->peek();
6352 22
            if ($tokenType === Tokens::T_SEMICOLON) {
6353 22
                break;
6354
            }
6355 22
            // We are here, so there must be a next declarator
6356
            $this->consumeToken(Tokens::T_COMMA);
6357
        }
6358 22
6359 22
        return $staticDeclaration;
6360 22
    }
6361
6362
    /**
6363 14
     * This method will parse a variable declarator.
6364 14
     *
6365
     * <code>
6366 22
     * // Parameter declarator
6367
     * function foo($x = 23) {
6368
     * }
6369
     * // Property declarator
6370
     * class Foo{
6371
     *     protected $bar = 42;
6372
     * }
6373
     * // Static declarator
6374
     * function baz() {
6375
     *     static $foo;
6376
     * }
6377
     * </code>
6378
     *
6379
     * @return \PDepend\Source\AST\ASTVariableDeclarator
6380
     * @since 0.9.6
6381
     */
6382 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...
6383
    {
6384
        $this->tokenStack->push();
6385
6386
        $name = $this->consumeToken(Tokens::T_VARIABLE)->image;
6387
        $this->consumeComments();
6388
6389 806
        $declarator = $this->builder->buildAstVariableDeclarator($name);
6390
6391 806
        if ($this->tokenizer->peek() === Tokens::T_EQUAL) {
6392
            $this->consumeToken(Tokens::T_EQUAL);
6393 806
            $declarator->setValue($this->parseStaticValueOrStaticArray());
6394 804
        }
6395
        return $this->setNodePositionsAndReturn($declarator);
6396 804
    }
6397
6398 804
    /**
6399 190
     * This method will parse a static value or a static array as it is
6400 190
     * used as default value for a parameter or property declaration.
6401 184
     *
6402 798
     * @return \PDepend\Source\AST\ASTValue
6403
     * @since 0.9.6
6404
     */
6405
    protected function parseStaticValueOrStaticArray()
6406
    {
6407
        $this->consumeComments();
6408
        if ($this->isArrayStartDelimiter()) {
6409
            // TODO: Use default value as value!
6410
            $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...
6411
6412 266
            $value = new ASTValue();
6413
            $value->setValue(array());
6414 266
6415 266
            return $value;
6416
        }
6417 70
        return $this->parseStaticValue();
6418
    }
6419 68
6420 68
    /**
6421
     * This method will parse a static default value as it is used for a
6422 68
     * parameter, property or constant declaration.
6423
     *
6424 236
     * @return \PDepend\Source\AST\ASTValue
6425
     * @since 0.9.5
6426
     */
6427
    protected function parseStaticValue()
6428
    {
6429
        $defaultValue = new ASTValue();
6430
6431
        $this->consumeComments();
6432
6433
        // By default all parameters positive signed
6434 264
        $signed = 1;
6435
6436 264
        $tokenType = $this->tokenizer->peek();
6437
        while ($tokenType !== Tokenizer::T_EOF) {
6438 264
            switch ($tokenType) {
6439
                case Tokens::T_COMMA:
6440
                case Tokens::T_SEMICOLON:
6441 264
                case Tokens::T_PARENTHESIS_CLOSE:
6442
                    if ($defaultValue->isValueAvailable() === true) {
6443 264
                        return $defaultValue;
6444 264
                    }
6445
                    throw new MissingValueException($this->tokenizer);
6446 262
                case Tokens::T_NULL:
6447 262
                    $this->consumeToken(Tokens::T_NULL);
6448 262
                    $defaultValue->setValue(null);
6449 248
                    break;
6450 244
                case Tokens::T_TRUE:
6451
                    $this->consumeToken(Tokens::T_TRUE);
6452 4
                    $defaultValue->setValue(true);
6453 258
                    break;
6454 68
                case Tokens::T_FALSE:
6455 68
                    $this->consumeToken(Tokens::T_FALSE);
6456 68
                    $defaultValue->setValue(false);
6457 248
                    break;
6458 10 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...
6459 10
                    $token = $this->consumeToken(Tokens::T_LNUMBER);
6460 10
                    $defaultValue->setValue($signed * (int) $token->image);
6461 240
                    break;
6462 8 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...
6463 8
                    $token = $this->consumeToken(Tokens::T_DNUMBER);
6464 8
                    $defaultValue->setValue($signed * (double) $token->image);
6465 234
                    break;
6466 206
                case Tokens::T_CONSTANT_ENCAPSED_STRING:
6467 206
                    $token = $this->consumeToken(Tokens::T_CONSTANT_ENCAPSED_STRING);
6468 206
                    $defaultValue->setValue(substr($token->image, 1, -1));
6469 78
                    break;
6470 6
                case Tokens::T_DOUBLE_COLON:
6471 6
                    $this->consumeToken(Tokens::T_DOUBLE_COLON);
6472 6
                    break;
6473 72
                case Tokens::T_CLASS_FQN:
6474 46
                    $this->consumeToken(Tokens::T_CLASS_FQN);
6475 46
                    break;
6476 46
                case Tokens::T_PLUS:
6477 30
                    $this->consumeToken(Tokens::T_PLUS);
6478 6
                    break;
6479 6
                case Tokens::T_MINUS:
6480 30
                    $this->consumeToken(Tokens::T_MINUS);
6481
                    $signed *= -1;
6482
                    break;
6483 30
                case Tokens::T_DOUBLE_QUOTE:
6484 4
                    $defaultValue->setValue($this->parseStringSequence($tokenType));
6485 4
                    break;
6486 30
                case Tokens::T_DIR:
6487 4
                case Tokens::T_FILE:
6488 4
                case Tokens::T_LINE:
6489 4
                case Tokens::T_SELF:
6490 30
                case Tokens::T_NS_C:
6491
                case Tokens::T_FUNC_C:
6492
                case Tokens::T_PARENT:
6493 30
                case Tokens::T_STRING:
6494 30
                case Tokens::T_STATIC:
6495 30
                case Tokens::T_CLASS_C:
6496 30
                case Tokens::T_METHOD_C:
6497 30
                case Tokens::T_BACKSLASH:
6498 30
                case Tokens::T_SQUARED_BRACKET_OPEN:
6499 30
                case Tokens::T_SQUARED_BRACKET_CLOSE:
6500 30
                    // There is a default value but we don't handle it at the moment.
6501 30
                    $defaultValue->setValue(null);
6502 30
                    $this->consumeToken($tokenType);
6503 30
                    break;
6504 30
                case Tokens::T_START_HEREDOC:
6505 30
                    $defaultValue->setValue(
6506 30
                        $this->parseHeredoc()->getChild(0)->getImage()
6507
                    );
6508 12
                    break;
6509 12
                default:
6510 12
                    return $this->parseStaticValueVersionSpecific($defaultValue);
6511 18
            }
6512 4
6513 4
            $this->consumeComments();
6514 4
6515 4
            $tokenType = $this->tokenizer->peek();
6516 16
        }
6517 16
6518 16
        // We should never reach this, so throw an exception
6519
        throw new TokenStreamEndException($this->tokenizer);
6520 258
    }
6521
6522 258
    /**
6523 258
     * Parses additional static values that are valid in the supported php version.
6524
     *
6525
     * @param  \PDepend\Source\AST\ASTValue $value
6526 2
     * @return \PDepend\Source\AST\ASTValue
6527
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
6528
     */
6529
    protected function parseStaticValueVersionSpecific(ASTValue $value)
6530
    {
6531
        throw $this->getUnexpectedTokenException();
6532
    }
6533
6534
    /**
6535
     * Checks if the given expression is a read/write variable as defined in
6536
     * the PHP zend_language_parser.y definition.
6537
     *
6538
     * @param \PDepend\Source\AST\ASTNode $expr The context node instance.
6539
     *
6540
     * @return boolean
6541
     * @since 0.10.0
6542
     */
6543
    private function isReadWriteVariable($expr)
6544
    {
6545
        return ($expr instanceof \PDepend\Source\AST\ASTVariable
6546
            || $expr instanceof \PDepend\Source\AST\ASTFunctionPostfix
6547
            || $expr instanceof \PDepend\Source\AST\ASTVariableVariable
6548
            || $expr instanceof \PDepend\Source\AST\ASTCompoundVariable
6549
            || $expr instanceof \PDepend\Source\AST\ASTMemberPrimaryPrefix);
6550 164
    }
6551
6552
    /**
6553 164
     * This method creates a qualified class or interface name based on the
6554 156
     * current parser state. By default method uses the current namespace scope
6555 154
     * as prefix for the given local name. And it will fallback to a previously
6556 164
     * parsed package annotation, when no namespace declaration was parsed.
6557
     *
6558
     * @param string $localName The local class or interface name.
6559
     *
6560
     * @return string
6561
     */
6562
    private function createQualifiedTypeName($localName)
6563
    {
6564
        return ltrim($this->getNamespaceOrPackageName() . '\\' . $localName, '\\');
6565
    }
6566
6567
    /**
6568
     * Returns the name of a declared names. When the parsed code is not namespaced
6569 836
     * this method will return the name from the @package annotation.
6570
     *
6571 836
     * @return string
6572
     * @since 0.9.8
6573
     */
6574
    private function getNamespaceOrPackageName()
6575
    {
6576
        if ($this->namespaceName === null) {
6577
            return $this->packageName;
6578
        }
6579
        return $this->namespaceName;
6580
    }
6581 836
6582
    /**
6583 836
     * Returns the currently active package or namespace.
6584 748
     *
6585
     * @return \PDepend\Source\AST\ASTNamespace
6586 88
     * @since 1.0.0
6587
     */
6588
    private function getNamespaceOrPackage()
6589
    {
6590
        $namespace = $this->builder->buildNamespace($this->getNamespaceOrPackageName());
6591
        $namespace->setPackageAnnotation(null === $this->namespaceName);
6592
6593
        return $namespace;
6594
    }
6595 836
6596
    /**
6597 836
     * Extracts the @package information from the given comment.
6598 836
     *
6599
     * @param string $comment
6600 836
     * @return string
6601
     */
6602
    private function parsePackageAnnotation($comment)
6603
    {
6604
        if (getenv('DISMISS_PACKAGES')) {
6605
            $this->packageName = null;
6606
            $this->globalPackageName = null;
6607
6608
            return;
6609 142
        }
6610
6611 142
        $package = Builder::DEFAULT_NAMESPACE;
6612
        if (preg_match('#\*\s*@package\s+(\S+)#', $comment, $match)) {
6613
            $package = trim($match[1]);
6614
            if (preg_match('#\*\s*@subpackage\s+(\S+)#', $comment, $match)) {
6615
                $package .= '\\' . trim($match[1]);
6616
            }
6617
        }
6618 142
6619 142
        // Check for doc level comment
6620 90
        if ($this->globalPackageName === Builder::DEFAULT_NAMESPACE
6621 90
            && $this->isFileComment() === true
6622 8
        ) {
6623 8
            $this->globalPackageName = $package;
6624 90
6625
            $this->compilationUnit->setComment($comment);
6626
        }
6627 142
        return $package;
6628 142
    }
6629 142
6630 12
    /**
6631
     * Checks that the current token could be used as file comment.
6632 12
     *
6633 12
     * This method checks that the previous token is an open tag and the following
6634 142
     * token is not a class, a interface, final, abstract or a function.
6635
     *
6636
     * @return boolean
6637
     */
6638
    protected function isFileComment()
6639
    {
6640
        if ($this->tokenizer->prev() !== Tokens::T_OPEN_TAG) {
6641
            return false;
6642
        }
6643
6644
        $notExpectedTags = array(
6645 142
            Tokens::T_CLASS,
6646
            Tokens::T_FINAL,
6647 142
            Tokens::T_TRAIT,
6648 48
            Tokens::T_ABSTRACT,
6649
            Tokens::T_FUNCTION,
6650
            Tokens::T_INTERFACE
6651
        );
6652 136
6653 136
        return !in_array($this->tokenizer->peek(), $notExpectedTags, true);
6654 136
    }
6655 136
6656 136
    /**
6657
     * Returns the class names of all <b>throws</b> annotations with in the
6658 136
     * given comment block.
6659
     *
6660 136
     * @param string $comment The context doc comment block.
6661
     *
6662
     * @return array
6663
     */
6664
    private function parseThrowsAnnotations($comment)
6665
    {
6666
        $throws = array();
6667
        if (preg_match_all(self::REGEXP_THROWS_TYPE, $comment, $matches) > 0) {
6668
            foreach ($matches[1] as $match) {
6669
                $throws[] = $this->useSymbolTable->lookup($match) ?: $match;
6670
            }
6671 1898
        }
6672
        return $throws;
6673 1898
    }
6674 1898
6675 18
    /**
6676 18
     * This method parses the given doc comment text for a return annotation and
6677 18
     * it returns the found return type.
6678 18
     *
6679 1898
     * @param string $comment A doc comment text.
6680
     *
6681
     * @return string|null
6682
     */
6683
    private function parseReturnAnnotation($comment)
6684
    {
6685
        if (0 === preg_match(self::REGEXP_RETURN_TYPE, $comment, $match)) {
6686
            return null;
6687
        }
6688
6689
        foreach (explode('|', end($match)) as $image) {
6690 1896
            $image = $this->useSymbolTable->lookup($image) ?: $image;
6691
6692 1896
            if (Type::isScalarType($image)) {
6693 1844
                continue;
6694
            }
6695
6696 84
            return $image;
6697 84
        }
6698
        return null;
6699 84
    }
6700 62
6701
    /**
6702
     * This method parses the given doc comment text for a var annotation and
6703 32
     * it returns the found property types.
6704 58
     *
6705 58
     * @param  string $comment A doc comment text.
6706
     * @return array<string>
6707
     */
6708
    private function parseVarAnnotation($comment)
6709
    {
6710
        if (preg_match(self::REGEXP_VAR_TYPE, $comment, $match) > 0) {
6711
            $useSymbolTable = $this->useSymbolTable;
6712
6713
            return array_map(
6714
                function ($image) use ($useSymbolTable) {
6715 118
                    return $useSymbolTable->lookup($image) ?: $image;
6716
                },
6717 118
                array_map('trim', explode('|', end($match)))
6718 42
            );
6719
        }
6720 42
        return array();
6721 42
    }
6722 42
6723 42
    /**
6724 42
     * This method will extract the type information of a property from it's
6725 42
     * doc comment information. The returned value will be <b>null</b> when no
6726
     * type information exists.
6727 76
     *
6728
     * @return \PDepend\Source\AST\ASTType|null
6729
     * @since 0.9.6
6730
     */
6731
    private function parseFieldDeclarationType()
6732
    {
6733
        // Skip, if ignore annotations is set
6734
        if ($this->ignoreAnnotations === true) {
6735
            return null;
6736
        }
6737
6738 122
        $reference = $this->parseFieldDeclarationClassOrInterfaceReference();
6739
        if ($reference !== null) {
6740
            return $reference;
6741 122
        }
6742 4
6743
        $annotations = $this->parseVarAnnotation($this->docComment);
6744
        foreach ($annotations as $annotation) {
6745 118
            if (Type::isPrimitiveType($annotation) === true) {
6746 118
                return $this->builder->buildAstScalarType(
6747 20
                    Type::getPrimitiveType($annotation)
6748
                );
6749
            } elseif (Type::isArrayType($annotation) === true) {
6750 102
                return $this->builder->buildAstTypeArray();
6751 102
            }
6752 26
        }
6753 22
        return null;
6754 22
    }
6755 22
6756 4
    /**
6757 4
     * Extracts non scalar types from a field doc comment and creates a
6758
     * matching type instance.
6759 76
     *
6760 76
     * @return \PDepend\Source\AST\ASTClassOrInterfaceReference|null
6761
     * @since 0.9.6
6762
     */
6763
    private function parseFieldDeclarationClassOrInterfaceReference()
6764
    {
6765
        $annotations = $this->parseVarAnnotation($this->docComment);
6766
        foreach ($annotations as $annotation) {
6767
            if (Type::isScalarType($annotation) === false) {
6768
                return $this->builder->buildAstClassOrInterfaceReference(
6769
                    $annotation
6770 118
                );
6771
            }
6772 118
        }
6773 118
        return null;
6774 42
    }
6775 20
6776
    /**
6777 20
     * This method parses a yield-statement node.
6778
     *
6779 104
     * @return \PDepend\Source\AST\ASTYieldStatement
6780 102
     */
6781
    private function parseYield()
6782
    {
6783
        $this->tokenStack->push();
6784
6785
        $token = $this->consumeToken(Tokens::T_YIELD);
6786
        $this->consumeComments();
6787
6788 18
        $yield = $this->builder->buildAstYieldStatement($token->image);
6789
6790 18
        $node = $this->parseOptionalExpression();
6791
        if ($node) {
6792 18
            $yield->addChild($node);
6793 18
6794
            if ($this->tokenizer->peek() === Tokens::T_DOUBLE_ARROW) {
6795 18
                $this->consumeToken(Tokens::T_DOUBLE_ARROW);
6796
6797 18
                $yield->addChild($this->parseOptionalExpression());
6798 18
            }
6799 18
        }
6800
6801 18
        $this->consumeComments();
6802 8
        if (Tokens::T_PARENTHESIS_CLOSE === $this->tokenizer->peek()) {
6803
            return $this->setNodePositionsAndReturn($yield);
6804 8
        }
6805 8
6806 18
        $this->parseStatementTermination();
6807
        return $this->setNodePositionsAndReturn($yield);
6808 18
    }
6809 18
6810 4
    /**
6811
     * Extracts documented <b>throws</b> and <b>return</b> types and sets them
6812
     * to the given <b>$callable</b> instance.
6813 14
     *
6814 14
     * @param  \PDepend\Source\AST\AbstractASTCallable $callable
6815
     * @return void
6816
     */
6817
    private function prepareCallable(AbstractASTCallable $callable)
6818
    {
6819
        // Skip, if ignore annotations is set
6820
        if ($this->ignoreAnnotations === true) {
6821
            return;
6822
        }
6823
6824 1902
        // Get all @throws Types
6825
        $throws = $this->parseThrowsAnnotations($callable->getComment());
6826
        foreach ($throws as $qualifiedName) {
6827 1902
            $callable->addExceptionClassReference(
6828 4
                $this->builder->buildAstClassOrInterfaceReference($qualifiedName)
6829
            );
6830
        }
6831
6832 1898
        // Stop here if return class already exists.
6833 1898
        if ($callable->hasReturnClass()) {
6834 18
            return;
6835 18
        }
6836 18
6837 1898
        // Get return annotation
6838
        $qualifiedName = $this->parseReturnAnnotation($callable->getComment());
6839
        if ($qualifiedName !== null) {
6840 1898
            $callable->setReturnClassReference(
6841 2
                $this->builder->buildAstClassOrInterfaceReference($qualifiedName)
6842
            );
6843
        }
6844
    }
6845 1896
6846 1896
    /**
6847 32
     * This method will consume the next token in the token stream. It will
6848 32
     * throw an exception if the type of this token is not identical with
6849 32
     * <b>$tokenType</b>.
6850 32
     *
6851 1896
     * @param  integer $tokenType The next expected token type.
6852
     * @return \PDepend\Source\Tokenizer\Token
6853
     * @throws \PDepend\Source\Parser\TokenStreamEndException
6854
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
6855
     */
6856
    protected function consumeToken($tokenType)
6857
    {
6858
        switch ($this->tokenizer->peek()) {
6859
            case $tokenType:
6860
                return $this->tokenStack->add($this->tokenizer->next());
6861
            case Tokenizer::T_EOF:
6862
                throw new TokenStreamEndException($this->tokenizer);
6863 2375
        }
6864
        throw $this->getUnexpectedTokenException();
6865 2375
    }
6866 2375
6867 2375
    /**
6868 24
     * This method will consume all comment tokens from the token stream.
6869 8
     *
6870 16
     * @return void
6871 16
     */
6872
    protected function consumeComments()
6873
    {
6874
        $type = $this->tokenizer->peek();
6875
        while ($type == Tokens::T_COMMENT || $type == Tokens::T_DOC_COMMENT) {
6876
            $token = $this->consumeToken($type);
6877
            $type  = $this->tokenizer->peek();
6878
6879 2375
            if (Tokens::T_COMMENT === $token->type) {
6880
                continue;
6881 2375
            }
6882 2375
6883 72
            $this->docComment = $token->image;
6884 72
            if (preg_match('(\s+@package\s+[^\s]+\s+)', $token->image)) {
6885
                $this->packageName = $this->parsePackageAnnotation($token->image);
6886 72
            }
6887 68
        }
6888
    }
6889
6890 6
    /**
6891 6
     * @return UnexpectedTokenException
6892
     */
6893
    protected function getUnexpectedTokenException(Token $token = null)
6894 6
    {
6895 2375
        return new UnexpectedTokenException(
6896
            (null === $token) ? $this->tokenizer->next() : $token,
6897
            $this->tokenizer->getSourceFile()
6898
        );
6899
    }
6900
6901
    /**
6902
     * Throws an UnexpectedTokenException
6903
     *
6904
     * @param \PDepend\Source\Tokenizer\Token $token
6905 36
     * @return void
6906
     * @throws \PDepend\Source\Parser\UnexpectedTokenException
6907 36
     * @since 2.2.5
6908 36
     */
6909 36
    protected function throwUnexpectedTokenException(Token $token = null)
6910 36
    {
6911
        throw $this->getUnexpectedTokenException($token);
6912
    }
6913
}
6914