1: <?php
2: declare(strict_types=1);
3:
4: namespace om\ICal\Parser;
5:
6: use Generator;
7: use om\ICal\ContentLine;
8:
9: /**
10: * Turns iCalendar data into content lines.
11: *
12: * Lines that are not valid content lines are reported through the $invalid callback
13: * (with the line and its number) and skipped.
14: */
15: final class Tokenizer {
16: /**
17: * @param iterable<int, string> $lines unfolded lines, see LineReader
18: * @param ?callable(string, int): void $invalid
19: * @return Generator<int, ContentLine>
20: */
21: public static function tokenize(iterable $lines, ?callable $invalid = null): Generator {
22: foreach ($lines as $number => $line) {
23: $contentLine = ContentLine::parse($line, $number);
24: if ($contentLine !== null) {
25: yield $contentLine;
26: } elseif ($invalid !== null) {
27: $invalid($line, $number);
28: }
29: }
30: }
31:
32: /**
33: * Content lines as [name, raw parameters, raw value, line number] without creating objects.
34: *
35: * @internal used by the parser
36: * @param iterable<int, string> $lines
37: * @param ?callable(string, int): void $invalid
38: * @return Generator<int, array{string, string, string, int}>
39: */
40: public static function rows(iterable $lines, ?callable $invalid = null): Generator {
41: foreach ($lines as $number => $line) {
42: $parts = ContentLine::split($line);
43: if ($parts !== null) {
44: $parts[] = $number;
45: yield $parts;
46: } elseif ($invalid !== null) {
47: $invalid($line, $number);
48: }
49: }
50: }
51:
52: /**
53: * @param ?callable(string, int): void $invalid
54: * @return Generator<int, ContentLine>
55: */
56: public static function fromString(string $content, ?callable $invalid = null): Generator {
57: return self::tokenize(LineReader::fromString($content), $invalid);
58: }
59:
60: /**
61: * @param ?callable(string, int): void $invalid
62: * @return Generator<int, ContentLine>
63: */
64: public static function fromFile(string $file, ?callable $invalid = null): Generator {
65: return self::tokenize(LineReader::fromFile($file), $invalid);
66: }
67:
68: /**
69: * @param resource $stream
70: * @param ?callable(string, int): void $invalid
71: * @return Generator<int, ContentLine>
72: */
73: public static function fromStream($stream, ?callable $invalid = null): Generator {
74: return self::tokenize(LineReader::fromStream($stream), $invalid);
75: }
76: }
77: