FunctionIdentifierRecorder::retrieveResolvedName()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 1
dl 0
loc 15
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\Node\FullyQualifiedFactory;
18
use Humbug\PhpScoper\PhpParser\NodeVisitor\ParentNodeAppender;
19
use Humbug\PhpScoper\PhpParser\NodeVisitor\Resolver\IdentifierResolver;
20
use Humbug\PhpScoper\PhpParser\UnexpectedParsingScenario;
21
use Humbug\PhpScoper\Symbol\EnrichedReflector;
22
use Humbug\PhpScoper\Symbol\SymbolsRegistry;
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\NodeVisitorAbstract;
32
33
/**
34
 * Records the user functions registered in the global namespace which have been whitelisted and whitelisted functions.
35
 *
36
 * @private
37
 */
38
final class FunctionIdentifierRecorder extends NodeVisitorAbstract
39
{
40
    private string $prefix;
41
    private IdentifierResolver $identifierResolver;
42
    private SymbolsRegistry $symbolsRegistry;
43
    private EnrichedReflector $enrichedReflector;
44
45
    public function __construct(
46
        string $prefix,
47
        IdentifierResolver $identifierResolver,
48
        SymbolsRegistry $symbolsRegistry,
49
        EnrichedReflector $enrichedReflector
50
    ) {
51
        $this->prefix = $prefix;
52
        $this->identifierResolver = $identifierResolver;
53
        $this->symbolsRegistry = $symbolsRegistry;
54
        $this->enrichedReflector = $enrichedReflector;
55
    }
56
57
    public function enterNode(Node $node): Node
58
    {
59
        if (!($node instanceof Identifier || $node instanceof Name || $node instanceof String_)
60
            || !ParentNodeAppender::hasParent($node)
61
        ) {
62
            return $node;
63
        }
64
65
        $resolvedName = $this->retrieveResolvedName($node);
66
67
        if (null !== $resolvedName
68
            && $this->enrichedReflector->isExposedFunction($resolvedName->toString())
69
        ) {
70
            $this->symbolsRegistry->recordFunction(
71
                $resolvedName,
72
                FullyQualifiedFactory::concat($this->prefix, $resolvedName),
73
            );
74
        }
75
76
        return $node;
77
    }
78
79
    private function retrieveResolvedName(Node $node): ?FullyQualified
80
    {
81
        if ($node instanceof Identifier) {
82
            return $this->retrieveResolvedNameForIdentifier($node);
83
        }
84
85
        if ($node instanceof Name) {
86
            return $this->retrieveResolvedNameForFuncCall($node);
87
        }
88
89
        if ($node instanceof String_) {
90
            return $this->retrieveResolvedNameForString($node);
91
        }
92
93
        throw UnexpectedParsingScenario::create();
94
    }
95
96
    private function retrieveResolvedNameForIdentifier(Identifier $identifier): ?FullyQualified
97
    {
98
        $parent = ParentNodeAppender::getParent($identifier);
99
100
        if (!($parent instanceof Function_)
101
            || $identifier === $parent->returnType
102
        ) {
103
            return null;
104
        }
105
106
        $resolvedName = $this->identifierResolver->resolveIdentifier($identifier);
107
108
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
109
    }
110
111
    private function retrieveResolvedNameForFuncCall(Name $name): ?FullyQualified
112
    {
113
        $parent = ParentNodeAppender::getParent($name);
114
115
        if (!($parent instanceof FuncCall)) {
116
            return null;
117
        }
118
119
        return $name instanceof FullyQualified ? $name : null;
120
    }
121
122
    private function retrieveResolvedNameForString(String_ $string): ?FullyQualified
123
    {
124
        $stringParent = ParentNodeAppender::getParent($string);
125
126
        if (!($stringParent instanceof Arg)) {
127
            return null;
128
        }
129
130
        $argParent = ParentNodeAppender::getParent($stringParent);
131
132
        if (!($argParent instanceof FuncCall)) {
133
            return null;
134
        }
135
136
        if (!self::isFunctionExistsCall($argParent)) {
137
            return null;
138
        }
139
140
        $resolvedName = $this->identifierResolver->resolveString($string);
141
142
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
0 ignored issues
show
introduced by
$resolvedName is always a sub-type of PhpParser\Node\Name\FullyQualified.
Loading history...
143
    }
144
145
    private static function isFunctionExistsCall(FuncCall $node): bool
146
    {
147
        $name = $node->name;
148
149
        return $name instanceof Name
150
            && $name->isFullyQualified()
151
            && $name->toString() === 'function_exists';
152
    }
153
}
154