Passed
Pull Request — master (#400)
by Théo
02:04
created

retrieveResolvedNameForString()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 14
c 1
b 0
f 0
nc 6
nop 1
dl 0
loc 26
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 Whitelist $newNameResolver;
44
    private $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
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
115
        $newResolvedName = $this->newNameResolver->getNameContext()->getResolvedName(NamedIdentifier::create($node), Node\Stmt\Use_::TYPE_NORMAL);
0 ignored issues
show
Bug introduced by
The method getNameContext() does not exist on Humbug\PhpScoper\Whitelist. ( Ignorable by Annotation )

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

115
        $newResolvedName = $this->newNameResolver->/** @scrutinizer ignore-call */ getNameContext()->getResolvedName(NamedIdentifier::create($node), Node\Stmt\Use_::TYPE_NORMAL);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
116
117
        if ((string) $resolvedName !== (string) $newResolvedName) {
118
            $x = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $x is dead and can be removed.
Loading history...
119
        }
120
121
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
122
    }
123
124
    private function retrieveResolvedNameForFuncCall(Name $node): ?FullyQualified
125
    {
126
        $parent = ParentNodeAppender::getParent($node);
127
128
        if (false === ($parent instanceof FuncCall)) {
129
            return null;
130
        }
131
132
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
133
        $newResolvedName = $this->newNameResolver->getNameContext()->getResolvedName($node, Node\Stmt\Use_::TYPE_NORMAL);
134
135
        if ((string) $resolvedName !== (string) $newResolvedName) {
136
            xdebug_break();
137
            $x = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $x is dead and can be removed.
Loading history...
138
        }
139
140
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
141
    }
142
143
    private function retrieveResolvedNameForString(String_ $node): ?FullyQualified
144
    {
145
        $stringParent = ParentNodeAppender::getParent($node);
146
147
        if (false === ($stringParent instanceof Arg)) {
148
            return null;
149
        }
150
151
        $argParent = ParentNodeAppender::getParent($stringParent);
152
153
        if (false === ($argParent instanceof FuncCall)
154
            || false === ($argParent->name instanceof FullyQualified)
155
            || 'function_exists' !== (string) $argParent->name
156
        ) {
157
            return null;
158
        }
159
160
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
161
        $x = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $x is dead and can be removed.
Loading history...
162
        $newResolvedName = $this->newNameResolver->getNameContext()->getResolvedName($node, Node\Stmt\Use_::TYPE_NORMAL);
163
164
        if ((string) $resolvedName !== (string) $newResolvedName) {
165
            $x = '';
166
        }
167
168
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
169
    }
170
}
171