Passed
Push — master ( 6c036d...1275a4 )
by Théo
01:42
created

retrieveResolvedNameForIdentifier()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 3
nop 1
dl 0
loc 11
rs 10
c 0
b 0
f 0
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\NodeVisitor\ParentNodeAppender;
18
use Humbug\PhpScoper\PhpParser\NodeVisitor\Resolver\FullyQualifiedNameResolver;
19
use Humbug\PhpScoper\Reflector;
20
use Humbug\PhpScoper\Whitelist;
21
use PhpParser\Node;
22
use PhpParser\Node\Arg;
23
use PhpParser\Node\Expr\FuncCall;
24
use PhpParser\Node\Identifier;
25
use PhpParser\Node\Name;
26
use PhpParser\Node\Name\FullyQualified;
27
use PhpParser\Node\Scalar\String_;
28
use PhpParser\Node\Stmt\Function_;
29
use PhpParser\NodeVisitorAbstract;
30
31
/**
32
 * Records the user functions registered in the global namespace which have been whitelisted and whitelisted functions.
33
 *
34
 * @private
35
 */
36
final class FunctionIdentifierRecorder extends NodeVisitorAbstract
37
{
38
    private $prefix;
39
    private $nameResolver;
40
    private $whitelist;
41
    private $reflector;
42
43
    public function __construct(
44
        string $prefix,
45
        FullyQualifiedNameResolver $nameResolver,
46
        Whitelist $whitelist,
47
        Reflector $reflector
48
    ) {
49
        $this->prefix = $prefix;
50
        $this->nameResolver = $nameResolver;
51
        $this->whitelist = $whitelist;
52
        $this->reflector = $reflector;
53
    }
54
55
    /**
56
     * @inheritdoc
57
     */
58
    public function enterNode(Node $node): Node
59
    {
60
        if (false === ($node instanceof Identifier || $node instanceof Name || $node instanceof String_)
61
            || false === ParentNodeAppender::hasParent($node)
62
        ) {
63
            return $node;
64
        }
65
66
        if (null === $resolvedName = $this->retrieveResolvedName($node)) {
67
            return $node;
68
        }
69
70
        if (
71
            false === $this->reflector->isFunctionInternal((string) $resolvedName)
72
            && (
73
                $this->whitelist->isGlobalWhitelistedFunction((string) $resolvedName)
74
                || $this->whitelist->isSymbolWhitelisted((string) $resolvedName)
75
            )
76
        ) {
77
            $this->whitelist->recordWhitelistedFunction(
78
                $resolvedName,
79
                FullyQualified::concat($this->prefix, $resolvedName)
80
            );
81
        }
82
83
        return $node;
84
    }
85
86
    private function retrieveResolvedName(Node $node): ?FullyQualified
87
    {
88
        if ($node instanceof Identifier) {
89
            return $this->retrieveResolvedNameForIdentifier($node);
90
        }
91
92
        if ($node instanceof Name) {
93
            return $this->retrieveResolvedNameForFuncCall($node);
94
        }
95
96
        if ($node instanceof String_) {
97
            return $this->retrieveResolvedNameForString($node);
98
        }
99
100
        return null;
101
    }
102
103
    private function retrieveResolvedNameForIdentifier(Identifier $node): ?FullyQualified
104
    {
105
        $parent = ParentNodeAppender::getParent($node);
106
107
        if (false === ($parent instanceof Function_) || $node === $parent->returnType) {
108
            return null;
109
        }
110
111
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
112
113
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
114
    }
115
116
    private function retrieveResolvedNameForFuncCall(Name $node): ?FullyQualified
117
    {
118
        $parent = ParentNodeAppender::getParent($node);
119
120
        if (false === ($parent instanceof FuncCall)) {
121
            return null;
122
        }
123
124
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
125
126
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
127
    }
128
129
    private function retrieveResolvedNameForString(String_ $node): ?FullyQualified
130
    {
131
        $stringParent = ParentNodeAppender::getParent($node);
132
133
        if (false === ($stringParent instanceof Arg)) {
134
            return null;
135
        }
136
137
        $argParent = ParentNodeAppender::getParent($stringParent);
138
139
        if (false === ($argParent instanceof FuncCall)
140
            || false === ($argParent->name instanceof FullyQualified)
141
            || 'function_exists' !== (string) $argParent->name
142
        ) {
143
            return null;
144
        }
145
146
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
147
148
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
149
    }
150
}
151