1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @link http://www.yiiframework.com/ |
4
|
|
|
* @copyright Copyright (c) 2008 Yii Software LLC |
5
|
|
|
* @license http://www.yiiframework.com/license/ |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace yii\http; |
9
|
|
|
|
10
|
|
|
use Psr\Http\Message\StreamInterface; |
11
|
|
|
use yii\di\Instance; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* MessageTrait provides set of methods to satisfy [[\Psr\Http\Message\MessageInterface]]. |
15
|
|
|
* |
16
|
|
|
* This trait should be applied to descendant of [[\yii\base\BaseObject]] implementing [[\Psr\Http\Message\MessageInterface]]. |
17
|
|
|
* |
18
|
|
|
* @property string $protocolVersion HTTP protocol version as a string. |
19
|
|
|
* @property string[][] $headers the message's headers. |
20
|
|
|
* @property StreamInterface $body the body of the message. |
21
|
|
|
* @property HeaderCollection $headerCollection The header collection. This property is read-only. |
22
|
|
|
* |
23
|
|
|
* @author Paul Klimov <[email protected]> |
24
|
|
|
* @since 2.1.0 |
25
|
|
|
*/ |
26
|
|
|
trait MessageTrait |
27
|
|
|
{ |
28
|
|
|
/** |
29
|
|
|
* @var string HTTP protocol version as a string. |
30
|
|
|
*/ |
31
|
|
|
private $_protocolVersion; |
32
|
|
|
/** |
33
|
|
|
* @var HeaderCollection header collection, which is used for headers storage. |
34
|
|
|
*/ |
35
|
|
|
private $_headerCollection; |
36
|
|
|
/** |
37
|
|
|
* @var StreamInterface the body of the message. |
38
|
|
|
*/ |
39
|
|
|
private $_body; |
40
|
|
|
|
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Retrieves the HTTP protocol version as a string. |
44
|
|
|
* @return string HTTP protocol version. |
45
|
|
|
*/ |
46
|
|
|
public function getProtocolVersion() |
47
|
|
|
{ |
48
|
|
|
if ($this->_protocolVersion === null) { |
49
|
|
|
$this->_protocolVersion = $this->defaultProtocolVersion(); |
50
|
|
|
} |
51
|
|
|
return $this->_protocolVersion; |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Specifies HTTP protocol version. |
56
|
|
|
* @param string $version HTTP protocol version |
57
|
|
|
*/ |
58
|
|
|
public function setProtocolVersion($version) |
59
|
|
|
{ |
60
|
|
|
$this->_protocolVersion = $version; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Return an instance with the specified HTTP protocol version. |
65
|
|
|
* |
66
|
|
|
* This method retains the immutability of the message and returns an instance that has the |
67
|
|
|
* new protocol version. |
68
|
|
|
* |
69
|
|
|
* @param string $version HTTP protocol version |
70
|
|
|
* @return static |
71
|
|
|
*/ |
72
|
|
|
public function withProtocolVersion($version) |
73
|
|
|
{ |
74
|
|
|
if ($this->getProtocolVersion() === $version) { |
75
|
|
|
return $this; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
$newInstance = clone $this; |
79
|
|
|
$newInstance->setProtocolVersion($version); |
80
|
|
|
return $newInstance; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Returns default HTTP protocol version to be used in case it is not explicitly set. |
85
|
|
|
* @return string HTTP protocol version. |
86
|
|
|
*/ |
87
|
|
|
protected function defaultProtocolVersion() |
88
|
|
|
{ |
89
|
|
|
if (!empty($_SERVER['SERVER_PROTOCOL'])) { |
90
|
|
|
return str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']); |
91
|
|
|
} |
92
|
|
|
return '1.0'; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Returns the header collection. |
97
|
|
|
* The header collection contains the currently registered HTTP headers. |
98
|
|
|
* @return HeaderCollection the header collection |
99
|
|
|
*/ |
100
|
|
|
public function getHeaderCollection() |
101
|
|
|
{ |
102
|
|
|
if ($this->_headerCollection === null) { |
103
|
|
|
$headerCollection = new HeaderCollection(); |
104
|
|
|
$headerCollection->fromArray($this->defaultHeaders()); |
105
|
|
|
$this->_headerCollection = $headerCollection; |
106
|
|
|
} |
107
|
|
|
return $this->_headerCollection; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Returns default message's headers, which should be present once [[headerCollection]] is instantiated. |
112
|
|
|
* @return string[][] an associative array of the message's headers. |
113
|
|
|
*/ |
114
|
|
|
protected function defaultHeaders() |
115
|
|
|
{ |
116
|
|
|
return []; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Sets up message's headers at batch, removing any previously existing ones. |
121
|
|
|
* @param string[][] $headers an associative array of the message's headers. |
122
|
|
|
*/ |
123
|
|
|
public function setHeaders($headers) |
124
|
|
|
{ |
125
|
|
|
$headerCollection = $this->getHeaderCollection(); |
126
|
|
|
$headerCollection->removeAll(); |
127
|
|
|
$headerCollection->fromArray($headers); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Sets up a particular message's header, removing any its previously existing value. |
132
|
|
|
* @param string $name Case-insensitive header field name. |
133
|
|
|
* @param string|string[] $value Header value(s). |
134
|
|
|
*/ |
135
|
|
|
public function setHeader($name, $value) |
136
|
|
|
{ |
137
|
|
|
$this->getHeaderCollection()->set($name, $value); |
|
|
|
|
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Appends the given value to the specified header. |
142
|
|
|
* Existing values for the specified header will be maintained. The new |
143
|
|
|
* value(s) will be appended to the existing list. If the header did not |
144
|
|
|
* exist previously, it will be added. |
145
|
|
|
* @param string $name Case-insensitive header field name to add. |
146
|
|
|
* @param string|string[] $value Header value(s). |
147
|
|
|
*/ |
148
|
|
|
public function addHeader($name, $value) |
149
|
|
|
{ |
150
|
|
|
$this->getHeaderCollection()->add($name, $value); |
|
|
|
|
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* Retrieves all message header values. |
155
|
|
|
* |
156
|
|
|
* The keys represent the header name as it will be sent over the wire, and |
157
|
|
|
* each value is an array of strings associated with the header. |
158
|
|
|
* |
159
|
|
|
* // Represent the headers as a string |
160
|
|
|
* foreach ($message->getHeaders() as $name => $values) { |
161
|
|
|
* echo $name . ": " . implode(", ", $values); |
162
|
|
|
* } |
163
|
|
|
* |
164
|
|
|
* // Emit headers iteratively: |
165
|
|
|
* foreach ($message->getHeaders() as $name => $values) { |
166
|
|
|
* foreach ($values as $value) { |
167
|
|
|
* header(sprintf('%s: %s', $name, $value), false); |
168
|
|
|
* } |
169
|
|
|
* } |
170
|
|
|
* |
171
|
|
|
* While header names are not case-sensitive, getHeaders() will preserve the |
172
|
|
|
* exact case in which headers were originally specified. |
173
|
|
|
* |
174
|
|
|
* @return string[][] Returns an associative array of the message's headers. Each |
175
|
|
|
* key MUST be a header name, and each value MUST be an array of strings |
176
|
|
|
* for that header. |
177
|
|
|
*/ |
178
|
|
|
public function getHeaders() |
179
|
|
|
{ |
180
|
|
|
return $this->getHeaderCollection()->toArray(); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* Checks if a header exists by the given case-insensitive name. |
185
|
|
|
* |
186
|
|
|
* @param string $name Case-insensitive header field name. |
187
|
|
|
* @return bool Returns true if any header names match the given header |
188
|
|
|
* name using a case-insensitive string comparison. Returns false if |
189
|
|
|
* no matching header name is found in the message. |
190
|
|
|
*/ |
191
|
|
|
public function hasHeader($name) |
192
|
|
|
{ |
193
|
|
|
return $this->getHeaderCollection()->has($name); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Retrieves a message header value by the given case-insensitive name. |
198
|
|
|
* |
199
|
|
|
* This method returns an array of all the header values of the given |
200
|
|
|
* case-insensitive header name. |
201
|
|
|
* |
202
|
|
|
* If the header does not appear in the message, this method will return an |
203
|
|
|
* empty array. |
204
|
|
|
* |
205
|
|
|
* @param string $name Case-insensitive header field name. |
206
|
|
|
* @return string[] An array of string values as provided for the given |
207
|
|
|
* header. If the header does not appear in the message, this method MUST |
208
|
|
|
* return an empty array. |
209
|
|
|
*/ |
210
|
|
|
public function getHeader($name) |
211
|
|
|
{ |
212
|
|
|
return $this->getHeaderCollection()->get($name, [], false); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Retrieves a comma-separated string of the values for a single header. |
217
|
|
|
* |
218
|
|
|
* This method returns all of the header values of the given |
219
|
|
|
* case-insensitive header name as a string concatenated together using |
220
|
|
|
* a comma. |
221
|
|
|
* |
222
|
|
|
* NOTE: Not all header values may be appropriately represented using |
223
|
|
|
* comma concatenation. For such headers, use getHeader() instead |
224
|
|
|
* and supply your own delimiter when concatenating. |
225
|
|
|
* |
226
|
|
|
* If the header does not appear in the message, this method MUST return |
227
|
|
|
* an empty string. |
228
|
|
|
* |
229
|
|
|
* @param string $name Case-insensitive header field name. |
230
|
|
|
* @return string A string of values as provided for the given header |
231
|
|
|
* concatenated together using a comma. If the header does not appear in |
232
|
|
|
* the message, this method MUST return an empty string. |
233
|
|
|
*/ |
234
|
|
|
public function getHeaderLine($name) |
235
|
|
|
{ |
236
|
|
|
return implode(',', $this->getHeader($name)); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Return an instance with the provided value replacing the specified header. |
241
|
|
|
* This method retains the immutability of the message and returns an instance that has the |
242
|
|
|
* new and/or updated header and value. |
243
|
|
|
* @param string $name Case-insensitive header field name. |
244
|
|
|
* @param string|string[] $value Header value(s). |
245
|
|
|
* @return static |
246
|
|
|
* @throws \InvalidArgumentException for invalid header names or values. |
247
|
|
|
*/ |
248
|
|
|
public function withHeader($name, $value) |
249
|
|
|
{ |
250
|
|
|
$newInstance = clone $this; |
251
|
|
|
$newInstance->setHeader($name, $value); |
252
|
|
|
return $newInstance; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Return an instance with the specified header appended with the given value. |
257
|
|
|
* |
258
|
|
|
* Existing values for the specified header will be maintained. The new |
259
|
|
|
* value(s) will be appended to the existing list. If the header did not |
260
|
|
|
* exist previously, it will be added. |
261
|
|
|
* |
262
|
|
|
* This method retains the immutability of the message and returns an instance that has the |
263
|
|
|
* new header and/or value. |
264
|
|
|
* |
265
|
|
|
* @param string $name Case-insensitive header field name to add. |
266
|
|
|
* @param string|string[] $value Header value(s). |
267
|
|
|
* @return static |
268
|
|
|
* @throws \InvalidArgumentException for invalid header names or values. |
269
|
|
|
*/ |
270
|
|
|
public function withAddedHeader($name, $value) |
271
|
|
|
{ |
272
|
|
|
$newInstance = clone $this; |
273
|
|
|
$newInstance->addHeader($name, $value); |
274
|
|
|
return $newInstance; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Return an instance without the specified header. |
279
|
|
|
* Header resolution performed without case-sensitivity. |
280
|
|
|
* This method retains the immutability of the message and returns an instance that removes |
281
|
|
|
* the named header. |
282
|
|
|
* @param string $name Case-insensitive header field name to remove. |
283
|
|
|
* @return static |
284
|
|
|
*/ |
285
|
|
|
public function withoutHeader($name) |
286
|
|
|
{ |
287
|
|
|
$newInstance = clone $this; |
288
|
|
|
$newInstance->getHeaderCollection()->remove($name); |
289
|
|
|
return $newInstance; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* Gets the body of the message. |
294
|
|
|
* @return StreamInterface Returns the body as a stream. |
295
|
|
|
*/ |
296
|
|
|
public function getBody() |
297
|
|
|
{ |
298
|
|
|
if (!$this->_body instanceof StreamInterface) { |
299
|
|
|
if ($this->_body === null) { |
300
|
|
|
$body = $this->defaultBody(); |
301
|
|
|
} elseif ($this->_body instanceof \Closure) { |
302
|
|
|
$body = call_user_func($this->_body, $this); |
303
|
|
|
} else { |
304
|
|
|
$body = $this->_body; |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
$this->_body = Instance::ensure($body, StreamInterface::class); |
308
|
|
|
} |
309
|
|
|
return $this->_body; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* Specifies message body. |
314
|
|
|
* @param StreamInterface|\Closure|array $body stream instance or its DI compatible configuration. |
315
|
|
|
*/ |
316
|
|
|
public function setBody($body) |
317
|
|
|
{ |
318
|
|
|
$this->_body = $body; |
|
|
|
|
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
/** |
322
|
|
|
* Return an instance with the specified message body. |
323
|
|
|
* This method retains the immutability of the message and returns an instance that has the |
324
|
|
|
* new body stream. |
325
|
|
|
* @param StreamInterface $body Body. |
326
|
|
|
* @return static |
327
|
|
|
* @throws \InvalidArgumentException When the body is not valid. |
328
|
|
|
*/ |
329
|
|
|
public function withBody(StreamInterface $body) |
330
|
|
|
{ |
331
|
|
|
if ($this->getBody() === $body) { |
332
|
|
|
return $this; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
$newInstance = clone $this; |
336
|
|
|
$newInstance->setBody($body); |
337
|
|
|
return $newInstance; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Returns default message body to be used in case it is not explicitly set. |
342
|
|
|
* @return StreamInterface default body instance. |
343
|
|
|
*/ |
344
|
|
|
protected function defaultBody() |
345
|
|
|
{ |
346
|
|
|
return new MemoryStream(); |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* This method is called after the object is created by cloning an existing one. |
351
|
|
|
*/ |
352
|
|
|
public function __clone() |
353
|
|
|
{ |
354
|
|
|
$this->cloneHttpMessageInternals(); |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* Ensures any internal object-type fields related to `MessageTrait` are cloned from their origins. |
359
|
|
|
* In case actual trait owner implementing method [[__clone()]], it must invoke this method within it. |
360
|
|
|
*/ |
361
|
|
|
private function cloneHttpMessageInternals() |
362
|
|
|
{ |
363
|
|
|
if (is_object($this->_headerCollection)) { |
364
|
|
|
$this->_headerCollection = clone $this->_headerCollection; |
365
|
|
|
} |
366
|
|
|
if (is_object($this->_body)) { |
367
|
|
|
$this->_body = clone $this->_body; |
368
|
|
|
} |
369
|
|
|
} |
370
|
|
|
} |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.