Completed
Push — master ( b2e200...1332b8 )
by Oscar
04:48
created

BasePath::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 6
rs 9.4286
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
3
namespace Psr7Middlewares\Middleware;
4
5
use Psr7Middlewares\Utils;
6
use Psr7Middlewares\Middleware;
7
use Psr\Http\Message\ServerRequestInterface;
8
use Psr\Http\Message\ResponseInterface;
9
10
/**
11
 * Middleware to strip the path prefix.
12
 */
13
class BasePath
14
{
15
    const KEY = 'BASE_PATH';
16
17
    use Utils\BasePathTrait;
18
19
    /**
20
     * @var bool
21
     */
22
    private $autodetect = false;
23
24
    /**
25
     * Returns the basePath.
26
     *
27
     * @param ServerRequestInterface $request
28
     *
29
     * @return string|null
30
     */
31
    public static function getBasePath(ServerRequestInterface $request)
32
    {
33
        return Middleware::getAttribute($request, self::KEY);
34
    }
35
36
    /**
37
     * Constructor. Set the path prefix.
38
     *
39
     * @param string|null $basePath
40
     */
41
    public function __construct($basePath = null)
42
    {
43
        if ($basePath !== null) {
44
            $this->basePath($basePath);
45
        }
46
    }
47
48
    /**
49
     * Autodetect the basePath.
50
     *
51
     * @param bool $autodetect
52
     * 
53
     * @return self
54
     */
55
    public function autodetect($autodetect = true)
56
    {
57
        $this->autodetect = $autodetect;
58
59
        return $this;
60
    }
61
62
    /**
63
     * Execute the middleware.
64
     *
65
     * @param ServerRequestInterface $request
66
     * @param ResponseInterface      $response
67
     * @param callable               $next
68
     *
69
     * @return ResponseInterface
70
     */
71
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
72
    {
73
        if ($this->autodetect) {
74
            $this->basePath(self::detectBasePath($request));
75
        }
76
77
        $uri = $request->getUri();
78
        $path = $this->getPath($uri->getPath());
79
        $request = $request->withUri($uri->withPath($path));
80
81
        $request = Middleware::setAttribute($request, self::KEY, $this->basePath);
82
83
        return $next($request, $response);
84
    }
85
86
    /**
87
     * Auto-detect the base path from the request environment.
88
     *
89
     * Uses a variety of criteria in order to detect the base URL of the request
90
     * (i.e., anything additional to the document root).
91
     * 
92
     * This code has been adapted from the Zend implementation:
93
     * https://github.com/zendframework/zend-http/blob/master/src/PhpEnvironment/Request.php
94
     * 
95
     * @param ServerRequestInterface $request
96
     *
97
     * @return string
98
     */
99
    protected static function detectBasePath(ServerRequestInterface $request)
100
    {
101
        $server = $request->getServerParams();
102
103
        $filename = isset($server['SCRIPT_FILENAME']) ? $server['SCRIPT_FILENAME'] : '';
104
        $scriptName = isset($server['SCRIPT_NAME']) ? $server['SCRIPT_NAME'] : null;
105
        $phpSelf = isset($server['PHP_SELF']) ? $server['PHP_SELF'] : null;
106
        $origScriptName = isset($server['ORIG_SCRIPT_NAME']) ? $server['ORIG_SCRIPT_NAME'] : null;
107
108
        if ($scriptName !== null && basename($scriptName) === $filename) {
109
            $baseUrl = $scriptName;
110
        } elseif ($phpSelf !== null && basename($phpSelf) === $filename) {
111
            $baseUrl = $phpSelf;
112
        } elseif ($origScriptName !== null && basename($origScriptName) === $filename) {
113
            // 1and1 shared hosting compatibility.
114
            $baseUrl = $origScriptName;
115
        } else {
116
            // Backtrack up the SCRIPT_FILENAME to find the portion
117
            // matching PHP_SELF.
118
            $baseUrl = '/';
119
            $basename = basename($filename);
120
121
            if ($basename) {
122
                $path = ($phpSelf ? trim($phpSelf, '/') : '');
123
                $basePos = strpos($path, $basename) ?: 0;
124
                $baseUrl .= substr($path, 0, $basePos).$basename;
125
            }
126
        }
127
128
        // If the baseUrl is empty, then simply return it.
129
        if (empty($baseUrl)) {
130
            return '';
131
        }
132
133
        // Does the base URL have anything in common with the request URI?
134
        $requestUri = $request->getUri()->getPath();
135
136
        // Full base URL matches.
137
        if (0 === strpos($requestUri, $baseUrl)) {
138
            return $baseUrl;
139
        }
140
141
        // Directory portion of base path matches.
142
        $baseDir = str_replace('\\', '/', dirname($baseUrl));
143
144
        if (0 === strpos($requestUri, $baseDir)) {
145
            return $baseDir;
146
        }
147
148
        $truncatedRequestUri = $requestUri;
149
150
        if (false !== ($pos = strpos($requestUri, '?'))) {
151
            $truncatedRequestUri = substr($requestUri, 0, $pos);
152
        }
153
154
        $basename = basename($baseUrl);
155
156
        // No match whatsoever
157
        if (empty($basename) || false === strpos($truncatedRequestUri, $basename)) {
158
            return '';
159
        }
160
161
        // If using mod_rewrite or ISAPI_Rewrite strip the script filename
162
        // out of the base path. $pos !== 0 makes sure it is not matching a
163
        // value from PATH_INFO or QUERY_STRING.
164
        if (strlen($requestUri) >= strlen($baseUrl)
165
            && (false !== ($pos = strpos($requestUri, $baseUrl)) && $pos !== 0)
166
        ) {
167
            $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
168
        }
169
170
        return $baseUrl;
171
    }
172
}
173