AnonymousClassReplacer   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 114
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

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

5 Methods

Rating   Name   Duplication   Size   Complexity  
A leaveNode() 0 26 3
A afterTraverse() 0 16 2
A getAnonymousClassHookIndex() 0 18 6
A moveAnonymousClassesToHook() 0 7 1
A convertToPhp5Statements() 0 8 1
1
<?php
2
3
namespace Spatie\Php7to5\NodeVisitors;
4
5
use PhpParser\Node;
6
use PhpParser\Node\Stmt\Declare_;
7
use PhpParser\Node\Stmt\Namespace_;
8
use PhpParser\Node\Stmt\Use_;
9
use PhpParser\NodeVisitorAbstract;
10
use Spatie\Php7to5\Converter;
11
use Spatie\Php7to5\Exceptions\InvalidPhpCode;
12
13
class AnonymousClassReplacer extends NodeVisitorAbstract
14
{
15
    /**
16
     * @var array
17
     */
18
    protected $anonymousClassNodes = [];
19
20
    /**
21
     * {@inheritdoc}
22
     */
23
    public function leaveNode(Node $node)
24
    {
25
        if (!$node instanceof Node\Expr\New_) {
26
            return;
27
        }
28
29
        $classNode = $node->class;
30
        if (!$classNode instanceof Node\Stmt\Class_) {
31
            return;
32
        }
33
34
        $newClassName = 'AnonymousClass'.count($this->anonymousClassNodes);
35
36
        $classNode->name = $newClassName;
37
38
        $this->anonymousClassNodes[] = $classNode;
39
40
        // Generate new code that instantiate our new class
41
        $newNode = new Node\Expr\New_(
42
            new Node\Expr\ConstFetch(
43
                new Node\Name($newClassName)
44
            )
45
        );
46
47
        return $newNode;
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53
    public function afterTraverse(array $nodes)
54
    {
55
        if (count($this->anonymousClassNodes) === 0) {
56
            return $nodes;
57
        }
58
59
        $anonymousClassStatements = $this->anonymousClassNodes;
60
61
        $anonymousClassStatements = $this->convertToPhp5Statements($anonymousClassStatements);
62
63
        $hookIndex = $this->getAnonymousClassHookIndex($nodes);
64
65
        $nodes = $this->moveAnonymousClassesToHook($nodes, $hookIndex, $anonymousClassStatements);
66
67
        return $nodes;
68
    }
69
70
    /**
71
     * Find the index of the first statement that is not a declare, use or namespace statement.
72
     *
73
     * @param array $statements
74
     *
75
     * @return int
76
     *
77
     * @throws \Spatie\Php7to5\Exceptions\InvalidPhpCode
78
     */
79
    protected function getAnonymousClassHookIndex(array $statements)
80
    {
81
        $hookIndex = false;
82
83
        foreach ($statements as $index => $statement) {
84
            if (!$statement instanceof Declare_ &&
85
                !$statement instanceof Use_ &&
86
                !$statement instanceof Namespace_) {
87
                $hookIndex = $index;
88
            }
89
        }
90
91
        if ($hookIndex === false) {
92
            throw InvalidPhpCode::noValidLocationFoundToInsertClasses();
93
        }
94
95
        return $hookIndex;
96
    }
97
98
    /**
99
     * @param array $nodes
100
     * @param $hookIndex
101
     * @param $anonymousClassStatements
102
     * 
103
     * @return array
104
     */
105
    protected function moveAnonymousClassesToHook(array $nodes, $hookIndex, $anonymousClassStatements)
106
    {
107
        $preStatements = array_slice($nodes, 0, $hookIndex);
108
        $postStatements = array_slice($nodes, $hookIndex);
109
110
        return array_merge($preStatements, $anonymousClassStatements, $postStatements);
111
    }
112
113
    /**
114
     * @param array $php7statements
115
     *
116
     * @return \PhpParser\Node[]
117
     */
118
    public function convertToPhp5Statements(array $php7statements)
119
    {
120
        $converter = Converter::getTraverser($php7statements);
0 ignored issues
show
Unused Code introduced by
The call to Converter::getTraverser() has too many arguments starting with $php7statements.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
121
122
        $php5Statements = $converter->traverse($php7statements);
123
124
        return $php5Statements;
125
    }
126
}
127