JsonParser   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 238
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 9
Bugs 1 Features 0
Metric Value
wmc 25
eloc 40
c 9
b 1
f 0
dl 0
loc 238
ccs 53
cts 53
cp 1
rs 10

17 Methods

Rating   Name   Duplication   Size   Complexity  
A parse() 0 3 1
A pointers() 0 7 3
A lazyPointer() 0 5 1
A lazyPointers() 0 7 3
A pointer() 0 5 1
A getIterator() 0 7 2
A decoder() 0 5 1
A onDecodingError() 0 5 1
A __construct() 0 5 1
A lazy() 0 3 1
A bytes() 0 5 1
A patchDecodingError() 0 4 2
A toArray() 0 3 1
A traverse() 0 4 3
A onSyntaxError() 0 5 1
A progress() 0 3 1
A wrap() 0 5 1
1
<?php
2
3
namespace Cerbero\JsonParser;
4
5
use Cerbero\JsonParser\Decoders\DecodedValue;
6
use Cerbero\JsonParser\Decoders\Decoder;
7
use Cerbero\JsonParser\Exceptions\SyntaxException;
8
use Cerbero\JsonParser\Pointers\Pointer;
9
use Cerbero\JsonParser\Sources\AnySource;
10
use Cerbero\JsonParser\Tokens\Lexer;
11
use Cerbero\JsonParser\Tokens\Parser;
12
use Cerbero\JsonParser\ValueObjects\Config;
13
use Cerbero\JsonParser\ValueObjects\Progress;
14
use Closure;
15
use IteratorAggregate;
16
use Traversable;
17
18
/**
19
 * The JSON parser entry-point.
20
 *
21
 * @implements IteratorAggregate<string|int, mixed>
22
 */
