Passed
Pull Request — master (#574)
by Théo
02:06
created

ClassAliasStmtAppender::shouldAppendStmt()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 3
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
A ClassAliasStmtAppender::isExposedClass() 0 6 3
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\FullyQualified;
23
use PhpParser\Node\Stmt;
24
use PhpParser\Node\Stmt\Class_;
25
use PhpParser\Node\Stmt\Expression;
26
use PhpParser\Node\Stmt\Interface_;
27
use PhpParser\Node\Stmt\Namespace_;
28
use PhpParser\NodeVisitorAbstract;
29
use UnexpectedValueException;
30
use function array_reduce;
31
32
/**
33
 * Appends a `class_alias` statement to the exposed 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
    private IdentifierResolver $identifierResolver;
62
63 549
    public function __construct(
64 549
        string $prefix,
65 549
        Whitelist $whitelist,
66
        IdentifierResolver $identifierResolver
67
    ) {
68
        $this->prefix = $prefix;
69
        $this->whitelist = $whitelist;
70
        $this->identifierResolver = $identifierResolver;
71 548
    }
72
73 548
    public function afterTraverse(array $nodes): array
74
    {
75 548
        $newNodes = [];
76 547
77 545
        foreach ($nodes as $node) {
78
            if ($node instanceof Namespace_) {
79
                $node = $this->appendToNamespaceStmt($node);
80 547
            }
81
82
            $newNodes[] = $node;
83 548
        }
84
85
        return $newNodes;
86 545
    }
87
88 545
    private function appendToNamespaceStmt(Namespace_ $namespace): Namespace_
89 545
    {
90 545
        $namespace->stmts = array_reduce(
91 545
            $namespace->stmts,
92
            fn (array $stmts, Stmt $stmt) => $this->createNamespaceStmts($stmts, $stmt),
93
            [],
94 545
        );
95
96
        return $namespace;
97
    }
98
99
    /**
100 532
     * @param Stmt[] $stmts
101
     *
102 532
     * @return Stmt[]
103
     */
104 532
    private function createNamespaceStmts(array $stmts, Stmt $stmt): array
105 486
    {
106
        $stmts[] = $stmt;
107
108 287
        $isClassOrInterface = $stmt instanceof Class_ || $stmt instanceof Interface_;
109
110
        if (!$isClassOrInterface) {
111
            return $stmts;
112 287
        }
113
114 287
        $name = $stmt->name;
115 287
116
        if (null === $name) {
117 273
            throw new UnexpectedValueException('Expected the class/interface statement to have a name but none found');
118 245
        }
119
120
        $resolvedName = $this->identifierResolver->resolveIdentifier($name);
121 253
122
        if ($resolvedName instanceof FullyQualified
123
            && $this->isExposedClass((string) $resolvedName)
124
        ) {
125 77
            $stmts[] = self::createAliasStmt($resolvedName, $stmt, $this->prefix);
126
        }
127 77
128
        return $stmts;
129
    }
130 77
131
    private function isExposedClass(string $resolvedName): bool
132 77
    {
133 77
        return !$this->whitelist->belongsToExcludedNamespace($resolvedName)
134 77
            && (
135 77
                $this->whitelist->isExposedClassFromGlobalNamespace($resolvedName)
136
                || $this->whitelist->isSymbolExposed($resolvedName)
137
            );
138 77
    }
139 77
140 77
    private static function createAliasStmt(
141
        FullyQualified $originalName,
142
        Node $stmt,
143 77
        string $prefix
144
    ): Expression
145 77
    {
146
        $call = new ClassAliasFuncCall(
147
            FullyQualifiedFactory::concat($prefix, $originalName),
148
            $originalName,
149
            $stmt->getAttributes(),
150
        );
151
152
        $expression = new Expression(
153
            $call,
154
            $stmt->getAttributes(),
155
        );
156
157
        ParentNodeAppender::setParent($call, $expression);
158
159
        return $expression;
160
    }
161
}
162