Completed
Push — master ( 30ef78...8f8c1f )
by Niels
16s queued 10s
created

CatchAllController::getUrlMatcherRouteCollection()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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