Passed
Push — master ( 514edf...359688 )
by Théo
02:17
created

FunctionIdentifierRecorder::enterNode()   B

Complexity

Conditions 9
Paths 4

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 9

Importance

Changes 0
Metric Value
cc 9
nc 4
nop 1
dl 0
loc 27
ccs 13
cts 13
cp 1
crap 9
rs 8.0555
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 535
    public function __construct(
43
        string $prefix,
44
        FullyQualifiedNameResolver $nameResolver,
45
        Whitelist $whitelist,
46
        Reflector $reflector
47
    ) {
48 535
        $this->prefix = $prefix;
49 535
        $this->nameResolver = $nameResolver;
50 535
        $this->whitelist = $whitelist;
51 535
        $this->reflector = $reflector;
52
    }
53
54
    /**
55
     * @inheritdoc
56
     */
57 534
    public function enterNode(Node $node): Node
58
    {
59 534
        if (false === ($node instanceof Identifier || $node instanceof Name || $node instanceof String_)
60 534
            || false === ParentNodeAppender::hasParent($node)
61
        ) {
62 534
            return $node;
63
        }
64
65 533
        if (null === $resolvedName = $this->retrieveResolvedName($node)) {
66 533
            return $node;
67
        }
68
69
        if (
70
            (
71 76
                $this->whitelist->isGlobalWhitelistedFunction((string) $resolvedName)
72 76
                || $this->whitelist->isSymbolWhitelisted((string) $resolvedName)
73
            )
74 76
            && false === $this->reflector->isFunctionInternal((string) $resolvedName)
75
        ) {
76 22
            $this->whitelist->recordWhitelistedFunction(
77 22
                $resolvedName,
78 22
                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 76
        return $node;
83
    }
84
85 533
    private function retrieveResolvedName(Node $node): ?FullyQualified
86
    {
87 533
        if ($node instanceof Identifier) {
88 381
            return $this->retrieveResolvedNameForIdentifier($node);
89
        }
90
91 530
        if ($node instanceof Name) {
92 530
            return $this->retrieveResolvedNameForFuncCall($node);
93
        }
94
95 86
        if ($node instanceof String_) {
96 86
            return $this->retrieveResolvedNameForString($node);
97
        }
98
99
        return null;
100
    }
101
102 381
    private function retrieveResolvedNameForIdentifier(Identifier $node): ?FullyQualified
103
    {
104 381
        $parent = ParentNodeAppender::getParent($node);
105
106 381
        if (false === ($parent instanceof Function_) || $node === $parent->returnType) {
107 376
            return null;
108
        }
109
110 18
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
111
112 18
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
113
    }
114
115 530
    private function retrieveResolvedNameForFuncCall(Name $node): ?FullyQualified
116
    {
117 530
        $parent = ParentNodeAppender::getParent($node);
118
119 530
        if (false === ($parent instanceof FuncCall)) {
120 530
            return null;
121
        }
122
123 76
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
124
125 76
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
126
    }
127
128 86
    private function retrieveResolvedNameForString(String_ $node): ?FullyQualified
129
    {
130 86
        $stringParent = ParentNodeAppender::getParent($node);
131
132 86
        if (false === ($stringParent instanceof Arg)) {
133 67
            return null;
134
        }
135
136 41
        $argParent = ParentNodeAppender::getParent($stringParent);
137
138 41
        if (false === ($argParent instanceof FuncCall)
139 41
            || false === ($argParent->name instanceof FullyQualified)
140 41
            || 'function_exists' !== (string) $argParent->name
141
        ) {
142 36
            return null;
143
        }
144
145 8
        $resolvedName = $this->nameResolver->resolveName($node)->getName();
146
147 8
        return $resolvedName instanceof FullyQualified ? $resolvedName : null;
148
    }
149
}
150