PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 27.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v27.5
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / vendor_prefixed / guzzlehttp / psr7 / src / MessageTrait.php

MessageTrait.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 27.5, at vendor_prefixed/guzzlehttp/psr7/src/MessageTrait.php

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