Passed
Push — master ( bf6fa9...817f88 )
by Nikolaos
06:58
created

ServerRequestFactory::parseUploadedFiles()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 38
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 38
ccs 0
cts 0
cp 0
rs 8.8333
c 0
b 0
f 0
cc 7
nc 5
nop 1
crap 56
1
<?php
2
3
/**
4
 * This file is part of the Phalcon Framework.
5
 *
6
 * (c) Phalcon Team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE.txt
9
 * file that was distributed with this source code.
10
 *
11
 * Implementation of this file has been influenced by Zend Diactoros
12
 *
13
 * @link    https://github.com/zendframework/zend-diactoros
14
 * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md
15
 */
16
17
declare(strict_types=1);
18
19
namespace Phalcon\Http\Message;
20
21
use Phalcon\Collection;
22
use Phalcon\Helper\Arr;
23
use Phalcon\Http\Message\Traits\ServerRequestFactoryTrait;
24
use Psr\Http\Message\ServerRequestFactoryInterface;
25
use Psr\Http\Message\ServerRequestInterface;
26
use Psr\Http\Message\UriInterface;
27
28
use function apache_request_headers;
29
use function function_exists;
30
use function parse_str;
31
use function str_replace;
32
use function substr;
33
34
class ServerRequestFactory implements ServerRequestFactoryInterface
35
{
36
    use ServerRequestFactoryTrait;
37
38
    /**
39
     * Create a new server request.
40
     *
41
     * Note that server-params are taken precisely as given - no
42
     * parsing/processing of the given values is performed, and, in particular,
43
     * no attempt is made to determine the HTTP method or URI, which must be
44
     * provided explicitly.
45
     *
46
     * @param string              $method       The HTTP method associated with
47
     *                                          the request.
48
     * @param UriInterface|string $uri          The URI associated with the
49
     *                                          request. If the value is a
50
     *                                          string, the factory MUST create
51
     *                                          a UriInterface instance based
52
     *                                          on it.
53
     * @param array               $serverParams Array of SAPI parameters with
54
     *                                          which to seed the generated
55
     *                                          request instance.
56
     *
57
     * @return ServerRequestInterface
58
     */
59
    public function createServerRequest(
60
        string $method,
61
        $uri,
62
        array $serverParams = []
63
    ): ServerRequestInterface {
64
        return new ServerRequest($method, $uri, $serverParams);
65
    }
66
67
    /**
68
     * Create a request from the supplied superglobal values.
69
     *
70
     * If any argument is not supplied, the corresponding superglobal value will
71
     * be used.
72
     *
73
     * The ServerRequest created is then passed to the fromServer() method in
74
     * order to marshal the request URI and headers.
75
     *
76
     * @param array $server  $_SERVER superglobal
77
     * @param array $get     $_GET superglobal
78
     * @param array $post    $_POST superglobal
79
     * @param array $cookies $_COOKIE superglobal
80
     * @param array $files   $_FILES superglobal
81
     *
82
     * @return ServerRequest
83
     * @see fromServer()
84
     */
85
    public function load(
86
        array $server = [],
87
        array $get = [],
88
        array $post = [],
89
        array $cookies = [],
90
        array $files = []
91
    ): ServerRequest {
92
        $method   = Arr::get($server, 'REQUEST_METHOD', 'GET');
93
        $protocol = Arr::get($server, 'SERVER_PROTOCOL', '1.1');
94
95
        $server  = $this->parseServer($server);
96
        $headers = $this->parseHeaders($server);
97
        $files   = $this->parseUploadedFiles($files);
98
99
        if (empty($cookies) && $headers->has('cookie')) {
100
            $cookies = $this->parseCookieHeader($headers->get('cookie'));
101
        }
102
103
        return new ServerRequest(
104
            $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

104
            /** @scrutinizer ignore-type */ $method,
Loading history...
105
            $this->parseUri($server, $headers),
106
            $server->toArray(),
107
            'php://input',
108
            $headers->toArray(),
109
            $cookies,
110
            $get,
111
            $files->toArray(),
112
            $post,
113
            $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

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