vendor/guzzlehttp/psr7/src/MessageTrait.php line 118

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