Completed
Push — 5.0 ( 07a939...7af4be )
by Kristof
13:59 queued 13:53
created

RedirectRouter::createWildcardRoute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Kunstmaan\RedirectBundle\Router;
4
5
use Doctrine\Common\Persistence\ObjectRepository;
6
use Kunstmaan\AdminBundle\Helper\DomainConfigurationInterface;
7
use Kunstmaan\RedirectBundle\Entity\Redirect;
8
use Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher;
9
use Symfony\Component\Routing\Exception\RouteNotFoundException;
10
use Symfony\Component\Routing\RequestContext;
11
use Symfony\Component\Routing\Route;
12
use Symfony\Component\Routing\RouteCollection;
13
use Symfony\Component\Routing\RouterInterface;
14
15
class RedirectRouter implements RouterInterface
16
{
17
    /** @var RequestContext */
18
    private $context;
19
20
    /** @var RouteCollection */
21
    private $routeCollection = null;
22
23
    /** @var ObjectRepository */
24
    private $redirectRepository;
25
26
    /** @var DomainConfigurationInterface */
27
    private $domainConfiguration;
28
29
    /**
30
     * @param ObjectRepository             $redirectRepository
31
     * @param DomainConfigurationInterface $domainConfiguration
32
     */
33
    public function __construct(ObjectRepository $redirectRepository, DomainConfigurationInterface $domainConfiguration)
34
    {
35
        $this->redirectRepository = $redirectRepository;
36
        $this->domainConfiguration = $domainConfiguration;
37
        $this->context = new RequestContext();
38
    }
39
40
    /**
41
     * Generates a URL or path for a specific route based on the given parameters.
42
     *
43
     * Parameters that reference placeholders in the route pattern will substitute them in the
44
     * path or host. Extra params are added as query string to the URL.
45
     *
46
     * When the passed reference type cannot be generated for the route because it requires a different
47
     * host or scheme than the current one, the method will return a more comprehensive reference
48
     * that includes the required params. For example, when you call this method with $referenceType = ABSOLUTE_PATH
49
     * but the route requires the https scheme whereas the current scheme is http, it will instead return an
50
     * ABSOLUTE_URL with the https scheme and the current host. This makes sure the generated URL matches
51
     * the route in any case.
52
     *
53
     * If there is no route with the given name, the generator must throw the RouteNotFoundException.
54
     *
55
     * @param string      $name          The name of the route
56
     * @param mixed       $parameters    An array of parameters
57
     * @param bool|string $referenceType The type of reference to be generated (one of the constants)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $referenceType not be integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
58
     *
59
     * @return string The generated URL
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use NoType.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
60
     *
61
     * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException              If the named route doesn't exist
62
     * @throws \Symfony\Component\Routing\Exception\MissingMandatoryParametersException When some parameters are missing that are mandatory for the route
63
     * @throws \Symfony\Component\Routing\Exception\InvalidParameterException           When a parameter value for a placeholder is not correct because
64
     *                                                                                  it does not match the requirement
65
     *
66
     * @api
67
     */
68
    public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
69
    {
70
        throw new RouteNotFoundException('You cannot generate a url from a redirect');
71
    }
72
73
    /**
74
     * Tries to match a URL path with a set of routes.
75
     *
76
     * If the matcher can not find information, it must throw one of the exceptions documented
77
     * below.
78
     *
79
     * @param string $pathinfo The path info to be parsed (raw format, i.e. not urldecoded)
80
     *
81
     * @return array An array of parameters
82
     *
83
     * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException If the resource could not be found
84
     * @throws \Symfony\Component\Routing\Exception\MethodNotAllowedException If the resource was found but the request method is not allowed
85
     *
86
     * @api
87
     */
88
    public function match($pathinfo)
89
    {
90
        $urlMatcher = new RedirectableUrlMatcher($this->getRouteCollection(), $this->getContext());
91
        $result = $urlMatcher->match($pathinfo);
92
93
        return $result;
94
    }
95
96
    /**
97
     * Gets the RouteCollection instance associated with this Router.
98
     *
99
     * @return \Symfony\Component\Routing\RouteCollection A RouteCollection instance
100
     */
101
    public function getRouteCollection()
102
    {
103
        if (is_null($this->routeCollection)) {
104
            $this->routeCollection = new RouteCollection();
105
            $this->initRoutes();
106
        }
107
108
        return $this->routeCollection;
109
    }
110
111
    private function initRoutes()
112
    {
113
        $redirects = $this->redirectRepository->findAll();
114
        $domain = $this->domainConfiguration->getHost();
115
116
        /** @var Redirect $redirect */
117
        foreach ($redirects as $redirect) {
118
            // Check for wildcard routing and adjust as required
119
            if ($this->isWildcardRedirect($redirect)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->isWildcardRedirect($redirect) of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
120
                $route = $this->createWildcardRoute($redirect);
121
            } else {
122
                $route = $this->createRoute($redirect);
123
            }
124
125
            // Only add the route when the domain matches or the domain is empty
126
            if ($redirect->getDomain() == $domain || !$redirect->getDomain()) {
127
                $this->routeCollection->add(
128
                    '_redirect_route_' . $redirect->getId(),
129
                    $route
130
                );
131
            }
132
        }
133
    }
134
135
    /**
136
     * @param Redirect $redirect
137
     *
138
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be string|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
139
     */
140
    private function isWildcardRedirect(Redirect $redirect)
141
    {
142
        $origin = $redirect->getOrigin();
143
        $matchSegment = substr($origin, 0, -1);
144
        if (substr($origin, -2) == '/*') {
145
            return $this->isPathInfoWildcardMatch($matchSegment);
146
        }
147
148
        return false;
149
    }
150
151
    private function isPathInfoWildcardMatch($matchSegment)
152
    {
153
        $path = $this->context->getPathInfo();
154
155
        return strstr($path, $matchSegment);
156
    }
157
158
    /**
159
     * @param Redirect $redirect
160
     *
161
     * @return Route
162
     */
163
    private function createRoute(Redirect $redirect)
164
    {
165
        return new Route(
166
            $redirect->getOrigin(), array(
167
                    '_controller' => 'FrameworkBundle:Redirect:urlRedirect',
168
                    'path' => $redirect->getTarget(),
169
                    'permanent' => $redirect->isPermanent(),
170
                ));
171
    }
172
173
    /**
174
     * @param Redirect $redirect
175
     *
176
     * @return Route
177
     */
178
    private function createWildcardRoute(Redirect $redirect)
179
    {
180
        $origin = $redirect->getOrigin();
181
        $target = $redirect->getTarget();
182
        $url = $this->context->getPathInfo();
183
184
        $origin = substr($origin, 0, -1);
185
        $target = substr($target, 0, -1);
186
        $pathInfo = str_replace($origin, $target, $url);
187
188
        $this->context->setPathInfo($pathInfo);
189
190
        return new Route(
191
            $url, array(
192
                    '_controller' => 'FrameworkBundle:Redirect:urlRedirect',
193
                    'path' => $url,
194
                    'permanent' => $redirect->isPermanent(),
195
                ));
196
    }
197
198
    /**
199
     * Gets the request context.
200
     *
201
     * @return \Symfony\Component\Routing\RequestContext The context
202
     *
203
     * @api
204
     */
205
    public function getContext()
206
    {
207
        return $this->context;
208
    }
209
210
    /**
211
     * Sets the request context.
212
     *
213
     * @param RequestContext $context The context
214
     *
215
     * @api
216
     */
217
    public function setContext(RequestContext $context)
218
    {
219
        $this->context = $context;
220
    }
221
}
222