Completed
Push — master ( 738f49...96678e )
by Tobias
19:54 queued 09:55
created

MessageTrait::withAddedHeader()   D

Complexity

Conditions 9
Paths 13

Size

Total Lines 34
Code Lines 22

Duplication

Lines 12
Ratio 35.29 %

Code Coverage

Tests 18
CRAP Score 9

Importance

Changes 0
Metric Value
dl 12
loc 34
ccs 18
cts 18
cp 1
rs 4.909
c 0
b 0
f 0
cc 9
eloc 22
nc 13
nop 2
crap 9
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Nyholm\Psr7;
6
7
use Nyholm\Psr7\Factory\StreamFactory;
8
use Psr\Http\Message\StreamInterface;
9
10
/**
11
 * Trait implementing functionality common to requests and responses.
12
 *
13
 * @author Michael Dowling and contributors to guzzlehttp/psr7
14
 * @author Tobias Nyholm <[email protected]>
15
 */
16
trait MessageTrait
17
{
18
    /** @var array Map of all registered headers, as original name => array of values */
19
    private $headers = [];
20
21
    /** @var array Map of lowercase header name => original name at registration */
22
    private $headerNames = [];
23
24
    /** @var string */
25
    private $protocol = '1.1';
26
27
    /** @var StreamInterface */
28
    private $stream;
29
30 5
    public function getProtocolVersion()
31
    {
32 5
        return $this->protocol;
33
    }
34
35 4
    public function withProtocolVersion($version)
36
    {
37 4
        if ($this->protocol === $version) {
38 1
            return $this;
39
        }
40
41 3
        $new = clone $this;
42 3
        $new->protocol = $version;
43
44 3
        return $new;
45
    }
46
47 16
    public function getHeaders(): array
48
    {
49 16
        return $this->headers;
50
    }
51
52 50
    public function hasHeader($header): bool
53
    {
54 50
        return isset($this->headerNames[strtolower($header)]);
55
    }
56
57 25
    public function getHeader($header): array
58
    {
59 25
        $header = strtolower($header);
60
61 25
        if (!isset($this->headerNames[$header])) {
62 5
            return [];
63
        }
64
65 24
        $header = $this->headerNames[$header];
66
67 24
        return $this->headers[$header];
68
    }
69
70 23
    public function getHeaderLine($header): string
71
    {
72 23
        return implode(', ', $this->getHeader($header));
73
    }
74
75 6
    public function withHeader($header, $value): self
76
    {
77 6 View Code Duplication
        if (!is_array($value)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
78 5
            $value = [$value];
79
        } elseif (empty($value)) {
80
            throw new \InvalidArgumentException('Header values must be non-empty strings');
81 6
        }
82 6
83
        if (!is_string($header) || empty($header)) {
84 6
            throw new \InvalidArgumentException('Header name must be non-empty strings');
85 6
        }
86 1
87 View Code Duplication
        foreach ($value as $v) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
88 6
            if (!is_string($v) || trim($v) ==='') {
89 6
                throw new \InvalidArgumentException('Header values must be non-empty strings');
90
            }
91 6
        }
92
93
        $value = $this->trimHeaderValues($value);
94 20
        $normalized = strtolower($header);
95
96 20
        $new = clone $this;
97 17
        if (isset($new->headerNames[$normalized])) {
98
            unset($new->headers[$new->headerNames[$normalized]]);
99 5
        }
100
        $new->headerNames[$normalized] = $header;
101
        $new->headers[$header] = $value;
102 20
103 20
        return $new;
104
    }
105 20
106 20
    public function withAddedHeader($header, $value): self
107 14
    {
108 14 View Code Duplication
        if (!is_array($value)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
109
            $value = [$value];
110 18
        } elseif (!empty($value)) {
111 18
            $value = array_values($value);
112
        } else {
113
            throw new \InvalidArgumentException('Header values must be non-empty strings');
114 20
        }
115
116
        if (!is_string($header) || empty($header)) {
117 5
            throw new \InvalidArgumentException('Header name must be non-empty strings');
118
        }
119 5
120 View Code Duplication
        foreach ($value as $v) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121 5
            if (!is_string($v) || trim($v) ==='') {
122 2
                throw new \InvalidArgumentException('Header values must be non-empty strings');
123
            }
124
        }
125 3
126
        $value = $this->trimHeaderValues($value);
127 3
        $normalized = strtolower($header);
128 3
129
        $new = clone $this;
130 3
        if (isset($new->headerNames[$normalized])) {
131
            $header = $this->headerNames[$normalized];
132
            $new->headers[$header] = array_merge($this->headers[$header], $value);
133 9
        } else {
134
            $new->headerNames[$normalized] = $header;
135 9
            $new->headers[$header] = $value;
136 3
        }
137
138
        return $new;
139 9
    }
140
141
    public function withoutHeader($header): self
142 4
    {
143
        $normalized = strtolower($header);
144 4
145 1
        if (!isset($this->headerNames[$normalized])) {
146
            return $this;
147
        }
148 3
149 3
        $header = $this->headerNames[$normalized];
150
151 3
        $new = clone $this;
152
        unset($new->headers[$header], $new->headerNames[$normalized]);
153
154 91
        return $new;
155
    }
156 91
157 91
    public function getBody(): StreamInterface
158 11
    {
159 10
        if (!$this->stream) {
160
            $this->stream = (new StreamFactory())->createStream('');
161
        }
162 11
163 11
        return $this->stream;
164 11
    }
165
166
    public function withBody(StreamInterface $body): self
167
    {
168 11
        if ($body === $this->stream) {
169 11
            return $this;
170
        }
171
172 91
        $new = clone $this;
173
        $new->stream = $body;
174
175
        return $new;
176
    }
177
178
    private function setHeaders(array $headers)
179
    {
180
        $this->headerNames = $this->headers = [];
181
        foreach ($headers as $header => $value) {
182
            if (!is_array($value)) {
183
                $value = [$value];
184
            }
185
186
            $value = $this->trimHeaderValues($value);
187
            $normalized = strtolower($header);
188
            if (isset($this->headerNames[$normalized])) {
189
                $header = $this->headerNames[$normalized];
190 29
                $this->headers[$header] = array_merge($this->headers[$header], $value);
191 29
            } else {
192 29
                $this->headerNames[$normalized] = $header;
193
                $this->headers[$header] = $value;
194
            }
195
        }
196
    }
197
198
    /**
199
     * Trims whitespace from the header values.
200
     *
201
     * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
202
     *
203
     * header-field = field-name ":" OWS field-value OWS
204
     * OWS          = *( SP / HTAB )
205
     *
206
     * @param string[] $values Header values
207
     *
208
     * @return string[] Trimmed header values
209
     *
210
     * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
211
     */
212
    private function trimHeaderValues(array $values): array
213
    {
214
        return array_map(function ($value) {
215
            return trim($value, " \t");
216
        }, $values);
217
    }
218
}
219