Passed
Branch master (c976c6)
by Théo
03:51
created

FunctionIdentifierRecorder::retrieveResolvedName()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.0312

Importance

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