Completed
Pull Request — master (#9)
by Haralan
09:02
created

TrailingSlashControllerProvider::boot()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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