Completed
Push — remove-yii-autoloader ( 560d61...5f8600 )
by Alexander
27:04 queued 23:12
created

MessageTrait::getHeaderLine()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
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);
0 ignored issues
show
Bug introduced by
It seems like $value defined by parameter $value on line 135 can also be of type array<integer,string>; however, yii\http\HeaderCollection::set() does only seem to accept string, maybe add an additional type check?

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.

Loading history...
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);
0 ignored issues
show
Bug introduced by
It seems like $value defined by parameter $value on line 148 can also be of type array<integer,string>; however, yii\http\HeaderCollection::add() does only seem to accept string, maybe add an additional type check?

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.

Loading history...
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;
0 ignored issues
show
Documentation Bug introduced by
It seems like $body can also be of type object<Closure> or array. However, the property $_body is declared as type object<Psr\Http\Message\StreamInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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
}