Issues (1)

src/Middleware/RouteMatchMiddleware.php (1 issue)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace HttpSoft\Router\Middleware;
6
7
use HttpSoft\Router\Route;
8
use HttpSoft\Router\RouteCollector;
9
use Psr\Http\Message\ResponseFactoryInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Http\Message\ServerRequestInterface;
12
use Psr\Http\Server\MiddlewareInterface;
13
use Psr\Http\Server\RequestHandlerInterface;
14
15
use function array_filter;
16
use function array_unique;
17
use function implode;
18
use function in_array;
19
use function is_string;
20
use function strtoupper;
21
22
final class RouteMatchMiddleware implements MiddlewareInterface
23
{
24
    /**
25
     * @var RouteCollector
26
     */
27
    private RouteCollector $router;
28
29
    /**
30
     * @var ResponseFactoryInterface
31
     */
32
    private ResponseFactoryInterface $responseFactory;
33
34
    /**
35
     * @var string[]
36
     */
37
    private array $allowedMethods = [];
38
39
    /**
40
     * @param RouteCollector $router
41
     * @param ResponseFactoryInterface $responseFactory
42
     * @param array|string[] $allowedMethods common allowed request methods for all routes.
43
     * @psalm-suppress MixedAssignment
44
     */
45 5
    public function __construct(
46
        RouteCollector $router,
47
        ResponseFactoryInterface $responseFactory,
48
        array $allowedMethods = ['HEAD']
49
    ) {
50 5
        $this->router = $router;
51 5
        $this->responseFactory = $responseFactory;
52
53 5
        foreach ($allowedMethods as $allowedMethod) {
54 5
            if (is_string($allowedMethod)) {
55 5
                $this->allowedMethods[] = strtoupper($allowedMethod);
56
            }
57
        }
58
    }
59
60
    /**
61
     * {@inheritDoc}
62
     */
63 5
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
64
    {
65 5
        if (!$route = $this->router->routes()->match($request, false)) {
66 1
            return $handler->handle($request);
67
        }
68
69 4
        if (!$this->isAllowedMethods($request->getMethod()) && !$route->isAllowedMethod($request->getMethod())) {
70 1
            return $this->getEmptyResponseWithAllowedMethods($route->getMethods());
71
        }
72
73 3
        foreach ($route->getMatchedParameters() as $name => $value) {
74 1
            $request = $request->withAttribute($name, $value);
75
        }
76
77 3
        return $handler->handle($request->withAttribute(Route::class, $route));
78
    }
79
80
    /**
81
     * @param string[] $methods
82
     * @return ResponseInterface
83
     */
84 1
    private function getEmptyResponseWithAllowedMethods(array $methods): ResponseInterface
85
    {
86 1
        foreach ($this->allowedMethods as $method) {
87 1
            $methods[] = $method;
88
        }
89
90 1
        $methods = implode(', ', array_unique(array_filter($methods)));
91 1
        return $this->responseFactory->createResponse(405)->withHeader('Allow', $methods);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->responseFa...ader('Allow', $methods) returns the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface.
Loading history...
92
    }
93
94
    /**
95
     * @param string $method
96
     * @return bool
97
     */
98 4
    private function isAllowedMethods(string $method): bool
99
    {
100 4
        return ($this->allowedMethods !== [] && in_array(strtoupper($method), $this->allowedMethods, true));
101
    }
102
}
103