Completed
Push — master ( 6d6774...64f3ed )
by Jeroen
11:23 queued 05:13
created

Kunstmaan/RedirectBundle/Router/RedirectRouter.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\Component\Routing\Exception\RouteNotFoundException;
9
use Symfony\Component\Routing\Matcher\UrlMatcher;
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;
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 5
    public function __construct(ObjectRepository $redirectRepository, DomainConfigurationInterface $domainConfiguration)
34
    {
35 5
        $this->redirectRepository = $redirectRepository;
36 5
        $this->domainConfiguration = $domainConfiguration;
37 5
        $this->context = new RequestContext();
38 5
    }
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)
58
     *
59
     * @return string The generated URL
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 1
    public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
69
    {
70 1
        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 1
    public function match($pathinfo)
89
    {
90 1
        $urlMatcher = new UrlMatcher($this->getRouteCollection(), $this->getContext());
91
92 1
        return $urlMatcher->match($pathinfo);
93
    }
94
95
    /**
96
     * Gets the RouteCollection instance associated with this Router.
97
     *
98
     * @return \Symfony\Component\Routing\RouteCollection A RouteCollection instance
99
     */
100 3
    public function getRouteCollection()
101
    {
102 3
        if (\is_null($this->routeCollection)) {
103 3
            $this->routeCollection = new RouteCollection();
104 3
            $this->initRoutes();
105
        }
106
107 3
        return $this->routeCollection;
108
    }
109
110 3
    private function initRoutes()
111
    {
112 3
        $redirects = $this->redirectRepository->findAll();
113 3
        $domain = $this->domainConfiguration->getHost();
114
115
        /** @var Redirect $redirect */
116 3
        foreach ($redirects as $redirect) {
117
            // Check for wildcard routing and adjust as required
118 3
            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...
119
                $route = $this->createWildcardRoute($redirect);
120
            } else {
121 3
                $route = $this->createRoute($redirect);
122
            }
123
124
            // Only add the route when the domain matches or the domain is empty
125 3
            if ($redirect->getDomain() == $domain || !$redirect->getDomain()) {
126 3
                $this->routeCollection->add(
127 3
                    '_redirect_route_' . $redirect->getId(),
128
                    $route
129
                );
130
            }
131
        }
132 3
    }
133
134
    /**
135
     * @param Redirect $redirect
136
     *
137
     * @return bool
0 ignored issues
show
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...
138
     */
139 3
    private function isWildcardRedirect(Redirect $redirect)
140
    {
141 3
        $origin = $redirect->getOrigin();
142 3
        $matchSegment = substr($origin, 0, -1);
143 3
        if (substr($origin, -2) == '/*') {
144
            return $this->isPathInfoWildcardMatch($matchSegment);
145
        }
146
147 3
        return false;
148
    }
149
150
    private function isPathInfoWildcardMatch($matchSegment)
151
    {
152
        $path = $this->context->getPathInfo();
153
154
        return strstr($path, $matchSegment);
155
    }
156
157
    /**
158
     * @param Redirect $redirect
159
     *
160
     * @return Route
161
     */
162 3
    private function createRoute(Redirect $redirect)
163
    {
164 3
        $needsUtf8 = false;
165 3
        foreach ([$redirect->getOrigin(), $redirect->getTarget()] as $item) {
166 3
            if (preg_match('/[\x80-\xFF]/', $item)) {
167 3
                $needsUtf8 = true;
168
169 3
                break;
170
            }
171
        }
172
173 3
        return new Route(
174 3
            $redirect->getOrigin(), [
175 3
                '_controller' => 'FrameworkBundle:Redirect:urlRedirect',
176 3
                'path' => $redirect->getTarget(),
177 3
                'permanent' => $redirect->isPermanent(),
178 3
            ], [], ['utf8' => $needsUtf8]);
179
    }
180
181
    /**
182
     * @param Redirect $redirect
183
     *
184
     * @return Route
185
     */
186
    private function createWildcardRoute(Redirect $redirect)
187
    {
188
        $origin = $redirect->getOrigin();
189
        $target = $redirect->getTarget();
190
        $url = $this->context->getPathInfo();
191
        $needsUtf8 = preg_match('/[\x80-\xFF]/', $redirect->getTarget());
192
193
        $origin = substr($origin, 0, -1);
194
        $target = substr($target, 0, -1);
195
        $pathInfo = str_replace($origin, $target, $url);
196
197
        $this->context->setPathInfo($pathInfo);
198
199
        return new Route($url, [
200
            '_controller' => 'FrameworkBundle:Redirect:urlRedirect',
201
            'path' => $url,
202
            'permanent' => $redirect->isPermanent(),
203
        ], [], ['utf8' => $needsUtf8]);
204
    }
205
206
    /**
207
     * Gets the request context.
208
     *
209
     * @return \Symfony\Component\Routing\RequestContext The context
210
     *
211
     * @api
212
     */
213 2
    public function getContext()
214
    {
215 2
        return $this->context;
216
    }
217
218
    /**
219
     * Sets the request context.
220
     *
221
     * @param RequestContext $context The context
222
     *
223
     * @api
224
     */
225 1
    public function setContext(RequestContext $context)
226
    {
227 1
        $this->context = $context;
228 1
    }
229
}
230