Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.00% covered (success)
90.00%
27 / 30
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
ResponseParser
90.00% covered (success)
90.00%
27 / 30
0.00% covered (danger)
0.00%
0 / 1
12.14
0.00% covered (danger)
0.00%
0 / 1
 parse
90.00% covered (success)
90.00%
27 / 30
0.00% covered (danger)
0.00%
0 / 1
12.14
1<?php
2
3declare(strict_types=1);
4
5namespace Ndrstmr\Icap;
6
7use Ndrstmr\Icap\DTO\IcapResponse;
8use Ndrstmr\Icap\Exception\IcapResponseException;
9
10/**
11 * Parses raw ICAP responses into IcapResponse objects.
12 */
13
14class ResponseParser implements ResponseParserInterface
15{
16    /**
17     * @param string $rawResponse
18     *
19     * @throws IcapResponseException
20     */
21    public function parse(string $rawResponse): IcapResponse
22    {
23        $parts = preg_split("/\r?\n\r?\n/", $rawResponse, 2);
24        if ($parts === false || count($parts) < 1) {
25            throw new IcapResponseException('Invalid ICAP response');
26        }
27        [$head, $body] = $parts + ['', ''];
28
29        $lines = preg_split('/\r?\n/', $head);
30        if ($lines === false || count($lines) === 0) {
31            throw new IcapResponseException('Invalid ICAP response');
32        }
33
34        $statusLine = array_shift($lines);
35        if (!preg_match('/ICAP\/1\.0\s+(\d+)/', (string)$statusLine, $m)) {
36            throw new IcapResponseException('Invalid status line');
37        }
38        $statusCode = (int)$m[1];
39
40        $headers = [];
41        foreach ($lines as $line) {
42            if ($line === '') {
43                continue;
44            }
45            [$name, $value] = explode(':', $line, 2);
46            $name = trim($name);
47            $value = trim($value);
48            $headers[$name][] = $value;
49        }
50
51        // decode chunked body if applicable
52        if ($body !== '' && preg_match('/^[0-9a-fA-F]+\r\n/i', $body)) {
53            $decoded = '';
54            while (preg_match('/^([0-9a-fA-F]+)\r\n/', $body, $mm)) {
55                $len = hexdec($mm[1]);
56                $body = substr($body, strlen($mm[0]));
57                if ($len === 0) {
58                    break;
59                }
60                $decoded .= substr($body, 0, (int)$len);
61                $body = substr($body, (int)$len + 2); // skip chunk and CRLF
62            }
63            $body = $decoded;
64        }
65
66        return new IcapResponse($statusCode, $headers, $body);
67    }
68}