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

retrieveResolvedNameForString()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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

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