ServerRequestFactoryTrait::calculateUriPath()   A
last analyzed

Complexity

Conditions 6
Paths 5

Size

Total Lines 36
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6.972

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 14
nc 5
nop 1
dl 0
loc 36
ccs 14
cts 20
cp 0.7
crap 6.972
rs 9.2222
c 1
b 0
f 0
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 11
    private function calculateUriHost(Collection $server, Collection $headers): array
46
    {
47 11
        $return = ['', null];
48
49 11
        if (false !== $this->getHeader($headers, 'host', false)) {
50 3
            $host = $this->getHeader($headers, 'host');
51 3
            return $this->calculateUriHostFromHeader($host);
52
        }
53
54 8
        if (true !== $server->has('SERVER_NAME')) {
55 7
            return $return;
56
        }
57
58 1
        $host = $server->get('SERVER_NAME');
59 1
        $port = $server->get('SERVER_PORT', null);
60
61 1
        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 3
    private function calculateUriHostFromHeader(string $host): array
72
    {
73 3
        $port = null;
74
75
        // works for regname, IPv4 & IPv6
76 3
        if (preg_match('|:(\d+)$|', $host, $matches)) {
77 1
            $host = substr($host, 0, -1 * (strlen($matches[1]) + 1));
78 1
            $port = (int) $matches[1];
79
        }
80
81 3
        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 11
    private function calculateUriPath(Collection $server): string
93
    {
94
        /**
95
         * IIS7 with URL Rewrite - double slash
96
         */
97 11
        $iisRewrite   = $server->get('IIS_WasUrlRewritten', null);
98 11
        $unencodedUrl = $server->get('UNENCODED_URL', '');
99
100 11
        if ('1' === $iisRewrite && true !== empty($unencodedUrl)) {
101 1
            return $unencodedUrl;
102
        }
103
104
        /**
105
         * REQUEST_URI
106
         */
107 10
        $requestUri = $server->get('REQUEST_URI', null);
108
109 10
        if (null !== $requestUri) {
110 1
            $result = preg_replace('#^[^/:]+://[^/]+#', '', $requestUri);
111
112 1
            if (!is_string($result)) {
113
                $result = "";
114
            }
115
116 1
            return $result;
117
        }
118
119
        /**
120
         * ORIG_PATH_INFO
121
         */
122 9
        $origPathInfo = $server->get('ORIG_PATH_INFO', null);
123 9
        if (empty($origPathInfo)) {
124 8
            return '/';
125
        }
126
127 1
        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 11
    private function calculateUriQuery(Collection $server): string
138
    {
139 11
        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 11
    private function calculateUriScheme(Collection $server, Collection $headers): string
151
    {
152
        // URI scheme
153 11
        $scheme  = 'https';
154 11
        $isHttps = true;
155 11
        if ($server->has('HTTPS')) {
156
            /** @var mixed $isHttps */
157 1
            $isHttps = (string) $server->get('HTTPS', 'on');
158 1
            $isHttps = 'off' !== mb_strtolower($isHttps);
159
        }
160
161 11
        $header = $this->getHeader($headers, 'x-forwarded-proto', 'https');
162 11
        if (true !== $isHttps || 'https' !== $header) {
163 1
            $scheme = 'http';
164
        }
165
166 11
        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 2
    private function createUploadedFile(array $file): UploadedFile
179
    {
180
        if (
181 2
            !isset($file['tmp_name']) ||
182 2
            !isset($file['size']) ||
183 2
            !isset($file['error'])
184
        ) {
185 1
            throw new InvalidArgumentException(
186
                'The file array must contain tmp_name, size and error; ' .
187 1
                'one or more are missing'
188
            );
189
        }
190
191 1
        return new UploadedFile(
192 1
            $file['tmp_name'],
193 1
            $file['size'],
194 1
            $file['error'],
195 1
            Arr::get($file, 'name'),
196 1
            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 11
    private function getHeader(Collection $headers, string $name, $defaultValue = null)
210
    {
211 11
        $value = $headers->get($name, $defaultValue);
212
213 11
        if (is_array($value)) {
214 1
            $value = implode(',', $value);
215
        }
216
217 11
        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 12
    private function parseUploadedFiles(array $files): Collection
229
    {
230 12
        $collection = new Collection();
231
232
        /**
233
         * Loop through the files and check them recursively
234
         */
235 12
        foreach ($files as $key => $file) {
236 2
            $key = (string) $key;
237
238
            /**
239
             * UriInterface
240
             */
241 2
            if ($file instanceof UploadedFileInterface) {
242 1
                $collection->set($key, $file);
243 1
                continue;
244
            }
245
246
            /**
247
             * file is array with 'tmp_name'
248
             */
249 2
            if (is_array($file) && isset($file['tmp_name'])) {
250 2
                $collection->set($key, $this->createUploadedFile($file));
251 1
                continue;
252
            }
253
254
            /**
255
             * file is array of elements - recursion
256
             */
257 1
            if (is_array($file)) {
258 1
                $data = $this->parseUploadedFiles($file);
259 1
                $collection->set($key, $data->toArray());
260 1
                continue;
261
            }
262
        }
263
264 11
        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 11
    private function parseUri(Collection $server, Collection $headers): Uri
276
    {
277 11
        $uri = new Uri('');
278
279
        /**
280
         * Scheme
281
         */
282 11
        $scheme = $this->calculateUriScheme($server, $headers);
283 11
        $uri    = $uri->withScheme($scheme);
284
285
        /**
286
         * Host/Port
287
         */
288 11
        [$host, $port] = $this->calculateUriHost($server, $headers);
289 11
        if (true !== empty($host)) {
290 4
            $uri = $uri->withHost($host);
291 4
            if (true !== empty($port)) {
292 2
                $uri = $uri->withPort($port);
293
            }
294
        }
295
296
        /**
297
         * Path
298
         */
299 11
        $path  = $this->calculateUriPath($server);
300 11
        $split = explode('#', $path);
301 11
        $path  = explode('?', $split[0]);
302 11
        $uri   = $uri->withPath($path[0]);
303
304 11
        if (count($split) > 1) {
305
            /**
306
             * Fragment
307
             */
308 1
            $uri = $uri->withFragment($split[1]);
309
        }
310
311
312
        /**
313
         * Query
314
         */
315 11
        $query = $this->calculateUriQuery($server);
316 11
        $uri   = $uri->withQuery($query);
317
318 11
        return $uri;
319
    }
320
}
321