Completed
Push — 5.1 ( caeb2e...f14ddd )
by Kristof
70:09 queued 59:10
created

RedirectRouter   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 8
dl 0
loc 209
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A generate() 0 4 1
A match() 0 7 1
A getRouteCollection() 0 9 2
A initRoutes() 0 25 5
A isWildcardRedirect() 0 10 2
A isPathInfoWildcardMatch() 0 6 1
A createRoute() 0 9 1
A createWildcardRoute() 0 19 1
A getContext() 0 4 1
A setContext() 0 4 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
                $needsUtf8 = (preg_match('/[\x80-\xFF]/', $redirect->getTarget()));
0 ignored issues
show
Unused Code introduced by
$needsUtf8 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

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