Completed
Push — master ( a514ed...c80037 )
by Théo
44:56 queued 16:09
created

retrieveResolvedNameForFuncCall()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 12
ccs 0
cts 0
cp 0
crap 12
rs 9.8666
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 521
{
37
    private $prefix;
38
    private $nameResolver;
39
    private $whitelist;
40
    private $reflector;
41 521
42 521
    public function __construct(
43 521
        string $prefix,
44
        FullyQualifiedNameResolver $nameResolver,
45
        Whitelist $whitelist,
46
        Reflector $reflector
47
    ) {
48
        $this->prefix = $prefix;
49 520
        $this->nameResolver = $nameResolver;
50
        $this->whitelist = $whitelist;
51 520
        $this->reflector = $reflector;
52 520
    }
53
54
    /**
55 379
     * @inheritdoc
56
     */
57 379
    public function enterNode(Node $node): Node
58 374
    {
59
        if (false === ($node instanceof Identifier || $node instanceof Name || $node instanceof String_)
60
            || false === ParentNodeAppender::hasParent($node)
61
        ) {
62 18
            return $node;
63
        }
64 18
65
        if (null === $resolvedName = $this->retrieveResolvedName($node)) {
66
            return $node;
67
        }
68
69 18
        if (
70 18
            (
71
                $this->whitelist->isGlobalWhitelistedFunction((string) $resolvedName)
72 10
                || $this->whitelist->isSymbolWhitelisted((string) $resolvedName)
73 10
            )
74 10
            && false === $this->reflector->isFunctionInternal((string) $resolvedName)
75
        ) {
76
            $this->whitelist->recordWhitelistedFunction(
77
                $resolvedName,
78 18
                FullyQualified::concat($this->prefix, $resolvedName)
1 ignored issue
show
Bug introduced by
It seems like \PhpParser\Node\Name\Ful...>prefix, $resolvedName) can be null; however, recordWhitelistedFunction() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
79
            );
80
        }
81
82
        return $node;
83
    }
84
85
    private function retrieveResolvedName(Node $node): ?FullyQualified
86
    {
87
        if ($node instanceof Identifier) {
88
            return $this->retrieveResolvedNameForIdentifier($node);
89
        }
90
91
        if ($node instanceof Name) {
92
            return $this->retrieveResolvedNameForFuncCall($node);
93
        }
94
95
        if ($node instanceof String_) {
96
            return $this->retrieveResolvedNameForString($node);
97
        }
98
99
        return null;
100
    }
101
102
    private function retrieveResolvedNameForIdentifier(Identifier $node): ?FullyQualified
103
    {
104
        $parent = ParentNodeAppender::getParent($node);
105
106
        if (false === ($parent instanceof Function_) || $node === $parent->returnType) {
107
            return null;
108
        }
109
110
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
111
112
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
113
    }
114
115
    private function retrieveResolvedNameForFuncCall(Name $node): ?FullyQualified
116
    {
117
        $parent = ParentNodeAppender::getParent($node);
118
119
        if (false === ($parent instanceof FuncCall)) {
120
            return null;
121
        }
122
123
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
124
125
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
126
    }
127
128
    private function retrieveResolvedNameForString(String_ $node): ?FullyQualified
129
    {
130
        $stringParent = ParentNodeAppender::getParent($node);
131
132
        if (false === ($stringParent instanceof Arg)) {
133
            return null;
134
        }
135
136
        $argParent = ParentNodeAppender::getParent($stringParent);
137
138
        if (false === ($argParent instanceof FuncCall)
139
            || false === ($argParent->name instanceof FullyQualified)
140
            || 'function_exists' !== (string) $argParent->name
141
        ) {
142
            return null;
143
        }
144
145
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
146
147
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
148
    }
149
}
150