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

Header::parseAndCollect()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 10
ccs 4
cts 4
cp 1
crap 2
rs 10
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
     * @return BaseHeaderValue[]
73
     */
74 56
    public function getValues(bool $ignoreIncorrect = true): array
75
    {
76 56
        $result = [];
77 56
        foreach ($this->collection as $header) {
78 56
            if ($ignoreIncorrect && $header->hasError()) {
79 1
                continue;
80
            }
81 55
            $result[] = $header;
82
        }
83 56
        return $result;
84
    }
85
86
    /**
87
     * @param bool $ignoreIncorrect
88
     * @return string[]
89
     */
90 28
    public function getStrings(bool $ignoreIncorrect = true): array
91
    {
92 28
        $result = [];
93 28
        foreach ($this->collection as $header) {
94 28
            if ($ignoreIncorrect && $header->hasError()) {
95
                continue;
96
            }
97 28
            $result[] = $header->__toString();
98
        }
99 28
        return $result;
100
    }
101
102
    /**
103
     * @param string[]|BaseHeaderValue[] $values
104
     */
105 15
    final public function withValues(array $values): self
106
    {
107 15
        $clone = clone $this;
108 15
        foreach ($values as $value) {
109 15
            $clone->addValue($value);
110
        }
111 15
        return $clone;
112
    }
113
114
    /**
115
     * @param string|BaseHeaderValue $value
116
     */
117 74
    final public function withValue($value): self
118
    {
119 74
        $clone = clone $this;
120 74
        $clone->addValue($value);
121 73
        return $clone;
122
    }
123
124
    /**
125
     * Export header values into HTTP message
126
     * @param MessageInterface $message Request or Response instance
127
     * @param bool $replace Replace existing headers
128
     * @param bool $ignoreIncorrect Don't export values that have error
129
     * @return MessageInterface
130
     */
131
    final public function inject(
132
        MessageInterface $message,
133
        bool $replace = true,
134
        bool $ignoreIncorrect = true
135
    ): MessageInterface {
136
        if ($replace) {
137
            $message = $message->withoutHeader($this->headerName);
138
        }
139
        foreach ($this->collection as $value) {
140
            if ($ignoreIncorrect && $value->hasError()) {
141
                continue;
142
            }
143
            $message = $message->withAddedHeader($this->headerName, $value->__toString());
144
        }
145
        return $message;
146
    }
147
148
    /**
149
     * Import header values from HTTP message
150
     * @param MessageInterface $message Request or Response instance
151
     */
152
    final public function extract(MessageInterface $message): self
153
    {
154
        return $this->withValues($message->getHeader($this->headerName));
155
    }
156
157
    /**
158
     * @param string|BaseHeaderValue $value
159
     */
160 87
    protected function addValue($value): void
161
    {
162 87
        if ($value instanceof BaseHeaderValue) {
163 2
            if (get_class($value) !== $this->headerClass) {
164 1
                throw new InvalidArgumentException(
165 1
                    sprintf('The value must be an instance of %s, %s given.', $this->headerClass, get_class($value))
166
                );
167
            }
168 1
            $this->collect($value);
169 1
            return;
170
        }
171 87
        if (is_string($value)) {
0 ignored issues
show
introduced by
The condition is_string($value) is always true.
Loading history...
172 87
            $this->parseAndCollect($value);
173 87
            return;
174
        }
175
        throw new InvalidArgumentException(
176
            sprintf('The value must be an instance of %s or string', $this->headerClass)
177
        );
178
    }
179
180 72
    protected function collect(BaseHeaderValue $value): void
181
    {
182 72
        $this->collection[] = $value;
183 72
    }
184
185 87
    private function parseAndCollect(string $body): void
186
    {
187
        /**
188
         * @var HeaderParsingParams $parsingParams
189
         * @see BaseHeaderValue::getParsingParams
190
         */
191 87
        $parsingParams = $this->headerClass::getParsingParams();
192
193 87
        foreach (ValueFieldParser::parse($body, $this->headerClass, $parsingParams) as $value) {
194 87
            $this->collect($value);
195
        }
196 87
    }
197
}
198