Http::prepareBaseUrl()   C
last analyzed

Complexity

Conditions 17
Paths 32

Size

Total Lines 53
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 59.8794

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 17
eloc 35
c 1
b 0
f 0
nc 32
nop 0
dl 0
loc 53
ccs 16
cts 34
cp 0.4706
crap 59.8794
rs 5.2166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Nip\Http\Request;
4
5
/**
6
 * Nip Framework
7
 *
8
 * @category   Nip
9
 * @copyright  2009 Nip Framework
10
 * @license    http://www.opensource.org/licenses/mit-license.php The MIT License
11
 * @version    SVN: $Id: Http.php 135 2009-05-27 16:48:23Z victor.stanciu $
12
 */
13
14
class Http
15
{
16
    protected $request;
17
18
    /**
19
     * @var string
20
     */
21
    protected $pathInfo;
22
23
    /**
24
     * @var string
25
     */
26
    protected $requestUri;
27
    /**
28
     * @var string
29
     */
30
    protected $baseUrl;
31
32
    /**
33
     * Normalizes a query string.
34
     *
35
     * It builds a normalized query string, where keys/value pairs are alphabetized,
36
     * have consistent escaping and unneeded delimiters are removed.
37
     *
38
     * @param string $qs Query string
39
     *
40
     * @return string A normalized query string for the Request
41
     */
42 1
    public static function normalizeQueryString($qs)
43
    {
44 1
        if ('' == $qs) {
45
            return '';
46
        }
47 1
        $parts = [];
48 1
        $order = [];
49 1
        foreach (explode('&', $qs) as $param) {
50 1
            if ('' === $param || '=' === $param[0]) {
51
                // Ignore useless delimiters, e.g. "x=y&".
52
                // Also ignore pairs with empty key, even if there was a value, e.g. "=value",
53
                // as such nameless values cannot be retrieved anyway.
54
                // PHP also does not include them when building _GET.
55
                continue;
56
            }
57 1
            $keyValuePair = explode('=', $param, 2);
58
            // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default
59
            // (as defined in enctype application/x-www-form-urlencoded).
60
            // PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str.
61
            // This is why we use urldecode and then normalize to
62
            // RFC 3986 with rawurlencode.
63 1
            $parts[] = isset($keyValuePair[1]) ?
64 1
                rawurlencode(urldecode($keyValuePair[0])) . '=' . rawurlencode(urldecode($keyValuePair[1])) : rawurlencode(urldecode($keyValuePair[0]));
65 1
            $order[] = urldecode($keyValuePair[0]);
66
        }
67 1
        array_multisort($order, SORT_ASC, $parts);
0 ignored issues
show
Bug introduced by
Nip\Http\Request\SORT_ASC cannot be passed to array_multisort() as the parameter $rest expects a reference. ( Ignorable by Annotation )

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

67
        array_multisort($order, /** @scrutinizer ignore-type */ SORT_ASC, $parts);
Loading history...
68
69 1
        return implode('&', $parts);
70
    }
71
72
    /**
73
     * Generates a normalized URI (URL) for the Request.
74
     *
75
     * @return string A normalized URI (URL) for the Request
76
     *
77
     * @see getQueryString()
78
     */
79 1
    public function getUri()
80
    {
81 1
        if (null !== $qs = $this->getQueryString()) {
82 1
            $qs = '?' . $qs;
83
        }
84 1
        return $this->getSchemeAndHttpHost() . $this->getBaseUrl() . $this->getRequest()->getPathInfo() . $qs;
85
    }
86
87
    /**
88
     * Generates the normalized query string for the Request.
89
     *
90
     * It builds a normalized query string, where keys/value pairs are alphabetized
91
     * and have consistent escaping.
92
     *
93
     * @return string|null A normalized query string for the Request
94
     */
95 1
    public function getQueryString()
96
    {
97 1
        $qs = static::normalizeQueryString($this->getRequest()->server->get('QUERY_STRING'));
98
99 1
        return '' === $qs ? null : $qs;
100
    }
101
102
    /**
103
     * @return mixed
104
     */
105 8
    public function getRequest()
106
    {
107 8
        return $this->request;
108
    }
109
110
    /*
111
     * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
112
     *
113
     * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
114
     *
115
     * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
116
     */
117
118
    /**
119
     * @param mixed $request
120
     */
121 8
    public function setRequest($request)
122
    {
123 8
        $this->request = $request;
124 8
    }
125
126
    /**
127
     * Gets the scheme and HTTP host.
128
     *
129
     * If the URL was called with basic authentication, the user
130
     * and the password are not added to the generated string.
131
     *
132
     * @return string The scheme and HTTP host
133
     */
134 8
    public function getSchemeAndHttpHost()
135
    {
136 8
        return $this->getScheme() . '://' . $this->getHttpHost();
137
    }
138
139
    /**
140
     * Gets the request's scheme.
141
     *
142
     * @return string
143
     */
144 8
    public function getScheme()
145
    {
146 8
        return $this->isSecure() ? 'https' : 'http';
147
    }
148
149
    /**
150
     * @return bool
151
     */
152 8
    public function isSecure()
153
    {
154 8
        $https = $this->getRequest()->server->get('HTTPS');
155
156 8
        return !empty($https) && 'off' !== strtolower($https);
157
    }
158
159
    /**
160
     * Returns the HTTP host being requested.
161
     *
162
     * The port name will be appended to the host if it's non-standard.
163
     *
164
     * @return string
165
     */
166 8
    public function getHttpHost()
167
    {
168 8
        $scheme = $this->getScheme();
169 8
        $port = $this->getPort();
170 8
        if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
171 8
            return $this->getHost();
172
        }
173
174
        return $this->getHost() . ':' . $port;
175
    }
