Passed
Pull Request — master (#400)
by Théo
01:54
created

retrieveResolvedNameForString()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 31
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 18
c 1
b 0
f 0
nc 6
nop 1
dl 0
loc 31
rs 8.8333
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\NamespaceStmt;
16
17
use Humbug\PhpScoper\PhpParser\Node\FullyQualifiedFactory;
18
use Humbug\PhpScoper\PhpParser\Node\NamedIdentifier;
19
use Humbug\PhpScoper\PhpParser\NodeVisitor\ParentNodeAppender;
20
use Humbug\PhpScoper\PhpParser\NodeVisitor\Resolver\FullyQualifiedNameResolver;
21
use Humbug\PhpScoper\Reflector;
22
use Humbug\PhpScoper\Whitelist;
23
use PhpParser\Node;
24
use PhpParser\Node\Arg;
25
use PhpParser\Node\Expr\FuncCall;
26
use PhpParser\Node\Identifier;
27
use PhpParser\Node\Name;
28
use PhpParser\Node\Name\FullyQualified;
29
use PhpParser\Node\Scalar\String_;
30
use PhpParser\Node\Stmt\Function_;
31
use PhpParser\NodeVisitor\NameResolver;
32
use PhpParser\NodeVisitorAbstract;
33
use function xdebug_break;
34
35
/**
36
 * Records the user functions registered in the global namespace which have been whitelisted and whitelisted functions.
37
 *
38
 * @private
39
 */
40
final class FunctionIdentifierRecorder extends NodeVisitorAbstract
41
{
42
    private string $prefix;
43
    private FullyQualifiedNameResolver $nameResolver;
44
    private NameResolver $newNameResolver;
45
    private Whitelist $whitelist;
46
    private Reflector $reflector;
47
48
    public function __construct(
49
        string $prefix,
50
        FullyQualifiedNameResolver $nameResolver,
51
        NameResolver $newNameResolver,
52
        Whitelist $whitelist,
53
        Reflector $reflector
54
    ) {
55
        $this->prefix = $prefix;
56
        $this->nameResolver = $nameResolver;
57
        $this->newNameResolver = $newNameResolver;
58
        $this->whitelist = $whitelist;
59
        $this->reflector = $reflector;
60
    }
61
62
    public function enterNode(Node $node): Node
63
    {
64
        if (false === ($node instanceof Identifier || $node instanceof Name || $node instanceof String_)
65
            || false === ParentNodeAppender::hasParent($node)
66
        ) {
67
            return $node;
68
        }
69
70
        if (null === $resolvedName = $this->retrieveResolvedName($node)) {
71
            return $node;
72
        }
73
74
        if (
75
            false === $this->reflector->isFunctionInternal((string) $resolvedName)
76
            && (
77
                $this->whitelist->isGlobalWhitelistedFunction((string) $resolvedName)
78
                || $this->whitelist->isSymbolWhitelisted((string) $resolvedName)
79
            )
80
        ) {
81
            $this->whitelist->recordWhitelistedFunction(
82
                $resolvedName,
83
                FullyQualifiedFactory::concat($this->prefix, $resolvedName)
84
            );
85
        }
86
87
        return $node;
88
    }
89
90
    private function retrieveResolvedName(Node $node): ?FullyQualified
91
    {
92
        if ($node instanceof Identifier) {
93
            return $this->retrieveResolvedNameForIdentifier($node);
94
        }
95
96
        if ($node instanceof Name) {
97
            return $this->retrieveResolvedNameForFuncCall($node);
98
        }
99
100
        if ($node instanceof String_) {
101
            return $this->retrieveResolvedNameForString($node);
102
        }
103
104
        return null;
105
    }
106
107
    private function retrieveResolvedNameForIdentifier(Identifier $node): ?FullyQualified
108
    {
109
        $parent = ParentNodeAppender::getParent($node);
110
111
        if (false === ($parent instanceof Function_) || $node === $parent->returnType) {
112
            return null;
113
        }
114
115
        $oldResolvedName = $this->nameResolver->resolveName($node)->getName();
116
        $resolvedName = $this->newNameResolver
117
            ->getNameContext()
118
            ->getResolvedName(
119
                NamedIdentifier::create($node),
120
                Node\Stmt\Use_::TYPE_NORMAL,
121
            );
122
123
        if ((string) $oldResolvedName !== (string) $resolvedName) {
124
            xdebug_break();
125
            $x = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $x is dead and can be removed.
Loading history...
126
        }
127
128
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
129
    }
130
131
    private function retrieveResolvedNameForFuncCall(Name $node): ?FullyQualified
132
    {
133
        $parent = ParentNodeAppender::getParent($node);
134
135
        if (false === ($parent instanceof FuncCall)) {
136
            return null;
137
        }
138
139
        $oldResolvedName = $this->nameResolver->resolveName($node)->getName();
140
        $resolvedName = $this->newNameResolver
141
            ->getNameContext()
142
            ->getResolvedName(
143
                $node,
144
                Node\Stmt\Use_::TYPE_NORMAL,
145
            );
146
147
        if ((string) $oldResolvedName !== (string) $resolvedName) {
148
            xdebug_break();
149
            $x = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $x is dead and can be removed.
Loading history...
150
        }
151
152
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
153
    }
154
155
    private function retrieveResolvedNameForString(String_ $node): ?FullyQualified
156
    {
157
        $stringParent = ParentNodeAppender::getParent($node);
158
159
        if (false === ($stringParent instanceof Arg)) {
160
            return null;
161
        }
162
163
        $argParent = ParentNodeAppender::getParent($stringParent);
164
165
        if (false === ($argParent instanceof FuncCall)
166
            || false === ($argParent->name instanceof FullyQualified)
167
            || 'function_exists' !== (string) $argParent->name
168
        ) {
169
            return null;
170
        }
171
172
        $oldResolvedName = $this->nameResolver->resolveName($node)->getName();
173
        $resolvedName = $this->newNameResolver
174
            ->getNameContext()
175
            ->getResolvedName(
176
                $node,
0 ignored issues
show
Bug introduced by
$node of type PhpParser\Node\Scalar\String_ is incompatible with the type PhpParser\Node\Name expected by parameter $name of PhpParser\NameContext::getResolvedName(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

176
                /** @scrutinizer ignore-type */ $node,
Loading history...
177
                Node\Stmt\Use_::TYPE_NORMAL,
178
            );
179
180
        if ((string) $oldResolvedName !== (string) $resolvedName) {
181
            xdebug_break();
182
            $x = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $x is dead and can be removed.
Loading history...
183
        }
184
185
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
186
    }
187
}
188