Passed
Push — master ( 76b76b...ca78d8 )
by Edward
02:11
created

EventGenerator::__invoke()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 26
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 19
c 1
b 0
f 0
nc 7
nop 0
dl 0
loc 26
rs 8.8333
1
<?php
2
declare(strict_types=1);
3
4
namespace Remorhaz\JSON\Data\Event;
5
6
use Generator;
7
use Remorhaz\JSON\Data\Path\PathInterface;
8
use Remorhaz\JSON\Data\Value\ArrayValueInterface;
9
use Remorhaz\JSON\Data\Value\NodeValueInterface;
10
use Remorhaz\JSON\Data\Value\ObjectValueInterface;
11
use Remorhaz\JSON\Data\Value\ScalarValueInterface;
12
13
final class EventGenerator
14
{
15
16
    private $stack;
17
18
    private $path;
19
20
    public function __construct(NodeValueInterface $value, PathInterface $path)
21
    {
22
        $this->stack = [$value];
23
        $this->path = $path;
24
    }
25
26
    public function __invoke(): Generator
27
    {
28
        while (true) {
29
            if (empty($this->stack)) {
30
                return;
31
            }
32
            $entity = array_pop($this->stack);
33
            switch (true) {
34
                case $entity instanceof EventInterface:
35
                    yield from $this->onEvent($entity);
36
                    break;
37
38
                case $entity instanceof ScalarValueInterface:
39
                    yield from $this->onScalarValue($entity);
40
                    break;
41
42
                case $entity instanceof ArrayValueInterface:
43
                    yield from $this->onArrayValue($entity);
44
                    break;
45
46
                case $entity instanceof ObjectValueInterface:
47
                    yield from $this->onObjectValue($entity);
48
                    break;
49
50
                default:
51
                    throw new Exception\UnexpectedEntityException($entity);
52
            }
53
        }
54
    }
55
56
    private function onEvent(EventInterface $event): Generator
57
    {
58
        switch (true) {
59
            case $event instanceof BeforeElementEventInterface:
60
                $this->path = $this
61
                    ->path
62
                    ->copyWithElement($event->getIndex());
63
                break;
64
65
            case $event instanceof BeforePropertyEventInterface:
66
                $this->path = $this
67
                    ->path
68
                    ->copyWithProperty($event->getName());
69
                break;
70
71
            case $event instanceof AfterElementEventInterface:
72
            case $event instanceof AfterPropertyEventInterface:
73
                $this->path = $this
74
                    ->path
75
                    ->copyParent();
76
                break;
77
        }
78
        yield $event;
79
    }
80
81
    private function onScalarValue(ScalarValueInterface $value): Generator
82
    {
83
        yield new ScalarEvent($value->getData(), $this->path);
84
    }
85
86
    private function onArrayValue(ArrayValueInterface $value): Generator
87
    {
88
        $localStack = [];
89
        foreach ($value->createChildIterator() as $index => $child) {
90
            $elementPath = $this
91
                ->path
92
                ->copyWithElement($index);
93
            array_push(
94
                $localStack,
95
                new BeforeElementEvent($index, $elementPath),
96
                $child,
97
                new AfterElementEvent($index, $elementPath)
98
            );
99
        }
100
        array_push(
101
            $this->stack,
102
            new AfterArrayEvent($this->path),
103
            ...array_reverse($localStack)
104
        );
105
        yield new BeforeArrayEvent($this->path);
106
    }
107
108
    private function onObjectValue(ObjectValueInterface $value): Generator
109
    {
110
        $localStack = [];
111
        foreach ($value->createChildIterator() as $name => $child) {
112
            $elementPath = $this
113
                ->path
114
                ->copyWithProperty($name);
115
            array_push(
116
                $localStack,
117
                new BeforePropertyEvent($name, $elementPath),
118
                $child,
119
                new AfterPropertyEvent($name, $elementPath)
120
            );
121
        }
122
        array_push(
123
            $this->stack,
124
            new AfterObjectEvent($this->path),
125
            ...array_reverse($localStack)
126
        );
127
        yield new BeforeObjectEvent($this->path);
128
    }
129
}
130