23
final class JsonParser implements IteratorAggregate
24
{
25
    /**
26
     * The configuration.
27
     *
28
     * @var Config
29
     */
30
    private readonly Config $config;
31
32
    /**
33
     * The lexer.
34
     *
35
     * @var Lexer
36
     */
37
    private readonly Lexer $lexer;
38
39
    /**
40
     * The parser.
41
     *
42
     * @var Parser
43
     */
44
    private readonly Parser $parser;
45
46
    /**
47
     * Instantiate the class statically
48
     *
49
     * @param mixed $source
50
     * @return self
51
     */
52 348
    public static function parse(mixed $source): self
53
    {
54 348
        return new self($source);
55
    }
56
57
    /**
58
     * Instantiate the class.
59
     *
60
     * @param mixed $source
61
     */
62 373
    public function __construct(mixed $source)
63
    {
64 373
        $this->config = new Config();
0 ignored issues
show
Bug introduced by
The property config is declared read-only in Cerbero\JsonParser\JsonParser.
Loading history...
65 373
        $this->lexer = new Lexer(new AnySource($source, $this->config));
0 ignored issues
show
Bug introduced by
The property lexer is declared read-only in Cerbero\JsonParser\JsonParser.
Loading history...
66 373
        $this->parser = new Parser($this->lexer->getIterator(), $this->config);
0 ignored issues
show
Bug introduced by
The property parser is declared read-only in Cerbero\JsonParser\JsonParser.
Loading history...
67
    }
68
69
    /**
70
     * Retrieve the lazily iterable JSON
71
     *
72
     * @return Traversable<string|int, mixed>
73
     */
74 170
    public function getIterator(): Traversable
75
    {
76
        try {
77 170
            yield from $this->parser;
78 15
        } catch (SyntaxException $e) {
0 ignored issues
show
Unused Code introduced by
catch (\Cerbero\JsonPars...ons\SyntaxException $e) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
79 11
            $e->setPosition($this->lexer->position());
80 11
            ($this->config->onSyntaxError)($e);
81
        }
82
    }
83
84
    /**
85
     * Set the JSON pointers
86
     *
87
     * @param string[]|array<string, Closure> $pointers
88
     * @return self
89
     */
90 69
    public function pointers(array $pointers): self
91
    {
92 69
        foreach ($pointers as $pointer => $callback) {
93 69
            $callback instanceof Closure ? $this->pointer($pointer, $callback) : $this->pointer($callback);
94
        }
95
96 61
        return $this;
97
    }
98
99
    /**
100
     * Set a JSON pointer
101
     *
102
     * @param string $pointer
103
     * @param ?Closure $callback
104
     * @return self
105
     */
106 201
    public function pointer(string $pointer, ?Closure $callback = null): self
107
    {
108 201
        $this->config->pointers->add(new Pointer($pointer, false, $callback));
109
110 197
        return $this;
111
    }
112
113
    /**
114
     * Set the lazy JSON pointers
115
     *
116
     * @param string[]|array<string, Closure> $pointers
117
     * @return self
118
     */
119 32
    public function lazyPointers(array $pointers): self
120
    {
121 32
        foreach ($pointers as $pointer => $callback) {
122 32
            $callback instanceof Closure ? $this->lazyPointer($pointer, $callback) : $this->lazyPointer($callback);
123
        }
124
125 32
        return $this;
126
    }
127
128
    /**
129
     * Set a lazy JSON pointer
130
     *
131
     * @param string $pointer
132
     * @param ?Closure $callback
133
     * @return self
134
     */
135 105
    public function lazyPointer(string $pointer, ?Closure $callback = null): self
136
    {
137 105
        $this->config->pointers->add(new Pointer($pointer, true, $callback));
138
139 105
        return $this;
140
    }
141
142
    /**
143
     * Set a lazy JSON pointer for the whole JSON
144
     *
145
     * @return self
146
     */
147 4
    public function lazy(): self
148
    {
149 4
        return $this->lazyPointer('');
150
    }
151
152
    /**
153
     * Traverse the JSON one key and value at a time
154
     *
155
     * @param ?Closure $callback
156
     * @return void
157
     */
158 16
    public function traverse(?Closure $callback = null): void
159
    {
160 16
        foreach ($this as $key => $value) {
161 9
            $callback && $callback($value, $key, $this);
162
        }
163
    }
164
165
    /**
166
     * Eager load the JSON into an array
167
     *
168
     * @return array<string|int, mixed>
169
     */
170 191
    public function toArray(): array
171
    {
172 191
        return $this->parser->toArray();
173
    }
174
175
    /**
176
     * Set the JSON decoder
177
     *
178
     * @param Decoder $decoder
179
     * @return self
180
     */
181 6
    public function decoder(Decoder $decoder): self
182
    {
183 6
        $this->config->decoder = $decoder;
184
185 6
        return $this;
186
    }
187
188
    /**
189
     * Retrieve the parsing progress
190
     *
191
     * @return Progress
192
     */
193 1
    public function progress(): Progress
194
    {
195 1
        return $this->lexer->progress();
196
    }
197
198
    /**
199
     * The number of bytes to read in each chunk
200
     *
201
     * @param int<1, max> $bytes
202
     * @return self
203
     */
204 6
    public function bytes(int $bytes): self
205
    {
206 6
        $this->config->bytes = $bytes;
207
208 6
        return $this;
209
    }
210
211
    /**
212
     * Set the patch to apply during a decoding error
213
     *
214
     * @param mixed $patch
215
     * @return self
216
     */
217 4
    public function patchDecodingError(mixed $patch = null): self
218
    {
219 4
        return $this->onDecodingError(function (DecodedValue $decoded) use ($patch) {
220 4
            $decoded->value = is_callable($patch) ? $patch($decoded) : $patch;
221 4
        });
222
    }
223
224
    /**
225
     * Set the logic to run during a decoding error
226
     *
227
     * @param Closure $callback
228
     * @return self
229
     */
230 5
    public function onDecodingError(Closure $callback): self
231
    {
232 5
        $this->config->onDecodingError = $callback;
233
234 5
        return $this;
235
    }
236
237
    /**
238
     * Set the logic to run during a syntax error
239
     *
240
     * @param Closure $callback
241
     * @return self
242
     */
243 1
    public function onSyntaxError(Closure $callback): self
244
    {
245 1
        $this->config->onSyntaxError = $callback;
246
247 1
        return $this;
248
    }
249
250
    /**
251
     * Set the logic to run for wrapping the parser
252
     *
253
     * @param Closure $callback
254
     * @return self
255
     */
256 2
    public function wrap(Closure $callback): self
257
    {
258 2
        $this->config->wrapper = $callback;
259
260 2
        return $this;
261
    }
262
}
263