Completed
Push — master ( 988ea9...868c80 )
by smiley
07:26
created

Message   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 239
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 239
rs 10
c 0
b 0
f 0
wmc 27
lcom 3
cbo 1
1
<?php
2
/**
3
 * Class Message
4
 *
5
 * @filesource   Message.php
6
 * @created      11.08.2018
7
 * @package      chillerlan\HTTP\Psr7
8
 * @author       smiley <[email protected]>
9
 * @copyright    2018 smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\HTTP\Psr7;
14
15
use chillerlan\HTTP\Psr17\StreamFactory;
16
use Psr\Http\Message\{MessageInterface, StreamInterface};
17
18
use function chillerlan\HTTP\Psr17\create_stream_from_input;
19
20
use function array_map, array_merge, implode, is_array, strtolower, trim;
21
22
abstract class Message implements MessageInterface{
23
24
	protected array $headers = [];
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_ARRAY, expecting T_FUNCTION or T_CONST
Loading history...
25
	/** @var string[] */
26
	protected array $headerNames = [];
27
28
	protected string $version;
29
30
	protected StreamInterface $body;
31
32
	protected StreamFactory $streamFactory;
33
34
	/**
35
	 * Message constructor.
36
	 *
37
	 * @param array|null                                             $headers
38
	 * @param null|string|resource|\Psr\Http\Message\StreamInterface $body
39
	 * @param string|null                                            $version
40
	 */
41
	public function __construct(array $headers = null, $body = null, string $version = null){
42
		$this->setHeaders(normalize_message_headers($headers ?? []));
43
44
		$this->version       = $version ?? '1.1';
45
		$this->streamFactory = new StreamFactory;
46
47
		$this->body = $body !== null && $body !== ''
48
			? create_stream_from_input($body)
49
			: $this->streamFactory->createStream();
50
	}
51
52
	/**
53
	 * @inheritDoc
54
	 */
55
	public function getProtocolVersion():string{
56
		return $this->version;
57
	}
58
59
	/**
60
	 * @inheritDoc
61
	 */
62
	public function withProtocolVersion($version):MessageInterface{
63
64
		if($this->version === $version){
65
			return $this;
66
		}
67
68
		$clone          = clone $this;
69
		$clone->version = $version;
70
71
		return $clone;
72
	}
73
74
	/**
75
	 * @inheritDoc
76
	 */
77
	public function getHeaders():array{
78
		return $this->headers;
79
	}
80
81
	/**
82
	 * @inheritDoc
83
	 */
84
	public function hasHeader($name):bool{
85
		return isset($this->headerNames[strtolower($name)]);
86
	}
87
88
	/**
89
	 * @inheritDoc
90
	 */
91
	public function getHeader($name):array{
92
93
		if(!$this->hasHeader($name)){
94
			return [];
95
		}
96
97
		return $this->headers[$this->headerNames[strtolower($name)]];
98
	}
99
100
	/**
101
	 * @inheritDoc
102
	 */
103
	public function getHeaderLine($name):string{
104
		return implode(', ', $this->getHeader($name));
105
	}
106
107
	/**
108
	 * @inheritDoc
109
	 */
110
	public function withHeader($name, $value):MessageInterface{
111
112
		if(!is_array($value)){
113
			$value = [$value];
114
		}
115
116
		$value      = $this->trimHeaderValues($value);
117
		$normalized = strtolower($name);
118
		$clone      = clone $this;
119
120
		if(isset($clone->headerNames[$normalized])){
121
			unset($clone->headers[$clone->headerNames[$normalized]]);
122
		}
123
124
		$clone->headerNames[$normalized] = $name;
125
		$clone->headers[$name]           = $value;
126
127
		return $clone;
128
	}
129
130
	/**
131
	 * @inheritDoc
132
	 */
133
	public function withAddedHeader($name, $value):MessageInterface{
134
135
		if(!is_array($value)){
136
			$value = [$value];
137
		}
138
139
		$value      = $this->trimHeaderValues($value);
140
		$normalized = strtolower($name);
141
		$clone      = clone $this;
142
143
		/** @noinspection DuplicatedCode */
144
		if(isset($clone->headerNames[$normalized])){
145
			$name                  = $this->headerNames[$normalized];
146
			$clone->headers[$name] = array_merge($this->headers[$name], $value);
147
		}
148
		else{
149
			$clone->headerNames[$normalized] = $name;
150
			$clone->headers[$name]           = $value;
151
		}
152
153
		return $clone;
154
	}
155
156
	/**
157
	 * @inheritDoc
158
	 */
159
	public function withoutHeader($name):MessageInterface{
160
		$normalized = strtolower($name);
161
162
		if(!isset($this->headerNames[$normalized])){
163
			return $this;
164
		}
165
166
		$name  = $this->headerNames[$normalized];
167
		$clone = clone $this;
168
169
		unset($clone->headers[$name], $clone->headerNames[$normalized]);
170
171
		return $clone;
172
	}
173
174
	/**
175
	 * @inheritDoc
176
	 */
177
	public function getBody():StreamInterface{
178
		return $this->body;
179
	}
180
181
	/**
182
	 * @inheritDoc
183
	 */
184
	public function withBody(StreamInterface $body):MessageInterface{
185
186
		if($body === $this->body){
187
			return $this;
188
		}
189
190
		$clone       = clone $this;
191
		$clone->body = $body;
192
193
		return $clone;
194
	}
195
196
	/**
197
	 * @param array $headers
198
	 */
199
	protected function setHeaders(array $headers):void{
200
		$this->headers     = [];
201
		$this->headerNames = [];
202
203
		foreach($headers as $name => $value){
204
205
			if(!is_array($value)){
206
				$value = [$value];
207
			}
208
209
			$value      = $this->trimHeaderValues($value);
210
			$normalized = strtolower($name);
211
212
			/** @noinspection DuplicatedCode */
213
			if(isset($this->headerNames[$normalized])){
214
				$name                 = $this->headerNames[$normalized];
215
				$this->headers[$name] = array_merge($this->headers[$name], $value);
216
			}
217
			else{
218
				$this->headerNames[$normalized] = $name;
219
				$this->headers[$name]           = $value;
220
			}
221
222
		}
223
	}
224
225
	/**
226
	 * Trims whitespace from the header values.
227
	 *
228
	 * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
229
	 *
230
	 * header-field = field-name ":" OWS field-value OWS
231
	 * OWS          = *( SP / HTAB )
232
	 *
233
	 * @param string[] $values Header values
234
	 *
235
	 * @return string[] Trimmed header values
236
	 *
237
	 * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
238
	 */
239
	protected function trimHeaderValues(array $values):array{
240
		return array_map(fn(string $value):string => trim($value, " \t"), $values);
241
	}
242
243
}
244