PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.1
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.1
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / vendor / guzzlehttp / psr7 / src / MessageTrait.php

MessageTrait.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.0.1, at vendor/guzzlehttp/psr7/src/MessageTrait.php

270 lines 7.7 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 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(
203 sprintf(
204 'Header value must be scalar or null but %s provided.',
205 is_object($value) ? get_class($value) : gettype($value)
206 )
207 );
208 }
209
210 $trimmed = trim((string)$value, " \t");
211 $this->assertValue($trimmed);
212
213 return $trimmed;
214 }, array_values($values));
215 }
216
217 /**
218 * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
219 *
220 * @param mixed $header
221 */
222 private function assertHeader($header): void
223 {
224 if (!is_string($header)) {
225 throw new \InvalidArgumentException(
226 sprintf(
227 'Header name must be a string but %s provided.',
228 is_object($header) ? get_class($header) : gettype($header)
229 )
230 );
231 }
232
233 if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) {
234 throw new \InvalidArgumentException(
235 sprintf('"%s" is not valid header name.', $header)
236 );
237 }
238 }
239
240 /**
241 * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
242 *
243 * field-value = *( field-content / obs-fold )
244 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
245 * field-vchar = VCHAR / obs-text
246 * VCHAR = %x21-7E
247 * obs-text = %x80-FF
248 * obs-fold = CRLF 1*( SP / HTAB )
249 */
250 private function assertValue(string $value): void
251 {
252 // The regular expression intentionally does not support the obs-fold production, because as
253 // per RFC 7230#3.2.4:
254 //
255 // A sender MUST NOT generate a message that includes
256 // line folding (i.e., that has any field-value that contains a match to
257 // the obs-fold rule) unless the message is intended for packaging
258 // within the message/http media type.
259 //
260 // Clients must not send a request with line folding and a server sending folded headers is
261 // likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
262 // folding is not likely to break any legitimate use case.
263 if (!preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D', $value)) {
264 throw new \InvalidArgumentException(
265 sprintf('"%s" is not valid header value.', $value)
266 );
267 }
268 }
269 }
270