Passed
Pull Request — master (#506)
by Théo
02:19
created

ClassAliasStmtAppender::appendToNamespaceStmt()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 1
rs 10
c 1
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\NodeVisitor;
16
17
use Humbug\PhpScoper\PhpParser\Node\ClassAliasFuncCall;
18
use Humbug\PhpScoper\PhpParser\Node\FullyQualifiedFactory;
19
use Humbug\PhpScoper\PhpParser\NodeVisitor\Resolver\IdentifierResolver;
20
use Humbug\PhpScoper\Whitelist;
21
use PhpParser\Node;
22
use PhpParser\Node\Name;
23
use PhpParser\Node\Name\FullyQualified;
24
use PhpParser\Node\Stmt;
25
use PhpParser\Node\Stmt\Class_;
26
use PhpParser\Node\Stmt\Expression;
27
use PhpParser\Node\Stmt\Interface_;
28
use PhpParser\Node\Stmt\Namespace_;
29
use PhpParser\NodeVisitorAbstract;
30
use function array_reduce;
31
32
/**
33
 * Appends a `class_alias` to the whitelisted classes.
34
 *
35
 * ```
36
 * namespace A;
37
 *
38
 * class Foo
39
 * {
40
 * }
41
 * ```
42
 *
43
 * =>
44
 *
45
 * ```
46
 * namespace Humbug\A;
47
 *
48
 * class Foo
49
 * {
50
 * }
51
 *
52
 * class_alias('Humbug\A\Foo', 'A\Foo', false);
53
 * ```
54
 *
55
 * @internal
56
 */
57
final class ClassAliasStmtAppender extends NodeVisitorAbstract
58
{
59
    private string $prefix;
60
    private Whitelist $whitelist;
61 549
62
    public function __construct(
63 549
        string $prefix,
64 549
        Whitelist $whitelist
65 549
    ) {
66
        $this->prefix = $prefix;
67
        $this->whitelist = $whitelist;
68
    }
69
70
    public function afterTraverse(array $nodes): array
71 548
    {
72
        $newNodes = [];
73 548
74
        foreach ($nodes as $node) {
75 548
            if ($node instanceof Namespace_) {
76 547
                $node = $this->appendToNamespaceStmt($node);
77 545
            }
78
79
            $newNodes[] = $node;
80 547
        }
81
82
        return $newNodes;
83 548
    }
84
85
    private function appendToNamespaceStmt(Namespace_ $namespace): Namespace_
86 545
    {
87
        $namespace->stmts = array_reduce(
88 545
            $namespace->stmts,
89 545
            fn (array $stmts, Stmt $stmt) => $this->createNamespaceStmts($stmts, $stmt),
90 545
            [],
91 545
        );
92
93
        return $namespace;
94 545
    }
95
96
    /**
97
     * @return Stmt[]
98
     */
99
    private function createNamespaceStmts(array $stmts, Stmt $stmt): array
100 532
    {
101
        $stmts[] = $stmt;
102 532
103
        if (!($stmt instanceof Class_ || $stmt instanceof Interface_)) {
104 532
            return $stmts;
105 486
        }
106
107
        $name = $stmt->name;
108 287
109
        if (null === $name) {
110
            return $stmts;
111
        }
112 287
113
        // We rely on the attribute here since we are in the afterTraverse(),
114 287
        // the PHP-Parser name resolver context would not be up to date in
115 287
        // regards of the current namespace hence the resolved name would be
116
        // incorrect.
117 273
        $resolvedName = $name->getAttribute('resolvedName');
118 245
119
        if (!($resolvedName instanceof FullyQualified)
120
            || !$this->shouldAppendStmt($resolvedName)
121 253
        ) {
122
            return $stmts;
123
        }
124
125 77
        $stmts[] = self::createAliasStmt($resolvedName, $stmt, $this->prefix);
126
127 77
        return $stmts;
128
    }
129
130 77
    private function shouldAppendStmt(Name $resolvedName): bool
131
    {
132 77
        $resolvedNameString = (string) $resolvedName;
133 77
134 77
        return !$this->whitelist->belongsToWhitelistedNamespace($resolvedNameString)
135 77
            && (
136
                $this->whitelist->isSymbolWhitelisted($resolvedNameString)
137
                || $this->whitelist->isGlobalWhitelistedClass($resolvedNameString)
138 77
            );
139 77
    }
140 77
141
    private static function createAliasStmt(
142
        FullyQualified $originalName,
143 77
        Node $stmt,
144
        string $prefix
145 77
    ): Expression
146
    {
147
        $call = new ClassAliasFuncCall(
148
            FullyQualifiedFactory::concat($prefix, $originalName),
149
            $originalName,
150
            $stmt->getAttributes(),
151
        );
152
153
        $expression = new Expression(
154
            $call,
155
            $stmt->getAttributes(),
156
        );
157
158
        $call->setAttribute(ParentNodeAppender::PARENT_ATTRIBUTE, $expression);
159
160
        return $expression;
161
    }
162
}
163