176
177
    /**
178
     * @return mixed
179
     */
180 8
    public function getPort()
181
    {
182 8
        return $this->getRequest()->server->get('SERVER_PORT');
183
    }
184
185
    /**
186
     * Returns the host name.
187
     *
188
     * This method can read the client host name from the "X-Forwarded-Host" header
189
     * when trusted proxies were set via "setTrustedProxies()".
190
     *
191
     * The "X-Forwarded-Host" header must contain the client host name.
192
     *
193
     * If your reverse proxy uses a different header name than "X-Forwarded-Host",
194
     * configure it via "setTrustedHeaderName()" with the "client-host" key.
195
     *
196
     * @return string
197
     *
198
     * @throws \UnexpectedValueException when the host name is invalid
199
     */
200 8
    public function getHost()
201
    {
202 8
        if (!$host = $this->getRequest()->headers->get('HOST')) {
203
            if (!$host = $this->getRequest()->server->get('SERVER_NAME')) {
204
                $host = $this->getRequest()->server->get('SERVER_ADDR', '');
205
            }
206
        }
207
        // trim and remove port number from host
208
        // host is lowercase as per RFC 952/2181
209 8
        $host = strtolower(preg_replace('/:\d+$/', '', trim($host)));
210
211
        // as the host can come from the user (HTTP_HOST and depending on the configuration,
212
        // SERVER_NAME too can come from the user)
213
        // check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
214
        // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names
215 8
        if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) {
216
            throw new \UnexpectedValueException(sprintf('Invalid Host "%s"', $host));
217
        }
218
219 8
        return $host;
220
    }
221
222
    /**
223
     * Returns the root URL from which this request is executed.
224
     *
225
     * The base URL never ends with a /.
226
     *
227
     * This is similar to getBasePath(), except that it also includes the
228
     * script filename (e.g. index.php) if one exists.
229
     *
230
     * @return string The raw URL (i.e. not urldecoded)
231
     */
232 8
    public function getBaseUrl()
233
    {
234 8
        if (null === $this->baseUrl) {
235 8
            $this->baseUrl = $this->prepareBaseUrl();
236
        }
237 8
        return $this->baseUrl;
238
    }
239
240
    /**
241
     * Returns the requested URI (path and query string).
242
     *
243
     * @return string The raw URI (i.e. not URI decoded)
244
     */
