Completed
Push — master ( d41cf1...36fce9 )
by Javi
23:31
created

UriResolver   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 219
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 39
lcom 1
cbo 2
dl 0
loc 219
rs 8.2857
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
C uri() 0 31 7
A getBaseUri() 0 8 2
A setBaseUri() 0 6 1
A setBaseUriPath() 0 6 1
A getBaseUriPath() 0 8 2
A getServerParam() 0 6 2
C detectBaseUri() 0 66 19
A detectBaseUriPath() 0 18 4
1
<?php
2
3
namespace Philae\Utils;
4
5
use Psr\Http\Message\ServerRequestInterface;
6
use Psr\Http\Message\UriInterface;
7
8
/**
9
 * Base URL and base path resolver based on Zend Http Request
10
 * and compatible with PSR-7.
11
 *
12
 * @link https://github.com/zendframework/zend-http/blob/master/src/PhpEnvironment/Request.php
13
 *
14
 */
15
class UriResolver implements UriResolverInterface
16
{
17
    /**
18
     * @var ServerRequestInterface
19
     */
20
    protected $request;
21
22
    /**
23
     * @var string
24
     */
25
    protected $baseUri;
26
27
    /**
28
     * @var string
29
     */
30
    protected $baseUriPath;
31
32
    public function __construct(ServerRequestInterface $request)
33
    {
34
        $this->request = $request;
35
    }
36
37
    /**
38
     * Returns a new uri object, a clone of the current request one, with the given modifications.
39
     *
40
     * @param string $to If the path is absolute, the base url path won't be prepended,
41
     *   if it is a relative path it will be prepended.
42
     * @param array $query If null, the current query will be preserved
43
     * @return UriInterface
44
     */
45
    public function uri(string $to = null, array $query = null): UriInterface
46
    {
47
        $baseUrl = $this->getBaseUriPath();
48
        $uri     = clone $this->request->getUri();
49
50
        if (is_null($to) && is_null($query)) {
51
            return $uri;
52
        }
53
54
        if (strpos($to, '?') !== false) {
55
            $queryString = explode('?', $to, 2);
56
            $to          = $queryString[0];
57
            if (isset($queryString[1])) {
58
                parse_str($queryString[1], $queryString);
59
                $query = array_merge($queryString, (array)$query);
60
            }
61
        }
62
63
        if (strpos($to, '/') !== 0) {
64
            $baseUrl = rtrim($baseUrl, '/');
65
            $to      = rtrim($baseUrl . '/' . trim($to, '/'), '/');
66
        }
67
68
        $uri = $uri->withPath($to);
69
70
        if (!is_null($query)) {
71
            $uri = $uri->withQuery(http_build_query($query));
72
        }
73
74
        return $uri;
75
    }
76
77
    public function getBaseUri(): string
78
    {
79
        if ($this->baseUri === null) {
80
            $this->setBaseUri($this->detectBaseUri());
81
        }
82
83
        return $this->baseUri;
84
    }
85
86
    /**
87
     * @param string $baseUri
88
     * @return $this
89
     */
90
    public function setBaseUri(string $baseUri)
91
    {
92
        $this->baseUri = $baseUri;
93
94
        return $this;
95
    }
96
97
    /**
98
     * Set the base path.
99
     *
100
     * @param  string $baseUriPath
101
     * @return self
102
     */
103
    public function setBaseUriPath(string $baseUriPath)
104
    {
105
        $this->baseUriPath = rtrim($baseUriPath, '/');
106
107
        return $this;
108
    }
109
110
    public function getBaseUriPath(): string
111
    {
112
        if ($this->baseUriPath === null) {
113
            $this->setBaseUriPath($this->detectBaseUriPath($this->getBaseUri()));
114
        }
115
116
        return $this->baseUriPath;
117
    }
118
119
    /**
120
     * @param string $name
121
     * @param mixed $default
122
     * @return string|mixed
123
     */
124
    protected function getServerParam(string $name, $default = '')
125
    {
126
        $params = $this->request->getServerParams();
127
128
        return isset($params[$name]) ? $params[$name] : $default;
129
    }
130
131
    /**
132
     * Auto-detect the base path from the request environment
133
     *
134
     * Uses a variety of criteria in order to detect the base URL of the request
135
     * (i.e., anything additional to the document root).
136
     *
137
     *
138
     * @return string
139
     */
140
    protected function detectBaseUri(): string
141
    {
142
        $filename       = $this->getServerParam('SCRIPT_FILENAME', '');
143
        $scriptName     = $this->getServerParam('SCRIPT_NAME');
144
        $phpSelf        = $this->getServerParam('PHP_SELF');
145
        $origScriptName = $this->getServerParam('ORIG_SCRIPT_NAME');
146
147
        if ($scriptName !== null && basename($scriptName) === $filename) {
148
            $baseUri = $scriptName;
149
        } elseif ($phpSelf !== null && basename($phpSelf) === $filename) {
150
            $baseUri = $phpSelf;
151
        } elseif ($origScriptName !== null && basename($origScriptName) === $filename) {
152
            // 1and1 shared hosting compatibility.
153
            $baseUri = $origScriptName;
154
        } else {
155
            // Backtrack up the SCRIPT_FILENAME to find the portion
156
            // matching PHP_SELF.
157
            $baseUri  = '/';
158
            $basename = basename($filename);
159
            if ($basename) {
160
                $path    = ($phpSelf ? trim($phpSelf, '/') : '');
161
                $basePos = strpos($path, $basename) ?: 0;
162
                $baseUri .= substr($path, 0, $basePos) . $basename;
163
            }
164
        }
165
166
        // If the baseUri is empty, then simply return it.
167
        if (empty($baseUri)) {
168
            return '';
169
        }
170
171
        // Does the base URL have anything in common with the request URI?
172
        $requestUri = $this->request->getUri()->getPath();
173
174
        // Full base URL matches.
175
        if (0 === strpos($requestUri, $baseUri)) {
176
            return $baseUri;
177
        }
178
179
        // Directory portion of base path matches.
180
        $baseDir = str_replace('\\', '/', dirname($baseUri));
181
        if (0 === strpos($requestUri, $baseDir)) {
182
            return $baseDir;
183
        }
184
        $truncatedRequestUri = $requestUri;
185
        if (false !== ($pos = strpos($requestUri, '?'))) {
186
            $truncatedRequestUri = substr($requestUri, 0, $pos);
187
        }
188
        $basename = basename($baseUri);
189
190
        // No match whatsoever
191
        if (empty($basename) || false === strpos($truncatedRequestUri, $basename)) {
192
            return '';
193
        }
194
195
        // If using mod_rewrite or ISAPI_Rewrite strip the script filename
196
        // out of the base path. $pos !== 0 makes sure it is not matching a
197
        // value from PATH_INFO or QUERY_STRING.
198
        if (strlen($requestUri) >= strlen($baseUri)
199
            && (false !== ($pos = strpos($requestUri, $baseUri)) && $pos !== 0)
200
        ) {
201
            $baseUri = substr($requestUri, 0, $pos + strlen($baseUri));
202
        }
203
204
        return $baseUri;
205
    }
206
207
    /**
208
     * Autodetect the base path of the request, without the filename if any.
209
     *
210
     * Uses several criteria to determine the base path of the request.
211
     *
212
     * @param string|null $baseUri
213
     * @return string
214
     */
215
    protected function detectBaseUriPath(string $baseUri = null): string
216
    {
217
        $baseUri = is_null($baseUri) ? $this->getBaseUri() : $baseUri;
218
219
        // Empty base url detected
220
        if ($baseUri === '') {
221
            return '';
222
        }
223
        $filename = basename($this->getServerParam('SCRIPT_FILENAME', ''));
224
225
        // basename() matches the script filename; return the directory
226
        if (basename($baseUri) === $filename) {
227
            return str_replace('\\', '/', dirname($baseUri));
228
        }
229
230
        // Base path is identical to base URL
231
        return $baseUri;
232
    }
233
}
234