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

Header::__construct()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.0144

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 12
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 17
ccs 11
cts 12
cp 0.9167
crap 5.0144
rs 9.5555
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 $nameOrClass Header name or header value class
26
     * @psalm-param class-string<BaseHeaderValue> $nameOrClass
27
     */
28 101
    public function __construct(string $nameOrClass)
29
    {
30 101
        $this->headerClass = $nameOrClass;
31 101
        if (class_exists($nameOrClass)) {
32 85
            if (!is_subclass_of($nameOrClass, BaseHeaderValue::class, true)) {
33 2
                throw new InvalidArgumentException("{$nameOrClass} is not a header.");
34
            }
35 83
            if (empty($nameOrClass::NAME)) {
36 1
                throw new InvalidArgumentException("{$nameOrClass} has no header name.");
37
            }
38 82
            $this->headerName = $nameOrClass::NAME;
39
        } else {
40 16
            if ($nameOrClass === '') {
41
                throw new InvalidArgumentException("Empty header name.");
42
            }
43 16
            $this->headerName = $nameOrClass;
44 16
            $this->headerClass = static::DEFAULT_VALUE_CLASS;
45
        }
46 98
    }
47
48
    final public function getIterator(): iterable
49
    {
50
        yield from $this->getValues(true);
51
    }
52
53
    final public function count(): int
54
    {
55
        return count($this->collection);
56
    }
57
58 29
    final public function getName(): string
59
    {
60 29
        return $this->headerName;
61
    }
62
63 4
    final public function getValueClass(): string
64
    {
65 4
        return $this->headerClass;
66
    }
67
68
    /**
69
     * @param bool $ignoreIncorrect
70
     * @return BaseHeaderValue[]
71
     */
72 56
    public function getValues(bool $ignoreIncorrect = true): array
73
    {
74 56
        $result = [];
75 56
        foreach ($this->collection as $header) {
76 56
            if ($ignoreIncorrect && $header->hasError()) {
77 1
                continue;
78
            }
79 55
            $result[] = $header;
80
        }
81 56
        return $result;
82
    }
83
84
    /**
85
     * @param bool $ignoreIncorrect
86
     * @return string[]
87
     */
88 28
    public function getStrings(bool $ignoreIncorrect = true): array
89
    {
90 28
        $result = [];
91 28
        foreach ($this->collection as $header) {
92 28
            if ($ignoreIncorrect && $header->hasError()) {
93
                continue;
94
            }
95 28
            $result[] = $header->__toString();
96
        }
97 28
        return $result;
98
    }
99
100
    /**
101
     * @param string[]|BaseHeaderValue[] $values
102
     */
103 15
    final public function withValues(array $values): self
104
    {
105 15
        $clone = clone $this;
106 15
        foreach ($values as $value) {
107 15
            $clone->addValue($value);
108
        }
109 15
        return $clone;
110
    }
111
112
    /**
113
     * @param string|BaseHeaderValue $value
114
     */
115 74
    final public function withValue($value): self
116
    {
117 74
        $clone = clone $this;
118 74
        $clone->addValue($value);
119 73
        return $clone;
120
    }
121
122
    /**
123
     * Export header values into HTTP message
124
     * @param MessageInterface $message Request or Response instance
125
     * @param bool $replace Replace existing headers
126
     * @param bool $ignoreIncorrect Don't export values that have error
127
     * @return MessageInterface
128
     */
129
    final public function inject(
130
        MessageInterface $message,
131
        bool $replace = true,
132
        bool $ignoreIncorrect = true
133
    ): MessageInterface {
134
        if ($replace) {
135
            $message = $message->withoutHeader($this->headerName);
136
        }
137
        foreach ($this->collection as $value) {
138
            if ($ignoreIncorrect && $value->hasError()) {
139
                continue;
140
            }
141
            $message = $message->withAddedHeader($this->headerName, $value->__toString());
142
        }
143
        return $message;
144
    }
145
146
    /**
147
     * Import header values from HTTP message
148
     * @param MessageInterface $message Request or Response instance
149
     */
150
    final public function extract(MessageInterface $message): self
151
    {
152
        return $this->withValues($message->getHeader($this->headerName));
153
    }
154
155
    /**
156
     * @param string|BaseHeaderValue $value
157
     */
158 87
    protected function addValue($value): void
159
    {
160 87
        if ($value instanceof BaseHeaderValue) {
161 2
            if (get_class($value) !== $this->headerClass) {
162 1
                throw new InvalidArgumentException(
163 1
                    sprintf('The value must be an instance of %s, %s given', $this->headerClass, get_class($value))
164
                );
165
            }
166 1
            $this->collect($value);
167 1
            return;
168
        }
169 87
        if (is_string($value)) {
0 ignored issues
show
introduced by
The condition is_string($value) is always true.
Loading history...
170 87
            $this->parseAndCollect($value);
171 87
            return;
172
        }
173
        throw new InvalidArgumentException(
174
            sprintf('The value must be an instance of %s or string', $this->headerClass)
175
        );
176
    }
177
178 72
    protected function collect(BaseHeaderValue $value): void
179
    {
180 72
        $this->collection[] = $value;
181 72
    }
182
183 87
    private function parseAndCollect(string $body): void
184
    {
185
        /**
186
         * @var HeaderParsingParams $parsingParams
187
         * @see BaseHeaderValue::getParsingParams
188
         */
189 87
        $parsingParams = $this->headerClass::getParsingParams();
190
191 87
        foreach (ValueFieldParser::parse($body, $this->headerClass, $parsingParams) as $value) {
192 87
            $this->collect($value);
193
        }
194 87
    }
195
}
196