PluginProbe
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI / 6.0.3
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI v6.0.3
6.0.3 6.0.2 6.0.1 6.0.0 5.8.6 5.8.5 5.8.4 5.8.3 5.8.2 5.8.1 5.8.0 5.7.9.2 5.7.9.1 5.7.8 5.7.9 5.7.6 5.7.7 5.7.5 5.7.4 5.7.3 5.7.2 5.7.1 trunk 5.6.0 5.6.1 All 34 releases
automatorwp / integrations / mailchimp / vendor / guzzlehttp / psr7 / src / MessageTrait.php

MessageTrait.php in AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI 6.0.3, at integrations/mailchimp/vendor/guzzlehttp/psr7/src/MessageTrait.php

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