245 8
    public function getRequestUri()
246
    {
247 8
        if (null === $this->requestUri) {
248 8
            $this->requestUri = $this->prepareRequestUri();
249
        }
250 8
        return $this->requestUri;
251
    }
252
253
    /**
254
     * @return bool|mixed
255
     */
256
    public function getSubdomain()
257
    {
258
        $name = $this->getServerName();
259
        if ($name) {
260
            if (substr_count($name, '.') > 1) {
261
                $parts = explode('.', $name);
262
                return reset($parts);
263
            }
264
        }
265
266
        return false;
267
    }
268
269
    /**
270
     * @return mixed
271
     */
272
    public function getServerName()
273
    {
274
        return $this->getRequest()->server->get('SERVER_NAME');
275
    }
276
277
    /**
278
     * @return bool|mixed|string
279
     */
280
    public function getRootDomain()
281
    {
282
        $name = $this->getServerName();
283
        if ($name) {
284
            if (substr_count($name, '.') > 1) {
285
                $parts = explode('.', $name);
286
                array_shift($parts);
287
                return implode('.', $parts);
288
            }
289
            return $name;
290
        }
291
292
        return false;
293
    }
294
295
    /*
296
     * Returns the prefix as encoded in the string when the string starts with
297
     * the given prefix, false otherwise.
298
     *
299
     * @param string $string The urlencoded string
300
     * @param string $prefix The prefix not encoded
301
     *
302
     * @return string|false The prefix as it is encoded in $string, or false
303
     */
304
305
    /**
306
     * @return bool
307
     */
308
    public function isConsole()
309
    {
310
        if (php_sapi_name() === 'cli') {
311
            return true;
312
        }
313
        if (php_sapi_name() === 'cgi-fcgi') {
314
            return true;
315
        }
316
317
        return false;
318
    }
319
320
    /**
321
     * Prepares the base URL.
322
     *
323
     * @return string
324
     */
325 8
    protected function prepareBaseUrl()
326
    {
327 8
        $filename = basename($this->getRequest()->server->get('SCRIPT_FILENAME'));
328 8
        if (basename($this->getRequest()->server->get('SCRIPT_NAME')) === $filename) {
329 8
            $baseUrl = $this->getRequest()->server->get('SCRIPT_NAME');
330
        } elseif (basename($this->getRequest()->server->get('PHP_SELF')) === $filename) {
331
            $baseUrl = $this->getRequest()->server->get('PHP_SELF');
332
        } elseif (basename($this->getRequest()->server->get('ORIG_SCRIPT_NAME')) === $filename) {
333
            $baseUrl = $this->getRequest()->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility
334
        } else {
335
            // Backtrack up the script_filename to find the portion matching
336
            // php_self
337
            $path = $this->getRequest()->server->get('PHP_SELF', '');
338
            $file = $this->getRequest()->server->get('SCRIPT_FILENAME', '');
339
            $segs = explode('/', trim($file, '/'));
340
            $segs = array_reverse($segs);
341
            $index = 0;
342
            $last = count($segs);
343
            $baseUrl = '';
344
            do {
345
                $seg = $segs[$index];
346
                $baseUrl = '/' . $seg . $baseUrl;
347
                ++$index;
348
            } while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos);
349
        }
350
        // Does the baseUrl have anything in common with the request_uri?
351 8
        $requestUri = $this->getRequestUri();
352 8
        if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
353
            // full $baseUrl matches
354 2
            return $prefix;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $prefix returns the type true which is incompatible with the documented return type string.
Loading history...
355
        }
356 6
        if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri,
357 6
                rtrim(dirname($baseUrl), '/' . DIRECTORY_SEPARATOR) . '/')
358
        ) {
359
            // directory portion of $baseUrl matches
360 5
            return rtrim($prefix, '/' . DIRECTORY_SEPARATOR);
0 ignored issues
show
Bug introduced by
$prefix of type true is incompatible with the type string expected by parameter $string of rtrim(). ( Ignorable by Annotation )

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

360
            return rtrim(/** @scrutinizer ignore-type */ $prefix, '/' . DIRECTORY_SEPARATOR);
Loading history...
361
        }
