Issues (8)

lib/Message/ResponseBuilder.php (1 issue)

Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Buzz\Message;
6
7
use Buzz\Exception\InvalidArgumentException;
8
use Http\Message\ResponseFactory as HTTPlugResponseFactory;
9
use Psr\Http\Message\ResponseFactoryInterface as PsrResponseFactory;
10
use Psr\Http\Message\ResponseInterface;
11
12
/**
13
 * @author Tobias Nyholm <[email protected]>
14
 */
15
final class ResponseBuilder
16
{
17
    /**
18
     * @var ResponseInterface
19
     */
20
    private $response;
21
    private $responseFactory;
22
23
    /**
24
     * @param HTTPlugResponseFactory|PsrResponseFactory $responseFactory
25
     */
26 241
    public function __construct($responseFactory)
27
    {
28 241
        if (!$responseFactory instanceof HTTPlugResponseFactory && !$responseFactory instanceof PsrResponseFactory) {
0 ignored issues
show
$responseFactory is always a sub-type of Psr\Http\Message\ResponseFactoryInterface.
Loading history...
29
            throw new InvalidArgumentException('First parameter to ResponseBuilder must be a response factory');
30
        }
31
32 241
        $this->responseFactory = $responseFactory;
33 241
        $this->response = $responseFactory->createResponse();
34 241
    }
35
36
    public function getResponseFromRawInput(string $raw, int $headerSize): ResponseInterface
37
    {
38
        $headers = substr($raw, 0, $headerSize);
39
        $this->parseHttpHeaders(explode("\n", $headers));
40
        $this->writeBody(substr($raw, $headerSize));
41
42
        return $this->getResponse();
43
    }
44
45 112
    private function filterHeaders(array $headers): array
46
    {
47 112
        $filtered = [];
48 112
        foreach ($headers as $header) {
49 112
            if (0 === stripos($header, 'http/')) {
50 112
                $filtered = [];
51 112
                $filtered[] = trim($header);
52 112
                continue;
53
            }
54
55
            // Make sure they are not empty
56 112
            $trimmed = trim($header);
57 112
            if (false === strpos($trimmed, ':')) {
58
                continue;
59
            }
60
61 112
            $filtered[] = $trimmed;
62
        }
63
64 112
        return $filtered;
65
    }
66
67 229
    public function setStatus(string $input): void
68
    {
69 229
        $parts = explode(' ', $input, 3);
70 229
        if (\count($parts) < 2 || 0 !== strpos(strtolower($parts[0]), 'http/')) {
71
            throw new InvalidArgumentException(sprintf('"%s" is not a valid HTTP status line', $input));
72
        }
73
74 229
        $this->response = $this->response->withStatus((int) $parts[1], isset($parts[2]) ? $parts[2] : '');
75 229
        $this->response = $this->response->withProtocolVersion((string) substr($parts[0], 5));
76 229
    }
77
78
    /**
79
     * Add a single HTTP header line.
80
     */
81 229
    public function addHeader(string $input): void
82
    {
83 229
        list($key, $value) = explode(':', $input, 2);
84 229
        $this->response = $this->response->withAddedHeader(trim($key), trim($value));
85 229
    }
86
87
    /**
88
     * Add HTTP headers. The input array is all the header lines from the HTTP message. Optionally including the
89
     * status line.
90
     */
91 112
    public function parseHttpHeaders(array $headers): void
92
    {
93 112
        $headers = $this->filterHeaders($headers);
94 112
        $statusLine = array_shift($headers);
95
96
        try {
97 112
            $this->setStatus($statusLine);
98
        } catch (InvalidArgumentException $e) {
99
            array_unshift($headers, $statusLine);
100
        }
101
102 112
        foreach ($headers as $header) {
103 112
            $this->addHeader($header);
104
        }
105 112
    }
106
107
    /**
108
     * Add some content to the body. This function writes the $input to a stream.
109
     *
110
     * @return int returns the number of bytes written
111
     */
112 225
    public function writeBody(string $input): int
113
    {
114 225
        return $this->response->getBody()->write($input);
115
    }
116
117 229
    public function getResponse(): ResponseInterface
118
    {
119 229
        $this->response->getBody()->rewind();
120 229
        $response = $this->response;
121 229
        $this->response = $this->responseFactory->createResponse();
122
123 229
        return $response;
124
    }
125
}
126