Lines 100.00% 13 / 13
Functions and Methods 100.00% 2 / 2
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
MessageParser 100.00% 13 / 13 100.00% 2 / 2 5 100.00% 1 / 1
 parse 100.00% 4 / 4 100.00% 1 / 1 1
 decompress 100.00% 9 / 9 100.00% 1 / 1 4
1<?php
2
3declare(strict_types=1);
4
5namespace Lorekeeper\Client\Message;
6
7use Lorekeeper\Server\Message\DelimitedMessage;
8use Lorekeeper\Server\Message\JsonMessage;
9use Lorekeeper\Server\Message\Message;
10use Lorekeeper\Server\Message\XmlMessage;
11use Psl\Option;
12
13final readonly class MessageParser
14{
15    private const string COMPRESSED_PREFIX = 'Z';
16
17    /** @return Option\Option<Message> */
18    public static function parse(string $raw): Option\Option
19    {
20        return self::decompress($raw)
21            ->andThen(static fn(string $message): Option\Option => XmlMessage::from($message)->or(JsonMessage::from(
22                $message,
23            ))->or(DelimitedMessage::from($message)));
24    }
25
26    /** @return Option\Option<string> */
27    private static function decompress(string $raw): Option\Option
28    {
29        if (!str_starts_with($raw, self::COMPRESSED_PREFIX)) {
30            return Option\some($raw);
31        }
32
33        $compressed = base64_decode(substr($raw, offset: 1), strict: true);
34        if ($compressed === false) {
35            return Option\none();
36        }
37
38        set_error_handler(static fn(): bool => true);
39        try {
40            $message = gzuncompress($compressed);
41        } finally {
42            restore_error_handler();
43        }
44
45        return $message === false ? Option\none() : Option\some($message);
46    }
47}