Completed
Push — 7.4 ( abf332 )
by Nikolaos
16:13
created

ServerRequestFactoryTrait::parseUploadedFiles()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 38
ccs 0
cts 21
cp 0
rs 8.6897
c 0
b 0
f 0
cc 6
nc 5
nop 1
crap 42
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
12
declare(strict_types=1);
13
14
namespace Phalcon\Http\Message\Traits;
15
16
use Phalcon\Collection;
17
use Phalcon\Helper\Arr;
18
use Phalcon\Http\Message\Exception\InvalidArgumentException;
19
use Phalcon\Http\Message\UploadedFile;
20
use Phalcon\Http\Message\Uri;
21
use Psr\Http\Message\UploadedFileInterface;
22
23
use function explode;
24
use function implode;
25
use function is_array;
26
use function ltrim;
27
use function preg_match;
28
use function preg_replace;
29
use function strlen;
30
use function substr;
31
32
/**
33
 * Trait ServerRequestFactoryTrait
34
 */
35
trait ServerRequestFactoryTrait
36
{
37
    /**
38
     * Calculates the host and port from the headers or the server superglobal
39
     *
40
     * @param Collection $server
41
     * @param Collection $headers
42
     *
43
     * @return array
44
     */
45
    private function calculateUriHost(Collection $server, Collection $headers): array
46
    {
47
        $return = ['', null];
48
49
        if (false !== $this->getHeader($headers, 'host', false)) {
50
            $host = $this->getHeader($headers, 'host');
51
            return $this->calculateUriHostFromHeader($host);
52
        }
53
54
        if (true !== $server->has('SERVER_NAME')) {
55
            return $return;
56
        }
57
58
        $host = $server->get('SERVER_NAME');
59
        $port = $server->get('SERVER_PORT', null);
60
61
        return [$host, $port];
62
    }
63
64
    /**
65
     * Get the host and calculate the port if present from the header
66
     *
67
     * @param string $host
68
     *
69
     * @return array
70
     */
71
    private function calculateUriHostFromHeader(string $host): array
72
    {
73
        $port = null;
74
75
        // works for regname, IPv4 & IPv6
76
        if (preg_match('|:(\d+)$|', $host, $matches)) {
77
            $host = substr($host, 0, -1 * (strlen($matches[1]) + 1));
78
            $port = (int) $matches[1];
79
        }
80
81
        return [$host, $port];
82
    }
83
84
    /**
85
     * Get the path from the request from IIS7/Rewrite, REQUEST_URL or
86
     * ORIG_PATH_INFO
87
     *
88
     * @param Collection $server
89
     *
90
     * @return string
91
     */
92
    private function calculateUriPath(Collection $server): string
93
    {
94
        /**
95
         * IIS7 with URL Rewrite - double slash
96
         */
97
        $iisRewrite   = $server->get('IIS_WasUrlRewritten', null);
98
        $unencodedUrl = $server->get('UNENCODED_URL', '');
99
100
        if ('1' === $iisRewrite && true !== empty($unencodedUrl)) {
101
            return $unencodedUrl;
102
        }
103
104
        /**
105
         * REQUEST_URI
106
         */
107
        $requestUri = $server->get('REQUEST_URI', null);
108
109
        if (null !== $requestUri) {
110
            $result = preg_replace('#^[^/:]+://[^/]+#', '', $requestUri);
111
112
            if (!is_string($result)) {
113
                $result = "";
114
            }
115
116
            return $result;
117
        }
118
119
        /**
120
         * ORIG_PATH_INFO
121
         */
122
        $origPathInfo = $server->get('ORIG_PATH_INFO', null);
123
        if (empty($origPathInfo)) {
124
            return '/';
125
        }
126
127
        return $origPathInfo;
128
    }
129
130
    /**
131
     * Get the query string from the server array
132
     *
133
     * @param Collection $server
134
     *
135
     * @return string
136
     */
137
    private function calculateUriQuery(Collection $server): string
138
    {
139
        return ltrim($server->get('QUERY_STRING', ''), '?');
140
    }
141
142
    /**
143
     * Calculates the scheme from the server variables
144
     *
145
     * @param Collection $server
146
     * @param Collection $headers
147
     *
148
     * @return string
149
     */
150
    private function calculateUriScheme(Collection $server, Collection $headers): string
151
    {
152
        // URI scheme
153
        $scheme  = 'https';
154
        $isHttps = true;
155
        if ($server->has('HTTPS')) {
156
            /** @var mixed $isHttps */
157
            $isHttps = (string) $server->get('HTTPS', 'on');
158
            $isHttps = 'off' !== mb_strtolower($isHttps);
159
        }
160
161
        $header = $this->getHeader($headers, 'x-forwarded-proto', 'https');
162
        if (true !== $isHttps || 'https' !== $header) {
163
            $scheme = 'http';
164
        }
165
166
        return $scheme;
167
    }
168
169
    /**
170
     * Create an UploadedFile object from an $_FILES array element
171
     *
172
     * @param array $file The $_FILES element
173
     *
174
     * @return UploadedFile
175
     *
176
     * @throws InvalidArgumentException If one of the elements is missing
177
     */
178
    private function createUploadedFile(array $file): UploadedFile
179
    {
180
        if (
181
            !isset($file['tmp_name']) ||
182
            !isset($file['size']) ||
183
            !isset($file['error'])
184
        ) {
185
            throw new InvalidArgumentException(
186
                'The file array must contain tmp_name, size and error; ' .
187
                'one or more are missing'
188
            );
189
        }
190
191
        return new UploadedFile(
192
            $file['tmp_name'],
193
            $file['size'],
194
            $file['error'],
195
            Arr::get($file, 'name'),
196
            Arr::get($file, 'type')
197
        );
198
    }
199
200
    /**
201
     * Returns a header
202
     *
203
     * @param Collection $headers
204
     * @param string     $name
205
     * @param mixed|null $defaultValue
206
     *
207
     * @return mixed|string
208
     */
209
    private function getHeader(Collection $headers, string $name, $defaultValue = null)
210
    {
211
        $value = $headers->get($name, $defaultValue);
212
213
        if (is_array($value)) {
214
            $value = implode(',', $value);
215
        }
216
217
        return $value;
218
    }
219
220
    /**
221
     * Traverses a $_FILES and creates UploadedFile objects from it. It is used
222
     * recursively
223
     *
224
     * @param array $files
225
     *
226
     * @return Collection
227
     */
228
    private function parseUploadedFiles(array $files): Collection
229
    {
230
        $collection = new Collection();
231
232
        /**
233
         * Loop through the files and check them recursively
234
         */
235
        foreach ($files as $key => $file) {
236
            $key = (string) $key;
237
238
            /**
239
             * UriInterface
240
             */
241
            if ($file instanceof UploadedFileInterface) {
242
                $collection->set($key, $file);
243
                continue;
244
            }
245
246
            /**
247
             * file is array with 'tmp_name'
248
             */
249
            if (is_array($file) && isset($file['tmp_name'])) {
250
                $collection->set($key, $this->createUploadedFile($file));
251
                continue;
252
            }
253
254
            /**
255
             * file is array of elements - recursion
256
             */
257
            if (is_array($file)) {
258
                $data = $this->parseUploadedFiles($file);
259
                $collection->set($key, $data->toArray());
260
                continue;
261
            }
262
        }
263
264
        return $collection;
265
    }
266
267
    /**
268
     * Calculates the Uri from the server superglobal or the headers
269
     *
270
     * @param Collection $server
271
     * @param Collection $headers
272
     *
273
     * @return Uri
274
     */
275
    private function parseUri(Collection $server, Collection $headers): Uri
276
    {
277
        $uri = new Uri('');
278
279
        /**
280
         * Scheme
281
         */
282
        $scheme = $this->calculateUriScheme($server, $headers);
283
        $uri    = $uri->withScheme($scheme);
284
285
        /**
286
         * Host/Port
287
         */
288
        [$host, $port] = $this->calculateUriHost($server, $headers);
0 ignored issues
show
Bug introduced by
The variable $host does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $port does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
289
        if (true !== empty($host)) {
290
            $uri = $uri->withHost($host);
291
            if (true !== empty($port)) {
292
                $uri = $uri->withPort($port);
293
            }
294
        }
295
296
        /**
297
         * Path
298
         */
299
        $path  = $this->calculateUriPath($server);
300
        $split = explode('#', $path);
301
        $path  = explode('?', $split[0]);
302
        $uri   = $uri->withPath($path[0]);
303
304
        if (count($split) > 1) {
305
            /**
306
             * Fragment
307
             */
308
            $uri = $uri->withFragment($split[1]);
309
        }
310
311
312
        /**
313
         * Query
314
         */
315
        $query = $this->calculateUriQuery($server);
316
        $uri   = $uri->withQuery($query);
317
318
        return $uri;
319
    }
320
}
321