Passed
Push — master ( aeae10...674e24 )
by Théo
10:17 queued 01:41
created

NamespaceStmtPrefixer::wrapNamespace()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 9
ccs 4
cts 4
cp 1
crap 2
rs 9.6666
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\NodeVisitor;
16
17
use Humbug\PhpScoper\NodeVisitor\Collection\NamespaceStmtCollection;
18
use PhpParser\Node;
19
use PhpParser\Node\Name;
20
use PhpParser\Node\Stmt\Class_;
21
use PhpParser\Node\Stmt\Interface_;
22
use PhpParser\Node\Stmt\Namespace_;
23
use PhpParser\NodeVisitorAbstract;
24
use function Humbug\PhpScoper\clone_node;
25
26
/**
27
 * Prefixes the relevant namespaces.
28
 *
29
 * ```
30
 * namespace Foo;
31
 * ```
32
 *
33
 * =>
34
 *
35
 * ```
36
 * namespace Humbug\Foo;
37
 * ```
38
 */
39
final class NamespaceStmtPrefixer extends NodeVisitorAbstract
40
{
41
    private $prefix;
42
    private $namespaceStatements;
43
    private $hasWhitelistedNode;
44
    private $globalWhitelister;
45
46 346
    public function __construct(
47
        string $prefix,
48
        NamespaceStmtCollection $namespaceStatements,
49
        callable $globalWhitelister
50
    ) {
51 346
        $this->prefix = $prefix;
52 346
        $this->namespaceStatements = $namespaceStatements;
53 346
        $this->globalWhitelister = $globalWhitelister;
54
    }
55
56
    /**
57
     * @inheritdoc
58
     */
59 346
    public function beforeTraverse(array $nodes)
60
    {
61 346
        $this->hasWhitelistedNode = $this->hasWhitelistedNode($nodes);
62
    }
63
64
    /**
65
     * @inheritdoc
66
     */
67 345
    public function enterNode(Node $node): Node
68
    {
69 345
        return ($node instanceof Namespace_)
70 132
            ? $this->prefixNamespaceStmt($node)
71 345
            : $node;
72
    }
73
74
    /**
75
     * @inheritdoc
76
     */
77 345
    public function leaveNode(Node $node)
78
    {
79
        return (
80 345
            !$this->hasWhitelistedNode
81 345
            || $node instanceof Namespace_
82 345
            || AppendParentNode::hasParent($node)
83 345
        ) ? $node : $this->wrapNamespace($node);
84
    }
85
86 132
    private function prefixNamespaceStmt(Namespace_ $namespace): Node
87
    {
88 132
        $originalNamespace = $namespace;
89
90 132
        if ($this->shouldPrefixStmt($namespace)) {
91 128
            $originalNamespace = clone_node($namespace);
92
93 128
            $namespace->name = Name::concat($this->prefix, $namespace->name);
94
        }
95
96 132
        $this->namespaceStatements->add($namespace, $originalNamespace);
97
98 132
        return $namespace;
99
    }
100
101 9
    private function wrapNamespace(Node $node): Node
102
    {
103 9
        if ($this->isWhitelistedNode($node)) {
104 9
            return new Namespace_(new Node\Name($this->prefix), [$node]);
105
        }
106
107
        // Anything else needs to be wrapped with global namespace.
108 5
        return new Namespace_(null, [$node]);
109
    }
110
111
    /**
112
     * @param Node[] $nodes
113
     *
114
     * @return bool
115
     */
116 346
    private function hasWhitelistedNode(array $nodes): bool
117
    {
118 346
        foreach ($nodes as $node) {
119 345
            if ($this->isWhitelistedNode($node)) {
120 345
                return true;
121
            }
122
        }
123
124 337
        return false;
125
    }
126
127 345
    private function isWhitelistedNode(Node $node)
128
    {
129 345
        if (($node instanceof Class_ || $node instanceof Interface_)
130 345
            && ($this->globalWhitelister)($node->name)
131
        ) {
132 10
            return true;
133
        }
134
135
        // Check nodes in the global namespaces.
136 341
        if ($node instanceof Namespace_ && null === $node->name) {
137 12
            foreach ($node->stmts as $statement) {
138 12
                if ($this->isWhitelistedNode($statement)) {
139 12
                    return true;
140
                }
141
            }
142
        }
143
144 341
        return false;
145
    }
146
147 132
    private function shouldPrefixStmt(Namespace_ $namespace): bool
148
    {
149 132
        if (null !== $namespace->name && $this->prefix !== $namespace->name->getFirst()) {
150 128
            return true;
151
        }
152
153 15
        if (null === $namespace->name && $this->hasWhitelistedNode([$namespace])) {
154 1
            return true;
155
        }
156
157 15
        return false;
158
    }
159
}
160