Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
70 / 70 |
|
100.00% |
18 / 18 |
CRAP | |
100.00% |
1 / 1 |
MessageTrait | |
100.00% |
70 / 70 |
|
100.00% |
18 / 18 |
30 | |
100.00% |
1 / 1 |
getProtocolVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
withProtocolVersion | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getHeaders | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasHeader | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getHeader | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getHeaderLine | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
withHeader | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
withAddedHeader | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
withoutHeader | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getBody | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
withBody | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
setHeaders | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
trimHeaderValues | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
setProtocolVersion | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setHeader | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
addHeader | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
3 | |||
deleteHeader | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
setBody | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace Tal; |
4 | |
5 | use GuzzleHttp\Psr7\Utils; |
6 | use Psr\Http\Message\MessageInterface; |
7 | use function GuzzleHttp\Psr7\stream_for; |
8 | use Psr\Http\Message\StreamInterface; |
9 | |
10 | /** |
11 | * Trait implementing functionality common to requests and responses. |
12 | */ |
13 | trait MessageTrait |
14 | { |
15 | /** @var array Map of all registered headers, as original name => array of values */ |
16 | protected $headers = []; |
17 | |
18 | /** @var array Map of lowercase header name => original name at registration */ |
19 | protected $headerNames = []; |
20 | |
21 | /** @var string */ |
22 | protected $protocol = '1.1'; |
23 | |
24 | /** @var StreamInterface */ |
25 | protected $stream; |
26 | |
27 | public function getProtocolVersion(): string |
28 | { |
29 | return $this->protocol; |
30 | } |
31 | |
32 | public function withProtocolVersion(string $version): MessageInterface |
33 | { |
34 | if ($this->protocol === $version) { |
35 | return $this; |
36 | } |
37 | |
38 | $new = clone $this; |
39 | return $new->setProtocolVersion($version); |
40 | } |
41 | |
42 | public function getHeaders(): array |
43 | { |
44 | return $this->headers; |
45 | } |
46 | |
47 | public function hasHeader(string $header): bool |
48 | { |
49 | return isset($this->headerNames[strtolower($header)]); |
50 | } |
51 | |
52 | public function getHeader(string $header): array |
53 | { |
54 | $header = strtolower($header); |
55 | |
56 | if (!isset($this->headerNames[$header])) { |
57 | return []; |
58 | } |
59 | |
60 | $header = $this->headerNames[$header]; |
61 | |
62 | return $this->headers[$header]; |
63 | } |
64 | |
65 | public function getHeaderLine(string $header): string |
66 | { |
67 | return implode(', ', $this->getHeader($header)); |
68 | } |
69 | |
70 | public function withHeader(string $header, $value): MessageInterface |
71 | { |
72 | $new = clone $this; |
73 | return $new->setHeader($header, $value); |
74 | } |
75 | |
76 | public function withAddedHeader(string $header, $value): MessageInterface |
77 | { |
78 | $new = clone $this; |
79 | return $new->addHeader($header, $value); |
80 | } |
81 | |
82 | public function withoutHeader(string $header): MessageInterface |
83 | { |
84 | $normalized = strtolower($header); |
85 | |
86 | if (!isset($this->headerNames[$normalized])) { |
87 | return $this; |
88 | } |
89 | |
90 | $new = clone $this; |
91 | return $new->deleteHeader($header); |
92 | } |
93 | |
94 | public function getBody(): StreamInterface |
95 | { |
96 | if (!$this->stream) { |
97 | $this->stream = Utils::streamFor(''); |
98 | } |
99 | |
100 | return $this->stream; |
101 | } |
102 | |
103 | public function withBody(StreamInterface $body): MessageInterface |
104 | { |
105 | if ($body === $this->stream) { |
106 | return $this; |
107 | } |
108 | |
109 | $new = clone $this; |
110 | return $new->setBody($body); |
111 | } |
112 | |
113 | protected function setHeaders(array $headers) |
114 | { |
115 | $this->headerNames = $this->headers = []; |
116 | foreach ($headers as $header => $value) { |
117 | if (!is_array($value)) { |
118 | $value = [$value]; |
119 | } |
120 | |
121 | $value = $this->trimHeaderValues($value); |
122 | $normalized = strtolower($header); |
123 | if (isset($this->headerNames[$normalized])) { |
124 | $header = $this->headerNames[$normalized]; |
125 | $this->headers[$header] = array_merge($this->headers[$header], $value); |
126 | } else { |
127 | $this->headerNames[$normalized] = $header; |
128 | $this->headers[$header] = $value; |
129 | } |
130 | } |
131 | } |
132 | |
133 | /** |
134 | * Trims whitespace from the header values. |
135 | * |
136 | * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field. |
137 | * |
138 | * header-field = field-name ":" OWS field-value OWS |
139 | * OWS = *( SP / HTAB ) |
140 | * |
141 | * @param string[] $values Header values |
142 | * |
143 | * @return string[] Trimmed header values |
144 | * |
145 | * @see https://tools.ietf.org/html/rfc7230#section-3.2.4 |
146 | */ |
147 | protected function trimHeaderValues(array $values) |
148 | { |
149 | return array_map(function ($value) { |
150 | return trim($value, " \t"); |
151 | }, $values); |
152 | } |
153 | |
154 | /** |
155 | * Sets the specified HTTP protocol version. |
156 | * |
157 | * The version string MUST contain only the HTTP version number (e.g., |
158 | * "1.1", "1.0"). |
159 | * |
160 | * @param string $version HTTP protocol version |
161 | * @return static |
162 | */ |
163 | protected function setProtocolVersion($version) |
164 | { |
165 | $this->protocol = $version; |
166 | return $this; |
167 | } |
168 | |
169 | /** |
170 | * Sets the provided value replacing the specified header. |
171 | * |
172 | * While header names are case-insensitive, the casing of the header will |
173 | * be preserved by this function, and returned from getHeaders(). |
174 | * |
175 | * @param string $header Case-insensitive header field name. |
176 | * @param string|string[] $value Header value(s). |
177 | * @return static |
178 | * @throws \InvalidArgumentException for invalid header names or values. |
179 | */ |
180 | protected function setHeader($header, $value) |
181 | { |
182 | if (!is_array($value)) { |
183 | $value = [$value]; |
184 | } |
185 | |
186 | $value = $this->trimHeaderValues($value); |
187 | $normalized = strtolower($header); |
188 | |
189 | if (isset($this->headerNames[$normalized])) { |
190 | unset($this->headers[$this->headerNames[$normalized]]); |
191 | } |
192 | $this->headerNames[$normalized] = $header; |
193 | $this->headers[$header] = $value; |
194 | |
195 | return $this; |
196 | } |
197 | |
198 | /** |
199 | * Adds the specified header appended with the given value. |
200 | * |
201 | * Existing values for the specified header will be maintained. The new |
202 | * value(s) will be appended to the existing list. If the header did not |
203 | * exist previously, it will be added. |
204 | * |
205 | * @param string $header Case-insensitive header field name to add. |
206 | * @param string|string[] $value Header value(s). |
207 | * @return static |
208 | * @throws \InvalidArgumentException for invalid header names or values. |
209 | */ |
210 | protected function addHeader($header, $value) |
211 | { |
212 | if (!is_array($value)) { |
213 | $value = [$value]; |
214 | } |
215 | |
216 | $value = $this->trimHeaderValues($value); |
217 | $normalized = strtolower($header); |
218 | |
219 | if (isset($this->headerNames[$normalized])) { |
220 | $header = $this->headerNames[$normalized]; |
221 | $this->headers[$header] = array_merge($this->headers[$header], $value); |
222 | } else { |
223 | $this->headerNames[$normalized] = $header; |
224 | $this->headers[$header] = $value; |
225 | } |
226 | |
227 | return $this; |
228 | } |
229 | |
230 | /** |
231 | * Deletes the specified header. |
232 | * |
233 | * Header resolution MUST be done without case-sensitivity. |
234 | * |
235 | * @param string $header Case-insensitive header field name to remove. |
236 | * @return static |
237 | */ |
238 | protected function deleteHeader($header) |
239 | { |
240 | $normalized = strtolower($header); |
241 | |
242 | $header = $this->headerNames[$normalized]; |
243 | |
244 | unset($this->headers[$header], $this->headerNames[$normalized]); |
245 | |
246 | return $this; |
247 | } |
248 | |
249 | /** |
250 | * Sets the specified message body. |
251 | * |
252 | * The body MUST be a StreamInterface object. |
253 | * |
254 | * @param StreamInterface $body Body. |
255 | * @return static |
256 | * @throws \InvalidArgumentException When the body is not valid. |
257 | */ |
258 | protected function setBody(StreamInterface $body) |
259 | { |
260 | $this->stream = $body; |
261 | return $this; |
262 | } |
263 | } |