Passed
Push — master ( ca348b...1f6505 )
by Marcio
03:43
created

Message::getHttpVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 1
c 1
b 0
f 1
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
/**
3
 *
4
 * KNUT7 K7F (https://marciozebedeu.com/)
5
 * KNUT7 K7F (tm) : Rapid Development Framework (https://marciozebedeu.com/)
6
 *
7
 * Licensed under The MIT License
8
 * For full copyright and license information, please see the LICENSE.txt
9
 * Redistributions of files must retain the above copyright notice.
10
 *
11
 * @link      https://github.com/knut7/framework/ for the canonical source repository
12
 * @copyright (c) 2015.  KNUT7  Software Technologies AO Inc. (https://marciozebedeu.com/)
13
 * @license   https://marciozebedeu.com/license/new-bsd New BSD License
14
 * @author    Marcio Zebedeu - [email protected]
15
 * @version   1.0.15
16
 *
17
 *
18
 */
19
20
namespace Ballybran\Core\Http;
21
22
abstract class Message
23
{
24
    /**
25
     * Request body.
26
     *
27
     * This should be a stream resource, string or a callback writing the body to php://output
28
     *
29
     * @var resource|string|callable
30
     */
31
    protected $body;
32
33
    /**
34
     * Contains the list of HTTP headers.
35
     *
36
     * @var array
37
     */
38
    protected $headers = [];
39
40
    /**
41
     * HTTP message version (1.0, 1.1 or 2.0).
42
     *
43
     * @var string
44
     */
45
    protected $httpVersion = '1.1';
46
47
    /**
48
     * Returns the body as a readable stream resource.
49
     *
50
     * Note that the stream may not be rewindable, and therefore may only be
51
     * read once.
52
     *
53
     * @return resource
54
     */
55
    public function getBodyAsStream()
56
    {
57
        $body = $this->getBody();
58
        if (is_callable($this->body)) {
59
            $body = $this->getBodyAsString();
60
        }
61
        if (is_string($body) || null === $body) {
62
            $stream = fopen('php://temp', 'r+');
63
            fwrite($stream, (string) $body);
0 ignored issues
show
Bug introduced by
It seems like $stream can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

63
            fwrite(/** @scrutinizer ignore-type */ $stream, (string) $body);
Loading history...
64
            rewind($stream);
0 ignored issues
show
Bug introduced by
It seems like $stream can also be of type false; however, parameter $handle of rewind() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

64
            rewind(/** @scrutinizer ignore-type */ $stream);
Loading history...
65
66
            return $stream;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $stream could also return false which is incompatible with the documented return type resource. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
67
        }
68
69
        return $body;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $body also could return the type callable which is incompatible with the documented return type resource.
Loading history...
70
    }
71
72
    /**
73
     * Returns the body as a string.
74
     *
75
     * Note that because the underlying data may be based on a stream, this
76
     * method could only work correctly the first time.
77
     */
78
    public function getBodyAsString(): string
79
    {
80
        $body = $this->getBody();
81
        if (is_string($body)) {
82
            return $body;
83
        }
84
        if (null === $body) {
0 ignored issues
show
introduced by
The condition null === $body is always false.
Loading history...
85
            return '';
86
        }
87
        if (is_callable($body)) {
88
            ob_start();
89
            $body();
90
91
            return ob_get_clean();
92
        }
93
        /**
94
         * @var string|int|null
95
         */
96
        $contentLength = $this->getHeader('Content-Length');
97
        if (is_int($contentLength) || ctype_digit($contentLength)) {
98
            return stream_get_contents($body, (int) $contentLength);
0 ignored issues
show
Bug introduced by
It seems like $body can also be of type callable; however, parameter $handle of stream_get_contents() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

98
            return stream_get_contents(/** @scrutinizer ignore-type */ $body, (int) $contentLength);
Loading history...
99
        }
100
101
        return stream_get_contents($body);
102
    }
103
104
    /**
105
     * Returns the message body, as it's internal representation.
106
     *
107
     * This could be either a string, a stream or a callback writing the body to php://output.
108
     *
109
     * @return resource|string|callable
110
     */
111
    public function getBody()
112
    {
113
        return $this->body;
114
    }
115
116
    /**
117
     * Replaces the body resource with a new stream, string or a callback writing the body to php://output.
118
     *
119
     * @param resource|string|callable $body
120
     */
121
    public function setBody($body)
122
    {
123
        $this->body = $body;
124
    }
125
126
    /**
127
     * Returns all the HTTP headers as an array.
128
     *
129
     * Every header is returned as an array, with one or more values.
130
     */
131
    public function getHeaders(): array
132
    {
133
        $result = [];
134
        foreach ($this->headers as $headerInfo) {
135
            $result[$headerInfo[0]] = $headerInfo[1];
136
        }
137
138
        return $result;
139
    }
140
141
    /**
142
     * Will return true or false, depending on if a HTTP header exists.
143
     */
144
    public function hasHeader(string $name): bool
145
    {
146
        return isset($this->headers[strtolower($name)]);
147
    }
148
149
    /**
150
     * Returns a specific HTTP header, based on it's name.
151
     *
152
     * The name must be treated as case-insensitive.
153
     * If the header does not exist, this method must return null.
154
     *
155
     * If a header appeared more than once in a HTTP request, this method will
156
     * concatenate all the values with a comma.
157
     *
158
     * Note that this not make sense for all headers. Some, such as
159
     * `Set-Cookie` cannot be logically combined with a comma. In those cases
160
     * you *should* use getHeaderAsArray().
161
     *
162
     * @return string|null
163
     */
164
    public function getHeader(string $name)
165
    {
166
        $name = strtolower($name);
167
168
        if (isset($this->headers[$name])) {
169
            return implode(',', $this->headers[$name][1]);
170
        }
171
172
        return null;
173
    }
174
175
    /**
176
     * Returns a HTTP header as an array.
177
     *
178
     * For every time the HTTP header appeared in the request or response, an
179
     * item will appear in the array.
180
     *
181
     * If the header did not exists, this method will return an empty array.
182
     *
183
     * @return string[]
184
     */
185
    public function getHeaderAsArray(string $name): array
186
    {
187
        $name = strtolower($name);
188
189
        if (isset($this->headers[$name])) {
190
            return $this->headers[$name][1];
191
        }
192
193
        return [];
194
    }
195
196
    /**
197
     * Updates a HTTP header.
198
     *
199
     * The case-sensitivity of the name value must be retained as-is.
200
     *
201
     * If the header already existed, it will be overwritten.
202
     *
203
     * @param string|string[] $value
204
     */
205
    public function setHeader(string $name, $value)
206
    {
207
        $this->headers[strtolower($name)] = [$name, (array) $value];
208
    }
209
210
    /**
211
     * Sets a new set of HTTP headers.
212
     *
213
     * The headers array should contain headernames for keys, and their value
214
     * should be specified as either a string or an array.
215
     *
216
     * Any header that already existed will be overwritten.
217
     */
218
    public function setHeaders(array $headers)
219
    {
220
        foreach ($headers as $name => $value) {
221
            $this->setHeader($name, $value);
222
        }
223
    }
224
225
    /**
226
     * Adds a HTTP header.
227
     *
228
     * This method will not overwrite any existing HTTP header, but instead add
229
     * another value. Individual values can be retrieved with
230
     * getHeadersAsArray.
231
     *
232
     * @param string|string[] $value
233
     */
234
    public function addHeader(string $name, $value)
235
    {
236
        $lName = strtolower($name);
237
        if (isset($this->headers[$lName])) {
238
            $this->headers[$lName][1] = array_merge(
239
                $this->headers[$lName][1],
240
                (array) $value
241
            );
242
        } else {
243
            $this->headers[$lName] = [
244
                $name,
245
                (array) $value,
246
            ];
247
        }
248
    }
249
250
    /**
251
     * Adds a new set of HTTP headers.
252
     *
253
     * Any existing headers will not be overwritten.
254
     */
255
    public function addHeaders(array $headers)
256
    {
257
        foreach ($headers as $name => $value) {
258
            $this->addHeader($name, $value);
259
        }
260
    }
261
262
    /**
263
     * Removes a HTTP header.
264
     *
265
     * The specified header name must be treated as case-insensitive.
266
     * This method should return true if the header was successfully deleted,
267
     * and false if the header did not exist.
268
     */
269
    public function removeHeader(string $name): bool
270
    {
271
        $name = strtolower($name);
272
        if (!isset($this->headers[$name])) {
273
            return false;
274
        }
275
        unset($this->headers[$name]);
276
277
        return true;
278
    }
279
280
    /**
281
     * Sets the HTTP version.
282
     *
283
     * Should be 1.0, 1.1 or 2.0.
284
     */
285
    public function setHttpVersion(string $version)
286
    {
287
        $this->httpVersion = $version;
288
    }
289
290
    /**
291
     * Returns the HTTP version.
292
     */
293
    public function getHttpVersion(): string
294
    {
295
        return $this->httpVersion;
296
    }
297
}