Passed
Pull Request — master (#22)
by Aleksei
02:39
created

Header   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Test Coverage

Coverage 74.32%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 63
dl 0
loc 187
ccs 55
cts 74
cp 0.7432
rs 9.76
c 1
b 0
f 0
wmc 33

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getValueClass() 0 3 1
A count() 0 3 1
A getName() 0 3 1
A getIterator() 0 3 1
A __construct() 0 19 5
A getStrings() 0 10 4
A getValues() 0 10 4
A withValue() 0 5 1
A parseAndCollect() 0 11 2
A collect() 0 3 1
A extract() 0 3 1
A withValues() 0 7 2
A addValue() 0 17 4
A inject() 0 15 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Http\Header;
6
7
use InvalidArgumentException;
8
use Psr\Http\Message\MessageInterface;
9
use Yiisoft\Http\Header\Parser\HeaderParsingParams;
10
use Yiisoft\Http\Header\Parser\ValueFieldParser;
11
use Yiisoft\Http\Header\Value\Unnamed\SimpleValue;
12
use Yiisoft\Http\Header\Internal\BaseHeaderValue;
13
14
class Header implements \IteratorAggregate, \Countable
15
{
16
    /** @psalm-var class-string<BaseHeaderValue> */
17
    protected string $headerClass;
18
    protected string $headerName;
19
    /** @var array<int, BaseHeaderValue> */
20
    protected array $collection = [];
21
22
    protected const DEFAULT_VALUE_CLASS = SimpleValue::class;
23
24
    /**
25
     * @param string|class-string $nameOrClass Header name or header value class
0 ignored issues
show
Documentation Bug introduced by
The doc comment string|class-string at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in string|class-string.
Loading history...
26
     * @psalm-param class-string<BaseHeaderValue> $nameOrClass
27
     */
28 101
    public function __construct(string $nameOrClass)
29
    {
30 101
        if (class_exists($nameOrClass)) {
31 85
            if (!is_subclass_of($nameOrClass, BaseHeaderValue::class, true)) {
32 2
                throw new InvalidArgumentException("{$nameOrClass} is not a header.");
33
            }
34
            /** @var class-string<BaseHeaderValue> $nameOrClass */
35 83
            $this->headerClass = $nameOrClass;
36 83
            if (empty($nameOrClass::NAME)) {
37 1
                throw new InvalidArgumentException("{$nameOrClass} has no header name.");
38
            }
39 82
            $this->headerName = $nameOrClass::NAME;
40
        } else {
41
            /** @var string $nameOrClass */
42 16
            if ($nameOrClass === '') {
43
                throw new InvalidArgumentException('Empty header name.');
44
            }
45 16
            $this->headerName = $nameOrClass;
46 16
            $this->headerClass = static::DEFAULT_VALUE_CLASS;
47
        }
48 98
    }
49
50
    final public function getIterator(): iterable
51
    {
52
        yield from $this->getValues(true);
53
    }
54
55
    final public function count(): int
56
    {
57
        return count($this->collection);
58
    }
59
60 29
    final public function getName(): string
61
    {
62 29
        return $this->headerName;
63
    }
64
65 4
    final public function getValueClass(): string
66
    {
67 4
        return $this->headerClass;
68
    }
69
70
    /**
71
     * @param bool $ignoreIncorrect
72
     *
73
     * @return BaseHeaderValue[]
74
     */
75 56
    public function getValues(bool $ignoreIncorrect = true): array
76
    {
77 56
        $result = [];
78 56
        foreach ($this->collection as $header) {
79 56
            if ($ignoreIncorrect && $header->hasError()) {
80 1
                continue;
81
            }
82 55
            $result[] = $header;
83
        }
84 56
        return $result;
85
    }
86
87
    /**
88
     * @param bool $ignoreIncorrect
89
     *
90
     * @return string[]
91
     */
92 28
    public function getStrings(bool $ignoreIncorrect = true): array
93
    {
94 28
        $result = [];
95 28
        foreach ($this->collection as $header) {
96 28
            if ($ignoreIncorrect && $header->hasError()) {
97
                continue;
98
            }
99 28
            $result[] = $header->__toString();
100
        }
101 28
        return $result;
102
    }
103
104
    /**
105
     * @param BaseHeaderValue[]|string[] $values
106
     */
107 15
    final public function withValues(array $values): self
108
    {
109 15
        $clone = clone $this;
110 15
        foreach ($values as $value) {
111 15
            $clone->addValue($value);
112
        }
113 15
        return $clone;
114
    }
115
116
    /**
117
     * @param BaseHeaderValue|string $value
118
     */
119 74
    final public function withValue($value): self
120
    {
121 74
        $clone = clone $this;
122 74
        $clone->addValue($value);
123 73
        return $clone;
124
    }
125
126
    /**
127
     * Export header values into HTTP message
128
     *
129
     * @param MessageInterface $message Request or Response instance
130
     * @param bool $replace Replace existing headers
131
     * @param bool $ignoreIncorrect Don't export values that have error
132
     *
133
     * @return MessageInterface
134
     */
135
    final public function inject(
136
        MessageInterface $message,
137
        bool $replace = true,
138
        bool $ignoreIncorrect = true
139
    ): MessageInterface {
140
        if ($replace) {
141
            $message = $message->withoutHeader($this->headerName);
142
        }
143
        foreach ($this->collection as $value) {
144
            if ($ignoreIncorrect && $value->hasError()) {
145
                continue;
146
            }
147
            $message = $message->withAddedHeader($this->headerName, $value->__toString());
148
        }
149
        return $message;
150
    }
151
152
    /**
153
     * Import header values from HTTP message
154
     *
155
     * @param MessageInterface $message Request or Response instance
156
     */
157
    final public function extract(MessageInterface $message): self
158
    {
159
        return $this->withValues($message->getHeader($this->headerName));
160
    }
161
162
    /**
163
     * @param BaseHeaderValue|string $value
164
     */
165 87
    protected function addValue($value): void
166
    {
167 87
        if ($value instanceof BaseHeaderValue) {
168 2
            if (get_class($value) !== $this->headerClass) {
169 1
                throw new InvalidArgumentException(
170 1
                    sprintf('The value must be an instance of %s, %s given.', $this->headerClass, get_class($value))
171
                );
172
            }
173 1
            $this->collect($value);
174 1
            return;
175
        }
176 87
        if (is_string($value)) {
0 ignored issues
show
introduced by
The condition is_string($value) is always true.
Loading history...
177 87
            $this->parseAndCollect($value);
178 87
            return;
179
        }
180
        throw new InvalidArgumentException(
181
            sprintf('The value must be an instance of %s or string', $this->headerClass)
182
        );
183
    }
184
185 72
    protected function collect(BaseHeaderValue $value): void
186
    {
187 72
        $this->collection[] = $value;
188 72
    }
189
190 87
    private function parseAndCollect(string $body): void
191
    {
192
        /**
193
         * @var HeaderParsingParams $parsingParams
194
         *
195
         * @see BaseHeaderValue::getParsingParams
196
         */
197 87
        $parsingParams = $this->headerClass::getParsingParams();
198
199 87
        foreach (ValueFieldParser::parse($body, $this->headerClass, $parsingParams) as $value) {
200 87
            $this->collect($value);
201
        }
202 87
    }
203
}
204