Passed
Push — master ( da5866...513800 )
by Théo
02:08
created

NodeTraverser::replaceGroupUseStatements()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 16
nc 5
nop 1
dl 0
loc 30
ccs 14
cts 14
cp 1
crap 5
rs 9.4222
c 0
b 0
f 0
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\PhpParser;
16
17
use Humbug\PhpScoper\PhpParser\Node\NameFactory;
18
use PhpParser\Node;
19
use PhpParser\Node\Stmt;
20
use PhpParser\Node\Stmt\Declare_;
21
use PhpParser\Node\Stmt\GroupUse;
22
use PhpParser\Node\Stmt\InlineHTML;
23
use PhpParser\Node\Stmt\Namespace_;
24
use PhpParser\Node\Stmt\Use_;
25
use PhpParser\Node\Stmt\UseUse;
26
use PhpParser\NodeTraverserInterface;
27
use PhpParser\NodeVisitor;
28
use function array_map;
29
use function array_slice;
30
use function array_splice;
31
use function array_values;
32
use function count;
33
use function current;
34
35
/**
36
 * @private
37 549
 */
38
final class NodeTraverser implements NodeTraverserInterface
39 549
{
40
    private NodeTraverserInterface $decoratedTraverser;
41 549
42
    public function __construct(NodeTraverserInterface $decoratedTraverser)
43
    {
44
        $this->decoratedTraverser = $decoratedTraverser;
45
    }
46
47 549
    public function addVisitor(NodeVisitor $visitor): void
48
    {
49 549
        $this->decoratedTraverser->addVisitor($visitor);
50 549
    }
51
52 549
    public function removeVisitor(NodeVisitor $visitor): void
53
    {
54
        $this->decoratedTraverser->removeVisitor($visitor);
55
    }
56
57
    public function traverse(array $nodes): array
58
    {
59
        $nodes = $this->wrapInNamespace($nodes);
60
        $nodes = $this->replaceGroupUseStatements($nodes);
61
62
        return $this->decoratedTraverser->traverse($nodes);
63
    }
64
65
    /**
66
     * Wrap the statements in a namespace when necessary:.
67
     *
68
     * ```php
69
     * #!/usr/bin/env php
70
     * <?php declare(strict_types=1);
71
     *
72
     * // A small comment
73
     *
74
     * if (\true) {
75
     *  echo "yo";
76
     * }
77
     * ```
78
     *
79
     * Will result in:
80
     *
81
     * ```php
82
     * #!/usr/bin/env php
83
     * <?php declare(strict_types=1);
84
     *
85
     * // A small comment
86
     *
87
     * namespace {
88 549
     *     if (\true) {
89
     *      echo "yo";
90 549
     *     }
91 1
     * }
92
     * ```
93
     *
94 548
     * @param Node[] $nodes
95
     *
96 548
     * @return Node[]
97 548
     */
98
    private function wrapInNamespace(array $nodes): array
99 548
    {
100 548
        if ([] === $nodes) {
101 11
            return $nodes;
102
        }
103
104 546
        $nodes = array_values($nodes);
105 546
106
        $firstRealStatementIndex = 0;
107
        $realStatements = [];
108
109
        foreach ($nodes as $i => $node) {
110 548
            if ($node instanceof Declare_ || $node instanceof InlineHTML) {
111
                continue;
112 548
            }
113 242
114
            $firstRealStatementIndex = $i;
115 242
            /** @var Stmt[] $realStatements */
116
            $realStatements = array_slice($nodes, $i);
117
118 548
            break;
119
        }
120
121
        $firstRealStatement = current($realStatements);
122
123
        if (false !== $firstRealStatement
124
            && !($firstRealStatement instanceof Namespace_)
125
        ) {
126 549
            $wrappedStatements = new Namespace_(null, $realStatements);
127
128 549
            array_splice(
129 548
                $nodes,
130 11
                $firstRealStatementIndex,
131
                count($realStatements),
132
                [$wrappedStatements],
133
            );
134 546
        }
135
136 546
        return $nodes;
137
    }
138 546
139 533
    /**
140 6
     * @param Node[] $nodes
141
     *
142 6
     * @return Node[]
143
     */
144 527
    private function replaceGroupUseStatements(array $nodes): array
145
    {
146
        foreach ($nodes as $node) {
147
            if (!($node instanceof Namespace_)) {
148 546
                continue;
149
            }
150
151 549
            $statements = $node->stmts;
152
153
            $newStatements = [];
154
155
            foreach ($statements as $statement) {
156
                if ($statement instanceof GroupUse) {
157
                    $uses_ = $this->createUses_($statement);
158
159 6
                    array_splice(
160
                        $newStatements,
161 6
                        count($newStatements),
162
                        0,
163 6
                        $uses_,
164 6
                    );
165 6
                } else {
166 6
                    $newStatements[] = $statement;
167 6
                }
168
            }
169
170 6
            $node->stmts = $newStatements;
171 6
        }
172 6
173 6
        return $nodes;
174
    }
175 6
176 6
    /**
177
     * @param GroupUse $node
178
     *
179
     * @return Use_[]
180
     */
181
    private function createUses_(GroupUse $node): array
182
    {
183
        return array_map(
184
            static function (UseUse $use) use ($node): Use_ {
185
                $newUse = new UseUse(
186
                    NameFactory::concat(
187
                        $node->prefix,
188
                        $use->name,
189
                        $use->name->getAttributes(),
190
                    ),
191
                    $use->alias,
192
                    $use->type,
193
                    $use->getAttributes(),
194
                );
195
196
                return new Use_(
197
                    [$newUse],
198
                    $node->type,
199
                    $node->getAttributes(),
200
                );
201
            },
202
            $node->uses,
203
        );
204
    }
205
}
206