1: <?php
2: declare(strict_types=1);
3:
4: namespace om\ICal;
5:
6: /**
7: * Writes components as iCalendar data (RFC 5545, section 3.1): CRLF line breaks and
8: * lines folded at 75 octets without splitting UTF-8 characters. Properties are written
9: * with their raw values, so unknown and X- properties are kept unchanged.
10: */
11: final class Serializer {
12: public static function serialize(Component $component): string {
13: $result = 'BEGIN:' . $component->name . "\r\n";
14: foreach ($component->properties as $property) {
15: $result .= self::fold((string) $property);
16: }
17: foreach ($component->components as $child) {
18: $result .= self::serialize($child);
19: }
20: return $result . 'END:' . $component->name . "\r\n";
21: }
22:
23: /**
24: * Fold a content line into chunks of at most 75 octets.
25: */
26: public static function fold(string $line): string {
27: $result = '';
28: $limit = 75;
29: while (strlen($line) > $limit) {
30: $cut = $limit;
31: while ($cut > 1 && (ord($line[$cut]) & 0xC0) === 0x80) {
32: $cut--; // do not split a multibyte character
33: }
34: $result .= substr($line, 0, $cut) . "\r\n ";
35: $line = substr($line, $cut);
36: $limit = 74; // the leading space counts
37: }
38: return $result . $line . "\r\n";
39: }
40: }
41: