IndexGeneratingWalker   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 102
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 14
dl 0
loc 102
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 1
A updateFileInfo() 0 6 1
A setIndex() 0 4 1
C enterNode() 0 37 8
B leaveNode() 0 19 7
A getResultScope() 0 4 1
1
<?php
2
3
namespace Padawan\Parser\Walker;
4
5
use Padawan\Domain\Scope\FileScope;
6
use Padawan\Domain\Scope\ClassScope;
7
use Padawan\Domain\Scope\FunctionScope;
8
use Padawan\Domain\Scope\MethodScope;
9
use Padawan\Domain\Project\Node\Uses;
10
use Padawan\Domain\Project\FQN;
11
use Padawan\Domain\Project\Index;
12
use Padawan\Parser\Transformer\FunctionTransformer;
13
use Padawan\Parser\Transformer\ClassAssignmentTransformer;
14
use Padawan\Parser\ClassParser;
15
use Padawan\Parser\InterfaceParser;
16
use Padawan\Parser\UseParser;
17
use Padawan\Parser\NamespaceParser;
18
use PhpParser\NodeVisitorAbstract;
19
use PhpParser\NodeTraverser;
20
use PhpParser\Node;
21
use PhpParser\Node\Stmt\Class_;
22
use PhpParser\Node\Stmt\Interface_;
23
use PhpParser\Node\Stmt\Function_;
24
use PhpParser\Node\Stmt\ClassMethod;
25
use PhpParser\Node\Stmt\Namespace_;
26
use PhpParser\Node\Stmt\Use_;
27
use PhpParser\Node\Expr\Assign;
28
29
class IndexGeneratingWalker extends NodeVisitorAbstract implements WalkerInterface
30
{
31
    public function __construct(
32
        ClassParser $classParser,
33
        InterfaceParser $interfaceParser,
34
        UseParser $useParser,
35
        NamespaceParser $namespaceParser,
36
        FunctionTransformer $functionTransformer,
37
        ClassAssignmentTransformer $constructorAssignments
38
    ) {
39
        $this->classTransformer = $classParser;
40
        $this->interfaceTransformer = $interfaceParser;
41
        $this->useTransformer = $useParser;
42
        $this->namespaceTransformer = $namespaceParser;
43
        $this->scope = new FileScope(new FQN);
44
        $this->fileScope = $this->scope;
45
        $this->functionTransformer = $functionTransformer;
46
        $this->constructorAssignments = $constructorAssignments;
47
    }
48
    public function updateFileInfo(Uses $uses, $file)
49
    {
50
        $this->file = $file;
51
        $this->scope = new FileScope($uses->getFQCN(), $uses);
0 ignored issues
show
Bug introduced by
It seems like $uses->getFQCN() can be null; however, __construct() 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...
52
        $this->fileScope = $this->scope;
53
    }
54
    public function setIndex(Index $index)
55
    {
56
        $this->index = $index;
57
    }
58
    public function enterNode(Node $node)
59
    {
60
        if ($node instanceof Class_) {
61
            $this->scope = new ClassScope(
62
                $this->scope,
63
                $this->classTransformer->parse($node, $this->scope->getNamespace(), $this->file)
0 ignored issues
show
Bug introduced by
It seems like $this->scope->getNamespace() can be null; however, parse() 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...
64
            );
65
            return null;
66
        } elseif ($node instanceof Interface_) {
67
            $this->scope = new ClassScope(
68
                $this->scope,
69
                $this->interfaceTransformer->parse($node, $this->scope->getNamespace(), $this->file)
0 ignored issues
show
Bug introduced by
It seems like $this->scope->getNamespace() can be null; however, parse() 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...
70
            );
71
            return null;
72
        } elseif ($node instanceof Function_) {
73
            $this->scope = new FunctionScope(
74
                $this->scope,
75
                $this->functionTransformer->tranform($node)
76
            );
77
            return null;
78
        } elseif ($node instanceof ClassMethod) {
79
            $this->scope = new MethodScope(
80
                $this->scope,
81
                $this->scope->getClass()->getMethod($node->name)
0 ignored issues
show
Bug introduced by
The call to getClass() misses a required argument $className.

This check looks for function calls that miss required arguments.

Loading history...
82
            );
83
            if ($node->name === '__construct') {
84
                return null;
85
            }
86
        } elseif ($node instanceof Namespace_) {
87
            $this->namespaceTransformer->parse($node);
88
            return null;
89
        } elseif ($node instanceof Use_) {
90
            $this->useTransformer->parse($node);
91
            return null;
92
        }
93
        return NodeTraverser::DONT_TRAVERSE_CHILDREN;
94
    }
95
    public function leaveNode(Node $node)
96
    {
97
        if ($node instanceof Class_) {
98
            $this->scope = $this->scope->getParent();
0 ignored issues
show
Bug introduced by
The method getParent does only exist in Padawan\Domain\Scope\Cla...omain\Scope\MethodScope, but not in Padawan\Domain\Scope\FileScope.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
99
        } elseif ($node instanceof Interface_) {
100
            $this->scope = $this->scope->getParent();
101
        } elseif ($node instanceof Function_) {
102
            $this->scope = $this->scope->getParent();
103
        } elseif ($node instanceof ClassMethod) {
104
            $this->scope = $this->scope->getParent();
105
        } elseif ($node instanceof Assign && $this->scope instanceof MethodScope) {
106
            $this->constructorAssignments->transform(
107
                $node,
108
                $this->scope->getClass(),
109
                $this->scope,
110
                $this->index
111
            );
112
        }
113
    }
114
    public function getResultScope()
115
    {
116
        return $this->fileScope;
117
    }
118
119
    private $scope;
120
    private $fileScope;
121
    private $file;
122
    private $classTransformer;
123
    private $interfaceTransformer;
124
    private $functionTransformer;
125
    private $useTransformer;
126
    private $namespaceTransformer;
127
    private $constructorAssignments;
128
    /** @var Index */
129
    private $index;
130
}
131