Test Setup Failed
Push — master ( 1fc96a...b6ad37 )
by Jan Philipp
03:06
created

NodeSearch::eachType()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 20
rs 8.8571
cc 5
eloc 11
nc 7
nop 1
1
<?php
2
3
4
namespace JanPiet\PhpTranspiler;
5
6
use PhpParser\Node;
7
8
class NodeSearch
9
{
10
    /**
11
     * @var \PhpParser\Node[]
12
     */
13
    private $tree;
14
15
    /**
16
     * @var Node[]
17
     */
18
    private $allNodes;
19
20
    /**
21
     * @var Node[]
22
     */
23
    private $parentChain;
24
25
    /**
26
     * NodeSearch constructor.
27
     * @param Node[] $nodes
28
     */
29
    public function __construct(array $nodes)
30
    {
31
        $this->tree = $nodes;
32
    }
33
34
    /**
35
     * @param \string[] ...$classNames
36
     * @return \Traversable
37
     */
38
    public function eachType(string ...$classNames): \Traversable
39
    {
40
        $this->update();
41
42
        foreach ($this->allNodes as $node) {
43
            $found = false;
44
            foreach ($classNames as $className) {
45
                if ($node instanceof  $className) {
46
                    $found = true;
47
                    break;
48
                }
49
            }
50
51
            if (!$found) {
52
                continue;
53
            }
54
55
            yield $node;
56
        }
57
    }
58
59
    public function findParent(string $class, Node $fromNode)
60
    {
61
        $this->update();
62
63
        do {
64
            $nodeId = spl_object_hash($fromNode);
65
            $fromNode = $this->parentChain[$nodeId];
66
            
67
            if (null === $fromNode) {
68
                throw new ParentNotFoundException('Could not find the parent "' . $class . '" for node');
69
            }
70
        } while (!$fromNode instanceof $class);
71
72
        return $fromNode;
73
    }
74
75
    /**
76
     * @param Node $newClass
77
     */
78
    public function appendToRoot(Node $newClass)
79
    {
80
        $this->tree[] = $newClass;
81
    }
82
83
    /**
84
     * @return \PhpParser\Node[]
85
     */
86
    public function getTree()
87
    {
88
        return $this->tree;
89
    }
90
91
    private function update()
92
    {
93
        $allNodes = [];
94
        $parentChain = [];
95
96
        foreach ($this->recurse($this->tree) as $parent => $node) {
97
            $nodeId = spl_object_hash($node);
98
            $allNodes[$nodeId] = $node;
99
            $parentChain[$nodeId] = $parent;
100
        }
101
102
        $this->allNodes = $allNodes;
103
        $this->parentChain = $parentChain;
104
    }
105
106
    /**
107
     * @param Node[] $nodes
108
     * @return \Traversable
109
     */
110
    private function recurse(array $nodes, Node $parent = null): \Traversable
111
    {
112
        foreach ($nodes as $node) {
113
            if (is_array($node)) {
114
                yield from $this->recurse($node, $node);
0 ignored issues
show
Documentation introduced by
$node is of type array, but the function expects a null|object<PhpParser\Node>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
115
            } elseif ($node instanceof Node) {
116
                yield $parent => $node;
117
118
                $subNodeNames = $node->getSubNodeNames();
119
120
                foreach ($subNodeNames as $subNodeName) {
121
                    $subNode = $node->{$subNodeName};
122
123
                    if (!is_array($subNode)) {
124
                        $subNode = [$subNode];
125
                    }
126
127
                    yield from $this->recurse($subNode, $node);
128
                }
129
            }
130
        }
131
    }
132
133
    /**
134
     * @param $sourceNode
135
     * @param $newNode
136
     */
137
    public function replaceNode($sourceNode, $newNode)
138
    {
139
        $this->update();
140
141
        $hash = spl_object_hash($sourceNode);
142
143
        if (array_key_exists($hash, $this->parentChain)) {
144
            $parents = [$this->parentChain[$hash]];
145
        } else {
146
            $parents = $this->tree;
147
        }
148
149
        foreach ($parents as $key => $parent) {
150
            if ($parent === $sourceNode) {
151
                $parent[$key] = $newNode;
152
                return;
153
            }
154
155
            foreach ($parent->getSubNodeNames() as $subNodeName) {
156
                $nodes = &$parent->{$subNodeName};
157
158
                if (!is_array($nodes)) {
159
                    if ($nodes === $sourceNode) {
160
                        $parent->{$subNodeName} = $newNode;
161
                        return;
162
                    }
163
164
                    continue;
165
                }
166
167
                $foundKey = false;
168
                foreach ($nodes as $key => $node) {
169
                    if ($node === $sourceNode) {
170
                        $foundKey = $key;
171
                    }
172
                }
173
174
                if (false !== $foundKey) {
175
                    $nodes[$foundKey] = $newNode;
176
                    return;
177
                }
178
            }
179
        }
180
        
181
        throw new NodeNotFoundException('Node not found, replace not possible');
182
    }
183
}
184