Completed
Pull Request — 5.6 (#2830)
by Jeroen
14:14
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 5
    public function __construct(ObjectRepository $redirectRepository, DomainConfigurationInterface $domainConfiguration)
30
    {
31 5
        $this->redirectRepository = $redirectRepository;
32 5
        $this->domainConfiguration = $domainConfiguration;
33 5
        $this->context = new RequestContext();
34 5
    }
35
36
    /**
37
     * Generates a URL or path for a specific route based on the given parameters.
38
     *
39
     * Parameters that reference placeholders in the route pattern will substitute them in the
40
     * path or host. Extra params are added as query string to the URL.
41
     *
42
     * When the passed reference type cannot be generated for the route because it requires a different
43
     * host or scheme than the current one, the method will return a more comprehensive reference
44
     * that includes the required params. For example, when you call this method with $referenceType = ABSOLUTE_PATH
45
     * but the route requires the https scheme whereas the current scheme is http, it will instead return an
46
     * ABSOLUTE_URL with the https scheme and the current host. This makes sure the generated URL matches
47
     * the route in any case.
48
     *
49
     * If there is no route with the given name, the generator must throw the RouteNotFoundException.
50
     *
51
     * @param string      $name          The name of the route
52
     * @param mixed       $parameters    An array of parameters
53
     * @param bool|string $referenceType The type of reference to be generated (one of the constants)
54
     *
55
     * @return string The generated URL
56
     *
57
     * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException              If the named route doesn't exist
58
     * @throws \Symfony\Component\Routing\Exception\MissingMandatoryParametersException When some parameters are missing that are mandatory for the route
59
     * @throws \Symfony\Component\Routing\Exception\InvalidParameterException           When a parameter value for a placeholder is not correct because
60
     *                                                                                  it does not match the requirement
61
     *
62
     * @api
63
     */
64 1
    public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH)
65
    {
66 1
        throw new RouteNotFoundException('You cannot generate a url from a redirect');
67
    }
68
69
    /**
70
     * Tries to match a URL path with a set of routes.
71
     *
72
     * If the matcher can not find information, it must throw one of the exceptions documented
73
     * below.
74
     *
75
     * @param string $pathinfo The path info to be parsed (raw format, i.e. not urldecoded)
76
     *
77
     * @return array An array of parameters
78
     *
79
     * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException If the resource could not be found
80
     * @throws \Symfony\Component\Routing\Exception\MethodNotAllowedException If the resource was found but the request method is not allowed
81
     *
82
     * @api
83
     */
84 1
    public function match($pathinfo)
85
    {
86 1
        $urlMatcher = new UrlMatcher($this->getRouteCollection(), $this->getContext());
87
88 1
        return $urlMatcher->match($pathinfo);
89
    }
90
91
    /**
92
     * Gets the RouteCollection instance associated with this Router.
93
     *
94
     * @return \Symfony\Component\Routing\RouteCollection A RouteCollection instance
95
     */
96 3
    public function getRouteCollection()
97
    {
98 3
        if (\is_null($this->routeCollection)) {
99 3
            $this->routeCollection = new RouteCollection();
100 3
            $this->initRoutes();
101
        }
102
103 3
        return $this->routeCollection;
104
    }
105
106 3
    private function initRoutes()
107
    {
108 3
        $redirects = $this->redirectRepository->findAll();
109 3
        $domain = $this->domainConfiguration->getHost();
110
111
        /** @var Redirect $redirect */
112 3
        foreach ($redirects as $redirect) {
113
            // Check for wildcard routing and adjust as required
114 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...
115
                $route = $this->createWildcardRoute($redirect);
116
            } else {
117 3
                $route = $this->createRoute($redirect);
118
            }
119
120
            // Only add the route when the domain matches or the domain is empty
121 3
            if ($redirect->getDomain() == $domain || !$redirect->getDomain()) {
122 3
                $this->routeCollection->add(
123 3
                    '_redirect_route_' . $redirect->getId(),
124
                    $route
125
                );
126
            }
127
        }
128 3
    }
129
130
    /**
131
     * @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...
132
     */
133 3
    private function isWildcardRedirect(Redirect $redirect)
134
    {
135 3
        $origin = $redirect->getOrigin();
136 3
        $matchSegment = substr($origin, 0, -1);
137 3
        if (substr($origin, -2) == '/*') {
138
            return $this->isPathInfoWildcardMatch($matchSegment);
139
        }
140
141 3
        return false;
142
    }
143
144
    private function isPathInfoWildcardMatch($matchSegment)
145
    {
146
        $path = $this->context->getPathInfo();
147
148
        return strstr($path, $matchSegment);
149
    }
150
151
    /**
152
     * @return Route
153
     */
154 3
    private function createRoute(Redirect $redirect)
155
    {
156 3
        $needsUtf8 = false;
157 3
        foreach ([$redirect->getOrigin(), $redirect->getTarget()] as $item) {
158 3
            if (preg_match('/[\x80-\xFF]/', $item)) {
159 3
                $needsUtf8 = true;
160
161 3
                break;
162
            }
163
        }
164
165 3
        return new Route(
166 3
            $redirect->getOrigin(), [
167 3
                '_controller' => 'FrameworkBundle:Redirect:urlRedirect',
168 3
                'path' => $redirect->getTarget(),
169 3
                'permanent' => $redirect->isPermanent(),
170 3
            ], [], ['utf8' => $needsUtf8]);
171
    }
172
173
    /**
174
     * @return Route
175
     */
176
    private function createWildcardRoute(Redirect $redirect)
177
    {
178
        $origin = $redirect->getOrigin();
179
        $target = $redirect->getTarget();
180
        $url = $this->context->getPathInfo();
181
        $needsUtf8 = preg_match('/[\x80-\xFF]/', $redirect->getTarget());
182
183
        $origin = substr($origin, 0, -1);
184
        $target = substr($target, 0, -1);
185
        $pathInfo = str_replace($origin, $target, $url);
186
187
        $this->context->setPathInfo($pathInfo);
188
189
        return new Route($url, [
190
            '_controller' => 'FrameworkBundle:Redirect:urlRedirect',
191
            'path' => $url,
192
            'permanent' => $redirect->isPermanent(),
193
        ], [], ['utf8' => $needsUtf8]);
194
    }
195
196
    /**
197
     * Gets the request context.
198
     *
199
     * @return \Symfony\Component\Routing\RequestContext The context
200
     *
201
     * @api
202
     */
203 2
    public function getContext()
204
    {
205 2
        return $this->context;
206
    }
207
208
    /**
209
     * Sets the request context.
210
     *
211
     * @param RequestContext $context The context
212
     *
213
     * @api
214
     */
215 1
    public function setContext(RequestContext $context)
216
    {
217 1
        $this->context = $context;
218 1
    }
219
}
220