Completed
Push — master ( f94444...a44080 )
by Théo
02:35 queued 20s
created

NameStmtPrefixer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 3
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
ccs 4
cts 4
cp 1
crap 1
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\Resolver\FullyQualifiedNameResolver;
18
use Humbug\PhpScoper\Reflector;
19
use PhpParser\Node;
20
use PhpParser\Node\Expr\ClassConstFetch;
21
use PhpParser\Node\Expr\ConstFetch;
22
use PhpParser\Node\Expr\FuncCall;
23
use PhpParser\Node\Expr\New_;
24
use PhpParser\Node\Expr\StaticCall;
25
use PhpParser\Node\Name;
26
use PhpParser\Node\Name\FullyQualified;
27
use PhpParser\Node\NullableType;
28
use PhpParser\Node\Param;
29
use PhpParser\Node\Stmt\Class_;
30
use PhpParser\Node\Stmt\ClassMethod;
31
use PhpParser\Node\Stmt\Function_;
32
use PhpParser\Node\Stmt\Interface_;
33
use PhpParser\NodeVisitorAbstract;
34
35
/**
36
 * ```
37
 * new Foo\Bar();
38
 * ```.
39
 *
40
 * =>
41
 *
42
 * ```
43
 * new \Humbug\Foo\Bar();
44
 * ```
45
 *
46
 * @private
47
 */
48
final class NameStmtPrefixer extends NodeVisitorAbstract
49
{
50
    public const PHP_FUNCTION_KEYWORDS = [
51
        'self',
52
        'static',
53
        'parent',
54
    ];
55
56
    private $prefix;
57
    private $nameResolver;
58
    private $reflector;
59
60
    /**
61
     * @param string                     $prefix
62
     * @param FullyQualifiedNameResolver $nameResolver
63
     * @param Reflector                  $reflector
64
     */
65 346
    public function __construct(
66
        string $prefix,
67
        FullyQualifiedNameResolver $nameResolver,
68
        Reflector $reflector
69
    ) {
70 346
        $this->prefix = $prefix;
71 346
        $this->nameResolver = $nameResolver;
72 346
        $this->reflector = $reflector;
73
    }
74
75
    /**
76
     * @inheritdoc
77
     */
78 345
    public function enterNode(Node $node): Node
79
    {
80 345
        return ($node instanceof Name && AppendParentNode::hasParent($node))
81 343
            ? $this->prefixName($node)
82 345
            : $node
83
        ;
84
    }
85
86 343
    private function prefixName(Name $name): Node
87
    {
88 343
        $parentNode = AppendParentNode::getParent($name);
89
90 343
        if ($parentNode instanceof NullableType) {
91 4
            if (false === AppendParentNode::hasParent($parentNode)) {
92
                return $name;
93
            }
94
95 4
            $parentNode = AppendParentNode::getParent($parentNode);
96
        }
97
98
        if (false === (
99 343
                $parentNode instanceof ConstFetch
100 343
                || $parentNode instanceof ClassConstFetch
101 343
                || $parentNode instanceof Param
102 343
                || $parentNode instanceof FuncCall
103 343
                || $parentNode instanceof StaticCall
104 343
                || $parentNode instanceof Function_
105 343
                || $parentNode instanceof ClassMethod
106 343
                || $parentNode instanceof New_
107 343
                || $parentNode instanceof Class_
108 343
                || $parentNode instanceof Interface_
109
            )
110
        ) {
111 343
            return $name;
112
        }
113
114
        if (
115
            (
116 262
                $parentNode instanceof FuncCall
117 262
                || $parentNode instanceof StaticCall
118 262
                || $parentNode instanceof ClassConstFetch
119 262
                || $parentNode instanceof New_
120 262
                || $parentNode instanceof Param
121
            )
122 262
            && in_array((string) $name, self::PHP_FUNCTION_KEYWORDS, true)
123
        ) {
124 6
            return $name;
125
        }
126
127 262
        if ($parentNode instanceof ConstFetch && 'null' === (string) $name) {
128 2
            return $name;
129
        }
130
131 262
        $resolvedValue = $this->nameResolver->resolveName($name);
132
133 262
        $resolvedName = $resolvedValue->getName();
134
135
        // Skip if is already prefixed
136 262
        if ($this->prefix === $resolvedName->getFirst()) {
137 3
            return $resolvedName;
138
        }
139
140
        // Check if the class can be prefixed
141 262
        if (false === ($parentNode instanceof ConstFetch || $parentNode instanceof FuncCall)) {
142 175
            if ($this->reflector->isClassInternal($resolvedName->toString())) {
143 38
                return $resolvedName;
144
            }
145
        }
146
147 240
        if ($parentNode instanceof ConstFetch
148
            && (
149 96
                $this->reflector->isConstantInternal($resolvedName->toString())
150
                // Constants have a fallback autoloading so we cannot prefix them when the name is ambiguous
151
                // See https://wiki.php.net/rfc/fallback-to-root-scope-deprecation
152 240
                || false === ($resolvedName instanceof FullyQualified)
153
            )
154
        ) {
155 64
            return $resolvedName;
156
        }
157
158
        if (
159 231
            $parentNode instanceof FuncCall
160
            && (
161 92
                $this->reflector->isFunctionInternal($resolvedName->toString())
162
                // Functions have a fallback autoloading so we cannot prefix them when the name is ambiguous
163
                // See https://wiki.php.net/rfc/fallback-to-root-scope-deprecation
164 231
                || false === ($resolvedName instanceof FullyQualified)
165
            )
166
        ) {
167 66
            return $resolvedName;
168
        }
169
170 211
        if ('self' === (string) $resolvedName && $parentNode instanceof ClassMethod) {
171 3
            return $name;
172
        }
173
174 211
        return FullyQualified::concat(
175 211
            $this->prefix,
176 211
            $resolvedName->toString(),
177 211
            $resolvedName->getAttributes()
178
        );
179
    }
180
}
181