Passed
Branch feature/first-release (5424a4)
by Andrea Marco
12:20
created

Parser   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 118
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 38
c 2
b 0
f 0
dl 0
loc 118
ccs 38
cts 38
cp 1
rs 10
wmc 22

5 Methods

Rating   Name   Duplication   Size   Complexity  
A toArray() 0 9 3
B getIterator() 0 29 9
A __construct() 0 3 1
A lazyLoad() 0 15 5
A fastForward() 0 10 4
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 369
    public function __construct(private readonly Generator $tokens, private readonly Config $config)
44
    {
45 369
        $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 357
    public function getIterator(): Traversable
54
    {
55 357
        $state = new State($this->config->pointers, fn () => new self($this->lazyLoad(), clone $this->config));
56
57 357
        foreach ($this->tokens as $token) {
58 353
            if ($this->isFastForwarding) {
59 7
                continue;
60 353
            } elseif (!$token->matches($state->expectedToken)) {
61 1
                throw new SyntaxException($token);
62
            }
63
64 353
            $state->mutateByToken($token);
65
66 353
            if (!$token->endsChunk() || $state->tree->isDeep()) {
67 353
                continue;
68
            }
69
70 351
            if ($state->hasBuffer()) {
71
                /** @var string|int $key */
72 261
                $key = $this->decoder->decode($state->tree->currentKey());
73 261
                $value = $this->decoder->decode($state->value());
74
75 260
                yield $key => $state->callPointer($value, $key);
76
77 260
                $value instanceof self && $value->fastForward();
78
            }
79
80 350
            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 30
    public function lazyLoad(): Generator
92
    {
93 30
        $depth = 0;
94
95
        do {
96 30
            yield $token = $this->tokens->current();
97
98 30
            if ($token instanceof CompoundBegin) {
99 30
                $depth++;
100 30
            } elseif ($token instanceof CompoundEnd) {
101 30
                $depth--;
102
            }
103
104 30
            $depth > 0 && $this->tokens->next();
105 30
        } 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 30
    public function fastForward(): void
130
    {
131 30
        if (!$this->tokens->valid()) {
132 23
            return;
133
        }
134
135 7
        $this->isFastForwarding = true;
136
137 7
        foreach ($this as $value) {
138
            $value instanceof self && $value->fastForward(); // @codeCoverageIgnore
139
        }
140
    }
141
}
142