dokuwiki-matrixnotifierwas-plugin – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | <?php |
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace GuzzleHttp\Psr7; |
||
6 | |||
7 | use Psr\Http\Message\MessageInterface; |
||
8 | use Psr\Http\Message\StreamInterface; |
||
9 | |||
10 | /** |
||
11 | * Trait implementing functionality common to requests and responses. |
||
12 | */ |
||
13 | trait MessageTrait |
||
14 | { |
||
15 | /** @var string[][] Map of all registered headers, as original name => array of values */ |
||
16 | private $headers = []; |
||
17 | |||
18 | /** @var string[] Map of lowercase header name => original name at registration */ |
||
19 | private $headerNames = []; |
||
20 | |||
21 | /** @var string */ |
||
22 | private $protocol = '1.1'; |
||
23 | |||
24 | /** @var StreamInterface|null */ |
||
25 | private $stream; |
||
26 | |||
27 | public function getProtocolVersion(): string |
||
28 | { |
||
29 | return $this->protocol; |
||
30 | } |
||
31 | |||
32 | public function withProtocolVersion($version): MessageInterface |
||
33 | { |
||
34 | if ($this->protocol === $version) { |
||
35 | return $this; |
||
36 | } |
||
37 | |||
38 | $new = clone $this; |
||
39 | $new->protocol = $version; |
||
40 | |||
41 | return $new; |
||
42 | } |
||
43 | |||
44 | public function getHeaders(): array |
||
45 | { |
||
46 | return $this->headers; |
||
47 | } |
||
48 | |||
49 | public function hasHeader($header): bool |
||
50 | { |
||
51 | return isset($this->headerNames[strtolower($header)]); |
||
52 | } |
||
53 | |||
54 | public function getHeader($header): array |
||
55 | { |
||
56 | $header = strtolower($header); |
||
57 | |||
58 | if (!isset($this->headerNames[$header])) { |
||
59 | return []; |
||
60 | } |
||
61 | |||
62 | $header = $this->headerNames[$header]; |
||
63 | |||
64 | return $this->headers[$header]; |
||
65 | } |
||
66 | |||
67 | public function getHeaderLine($header): string |
||
68 | { |
||
69 | return implode(', ', $this->getHeader($header)); |
||
70 | } |
||
71 | |||
72 | public function withHeader($header, $value): MessageInterface |
||
73 | { |
||
74 | $this->assertHeader($header); |
||
75 | $value = $this->normalizeHeaderValue($value); |
||
76 | $normalized = strtolower($header); |
||
77 | |||
78 | $new = clone $this; |
||
79 | if (isset($new->headerNames[$normalized])) { |
||
80 | unset($new->headers[$new->headerNames[$normalized]]); |
||
81 | } |
||
82 | $new->headerNames[$normalized] = $header; |
||
83 | $new->headers[$header] = $value; |
||
84 | |||
85 | return $new; |
||
86 | } |
||
87 | |||
88 | public function withAddedHeader($header, $value): MessageInterface |
||
89 | { |
||
90 | $this->assertHeader($header); |
||
91 | $value = $this->normalizeHeaderValue($value); |
||
92 | $normalized = strtolower($header); |
||
93 | |||
94 | $new = clone $this; |
||
95 | if (isset($new->headerNames[$normalized])) { |
||
96 | $header = $this->headerNames[$normalized]; |
||
97 | $new->headers[$header] = array_merge($this->headers[$header], $value); |
||
98 | } else { |
||
99 | $new->headerNames[$normalized] = $header; |
||
100 | $new->headers[$header] = $value; |
||
101 | } |
||
102 | |||
103 | return $new; |
||
104 | } |
||
105 | |||
106 | public function withoutHeader($header): MessageInterface |
||
107 | { |
||
108 | $normalized = strtolower($header); |
||
109 | |||
110 | if (!isset($this->headerNames[$normalized])) { |
||
111 | return $this; |
||
112 | } |
||
113 | |||
114 | $header = $this->headerNames[$normalized]; |
||
115 | |||
116 | $new = clone $this; |
||
117 | unset($new->headers[$header], $new->headerNames[$normalized]); |
||
118 | |||
119 | return $new; |
||
120 | } |
||
121 | |||
122 | public function getBody(): StreamInterface |
||
123 | { |
||
124 | if (!$this->stream) { |
||
125 | $this->stream = Utils::streamFor(''); |
||
126 | } |
||
127 | |||
128 | return $this->stream; |
||
129 | } |
||
130 | |||
131 | public function withBody(StreamInterface $body): MessageInterface |
||
132 | { |
||
133 | if ($body === $this->stream) { |
||
134 | return $this; |
||
135 | } |
||
136 | |||
137 | $new = clone $this; |
||
138 | $new->stream = $body; |
||
139 | |||
140 | return $new; |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * @param (string|string[])[] $headers |
||
145 | */ |
||
146 | private function setHeaders(array $headers): void |
||
147 | { |
||
148 | $this->headerNames = $this->headers = []; |
||
149 | foreach ($headers as $header => $value) { |
||
150 | // Numeric array keys are converted to int by PHP. |
||
151 | $header = (string) $header; |
||
152 | |||
153 | $this->assertHeader($header); |
||
154 | $value = $this->normalizeHeaderValue($value); |
||
155 | $normalized = strtolower($header); |
||
156 | if (isset($this->headerNames[$normalized])) { |
||
157 | $header = $this->headerNames[$normalized]; |
||
158 | $this->headers[$header] = array_merge($this->headers[$header], $value); |
||
159 | } else { |
||
160 | $this->headerNames[$normalized] = $header; |
||
161 | $this->headers[$header] = $value; |
||
162 | } |
||
163 | } |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * @param mixed $value |
||
168 | * |
||
169 | * @return string[] |
||
170 | */ |
||
171 | private function normalizeHeaderValue($value): array |
||
172 | { |
||
173 | if (!is_array($value)) { |
||
174 | return $this->trimAndValidateHeaderValues([$value]); |
||
175 | } |
||
176 | |||
177 | if (count($value) === 0) { |
||
178 | throw new \InvalidArgumentException('Header value can not be an empty array.'); |
||
179 | } |
||
180 | |||
181 | return $this->trimAndValidateHeaderValues($value); |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * Trims whitespace from the header values. |
||
186 | * |
||
187 | * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field. |
||
188 | * |
||
189 | * header-field = field-name ":" OWS field-value OWS |
||
190 | * OWS = *( SP / HTAB ) |
||
191 | * |
||
192 | * @param mixed[] $values Header values |
||
193 | * |
||
194 | * @return string[] Trimmed header values |
||
195 | * |
||
196 | * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4 |
||
197 | */ |
||
198 | private function trimAndValidateHeaderValues(array $values): array |
||
199 | { |
||
200 | return array_map(function ($value) { |
||
201 | if (!is_scalar($value) && null !== $value) { |
||
202 | throw new \InvalidArgumentException(sprintf( |
||
203 | 'Header value must be scalar or null but %s provided.', |
||
204 | is_object($value) ? get_class($value) : gettype($value) |
||
205 | )); |
||
206 | } |
||
207 | |||
208 | $trimmed = trim((string) $value, " \t"); |
||
209 | $this->assertValue($trimmed); |
||
210 | |||
211 | return $trimmed; |
||
212 | }, array_values($values)); |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 |
||
217 | * |
||
218 | * @param mixed $header |
||
219 | */ |
||
220 | private function assertHeader($header): void |
||
221 | { |
||
222 | if (!is_string($header)) { |
||
223 | throw new \InvalidArgumentException(sprintf( |
||
224 | 'Header name must be a string but %s provided.', |
||
225 | is_object($header) ? get_class($header) : gettype($header) |
||
226 | )); |
||
227 | } |
||
228 | |||
229 | if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) { |
||
230 | throw new \InvalidArgumentException( |
||
231 | sprintf('"%s" is not valid header name.', $header) |
||
232 | ); |
||
233 | } |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 |
||
238 | * |
||
239 | * field-value = *( field-content / obs-fold ) |
||
240 | * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] |
||
241 | * field-vchar = VCHAR / obs-text |
||
242 | * VCHAR = %x21-7E |
||
243 | * obs-text = %x80-FF |
||
244 | * obs-fold = CRLF 1*( SP / HTAB ) |
||
245 | */ |
||
246 | private function assertValue(string $value): void |
||
247 | { |
||
248 | // The regular expression intentionally does not support the obs-fold production, because as |
||
249 | // per RFC 7230#3.2.4: |
||
250 | // |
||
251 | // A sender MUST NOT generate a message that includes |
||
252 | // line folding (i.e., that has any field-value that contains a match to |
||
253 | // the obs-fold rule) unless the message is intended for packaging |
||
254 | // within the message/http media type. |
||
255 | // |
||
256 | // Clients must not send a request with line folding and a server sending folded headers is |
||
257 | // likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting |
||
258 | // folding is not likely to break any legitimate use case. |
||
259 | if (!preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D', $value)) { |
||
260 | throw new \InvalidArgumentException( |
||
261 | sprintf('"%s" is not valid header value.', $value) |
||
262 | ); |
||
263 | } |
||
264 | } |
||
265 | } |