Completed
Push — master ( 337ea5...293404 )
by Harry
03:06
created

TrailingSlashControllerProvider::connect()   B

Complexity

Conditions 2
Paths 1

Size

Total Lines 43
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 2

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 43
ccs 25
cts 25
cp 1
rs 8.8571
cc 2
eloc 21
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Graze\Silex\ControllerProvider;
4
5
use Silex\Application;
6
use Silex\ControllerProviderInterface;
7
use Silex\ServiceProviderInterface;
8
use Symfony\Component\HttpFoundation\Request;
9
use Symfony\Component\HttpKernel\HttpKernelInterface;
10
use Symfony\Component\Routing\Matcher\UrlMatcher;
11
12
/**
13
 * A controller provider to convert requests missing a trailing slash into an
14
 * internal sub-request with a slash appended to the requests url.
15
 *
16
 * NOTE: You _should_ define all other routes with a trailing slash.
17
 *
18
 * Usage:
19
 *
20
 * ```
21
 * $provider = new \Graze\Silex\ControllerProvider\TrailingSlashControllerProvider();
22
 * $app->register($provider);
23
 * $app->mount('/', $provider);
24
 * ```
25
 */
26
final class TrailingSlashControllerProvider implements ControllerProviderInterface, ServiceProviderInterface
27
{
28
    /**
29
     * @param Application $app
30
     *
31
     * @return \Silex\ControllerCollection
32
     */
33 49
    public function connect(Application $app)
34
    {
35 49
        $controllers = $app['controllers_factory'];
36
37
        $handler = function ($resource) use ($app) {
38 39
            if ($app['logger']) {
39 1
                $app['logger']->debug(sprintf('Appending a trailing slash for the request to `/%s`.', $resource));
40 1
            }
41
42 39
            $request = Request::create(
43 39
                '/' . $resource . '/',
44 39
                $app['request']->getMethod(),
45 39
                [],
46 39
                $app['request']->cookies->all(),
47 39
                $app['request']->files->all(),
48 39
                $app['request']->server->all(),
49 39
                $app['request']->getContent()
50 39
            );
51 39
            $request = $request->duplicate(
52 39
                $app['request']->query->all(),
53 39
                $app['request']->request->all()
54 39
            );
55 39
            $request->headers->replace($app['request']->headers->all());
56
57
            // Make an internal sub-request based off the request that would have 404'd.
58
            // http://silex.sensiolabs.org/doc/usage.html#forwards
59 39
            return $app->handle($request, HttpKernelInterface::SUB_REQUEST);
60 49
        };
61
62
        /**
63
         * Register the catch-all route.
64
         *
65
         * Use a look behind assertion to ensure we only match routes
66
         * with no trailing slash.
67
         *
68
         * @link https://stackoverflow.com/questions/16398471/regex-not-ending-with
69
         */
70 49
        $controllers->match('/{resource}', $handler)
71 49
                    ->assert('resource', '.*(?<!\/)$')
72 49
                    ->bind('no_trailing_slash_handler');
73
74 49
        return $controllers;
75
    }
76
77
    /**
78
     * @param Application $app
79
     */
80 49
    public function register(Application $app)
81
    {
82
        /**
83
         * We override the default RedirectableUrlMatcher so that Silex doesn't
84
         * respond with 301 to GET requests missing a trailing slash.
85
         */
86 49
        $app['url_matcher'] = $app->share(
87 49
            function () use ($app) {
88 49
                if ($app['logger']) {
89 1
                    $app['logger']->debug(sprintf('Overriding the default Silex url matcher to %s.', UrlMatcher::class));
90 1
                }
91
92 49
                return new UrlMatcher($app['routes'], $app['request_context']);
93
            }
94 49
        );
95 49
    }
96
97
    /**
98
     * @param Application $app
99
     */
100 48
    public function boot(Application $app)
101
    {
102 48
    }
103
}
104