88use Kestrel \JsoncParser \Parser \JsonVisitor ;
99use Kestrel \JsoncParser \Parser \ParseErrorCode ;
1010
11+ /**
12+ * A reusable visitor that records events to an array.
13+ * Configure which events to record via constructor parameters.
14+ */
15+ class EventRecordingVisitor implements JsonVisitor
16+ {
17+ /** @var array<array<mixed>> */
18+ public array $ events = [];
19+
20+ public function __construct (
21+ private bool $ recordObject = true ,
22+ private bool $ recordArray = true ,
23+ private bool $ recordLiteral = true ,
24+ private bool $ recordSeparator = true ,
25+ private bool $ recordComment = true ,
26+ private bool $ recordError = true ,
27+ ) {
28+ }
29+
30+ public function onObjectBegin (int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): bool |null
31+ {
32+ if ($ this ->recordObject ) {
33+ $ this ->events [] = ['onObjectBegin ' , $ offset , $ pathSupplier ()];
34+ }
35+ return null ;
36+ }
37+
38+ public function onObjectProperty (string $ property , int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): void
39+ {
40+ if ($ this ->recordObject ) {
41+ $ this ->events [] = ['onObjectProperty ' , $ property , $ pathSupplier ()];
42+ }
43+ }
44+
45+ public function onObjectEnd (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
46+ {
47+ if ($ this ->recordObject ) {
48+ $ this ->events [] = ['onObjectEnd ' , $ offset ];
49+ }
50+ }
51+
52+ public function onArrayBegin (int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): bool |null
53+ {
54+ if ($ this ->recordArray ) {
55+ $ this ->events [] = ['onArrayBegin ' , $ offset , $ pathSupplier ()];
56+ }
57+ return null ;
58+ }
59+
60+ public function onArrayEnd (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
61+ {
62+ if ($ this ->recordArray ) {
63+ $ this ->events [] = ['onArrayEnd ' , $ offset ];
64+ }
65+ }
66+
67+ public function onLiteralValue (mixed $ value , int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): void
68+ {
69+ if ($ this ->recordLiteral ) {
70+ $ this ->events [] = ['onLiteralValue ' , $ value , $ pathSupplier ()];
71+ }
72+ }
73+
74+ public function onSeparator (string $ character , int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
75+ {
76+ if ($ this ->recordSeparator ) {
77+ $ this ->events [] = ['onSeparator ' , $ character ];
78+ }
79+ }
80+
81+ public function onComment (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
82+ {
83+ if ($ this ->recordComment ) {
84+ $ this ->events [] = ['onComment ' , $ offset ];
85+ }
86+ }
87+
88+ public function onError (ParseErrorCode $ error , int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
89+ {
90+ if ($ this ->recordError ) {
91+ $ this ->events [] = ['onError ' , $ error ];
92+ }
93+ }
94+ }
95+
1196describe ('parse: literals ' , function () {
1297 test ('parses boolean literals ' , function () {
1398 assertValidParse ('true ' , true );
292377
293378describe ('visit: object ' , function () {
294379 test ('visits empty object ' , function () {
295- /** @var array<array<mixed>> $events */
296- $ events = [];
297-
298- $ visitor = new class ($ events ) implements JsonVisitor {
299- /**
300- * @param array<array<mixed>> $events
301- * @phpstan-ignore property.onlyWritten (property is accessed via reference binding)
302- */
303- public function __construct (private array &$ events )
304- {
305- }
306-
307- public function onObjectBegin (int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): bool |null
308- {
309- $ this ->events [] = ['onObjectBegin ' , $ offset , $ pathSupplier ()];
310- return null ;
311- }
312- public function onObjectProperty (string $ property , int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): void
313- {
314- $ this ->events [] = ['onObjectProperty ' , $ property , $ pathSupplier ()];
315- }
316- public function onObjectEnd (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
317- {
318- $ this ->events [] = ['onObjectEnd ' , $ offset ];
319- }
320- public function onArrayBegin (int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): bool |null
321- {
322- $ this ->events [] = ['onArrayBegin ' , $ offset , $ pathSupplier ()];
323- return null ;
324- }
325- public function onArrayEnd (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
326- {
327- $ this ->events [] = ['onArrayEnd ' , $ offset ];
328- }
329- public function onLiteralValue (mixed $ value , int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): void
330- {
331- $ this ->events [] = ['onLiteralValue ' , $ value , $ pathSupplier ()];
332- }
333- public function onSeparator (string $ character , int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
334- {
335- $ this ->events [] = ['onSeparator ' , $ character ];
336- }
337- public function onComment (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
338- {
339- $ this ->events [] = ['onComment ' , $ offset ];
340- }
341- public function onError (ParseErrorCode $ error , int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
342- {
343- $ this ->events [] = ['onError ' , $ error ];
344- }
345- };
380+ $ visitor = new EventRecordingVisitor (
381+ recordArray: false ,
382+ recordLiteral: false ,
383+ recordComment: false ,
384+ recordError: false ,
385+ );
346386
347387 JsoncParser::visit ('{ } ' , $ visitor );
348388
349- expect ($ events )->toBe ([
389+ expect ($ visitor -> events )->toBe ([
350390 ['onObjectBegin ' , 0 , []],
351391 ['onObjectEnd ' , 2 ],
352392 ]);
353393 });
354394
355395 test ('visits simple object ' , function () {
356- /** @var array<array<mixed>> $events */
357- $ events = [];
358-
359- $ visitor = new class ($ events ) implements JsonVisitor {
360- /**
361- * @param array<array<mixed>> $events
362- * @phpstan-ignore property.onlyWritten (property is accessed via reference binding)
363- */
364- public function __construct (private array &$ events )
365- {
366- }
367-
368- public function onObjectBegin (int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): bool |null
369- {
370- $ this ->events [] = ['onObjectBegin ' , $ offset , $ pathSupplier ()];
371- return null ;
372- }
373- public function onObjectProperty (string $ property , int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): void
374- {
375- $ this ->events [] = ['onObjectProperty ' , $ property , $ pathSupplier ()];
376- }
377- public function onObjectEnd (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
378- {
379- $ this ->events [] = ['onObjectEnd ' , $ offset ];
380- }
381- public function onArrayBegin (int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): bool |null
382- {
383- $ this ->events [] = ['onArrayBegin ' , $ offset , $ pathSupplier ()];
384- return null ;
385- }
386- public function onArrayEnd (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
387- {
388- $ this ->events [] = ['onArrayEnd ' , $ offset ];
389- }
390- public function onLiteralValue (mixed $ value , int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): void
391- {
392- $ this ->events [] = ['onLiteralValue ' , $ value , $ pathSupplier ()];
393- }
394- public function onSeparator (string $ character , int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
395- {
396- $ this ->events [] = ['onSeparator ' , $ character ];
397- }
398- public function onComment (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
399- {
400- $ this ->events [] = ['onComment ' , $ offset ];
401- }
402- public function onError (ParseErrorCode $ error , int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
403- {
404- $ this ->events [] = ['onError ' , $ error ];
405- }
406- };
396+ $ visitor = new EventRecordingVisitor (
397+ recordArray: false ,
398+ recordComment: false ,
399+ recordError: false ,
400+ );
407401
408402 JsoncParser::visit ('{ "foo": "bar" } ' , $ visitor );
409403
410- expect ($ events )->toBe ([
404+ expect ($ visitor -> events )->toBe ([
411405 ['onObjectBegin ' , 0 , []],
412406 ['onObjectProperty ' , 'foo ' , []],
413407 ['onSeparator ' , ': ' ],
@@ -419,110 +413,32 @@ public function onError(ParseErrorCode $error, int $offset, int $length, int $st
419413
420414describe ('visit: array ' , function () {
421415 test ('visits empty array ' , function () {
422- /** @var array<array<mixed>> $events */
423- $ events = [];
424-
425- $ visitor = new class ($ events ) implements JsonVisitor {
426- /**
427- * @param array<array<mixed>> $events
428- * @phpstan-ignore property.onlyWritten (property is accessed via reference binding)
429- */
430- public function __construct (private array &$ events )
431- {
432- }
433-
434- public function onObjectBegin (int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): bool |null
435- {
436- return null ;
437- }
438- public function onObjectProperty (string $ property , int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): void
439- {
440- }
441- public function onObjectEnd (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
442- {
443- }
444- public function onArrayBegin (int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): bool |null
445- {
446- $ this ->events [] = ['onArrayBegin ' , $ offset , $ pathSupplier ()];
447- return null ;
448- }
449- public function onArrayEnd (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
450- {
451- $ this ->events [] = ['onArrayEnd ' , $ offset ];
452- }
453- public function onLiteralValue (mixed $ value , int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): void
454- {
455- $ this ->events [] = ['onLiteralValue ' , $ value , $ pathSupplier ()];
456- }
457- public function onSeparator (string $ character , int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
458- {
459- }
460- public function onComment (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
461- {
462- }
463- public function onError (ParseErrorCode $ error , int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
464- {
465- }
466- };
416+ $ visitor = new EventRecordingVisitor (
417+ recordObject: false ,
418+ recordSeparator: false ,
419+ recordComment: false ,
420+ recordError: false ,
421+ );
467422
468423 JsoncParser::visit ('[] ' , $ visitor );
469424
470- expect ($ events )->toBe ([
425+ expect ($ visitor -> events )->toBe ([
471426 ['onArrayBegin ' , 0 , []],
472427 ['onArrayEnd ' , 1 ],
473428 ]);
474429 });
475430
476431 test ('visits array with values ' , function () {
477- /** @var array<array<mixed>> $events */
478- $ events = [];
479-
480- $ visitor = new class ($ events ) implements JsonVisitor {
481- /**
482- * @param array<array<mixed>> $events
483- * @phpstan-ignore property.onlyWritten (property is accessed via reference binding)
484- */
485- public function __construct (private array &$ events )
486- {
487- }
488-
489- public function onObjectBegin (int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): bool |null
490- {
491- return null ;
492- }
493- public function onObjectProperty (string $ property , int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): void
494- {
495- }
496- public function onObjectEnd (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
497- {
498- }
499- public function onArrayBegin (int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): bool |null
500- {
501- $ this ->events [] = ['onArrayBegin ' , $ offset , $ pathSupplier ()];
502- return null ;
503- }
504- public function onArrayEnd (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
505- {
506- $ this ->events [] = ['onArrayEnd ' , $ offset ];
507- }
508- public function onLiteralValue (mixed $ value , int $ offset , int $ length , int $ startLine , int $ startCharacter , \Closure $ pathSupplier ): void
509- {
510- $ this ->events [] = ['onLiteralValue ' , $ value , $ pathSupplier ()];
511- }
512- public function onSeparator (string $ character , int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
513- {
514- }
515- public function onComment (int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
516- {
517- }
518- public function onError (ParseErrorCode $ error , int $ offset , int $ length , int $ startLine , int $ startCharacter ): void
519- {
520- }
521- };
432+ $ visitor = new EventRecordingVisitor (
433+ recordObject: false ,
434+ recordSeparator: false ,
435+ recordComment: false ,
436+ recordError: false ,
437+ );
522438
523439 JsoncParser::visit ('[ true, null ] ' , $ visitor );
524440
525- expect ($ events )->toBe ([
441+ expect ($ visitor -> events )->toBe ([
526442 ['onArrayBegin ' , 0 , []],
527443 ['onLiteralValue ' , true , [0 ]],
528444 ['onLiteralValue ' , null , [1 ]],
0 commit comments