‪TYPO3CMS  ‪main
Message.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
18 namespace ‪TYPO3\CMS\Core\Http;
19 
20 use Psr\Http\Message\MessageInterface;
21 use Psr\Http\Message\StreamInterface;
22 
31 class ‪Message implements MessageInterface
32 {
36  protected string ‪$protocolVersion = '1.1';
37 
42  protected array ‪$headers = [];
43 
48  protected array ‪$lowercasedHeaderNames = [];
49 
53  protected ?StreamInterface ‪$body = null;
54 
62  public function ‪getProtocolVersion(): string
63  {
65  }
66 
80  public function ‪withProtocolVersion(string $version): MessageInterface
81  {
82  $clonedObject = clone $this;
83  $clonedObject->protocolVersion = $version;
84  return $clonedObject;
85  }
86 
114  public function ‪getHeaders(): array
115  {
116  return ‪$this->headers;
117  }
118 
127  public function ‪hasHeader(string $name): bool
128  {
129  return isset($this->lowercasedHeaderNames[strtolower($name)]);
130  }
131 
146  public function ‪getHeader(string $name): array
147  {
148  if (!$this->‪hasHeader($name)) {
149  return [];
150  }
151  $header = $this->lowercasedHeaderNames[strtolower($name)];
152  $headerValue = $this->headers[$header];
153  if (is_array($headerValue)) {
154  return $headerValue;
155  }
156  return [$headerValue];
157  }
158 
178  public function ‪getHeaderLine(string $name): string
179  {
180  $headerValue = $this->‪getHeader($name);
181  if (empty($headerValue)) {
182  return '';
183  }
184  return implode(',', $headerValue);
185  }
186 
202  public function ‪withHeader(string $name, $value): MessageInterface
203  {
204  if (is_string($value)) {
205  $value = [$value];
206  }
207 
208  if (!is_array($value) || !$this->‪arrayContainsOnlyStrings($value)) {
209  throw new \InvalidArgumentException('Invalid header value for header "' . $name . '". The value must be a string or an array of strings.', 1436717266);
210  }
211 
212  $this->‪validateHeaderName($name);
213  $this->‪validateHeaderValues($value);
214  $lowercasedHeaderName = strtolower($name);
215 
216  $clonedObject = clone $this;
217  $clonedObject->headers[$name] = $value;
218  $clonedObject->lowercasedHeaderNames[$lowercasedHeaderName] = $name;
219  return $clonedObject;
220  }
221 
238  public function ‪withAddedHeader(string $name, $value): MessageInterface
239  {
240  if (is_string($value)) {
241  $value = [$value];
242  }
243  if (!is_array($value) || !$this->‪arrayContainsOnlyStrings($value)) {
244  throw new \InvalidArgumentException('Invalid header value for header "' . $name . '". The header value must be a string or array of strings', 1436717267);
245  }
246  $this->‪validateHeaderName($name);
247  $this->‪validateHeaderValues($value);
248  if (!$this->‪hasHeader($name)) {
249  return $this->‪withHeader($name, $value);
250  }
251  $name = $this->lowercasedHeaderNames[strtolower($name)];
252  $clonedObject = clone $this;
253  $clonedObject->headers[$name] = array_merge($this->headers[$name], $value);
254  return $clonedObject;
255  }
256 
269  public function ‪withoutHeader(string $name): MessageInterface
270  {
271  if (!$this->‪hasHeader($name)) {
272  return clone $this;
273  }
274  // fetch the original header from the lowercased version
275  $lowercasedHeader = strtolower($name);
276  $name = $this->lowercasedHeaderNames[$lowercasedHeader];
277  $clonedObject = clone $this;
278  unset($clonedObject->headers[$name], $clonedObject->lowercasedHeaderNames[$lowercasedHeader]);
279  return $clonedObject;
280  }
281 
287  public function ‪getBody(): StreamInterface
288  {
289  if ($this->body === null) {
290  $this->body = new ‪Stream('php://temp', 'r+');
291  }
292  return ‪$this->body;
293  }
294 
307  public function ‪withBody(StreamInterface ‪$body): MessageInterface
308  {
309  $clonedObject = clone $this;
310  $clonedObject->body = ‪$body;
311  return $clonedObject;
312  }
313 
319  protected function ‪assertHeaders(array ‪$headers): void
320  {
321  foreach (‪$headers as $name => $headerValues) {
322  $this->‪validateHeaderName($name);
323  // check if all values are correct
324  array_walk($headerValues, static function (string $value, string $key, ‪Message $messageObject): void {
325  if (!$messageObject->‪isValidHeaderValue($value)) {
326  throw new \InvalidArgumentException('Invalid header value for header "' . $key . '"', 1436717268);
327  }
328  }, $this);
329  }
330  }
331 
340  protected function ‪filterHeaders(array $originalHeaders): array
341  {
342  $headerNames = ‪$headers = [];
343  foreach ($originalHeaders as $header => $value) {
344  if (!is_string($header) || (!is_array($value) && !is_scalar($value))) {
345  continue;
346  }
347  if (!is_array($value)) {
348  $value = [(string)$value];
349  }
350  $headerNames[strtolower($header)] = $header;
351  ‪$headers[$header] = $value;
352  }
353  return [$headerNames, ‪$headers];
354  }
355 
359  protected function ‪arrayContainsOnlyStrings(array $data): bool
360  {
361  return array_reduce($data, static function ($original, $item) {
362  return is_string($item) ? $original : false;
363  }, true);
364  }
365 
373  protected function ‪validateHeaderValues(array $values): void
374  {
375  array_walk($values, static function (string $value, string $key, ‪Message $messageObject): void {
376  if (!$messageObject->‪isValidHeaderValue($value)) {
377  throw new \InvalidArgumentException('Invalid header value for header "' . $key . '"', 1436717269);
378  }
379  }, $this);
380  }
381 
397  public function ‪filter(string $value): string
398  {
399  $length = strlen($value);
400  $string = '';
401  for ($i = 0; $i < $length; $i += 1) {
402  $ascii = ord($value[$i]);
403 
404  // Detect continuation sequences
405  if ($ascii === 13) {
406  $lf = ord($value[$i + 1]);
407  $ws = ord($value[$i + 2]);
408  if ($lf === 10 && in_array($ws, [9, 32], true)) {
409  $string .= $value[$i] . $value[$i + 1];
410  $i += 1;
411  }
412  continue;
413  }
414 
415  // Non-visible, non-whitespace characters
416  // 9 === horizontal tab
417  // 32-126, 128-254 === visible
418  // 127 === DEL
419  // 255 === null byte
420  if (($ascii < 32 && $ascii !== 9) || $ascii === 127 || $ascii > 254) {
421  continue;
422  }
423 
424  $string .= $value[$i];
425  }
426 
427  return $string;
428  }
429 
437  public function ‪validateHeaderName(string $name): void
438  {
439  if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $name)) {
440  throw new \InvalidArgumentException('Invalid header name, given "' . $name . '"', 1436717270);
441  }
442  }
443 
454  public function ‪isValidHeaderValue(string $value): bool
455  {
456  // Any occurrence of \r or \n is invalid
457  if (strpbrk($value, "\r\n") !== false) {
458  return false;
459  }
460 
461  foreach (unpack('C*', $value) as $ascii) {
462  // Non-visible, non-whitespace characters
463  // 9 === horizontal tab
464  // 32-126, 128-254 === visible
465  // 127 === DEL
466  // 255 === null byte
467  if (($ascii < 32 && $ascii !== 9) || $ascii === 127 || $ascii > 254) {
468  return false;
469  }
470  }
471 
472  return true;
473  }
474 }
‪TYPO3\CMS\Core\Http\Message\getBody
‪StreamInterface getBody()
Definition: Message.php:287
‪TYPO3\CMS\Core\Http\Message\getHeaders
‪array getHeaders()
Definition: Message.php:114
‪TYPO3\CMS\Core\Http\Message\getHeaderLine
‪string getHeaderLine(string $name)
Definition: Message.php:178
‪TYPO3\CMS\Core\Http\Message\withAddedHeader
‪static withAddedHeader(string $name, $value)
Definition: Message.php:238
‪TYPO3\CMS\Core\Http\Message\withoutHeader
‪static withoutHeader(string $name)
Definition: Message.php:269
‪TYPO3\CMS\Core\Http\Message\assertHeaders
‪assertHeaders(array $headers)
Definition: Message.php:319
‪TYPO3\CMS\Core\Http\Message\$protocolVersion
‪string $protocolVersion
Definition: Message.php:36
‪TYPO3\CMS\Core\Http\Message\isValidHeaderValue
‪isValidHeaderValue(string $value)
Definition: Message.php:454
‪TYPO3\CMS\Core\Http\Message
Definition: Message.php:32
‪TYPO3\CMS\Core\Http\Message\filter
‪filter(string $value)
Definition: Message.php:397
‪TYPO3\CMS\Core\Http\Stream
Definition: Stream.php:31
‪TYPO3\CMS\Core\Http\Message\$body
‪StreamInterface $body
Definition: Message.php:53
‪TYPO3\CMS\Core\Http\Message\getHeader
‪string[] getHeader(string $name)
Definition: Message.php:146
‪TYPO3\CMS\Core\Http\Message\getProtocolVersion
‪string getProtocolVersion()
Definition: Message.php:62
‪TYPO3\CMS\Core\Http\Message\withHeader
‪static withHeader(string $name, $value)
Definition: Message.php:202
‪TYPO3\CMS\Core\Http\Message\withBody
‪static withBody(StreamInterface $body)
Definition: Message.php:307
‪TYPO3\CMS\Core\Http\Message\validateHeaderValues
‪validateHeaderValues(array $values)
Definition: Message.php:373
‪TYPO3\CMS\Core\Http\Message\$lowercasedHeaderNames
‪array $lowercasedHeaderNames
Definition: Message.php:48
‪TYPO3\CMS\Core\Http\Message\validateHeaderName
‪validateHeaderName(string $name)
Definition: Message.php:437
‪TYPO3\CMS\Core\Http\Message\filterHeaders
‪array filterHeaders(array $originalHeaders)
Definition: Message.php:340
‪TYPO3\CMS\Core\Http\Message\arrayContainsOnlyStrings
‪arrayContainsOnlyStrings(array $data)
Definition: Message.php:359
‪TYPO3\CMS\Core\Http\Message\hasHeader
‪bool hasHeader(string $name)
Definition: Message.php:127
‪TYPO3\CMS\Core\Http\Message\$headers
‪array $headers
Definition: Message.php:42
‪TYPO3\CMS\Core\Http\Message\withProtocolVersion
‪static withProtocolVersion(string $version)
Definition: Message.php:80
‪TYPO3\CMS\Core\Http
Definition: AbstractApplication.php:18