Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
11 / 11
CRAP
100.00% covered (success)
100.00%
1 / 1
MessageTrait
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
11 / 11
14
100.00% covered (success)
100.00%
1 / 1
 getProtocolVersion
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withProtocolVersion
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getHeaders
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 hasHeader
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHeader
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHeaderLine
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withHeader
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 withAddedHeader
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 withoutHeader
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getBody
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 withBody
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Engelsystem\Http;
6
7use InvalidArgumentException;
8use Nyholm\Psr7\Stream;
9use Psr\Http\Message\StreamInterface;
10
11/**
12 * @implements MessageInterface
13 * @extends SymfonyResponse
14 */
15trait MessageTrait
16{
17    /**
18     * Retrieves the HTTP protocol version as a string.
19     *
20     * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
21     *
22     * @return string HTTP protocol version.
23     */
24    public function getProtocolVersion(): string
25    {
26        return parent::getProtocolVersion();
27    }
28
29    /**
30     * Return an instance with the specified HTTP protocol version.
31     *
32     * The version string MUST contain only the HTTP version number (e.g.,
33     * "1.1", "1.0").
34     *
35     * This method MUST be implemented in such a way as to retain the
36     * immutability of the message, and MUST return an instance that has the
37     * new protocol version.
38     *
39     * @param string $version HTTP protocol version
40     * @return static
41     */
42    public function withProtocolVersion(mixed $version): static
43    {
44        $new = clone $this;
45        if (method_exists($new, 'setProtocolVersion')) {
46            $new->setProtocolVersion($version);
47        } else {
48            $new->server->set('SERVER_PROTOCOL', $version);
49        }
50
51        return $new;
52    }
53
54    /**
55     * Retrieves all message header values.
56     *
57     * The keys represent the header name as it will be sent over the wire, and
58     * each value is an array of strings associated with the header.
59     *
60     *     // Represent the headers as a string
61     *     foreach ($message->getHeaders() as $name => $values) {
62     *         echo $name . ": " . implode(", ", $values);
63     *     }
64     *
65     *     // Emit headers iteratively:
66     *     foreach ($message->getHeaders() as $name => $values) {
67     *         foreach ($values as $value) {
68     *             header(sprintf('%s: %s', $name, $value), false);
69     *         }
70     *     }
71     *
72     * While header names are not case-sensitive, getHeaders() will preserve the
73     * exact case in which headers were originally specified.
74     *
75     * @return string[][] Returns an associative array of the message's headers. Each
76     *     key MUST be a header name, and each value MUST be an array of strings
77     *     for that header.
78     */
79    public function getHeaders(): array
80    {
81        if (method_exists($this->headers, 'allPreserveCase')) {
82            return $this->headers->allPreserveCase();
83        }
84
85        return $this->headers->all();
86    }
87
88    /**
89     * Checks if a header exists by the given case-insensitive name.
90     *
91     * @param string $name Case-insensitive header field name.
92     * @return bool Returns true if any header names match the given header
93     *                     name using a case-insensitive string comparison. Returns false if
94     *                     no matching header name is found in the message.
95     */
96    public function hasHeader(mixed $name): bool
97    {
98        return $this->headers->has($name);
99    }
100
101    /**
102     * Retrieves a message header value by the given case-insensitive name.
103     *
104     * This method returns an array of all the header values of the given
105     * case-insensitive header name.
106     *
107     * If the header does not appear in the message, this method MUST return an
108     * empty array.
109     *
110     * @param string $name Case-insensitive header field name.
111     * @return string[] An array of string values as provided for the given
112     *                     header. If the header does not appear in the message, this method MUST
113     *                     return an empty array.
114     */
115    public function getHeader(mixed $name): array
116    {
117        return $this->headers->all($name);
118    }
119
120    /**
121     * Retrieves a comma-separated string of the values for a single header.
122     *
123     * This method returns all of the header values of the given
124     * case-insensitive header name as a string concatenated together using
125     * a comma.
126     *
127     * NOTE: Not all header values may be appropriately represented using
128     * comma concatenation. For such headers, use getHeader() instead
129     * and supply your own delimiter when concatenating.
130     *
131     * If the header does not appear in the message, this method MUST return
132     * an empty string.
133     *
134     * @param string $name Case-insensitive header field name.
135     * @return string A string of values as provided for the given header
136     *                     concatenated together using a comma. If the header does not appear in
137     *                     the message, this method MUST return an empty string.
138     */
139    public function getHeaderLine(mixed $name): string
140    {
141        return implode(',', $this->getHeader($name));
142    }
143
144    /**
145     * Return an instance with the provided value replacing the specified header.
146     *
147     * While header names are case-insensitive, the casing of the header will
148     * be preserved by this function, and returned from getHeaders().
149     *
150     * This method MUST be implemented in such a way as to retain the
151     * immutability of the message, and MUST return an instance that has the
152     * new and/or updated header and value.
153     *
154     * @param string          $name  Case-insensitive header field name.
155     * @param string|string[] $value Header value(s).
156     * @return static
157     * @throws InvalidArgumentException for invalid header names or values.
158     */
159    public function withHeader(mixed $name, mixed $value): static
160    {
161        $new = clone $this;
162        $new->headers->set($name, $value);
163
164        return $new;
165    }
166
167    /**
168     * Return an instance with the specified header appended with the given value.
169     *
170     * Existing values for the specified header will be maintained. The new
171     * value(s) will be appended to the existing list. If the header did not
172     * exist previously, it will be added.
173     *
174     * This method MUST be implemented in such a way as to retain the
175     * immutability of the message, and MUST return an instance that has the
176     * new header and/or value.
177     *
178     * @param string          $name  Case-insensitive header field name to add.
179     * @param string|string[] $value Header value(s).
180     * @return static
181     * @throws InvalidArgumentException for invalid header names or values.
182     */
183    public function withAddedHeader(mixed $name, mixed $value): static
184    {
185        $new = clone $this;
186        $new->headers->set($name, $value, false);
187
188        return $new;
189    }
190
191    /**
192     * Return an instance without the specified header.
193     *
194     * Header resolution MUST be done without case-sensitivity.
195     *
196     * This method MUST be implemented in such a way as to retain the
197     * immutability of the message, and MUST return an instance that removes
198     * the named header.
199     *
200     * @param string $name Case-insensitive header field name to remove.
201     * @return static
202     */
203    public function withoutHeader(mixed $name): static
204    {
205        $new = clone $this;
206        $new->headers->remove($name);
207
208        return $new;
209    }
210
211    /**
212     * Gets the body of the message.
213     *
214     * @return StreamInterface Returns the body as a stream.
215     */
216    public function getBody(): StreamInterface
217    {
218        $stream = Stream::create((string) $this->getContent());
219        $stream->rewind();
220
221        return $stream;
222    }
223
224    /**
225     * Return an instance with the specified message body.
226     *
227     * The body MUST be a StreamInterface object.
228     *
229     * This method MUST be implemented in such a way as to retain the
230     * immutability of the message, and MUST return a new instance that has the
231     * new body stream.
232     *
233     * @param StreamInterface $body Body.
234     * @return static
235     * @throws InvalidArgumentException When the body is not valid.
236     */
237    public function withBody(StreamInterface $body): static
238    {
239        $new = clone $this;
240
241        if (method_exists($new, 'setContent')) {
242            $new->setContent((string) $body);
243        } else {
244            $new->content = $body;
245        }
246
247        return $new;
248    }
249}