CatchAllController::getUrlMatcherRouteCollection()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 12
rs 10
cc 3
nc 3
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the OpenapiBundle package.
7
 *
8
 * (c) Niels Nijens <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Nijens\OpenapiBundle\Controller;
15
16
use Symfony\Component\HttpFoundation\Request;
17
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
18
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
19
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
20
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
21
use Symfony\Component\Routing\Matcher\UrlMatcher;
22
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
23
use Symfony\Component\Routing\RouteCollection;
24
use Symfony\Component\Routing\RouterInterface;
25
26
/**
27
 * Handles exceptions for routes unavailable within OpenAPI specifications.
28
 *
29
 * @author Niels Nijens <[email protected]>
30
 */
31
class CatchAllController
32
{
33
    /**
34
     * The routing reference to this controller.
35
     *
36
     * @var string
37
     */
38
    public const CONTROLLER_REFERENCE = 'nijens_openapi.controller.catch_all';
39
40
    /**
41
     * @var RouterInterface
42
     */
43
    private $router;
44
45
    /**
46
     * Constructs a new CatchAllController instance.
47
     */
48
    public function __construct(RouterInterface $router)
49
    {
50
        $this->router = $router;
51
    }
52
53
    /**
54
     * Throws a NotFoundHttpException or MethodNotAllowedException when this controller action is reached.
55
     *
56
     * @throws NotFoundHttpException         when the route could not be found
57
     * @throws MethodNotAllowedHttpException when the route was found but the request method is not allowed
58
     */
59
    public function __invoke(Request $request): void
60
    {
61
        $exceptionMessage = sprintf("No route found for '%s %s'.", $request->getMethod(), $request->getPathInfo());
62
        $exception = new NotFoundHttpException($exceptionMessage);
63
64
        try {
65
            $urlMatcher = $this->createUrlMatcher();
66
            $urlMatcher->match($request->getPathInfo());
67
        } catch (ResourceNotFoundException $exception) {
68
            $exception = new NotFoundHttpException($exceptionMessage, $exception);
69
        } catch (MethodNotAllowedException $exception) {
70
            $exceptionMessage = sprintf(
71
                "No route found for '%s %s': Method Not Allowed (Allowed: %s).",
72
                $request->getMethod(),
73
                $request->getPathInfo(),
74
                implode(', ', $exception->getAllowedMethods())
75
            );
76
77
            $exception = new MethodNotAllowedHttpException(
78
                $exception->getAllowedMethods(),
79
                $exceptionMessage,
80
                $exception
81
            );
82
        }
83
84
        throw $exception;
85
    }
86
87
    /**
88
     * Returns a new URL matcher to match the request with existing API routes.
89
     */
90
    private function createUrlMatcher(): UrlMatcherInterface
91
    {
92
        return new UrlMatcher(
93
            $this->getUrlMatcherRouteCollection(),
94
            $this->router->getContext()
95
        );
96
    }
97
98
    /**
99
     * Returns a RouteCollection cloned from the router with the 'catch-all' route removed.
100
     */
101
    private function getUrlMatcherRouteCollection(): RouteCollection
102
    {
103
        $routeCollection = clone $this->router->getRouteCollection();
104
        foreach ($routeCollection as $routeName => $route) {
105
            if ($route->getDefault('_controller') !== self::CONTROLLER_REFERENCE) {
106
                continue;
107
            }
108
109
            $routeCollection->remove($routeName);
110
        }
111
112
        return $routeCollection;
113
    }
114
}
115