Passed
Push — master ( 3a35da...a738f6 )
by Nikolaos
02:35
created

ServerRequestFactory   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 219
Duplicated Lines 0 %

Test Coverage

Coverage 98.44%

Importance

Changes 0
Metric Value
eloc 61
dl 0
loc 219
ccs 63
cts 64
cp 0.9844
rs 10
c 0
b 0
f 0
wmc 20

8 Methods

Rating   Name   Duplication   Size   Complexity  
A load() 0 29 3
A checkContentHeader() 0 6 2
A checkHttpHeader() 0 9 2
A parseHeaders() 0 26 5
A createServerRequest() 0 6 1
A parseServer() 0 17 4
A parseCookieHeader() 0 16 1
A getHeaders() 0 7 2
1
<?php
2
3
/**
4
 * This file is part of the Phalcon Framework.
5
 *
6
 * For the full copyright and license information, please view the LICENSE.md
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Phalcon\Http\Message;
13
14
use Phalcon\Collection;
15
use Phalcon\Helper\Arr;
16
use Phalcon\Http\Message\Traits\ServerRequestFactoryTrait;
17
use Psr\Http\Message\ServerRequestFactoryInterface;
18
use Psr\Http\Message\ServerRequestInterface;
19
use Psr\Http\Message\UriInterface;
20
21
use function apache_request_headers;
22
use function function_exists;
23
use function parse_str;
24
use function str_replace;
25
use function substr;
26
27
class ServerRequestFactory implements ServerRequestFactoryInterface
28
{
29
    use ServerRequestFactoryTrait;
30
31
    /**
32
     * Create a new server request.
33
     *
34
     * Note that server-params are taken precisely as given - no
35
     * parsing/processing of the given values is performed, and, in particular,
36
     * no attempt is made to determine the HTTP method or URI, which must be
37
     * provided explicitly.
38
     *
39
     * @param string              $method       The HTTP method associated with
40
     *                                          the request.
41
     * @param UriInterface|string $uri          The URI associated with the
42
     *                                          request. If the value is a
43
     *                                          string, the factory MUST create
44
     *                                          a UriInterface instance based
45
     *                                          on it.
46
     * @param array               $serverParams Array of SAPI parameters with
47
     *                                          which to seed the generated
48
     *                                          request instance.
49
     *
50
     * @return ServerRequestInterface
51
     */
52 1
    public function createServerRequest(
53
        string $method,
54
        $uri,
55
        array $serverParams = []
56
    ): ServerRequestInterface {
57 1
        return new ServerRequest($method, $uri, $serverParams);
58
    }
59
60
    /**
61
     * Create a request from the supplied superglobal values.
62
     *
63
     * If any argument is not supplied, the corresponding superglobal value will
64
     * be used.
65
     *
66
     * The ServerRequest created is then passed to the fromServer() method in
67
     * order to marshal the request URI and headers.
68
     *
69
     * @param array $server  $_SERVER superglobal
70
     * @param array $get     $_GET superglobal
71
     * @param array $post    $_POST superglobal
72
     * @param array $cookies $_COOKIE superglobal
73
     * @param array $files   $_FILES superglobal
74
     *
75
     * @return ServerRequest
76
     * @see fromServer()
77
     */
78 12
    public function load(
79
        array $server = [],
80
        array $get = [],
81
        array $post = [],
82
        array $cookies = [],
83
        array $files = []
84
    ): ServerRequest {
85 12
        $method   = Arr::get($server, 'REQUEST_METHOD', 'GET');
86 12
        $protocol = Arr::get($server, 'SERVER_PROTOCOL', '1.1');
87
88 12
        $server  = $this->parseServer($server);
89 12
        $headers = $this->parseHeaders($server);
90 12
        $files   = $this->parseUploadedFiles($files);
91
92 11
        if (empty($cookies) && $headers->has('cookie')) {
93 1
            $cookies = $this->parseCookieHeader($headers->get('cookie'));
94
        }
95
96 11
        return new ServerRequest(
97 11
            $method,
0 ignored issues
show
Bug introduced by
It seems like $method can also be of type null; however, parameter $method of Phalcon\Http\Message\ServerRequest::__construct() does only seem to accept string, 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

97
            /** @scrutinizer ignore-type */ $method,
Loading history...
98 11
            $this->parseUri($server, $headers),
99 11
            $server->toArray(),
100 11
            'php://input',
101 11
            $headers->toArray(),
102
            $cookies,
103
            $get,
104 11
            $files->toArray(),
105
            $post,
106
            $protocol
0 ignored issues
show
Bug introduced by
It seems like $protocol can also be of type null; however, parameter $protocol of Phalcon\Http\Message\ServerRequest::__construct() does only seem to accept string, 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

106
            /** @scrutinizer ignore-type */ $protocol
Loading history...
107
        );
108
    }
