BasePath::getGenerator()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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