Passed
Push — master ( 01e41e...21756f )
by Théo
02:37
created

NodeTraverser::wrapInNamespace()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 13
nc 6
nop 1
dl 0
loc 25
rs 8.439
c 0
b 0
f 0
ccs 13
cts 13
cp 1
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the humbug/php-scoper package.
7
 *
8
 * Copyright (c) 2017 Théo FIDRY <[email protected]>,
9
 *                    Pádraic Brady <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Humbug\PhpScoper;
16
17
// TODO: re-organise the classes location as this no longer makes sense. Not done here to try to keep the diff humanly
18
// readable.
19
use PhpParser\Node;
20
use PhpParser\Node\Name;
21
use PhpParser\Node\Stmt\Declare_;
22
use PhpParser\Node\Stmt\GroupUse;
23
use PhpParser\Node\Stmt\InlineHTML;
24
use PhpParser\Node\Stmt\Namespace_;
25
use PhpParser\Node\Stmt\Use_;
26
use PhpParser\Node\Stmt\UseUse;
27
use PhpParser\NodeTraverser as PhpParserNodeTraverser;
28
29
final class NodeTraverser extends PhpParserNodeTraverser
30
{
31
    private $prefix;
32
33 338
    public function __construct(string $prefix)
34
    {
35 338
        parent::__construct();
36
37 338
        $this->prefix = $prefix;
38
    }
39
40
    /**
41
     * @inheritdoc
42
     */
43 338
    public function traverse(array $nodes)
44
    {
45 338
        $nodes = $this->wrapInNamespace($nodes);
46 338
        $nodes = $this->replaceGroupUseStatements($nodes);
47
48 338
        return parent::traverse($nodes);
49
    }
50
51
    /**
52
     * Wrap the statements in a namespace when necessary:.
53
     *
54
     * ```php
55
     * #!/usr/bin/env php
56
     * <?php declare(strict_types=1);
57
     *
58
     * // A small comment
59
     *
60
     * if (\true) {
61
     *  echo "yo";
62
     * }
63
     * ```
64
     *
65
     * Will result in:
66
     *
67
     * ```php
68
     * #!/usr/bin/env php
69
     * <?php declare(strict_types=1);
70
     *
71
     * // A small comment
72
     *
73
     * namespace {
74
     *     if (\true) {
75
     *      echo "yo";
76
     *     }
77
     * }
78
     * ```
79
     *
80
     * @param Node[] $nodes
81
     *
82
     * @return Node[]
83
     */
84 338
    private function wrapInNamespace(array $nodes): array
85
    {
86 338
        $realStatements = [];
87
88 338
        foreach ($nodes as $i => $node) {
89 337
            if ($node instanceof Declare_ || $node instanceof InlineHTML) {
90 7
                continue;
91
            }
92
93 335
            $firstRealStatementIndex = $i;
94 335
            $realStatements = array_slice($nodes, $i);
95
96 335
            break;
97
        }
98
99 338
        $firstRealStatement = current($realStatements);
100
101 338
        if (false !== $firstRealStatement && false === ($firstRealStatement instanceof Namespace_)) {
102 134
            $wrappedStatements = new Namespace_(null, $realStatements);
103
104 134
            array_splice($nodes, $firstRealStatementIndex, count($realStatements), [$wrappedStatements]);
0 ignored issues
show
Bug introduced by
The variable $firstRealStatementIndex does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
105
        }
106
107 338
        return $nodes;
108
    }
109
110
    /**
111
     * @param Node[] $nodes
112
     *
113
     * @return Node[]
114
     */
115 338
    private function replaceGroupUseStatements(array $nodes): array
116
    {
117 338
        foreach ($nodes as $node) {
118 337
            if (false === ($node instanceof Namespace_)) {
119 7
                continue;
120
            }
121
122
            /** @var Namespace_ $node */
123 335
            $statements = $node->stmts;
124
125 335
            $newStatements = [];
126
127 335
            foreach ($statements as $statement) {
128 322
                if ($statement instanceof GroupUse) {
129 6
                    $uses_ = $this->createUses_($statement);
130
131 6
                    array_splice($newStatements, count($newStatements), 0, $uses_);
132
                } else {
133 322
                    $newStatements[] = $statement;
134
                }
135
            }
136
137 335
            $node->stmts = $newStatements;
138
        }
139
140 338
        return $nodes;
141
    }
142
143
    /**
144
     * @param GroupUse $node
145
     *
146
     * @return Use_[]
147
     */
148 6
    private function createUses_(GroupUse $node): array
149
    {
150 6
        return array_map(
151 6
            function (UseUse $use) use ($node): Use_ {
152 6
                $newUse = new UseUse(
153 6
                    Name::concat($node->prefix, $use->name, $use->name->getAttributes()),
154 6
                    $use->alias,
155 6
                    $use->type,
156 6
                    $use->getAttributes()
157
                );
158
159 6
                return new Use_(
160 6
                    [$newUse],
161 6
                    $node->type,
162 6
                    $node->getAttributes()
163
                );
164 6
            },
165
            $node->uses
166
        );
167
    }
168
}
169