109
110
    /**
111
     * Returns the apache_request_headers if it exists
112
     *
113
     * @return array|false
114
     */
115 11
    protected function getHeaders()
116
    {
117 11
        if (function_exists('apache_request_headers')) {
118
            return apache_request_headers();
119
        }
120
121 11
        return false;
122
    }
123
124
    /**
125
     * Checks if a header starts with CONTENT_ and adds it to the collection
126
     *
127
     * @param string     $key
128
     * @param mixed      $value
129
     * @param Collection $headers
130
     */
131 9
    private function checkContentHeader(string $key, $value, Collection $headers): void
132
    {
133 9
        if (mb_strpos($key, 'CONTENT_') === 0) {
134 1
            $key  = (string) substr($key, 8);
135 1
            $name = 'content-' . mb_strtolower($key);
136 1
            $headers->set($name, $value);
137
        }
138 9
    }
139
140
    /**
141
     * Checks if a header starts with HTTP_ and adds it to the collection
142
     *
143
     * @param string     $key
144
     * @param mixed      $value
145
     * @param Collection $headers
146
     */
147 9
    private function checkHttpHeader(string $key, $value, Collection $headers): void
148
    {
149 9
        if (mb_strpos($key, 'HTTP_') === 0) {
150 5
            $name = str_replace(
151 5
                '_',
152 5
                '-',
153 5
                mb_strtolower(substr($key, 5))
154
            );
155 5
            $headers->set($name, $value);
156
        }
157 9
    }
158
159
    /**
160
     * Parse a cookie header according to RFC 6265.
161
     *
162
     * @param string $cookieHeader A string cookie header value.
163
     *
164
     * @return array key/value cookie pairs.
165
     *
166
     */
167 1
    private function parseCookieHeader($cookieHeader): array
168
    {
169 1
        $cookies = [];
170 1
        parse_str(
171 1
            strtr(
172 1
                $cookieHeader,
173
                [
174 1
                    '&' => '%26',
175
                    '+' => '%2B',
176
                    ';' => '&',
177
                ]
178
            ),
179 1
            $cookies
180
        );
181
182 1
        return $cookies;
183
    }
184
185
    /**
186
     * Processes headers from SAPI
187
     *
188
     * @param Collection $server
189
     *
190
     * @return Collection
191
     */
192 12
    private function parseHeaders(Collection $server): Collection
193
    {
194 12
        $headers = new Collection();
195 12
        foreach ($server as $key => $value) {
196 9
            if ('' !== $value) {
197
                /**
198
                 * Apache prefixes environment variables with REDIRECT_
199
                 * if they are added by rewrite rules
200
                 */
201 9
                if (mb_strpos($key, 'REDIRECT_') === 0) {
202 1
                    $key = (string) substr($key, 9);
203
                    /**
204
                     * We will not overwrite existing variables with the
205
                     * prefixed versions, though
206
                     */
207 1
                    if ($server->has($key)) {
208 1
                        continue;
209
                    }
210
                }
211
212 9
                $this->checkHttpHeader($key, $value, $headers);
213 9
                $this->checkContentHeader($key, $value, $headers);
214
            }
215
        }
216
217 12
        return $headers;
218
    }
219
220
    /**
221
     * Parse the $_SERVER array amd return it back after looking for the
222
     * authorization header
223
     *
224
     * @param array $server Either verbatim, or with an added
225
     *                      HTTP_AUTHORIZATION header.
226
     *
227
     * @return Collection
228
     */
229 12
    private function parseServer(array $server): Collection
230
    {
231 12
        $collection = new Collection($server);
232 12
        $headers    = $this->getHeaders();
233
234 12
        if (true !== $collection->has("HTTP_AUTHORIZATION") && false !== $headers) {
235 1
            $headersCollection = new Collection($headers);
236
237 1
            if ($headersCollection->has('Authorization')) {
238 1
                $collection->set(
239 1
                    'HTTP_AUTHORIZATION',
240 1
                    $headersCollection->get('Authorization')
241
                );
242
            }
243
        }
244
245 12
        return $collection;
246
    }
247
}
248