362 1
        $truncatedRequestUri = $requestUri;
363 1
        if (false !== $pos = strpos($requestUri, '?')) {
364 1
            $truncatedRequestUri = substr($requestUri, 0, $pos);
365
        }
366 1
        $basename = basename($baseUrl);
367 1
        if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
368
            // no match whatsoever; set it blank
369 1
            return '';
370
        }
371
        // If using mod_rewrite or ISAPI_Rewrite strip the script filename
372
        // out of baseUrl. $pos !== 0 makes sure it is not matching a value
373
        // from PATH_INFO or QUERY_STRING
374
        if (strlen($requestUri) >= strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && $pos !== 0) {
375
            $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
376
        }
377
        return rtrim($baseUrl, '/' . DIRECTORY_SEPARATOR);
378
    }
379
380
    /**
381
     * @return string
382
     */
383 8
    protected function prepareRequestUri()
384
    {
385 8
        $requestUri = '';
386 8
        if ($this->getRequest()->headers->has('X_ORIGINAL_URL')) {
387
            // IIS with Microsoft Rewrite Module
388
            $requestUri = $this->getRequest()->headers->get('X_ORIGINAL_URL');
389
            $this->getRequest()->headers->remove('X_ORIGINAL_URL');
390
            $this->getRequest()->server->remove('HTTP_X_ORIGINAL_URL');
391
            $this->getRequest()->server->remove('UNENCODED_URL');
392
            $this->getRequest()->server->remove('IIS_WasUrlRewritten');
393 8
        } elseif ($this->getRequest()->headers->has('X_REWRITE_URL')) {
394
            // IIS with ISAPI_Rewrite
395
            $requestUri = $this->getRequest()->headers->get('X_REWRITE_URL');
396
            $this->getRequest()->headers->remove('X_REWRITE_URL');
397 8
        } elseif ($this->getRequest()->server->get('IIS_WasUrlRewritten') == '1' && $this->getRequest()->server->get('UNENCODED_URL') != '') {
398
            // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem)
399
            $requestUri = $this->getRequest()->server->get('UNENCODED_URL');
400
            $this->getRequest()->server->remove('UNENCODED_URL');
401
            $this->getRequest()->server->remove('IIS_WasUrlRewritten');
402 8
        } elseif ($this->getRequest()->server->has('REQUEST_URI')) {
403 8
            $requestUri = $this->getRequest()->server->get('REQUEST_URI');
404
            // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path, only use URL path
405 8
            $schemeAndHttpHost = $this->getSchemeAndHttpHost();
406 8
            if (strpos($requestUri, $schemeAndHttpHost) === 0) {
407 8
                $requestUri = substr($requestUri, strlen($schemeAndHttpHost));
408
            }
409
        } elseif ($this->getRequest()->server->has('ORIG_PATH_INFO')) {
410
            // IIS 5.0, PHP as CGI
411
            $requestUri = $this->getRequest()->server->get('ORIG_PATH_INFO');
412
            if ('' != $this->getRequest()->server->get('QUERY_STRING')) {
413
                $requestUri .= '?' . $this->getRequest()->server->get('QUERY_STRING');
414
            }
415
            $this->getRequest()->server->remove('ORIG_PATH_INFO');
416
        }
417
        // normalize the request URI to ease creating sub-requests from this request
418 8
        $this->getRequest()->server->set('REQUEST_URI', $requestUri);
419 8
        return $requestUri;
420
    }
421
422
    /**
423
     * @param $string
424
     * @param $prefix
425
     * @return bool
426
     */
427 7
    private function getUrlencodedPrefix($string, $prefix)
428
    {
429 7
        if (0 !== strpos(rawurldecode($string), $prefix)) {
430 5
            return false;
431
        }
432 7
        $len = strlen($prefix);
433 7
        if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) {
434 7
            return $match[0];
435
        }
436
437
        return false;
438
    }
439
}
440