PathMiddleware   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 69
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 27
c 5
b 0
f 0
dl 0
loc 69
ccs 27
cts 27
cp 1
rs 10
wmc 16

3 Methods

Rating   Name   Duplication   Size   Complexity  
A resolveUri() 0 13 3
A __construct() 0 8 2
B process() 0 26 11
1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of Flight Routing.
5
 *
6
 * PHP version 8.0 and above required
7
 *
8
 * @author    Divine Niiquaye Ibok <[email protected]>
9
 * @copyright 2019 Divine Niiquaye Ibok (https://divinenii.com/)
10
 * @license   https://opensource.org/licenses/BSD-3-Clause License
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 */
15
16
namespace Flight\Routing\Middlewares;
17
18
use Flight\Routing\Router;
19
use Psr\Http\Message\{ResponseInterface, ServerRequestInterface, UriInterface};
20
use Psr\Http\Server\{MiddlewareInterface, RequestHandlerInterface};
21
22
/**
23
 * This middleware increases SEO (search engine optimization) by preventing duplication
24
 * of content at different URLs including resolving sub-directory paths.
25
 *
26
 * The response status code is 302 if the permanent parameter is false (default),
27
 * and 301 if the redirection is permanent on redirection. If keep request method
28
 * parameter is true, response code 307, and if permanent is true status code is 308.
29
 *
30
 * @author Divine Niiquaye Ibok <[email protected]>
31
 */
32
final class PathMiddleware implements MiddlewareInterface
33
{
34
    /**
35
     * Slashes supported on browser when used.
36
     */
37
    public const SUB_FOLDER = __CLASS__.'::subFolder';
38
39
    /** @var array<string,string> */
40
    private array $uriSuffixes = [];
41
42
    /**
43
     * @param bool              $permanent         Whether the redirection is permanent
44
     * @param bool              $keepRequestMethod Whether redirect action should keep HTTP request method
45
     * @param array<int,string> $uriSuffixes       List of slashes to re-route, defaults to ['/']
46
     */
47 51
    public function __construct(
48
        private bool $permanent = false,
49
        private bool $keepRequestMethod = false,
50
        array $uriSuffixes = []
51
    ) {
52 51
        $this->permanent = $permanent;
53 51
        $this->keepRequestMethod = $keepRequestMethod;
54 51
        $this->uriSuffixes = empty($uriSuffixes) ? ['/' => '/'] : \array_combine($uriSuffixes, $uriSuffixes);
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60 51
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
61
    {
62 51
        $requestPath = ($requestUri = self::resolveUri($request))->getPath(); // Determine right request uri path.
63 51
        $response = $handler->handle($request);
64
65 43
        if (!empty($route = $request->getAttribute(Router::class, []))) {
66 42
            $this->uriSuffixes['/'] ??= '/';
67 42
            $routeEndTail = $this->uriSuffixes[$route['path'][-1]] ?? null;
68 42
            $requestEndTail = $this->uriSuffixes[$requestPath[-1]] ?? null;
69
70 42
            if ($requestEndTail === $requestPath || $routeEndTail === $requestEndTail) {
71 25
                return $response;
72
            }
73
74
            // Resolve request tail end to avoid conflicts and infinite redirection looping ...
75 17
            if (null === $requestEndTail && null !== $routeEndTail) {
76 9
                $requestPath .= $routeEndTail;
77 8
            } elseif (null === $routeEndTail && $requestEndTail) {
78 8
                $requestPath = \substr($requestPath, 0, -1);
79
            }
80
81 17
            $statusCode = $this->keepRequestMethod ? ($this->permanent ? 308 : 307) : ($this->permanent ? 301 : 302);
82 17
            $response = $response->withHeader('Location', (string) $requestUri->withPath($requestPath))->withStatus($statusCode);
83
        }
84
85 18
        return $response;
86
    }
87
88 51
    public static function resolveUri(ServerRequestInterface &$request): UriInterface
89
    {
90 51
        $requestUri = $request->getUri();
91 51
        $pathInfo = $request->getServerParams()['PATH_INFO'] ?? '';
92
93
        // Checks if the project is in a sub-directory, expect PATH_INFO in $_SERVER.
94 51
        if ('' !== $pathInfo && $pathInfo !== $requestUri->getPath()) {
95 1
            $request = $request->withAttribute(self::SUB_FOLDER, \substr($requestUri->getPath(), 0, -\strlen($pathInfo)));
96
97 1
            return $requestUri->withPath($pathInfo);
98
        }
99
100 50
        return $requestUri;
101
    }
102
}
103