xref: /plugin/dw2pdf/vendor/mpdf/psr-http-message-shim/src/Request.php (revision 92200750cd9cfc4a9489fe7c04c6e41b5a3de6ee)
1<?php
2
3namespace Mpdf\PsrHttpMessageShim;
4
5use Psr\Http\Message\MessageInterface;
6use Psr\Http\Message\RequestInterface;
7use Psr\Http\Message\StreamInterface;
8use Psr\Http\Message\UriInterface;
9
10/**
11 * PSR-7 URI implementation ported from nyholm/psr7 and adapted for PHP 5.6
12 *
13 * @link https://github.com/Nyholm/psr7/blob/master/src/Uri.php
14 */
15class Request implements \Psr\Http\Message\RequestInterface
16{
17
18	/** @var string */
19	private $method;
20
21	/** @var null|string */
22	private $requestTarget;
23
24	/** @var null|UriInterface */
25	private $uri;
26
27	/** @var array Map of all registered headers, as original name => array of values */
28	private $headers = [];
29
30	/** @var array Map of lowercase header name => original name at registration */
31	private $headerNames = [];
32
33	/** @var string */
34	private $protocol;
35
36	/** @var StreamInterface */
37	private $stream;
38
39	/**
40	 * @param string                               $method  HTTP method
41	 * @param string|UriInterface                  $uri     URI
42	 * @param array                                $headers Request headers
43	 * @param string|null|resource|StreamInterface $body    Request body
44	 * @param string                               $version Protocol version
45	 */
46	public function __construct(
47		$method,
48		$uri,
49		array $headers = [],
50		$body = null,
51		$version = '1.1'
52	) {
53		if (!($uri instanceof UriInterface)) {
54			$uri = new Uri($uri);
55		}
56
57		$this->method = $method;
58		$this->uri = $uri;
59		$this->setHeaders($headers);
60		$this->protocol = $version;
61
62		if (!$this->hasHeader('Host')) {
63			$this->updateHostFromUri();
64		}
65
66		if ($body !== '' && $body !== null) {
67			$this->stream = Stream::create($body);
68		}
69	}
70
71	public function getRequestTarget(): string
72	{
73		if ($this->requestTarget !== null) {
74			return $this->requestTarget;
75		}
76
77		$target = $this->uri->getPath();
78		if ($target == '') {
79			$target = '/';
80		}
81		if ($this->uri->getQuery() != '') {
82			$target .= '?'.$this->uri->getQuery();
83		}
84
85		return $target;
86	}
87
88	public function withRequestTarget(string $requestTarget): RequestInterface
89	{
90		if (preg_match('#\s#', $requestTarget)) {
91			throw new \InvalidArgumentException('Invalid request target provided; cannot contain whitespace');
92		}
93
94		$new = clone $this;
95		$new->requestTarget = $requestTarget;
96
97		return $new;
98	}
99
100	public function getMethod(): string
101	{
102		return $this->method;
103	}
104
105	public function withMethod(string $method): RequestInterface
106	{
107		$new = clone $this;
108		$new->method = $method;
109
110		return $new;
111	}
112
113	public function getUri(): UriInterface
114	{
115		return $this->uri;
116	}
117
118	public function withUri(UriInterface $uri, bool $preserveHost = false): RequestInterface
119	{
120		if ($uri === $this->uri) {
121			return $this;
122		}
123
124		$new = clone $this;
125		$new->uri = $uri;
126
127		if (!$preserveHost || !$this->hasHeader('Host')) {
128			$new->updateHostFromUri();
129		}
130
131		return $new;
132	}
133
134	private function updateHostFromUri()
135	{
136		$host = $this->uri->getHost();
137
138		if ($host == '') {
139			return;
140		}
141
142		if (($port = $this->uri->getPort()) !== null) {
143			$host .= ':'.$port;
144		}
145
146		if (isset($this->headerNames['host'])) {
147			$header = $this->headerNames['host'];
148		} else {
149			$header = 'Host';
150			$this->headerNames['host'] = 'Host';
151		}
152		// Ensure Host is the first header.
153		// See: http://tools.ietf.org/html/rfc7230#section-5.4
154		$this->headers = [$header => [$host]] + $this->headers;
155	}
156
157	public function getProtocolVersion(): string
158	{
159		return $this->protocol;
160	}
161
162	public function withProtocolVersion(string $version): MessageInterface
163	{
164		if ($this->protocol === $version) {
165			return $this;
166		}
167
168		$new = clone $this;
169		$new->protocol = $version;
170
171		return $new;
172	}
173
174	public function getHeaders(): array
175	{
176		return $this->headers;
177	}
178
179	public function hasHeader(string $header): bool
180	{
181		return isset($this->headerNames[strtolower($header)]);
182	}
183
184	public function getHeader(string $header): array
185	{
186		$header = strtolower($header);
187
188		if (!isset($this->headerNames[$header])) {
189			return [];
190		}
191
192		$header = $this->headerNames[$header];
193
194		return $this->headers[$header];
195	}
196
197	public function getHeaderLine(string $header):string
198	{
199		return implode(', ', $this->getHeader($header));
200	}
201
202	public function withHeader(string $header, $value): MessageInterface
203	{
204		if (!is_array($value)) {
205			$value = [$value];
206		}
207
208		$value = $this->trimHeaderValues($value);
209		$normalized = strtolower($header);
210
211		$new = clone $this;
212		if (isset($new->headerNames[$normalized])) {
213			unset($new->headers[$new->headerNames[$normalized]]);
214		}
215		$new->headerNames[$normalized] = $header;
216		$new->headers[$header] = $value;
217
218		return $new;
219	}
220
221	public function withAddedHeader(string $header, $value): MessageInterface
222	{
223		if (!is_array($value)) {
224			$value = [$value];
225		}
226
227		$value = $this->trimHeaderValues($value);
228		$normalized = strtolower($header);
229
230		$new = clone $this;
231		if (isset($new->headerNames[$normalized])) {
232			$header = $this->headerNames[$normalized];
233			$new->headers[$header] = array_merge($this->headers[$header], $value);
234		} else {
235			$new->headerNames[$normalized] = $header;
236			$new->headers[$header] = $value;
237		}
238
239		return $new;
240	}
241
242	public function withoutHeader(string $header): MessageInterface
243	{
244		$normalized = strtolower($header);
245
246		if (!isset($this->headerNames[$normalized])) {
247			return $this;
248		}
249
250		$header = $this->headerNames[$normalized];
251
252		$new = clone $this;
253		unset($new->headers[$header], $new->headerNames[$normalized]);
254
255		return $new;
256	}
257
258	public function getBody(): StreamInterface
259	{
260		if (!$this->stream) {
261			$this->stream = Stream::create('');
262			$this->stream->rewind();
263		}
264
265		return $this->stream;
266	}
267
268	public function withBody(StreamInterface $body): MessageInterface
269	{
270		if ($body === $this->stream) {
271			return $this;
272		}
273
274		$new = clone $this;
275		$new->stream = $body;
276
277		return $new;
278	}
279
280	private function setHeaders(array $headers)
281	{
282		$this->headerNames = $this->headers = [];
283		foreach ($headers as $header => $value) {
284			if (!is_array($value)) {
285				$value = [$value];
286			}
287
288			$value = $this->trimHeaderValues($value);
289			$normalized = strtolower($header);
290			if (isset($this->headerNames[$normalized])) {
291				$header = $this->headerNames[$normalized];
292				$this->headers[$header] = array_merge($this->headers[$header], $value);
293			} else {
294				$this->headerNames[$normalized] = $header;
295				$this->headers[$header] = $value;
296			}
297		}
298	}
299
300	/**
301	 * Trims whitespace from the header values.
302	 *
303	 * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
304	 *
305	 * header-field = field-name ":" OWS field-value OWS
306	 * OWS          = *( SP / HTAB )
307	 *
308	 * @param string[] $values Header values
309	 *
310	 * @return string[] Trimmed header values
311	 *
312	 * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
313	 */
314	private function trimHeaderValues(array $values)
315	{
316		return array_map(function ($value) {
317			return trim($value, " \t");
318		}, $values);
319	}
320
321}
322