GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Parser   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 116
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 0
Metric Value
wmc 11
lcom 1
cbo 13
dl 0
loc 116
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A parseFunction() 0 13 2
A getLocatedFunctionNodesIn() 0 23 2
B getMatchingFunctionNode() 0 46 6
1
<?php
2
3
namespace Pinq\Parsing\PhpParser;
4
5
use PhpParser\ParserFactory;
6
use Pinq\Parsing\FunctionStructure;
7
use Pinq\Parsing\IFunctionReflection;
8
use Pinq\Parsing\InvalidFunctionException;
9
use Pinq\Parsing\ParserBase;
10
use PhpParser;
11
use Pinq\Parsing\Resolvers\FunctionMagicResolver;
12
13
/**
14
 * Function parser implementation utilising nikic\PHP-Parser to
15
 * accurately locate and convert functions into the equivalent
16
 * expression tree
17
 *
18
 * @author Elliot Levin <[email protected]>
19
 */
20
class Parser extends ParserBase
21
{
22
    /**
23
     * The PHP-Parser parser instance, static because it is expensive
24
     * to instantiate.
25
     *
26
     * @var PhpParser\Parser
27
     */
28
    private static $phpParser;
29
30
    /**
31
     * The array containing the located functions nodes grouped by the
32
     * file in which they were defined then grouped by their respective
33
     * location hashes.
34
     *
35
     * @var array<array<array<LocatedFunctionNode>>>
36
     */
37
    private static $locatedFunctions;
38
39
    public function __construct()
40
    {
41
42
    }
43
44
    protected function parseFunction(IFunctionReflection $reflection, $filePath)
45
    {
46
        if (self::$phpParser === null) {
47
            self::$phpParser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
48
        }
49
50
        $locatedFunctionNodes = $this->getLocatedFunctionNodesIn($filePath);
51
        $matchingFunctionNode = $this->getMatchingFunctionNode($locatedFunctionNodes, $reflection);
52
53
        return new FunctionStructure(
54
                $matchingFunctionNode->getDeclaration(),
55
                AST::convert($matchingFunctionNode->getBodyNodes()));
56
    }
57
58
    private function getLocatedFunctionNodesIn($filePath)
59
    {
60
        if (!isset(self::$locatedFunctions[$filePath])) {
61
            $parsedNodes = self::$phpParser->parse(file_get_contents($filePath));
62
63
            //Resolve any relative, used or aliased types to their fully qualified equivalent
64
            $namespaceResolverTraverser = new PhpParser\NodeTraverser();
65
            $namespaceResolver = new PhpParser\NodeVisitor\NameResolver();
66
            $namespaceResolverTraverser->addVisitor($namespaceResolver);
67
            $resolvedNodes = $namespaceResolverTraverser->traverse($parsedNodes);
0 ignored issues
show
Bug introduced by
It seems like $parsedNodes defined by self::$phpParser->parse(...et_contents($filePath)) on line 61 can also be of type null; however, PhpParser\NodeTraverser::traverse() does only seem to accept array<integer,object<PhpParser\Node>>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
68
69
            //Locate all function nodes
70
            $functionLocatorTraverser = new PhpParser\NodeTraverser();
71
            $functionLocator = new Visitors\FunctionLocatorVisitor($filePath);
72
            $functionLocatorTraverser->addVisitor($functionLocator);
73
74
            $functionLocatorTraverser->traverse($resolvedNodes);
75
76
            self::$locatedFunctions[$filePath] = $functionLocator->getLocatedFunctionNodesMap();
77
        }
78
79
        return self::$locatedFunctions[$filePath];
80
    }
81
82
    /**
83
     * @param array <array<LocatedFunctionNode>> $locatedFunctionNodes
84
     * @param IFunctionReflection                $reflection
85
     *
86
     * @throws InvalidFunctionException
87
     * @return LocatedFunctionNode
88
     */
89
    private function getMatchingFunctionNode(array $locatedFunctionNodes, IFunctionReflection $reflection)
90
    {
91
        $locationHash = $reflection->getLocation()->getHash();
92
93
        if (empty($locatedFunctionNodes[$locationHash])) {
94
            throw InvalidFunctionException::invalidFunctionMessage(
95
                    'Cannot parse function, the function could not be located',
96
                    $reflection->getInnerReflection()
97
            );
98
        }
99
100
        // If multiple functions defined on a single line we
101
        // perform all possible resolution to resolve the conflict.
102
        // Magic constants / scopes are resolved in parameter expressions
103
        // to allow matching of functions with these special constants in
104
        // default values.
105
        /** @var $matchedFunctionsByLocation LocatedFunctionNode[] */
106
        $matchedFunctionsByLocation = $locatedFunctionNodes[$locationHash];
107
        $functionSignature          = $reflection->getSignature();
108
        $fullyMatchedFunctions      = [];
109
110
        foreach ($matchedFunctionsByLocation as $matchedFunction) {
111
            $magicData                        = $reflection->resolveMagic($matchedFunction->getDeclaration());
112
            $resolvedMatchedFunctionSignature = $matchedFunction->getSignature()->resolveMagic($magicData);
113
114
            if ($functionSignature->getHash() === $resolvedMatchedFunctionSignature->getHash()) {
115
                $fullyMatchedFunctions[] = $matchedFunction;
116
            }
117
        }
118
119
        if (empty($fullyMatchedFunctions)) {
120
            throw InvalidFunctionException::invalidFunctionMessage(
121
                    'Cannot parse function, the function\'s signature could not be matched',
122
                    $reflection->getInnerReflection()
123
            );
124
        } elseif (count($fullyMatchedFunctions) > 1) {
125
            throw InvalidFunctionException::invalidFunctionMessage(
126
                    'Cannot parse function, %d ambiguous functions are defined on the same line '
127
                    . 'with identical signatures',
128
                    $reflection->getInnerReflection(),
129
                    count($locatedFunctionNodes[$locationHash])
130
            );
131
        }
132
133
        return $fullyMatchedFunctions[0];
134
    }
135
}
136