Completed
Push — master ( b280db...3fdb6d )
by Freek
02:18
created

AnonymousClassReplacer::convertToPhp5Statements()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 1
eloc 4
nc 1
nop 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
                break;
90
            }
91
        }
92
93
        if ($hookIndex === false) {
94
            throw InvalidPhpCode::noValidLocationFoundToInsertClasses();
95
        }
96
97
        return $hookIndex;
98
    }
99
100
    /**
101
     * @param array $nodes
102
     * @param $hookIndex
103
     * @param $anonymousClassStatements
104
     * 
105
     * @return array
106
     */
107
    protected function moveAnonymousClassesToHook(array $nodes, $hookIndex, $anonymousClassStatements)
108
    {
109
        $preStatements = array_slice($nodes, 0, $hookIndex);
110
        $postStatements = array_slice($nodes, $hookIndex);
111
112
        return array_merge($preStatements, $anonymousClassStatements, $postStatements);
113
    }
114
115
    /**
116
     * @param array $php7statements
117
     *
118
     * @return \PhpParser\Node[]
119
     */
120
    public function convertToPhp5Statements(array $php7statements)
121
    {
122
        $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...
123
124
        $php5Statements = $converter->traverse($php7statements);
125
126
        return $php5Statements;
127
    }
128
}
129