vendor/nyholm/psr7/src/MessageTrait.php line 120

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Nyholm\Psr7;
  4. use Psr\Http\Message\StreamInterface;
  5. /**
  6.  * Trait implementing functionality common to requests and responses.
  7.  *
  8.  * @author Michael Dowling and contributors to guzzlehttp/psr7
  9.  * @author Tobias Nyholm <tobias.nyholm@gmail.com>
  10.  * @author Martijn van der Ven <martijn@vanderven.se>
  11.  *
  12.  * @internal should not be used outside of Nyholm/Psr7 as it does not fall under our BC promise
  13.  */
  14. trait MessageTrait
  15. {
  16.     /** @var array Map of all registered headers, as original name => array of values */
  17.     private $headers = [];
  18.     /** @var array Map of lowercase header name => original name at registration */
  19.     private $headerNames = [];
  20.     /** @var string */
  21.     private $protocol '1.1';
  22.     /** @var StreamInterface|null */
  23.     private $stream;
  24.     public function getProtocolVersion(): string
  25.     {
  26.         return $this->protocol;
  27.     }
  28.     public function withProtocolVersion($version): self
  29.     {
  30.         if ($this->protocol === $version) {
  31.             return $this;
  32.         }
  33.         $new = clone $this;
  34.         $new->protocol $version;
  35.         return $new;
  36.     }
  37.     public function getHeaders(): array
  38.     {
  39.         return $this->headers;
  40.     }
  41.     public function hasHeader($header): bool
  42.     {
  43.         return isset($this->headerNames[\strtr($header'ABCDEFGHIJKLMNOPQRSTUVWXYZ''abcdefghijklmnopqrstuvwxyz')]);
  44.     }
  45.     public function getHeader($header): array
  46.     {
  47.         $header = \strtr($header'ABCDEFGHIJKLMNOPQRSTUVWXYZ''abcdefghijklmnopqrstuvwxyz');
  48.         if (!isset($this->headerNames[$header])) {
  49.             return [];
  50.         }
  51.         $header $this->headerNames[$header];
  52.         return $this->headers[$header];
  53.     }
  54.     public function getHeaderLine($header): string
  55.     {
  56.         return \implode(', '$this->getHeader($header));
  57.     }
  58.     public function withHeader($header$value): self
  59.     {
  60.         $value $this->validateAndTrimHeader($header$value);
  61.         $normalized = \strtr($header'ABCDEFGHIJKLMNOPQRSTUVWXYZ''abcdefghijklmnopqrstuvwxyz');
  62.         $new = clone $this;
  63.         if (isset($new->headerNames[$normalized])) {
  64.             unset($new->headers[$new->headerNames[$normalized]]);
  65.         }
  66.         $new->headerNames[$normalized] = $header;
  67.         $new->headers[$header] = $value;
  68.         return $new;
  69.     }
  70.     public function withAddedHeader($header$value): self
  71.     {
  72.         if (!\is_string($header) || '' === $header) {
  73.             throw new \InvalidArgumentException('Header name must be an RFC 7230 compatible string.');
  74.         }
  75.         $new = clone $this;
  76.         $new->setHeaders([$header => $value]);
  77.         return $new;
  78.     }
  79.     public function withoutHeader($header): self
  80.     {
  81.         $normalized = \strtr($header'ABCDEFGHIJKLMNOPQRSTUVWXYZ''abcdefghijklmnopqrstuvwxyz');
  82.         if (!isset($this->headerNames[$normalized])) {
  83.             return $this;
  84.         }
  85.         $header $this->headerNames[$normalized];
  86.         $new = clone $this;
  87.         unset($new->headers[$header], $new->headerNames[$normalized]);
  88.         return $new;
  89.     }
  90.     public function getBody(): StreamInterface
  91.     {
  92.         if (null === $this->stream) {
  93.             $this->stream Stream::create('');
  94.         }
  95.         return $this->stream;
  96.     }
  97.     public function withBody(StreamInterface $body): self
  98.     {
  99.         if ($body === $this->stream) {
  100.             return $this;
  101.         }
  102.         $new = clone $this;
  103.         $new->stream $body;
  104.         return $new;
  105.     }
  106.     private function setHeaders(array $headers): void
  107.     {
  108.         foreach ($headers as $header => $value) {
  109.             if (\is_int($header)) {
  110.                 // If a header name was set to a numeric string, PHP will cast the key to an int.
  111.                 // We must cast it back to a string in order to comply with validation.
  112.                 $header = (string) $header;
  113.             }
  114.             $value $this->validateAndTrimHeader($header$value);
  115.             $normalized = \strtr($header'ABCDEFGHIJKLMNOPQRSTUVWXYZ''abcdefghijklmnopqrstuvwxyz');
  116.             if (isset($this->headerNames[$normalized])) {
  117.                 $header $this->headerNames[$normalized];
  118.                 $this->headers[$header] = \array_merge($this->headers[$header], $value);
  119.             } else {
  120.                 $this->headerNames[$normalized] = $header;
  121.                 $this->headers[$header] = $value;
  122.             }
  123.         }
  124.     }
  125.     /**
  126.      * Make sure the header complies with RFC 7230.
  127.      *
  128.      * Header names must be a non-empty string consisting of token characters.
  129.      *
  130.      * Header values must be strings consisting of visible characters with all optional
  131.      * leading and trailing whitespace stripped. This method will always strip such
  132.      * optional whitespace. Note that the method does not allow folding whitespace within
  133.      * the values as this was deprecated for almost all instances by the RFC.
  134.      *
  135.      * header-field = field-name ":" OWS field-value OWS
  136.      * field-name   = 1*( "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^"
  137.      *              / "_" / "`" / "|" / "~" / %x30-39 / ( %x41-5A / %x61-7A ) )
  138.      * OWS          = *( SP / HTAB )
  139.      * field-value  = *( ( %x21-7E / %x80-FF ) [ 1*( SP / HTAB ) ( %x21-7E / %x80-FF ) ] )
  140.      *
  141.      * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
  142.      */
  143.     private function validateAndTrimHeader($header$values): array
  144.     {
  145.         if (!\is_string($header) || !== \preg_match("@^[!#$%&'*+.^_`|~0-9A-Za-z-]+$@"$header)) {
  146.             throw new \InvalidArgumentException('Header name must be an RFC 7230 compatible string.');
  147.         }
  148.         if (!\is_array($values)) {
  149.             // This is simple, just one value.
  150.             if ((!\is_numeric($values) && !\is_string($values)) || !== \preg_match("@^[ \t\x21-\x7E\x80-\xFF]*$@", (string) $values)) {
  151.                 throw new \InvalidArgumentException('Header values must be RFC 7230 compatible strings.');
  152.             }
  153.             return [\trim((string) $values" \t")];
  154.         }
  155.         if (empty($values)) {
  156.             throw new \InvalidArgumentException('Header values must be a string or an array of strings, empty array given.');
  157.         }
  158.         // Assert Non empty array
  159.         $returnValues = [];
  160.         foreach ($values as $v) {
  161.             if ((!\is_numeric($v) && !\is_string($v)) || !== \preg_match("@^[ \t\x21-\x7E\x80-\xFF]*$@", (string) $v)) {
  162.                 throw new \InvalidArgumentException('Header values must be RFC 7230 compatible strings.');
  163.             }
  164.             $returnValues[] = \trim((string) $v" \t");
  165.         }
  166.         return $returnValues;
  167.     }
  168. }