Passed
Push — master ( f13532...bf7dba )
by Andrea Marco
02:44 queued 12s
created

Parser::getIterator()   B

Complexity

Conditions 9
Paths 10

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 9

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 16
c 1
b 0
f 0
nc 10
nop 0
dl 0
loc 29
ccs 17
cts 17
cp 1
crap 9
rs 8.0555
1
<?php
2
3
namespace Cerbero\JsonParser\Tokens;
4
5
use Cerbero\JsonParser\Decoders\ConfigurableDecoder;
6
use Cerbero\JsonParser\Exceptions\SyntaxException;
7
use Cerbero\JsonParser\Tokens\CompoundBegin;
8
use Cerbero\JsonParser\Tokens\CompoundEnd;
9
use Cerbero\JsonParser\Tokens\Token;
10
use Cerbero\JsonParser\ValueObjects\Config;
11
use Cerbero\JsonParser\ValueObjects\State;
12
use Generator;
13
use IteratorAggregate;
14
use Traversable;
15
16
/**
17
 * The JSON parser.
18
 *
19
 * @implements IteratorAggregate<string|int, mixed>
20
 */
21
final class Parser implements IteratorAggregate
22
{
23
    /**
24
     * The decoder handling potential errors.
25
     *
26
     * @var ConfigurableDecoder
27
     */
28
    private readonly ConfigurableDecoder $decoder;
29
30
    /**
31
     * Whether the parser is fast-forwarding.
32
     *
33
     * @var bool
34
     */
35
    private bool $isFastForwarding = false;
36
37
    /**
38
     * Instantiate the class.
39
     *
40
     * @param Generator<int, Token> $tokens
41
     * @param Config $config
42
     */
43 371
    public function __construct(private readonly Generator $tokens, private readonly Config $config)
44
    {
45 371
        $this->decoder = new ConfigurableDecoder($config);
0 ignored issues
show
Bug introduced by
The property decoder is declared read-only in Cerbero\JsonParser\Tokens\Parser.
Loading history...
46
    }
47
48
    /**
49
     * Retrieve the JSON fragments
50
     *
51
     * @return Traversable<string|int, mixed>
52
     */
53 359
    public function getIterator(): Traversable
54
    {
55 359
        $state = new State($this->config->pointers, fn () => new self($this->lazyLoad(), clone $this->config));
56
57 359
        foreach ($this->tokens as $token) {
58 355
            if ($this->isFastForwarding) {
59 9
                continue;
60 355
            } elseif (!$token->matches($state->expectedToken)) {
61 1
                throw new SyntaxException($token);
62
            }
63
64 355
            $state->mutateByToken($token);
65
66 355
            if (!$token->endsChunk() || $state->tree->isDeep()) {
67 355
                continue;
68
            }
69
70 353
            if ($state->hasBuffer()) {
71
                /** @var string|int $key */
72 263
                $key = $this->decoder->decode($state->tree->currentKey());
73 263
                $value = $this->decoder->decode($state->value());
74
75 262
                yield $key => $state->callPointer($value, $key);
76
77 262
                $value instanceof self && $value->fastForward();
78
            }
79
80 352
            if ($state->canStopParsing()) {
81 162
                break;
82
            }
83
        }
84
    }
85
86
    /**
87
     * Retrieve the generator to lazy load the current compound
88
     *
89
     * @return Generator<int, Token>
90
     */
91 32
    public function lazyLoad(): Generator
92
    {
93 32
        $depth = 0;
94
95
        do {
96 32
            yield $token = $this->tokens->current();
97
98 32
            if ($token instanceof CompoundBegin) {
99 32
                $depth++;
100 32
            } elseif ($token instanceof CompoundEnd) {
101 32
                $depth--;
102
            }
103
104 32
            $depth > 0 && $this->tokens->next();
105 32
        } while ($depth > 0);
106
    }
107
108
    /**
109
     * Eager load the current compound into an array
110
     *
111
     * @return array<string|int, mixed>
112
     */
113 191
    public function toArray(): array
114
    {
115 191
        $array = [];
116
117 191
        foreach ($this as $key => $value) {
118 137
            $array[$key] = $value instanceof self ? $value->toArray() : $value;
119
        }
120
121 191
        return $array;
122
    }
123
124
    /**
125
     * Fast-forward the parser
126
     *
127
     * @return void
128
     */
129 32
    public function fastForward(): void
130
    {
131 32
        if (!$this->tokens->valid()) {
132 23
            return;
133
        }
134
135 9
        $this->isFastForwarding = true;
136
137 9
        foreach ($this as $value) {
138
            $value instanceof self && $value->fastForward(); // @codeCoverageIgnore
139
        }
140
    }
141
}
142