Issues (5)

src/Url/Provider/SymfonyUrlProvider.php (2 issues)

Severity
1
<?php
2
3
namespace LAG\SmokerBundle\Url\Provider;
4
5
use Generator;
6
use LAG\SmokerBundle\Contracts\Url\Provider\UrlProviderInterface;
7
use LAG\SmokerBundle\Exception\Exception;
8
use LAG\SmokerBundle\Url\Collection\UrlCollection;
9
use LAG\SmokerBundle\Url\Requirements\Registry\RequirementsProviderRegistry;
10
use LAG\SmokerBundle\Url\Url;
11
use LAG\SmokerBundle\Url\UrlInfo;
12
use Symfony\Component\OptionsResolver\OptionsResolver;
13
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
14
use Symfony\Component\Routing\Exception\NoConfigurationException;
15
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
16
use Symfony\Component\Routing\Route;
17
use Symfony\Component\Routing\Router;
18
use Symfony\Component\Routing\RouterInterface;
19
20
class SymfonyUrlProvider implements UrlProviderInterface
21
{
22
    /**
23
     * @var RouterInterface
24
     */
25
    protected $router;
26
27
    /**
28
     * @var RequirementsProviderRegistry
29
     */
30
    protected $requirementsProviderRegistry;
31
32
    /**
33
     * @var array
34
     */
35
    protected $routes;
36
37
    /**
38
     * @var array
39
     */
40
    protected $mapping;
41
42
    /**
43
     * @var array
44
     */
45
    protected $routingConfiguration;
46
47
    /**
48
     * SymfonyRoutingProvider constructor.
49
     */
50 8
    public function __construct(
51
        array $routingConfiguration,
52
        array $routes,
53
        array $mapping,
54
        RouterInterface $router,
55
        RequirementsProviderRegistry $requirementsProviderRegistry
56
    ) {
57 8
        $this->router = $router;
58 8
        $this->requirementsProviderRegistry = $requirementsProviderRegistry;
59 8
        $this->routes = $routes;
60 8
        $this->mapping = $mapping;
61 8
        $this->routingConfiguration = $routingConfiguration;
62 8
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67 2
    public function getName(): string
68
    {
69 2
        return 'symfony';
70
    }
71
72
    /**
73
     * {@inheritdoc}
74
     */
75 4
    public function supports(string $url): bool
76
    {
77 4
        $urlParts = parse_url($url);
78
79 4
        if (!is_array($urlParts) || !key_exists('path', $urlParts)) {
0 ignored issues
show
The condition is_array($urlParts) is always true.
Loading history...
80
            return false;
81
        }
82 4
        $path = $urlParts['path'];
83
84
        try {
85 4
            $this->router->match($path);
86 3
        } catch (NoConfigurationException $exception) {
87 1
            return false;
88 2
        } catch (ResourceNotFoundException $exception) {
89 1
            return false;
90 1
        } catch (MethodNotAllowedException $exception) {
91 1
            return false;
92
        }
93
94 1
        return true;
95
    }
96
97
    /**
98
     * {@inheritdoc}
99
     */
100 1
    public function match(string $url): UrlInfo
101
    {
102 1
        $urlParts = parse_url($url);
103
104 1
        if (!is_array($urlParts) || !key_exists('path', $urlParts)) {
0 ignored issues
show
The condition is_array($urlParts) is always true.
Loading history...
105
            throw new Exception('Can not extract the path from the url "'.$url.'"');
106
        }
107 1
        $path = $urlParts['path'];
108 1
        $routingInfo = $this->router->match($path);
109
110 1
        $resolver = new OptionsResolver();
111
        $resolver
112 1
            ->setDefaults([
113 1
                'scheme' => '',
114
                'host' => '',
115
                'port' => 80,
116
                'path' => '',
117
                'query' => '',
118
                'fragment' => '',
119
            ])
120
        ;
121 1
        $urlParts = $resolver->resolve($urlParts);
122
123 1
        $urlInfo = new UrlInfo(
124 1
            $urlParts['scheme'],
125 1
            $urlParts['host'],
126 1
            (int) $urlParts['port'],
127 1
            $urlParts['path'],
128 1
            $urlParts['query'],
129 1
            $urlParts['fragment'],
130 1
            $routingInfo['_route'],
131
            $routingInfo
132
        );
133
134 1
        return $urlInfo;
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140 1
    public function configure(OptionsResolver $resolver): void
141
    {
142 1
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147 2
    public function getCollection(array $options = []): UrlCollection
148
    {
149 2
        $collection = new UrlCollection();
150 2
        $routes = $this->router->getRouteCollection()->all();
151 2
        $this->defineContext();
152
153 2
        foreach ($this->routes as $routeName => $routeOptions) {
154
            // The provided routes should be present in the Symfony routing
155 2
            if (!key_exists($routeName, $routes)) {
156 2
                continue;
157
            }
158 2
            $route = $routes[$routeName];
159
160
            // Two cases: if the route is dynamic, we should generate an url for each requirements provided by
161
            // requirements providers. If the route is static, one url is generated
162 2
            if ($this->hasRouteRequirements($route)) {
163 1
                $routeParametersCollection = $this->getRouteRequirements($routeName);
164
165 1
                foreach ($routeParametersCollection as $routeParameters) {
166 1
                    $urlParameters = [];
167
168 1
                    if (key_exists('_identifiers', $routeParameters)) {
169
                        $urlParameters['identifiers'] = $routeParameters['_identifiers'];
170
                        unset($routeParameters['_identifiers']);
171
                    }
172
                    // Use the absolute url parameters to preserve the route configuration, especially if an host is
173
                    // configured in the Symfony routing
174 1
                    $url = $this->router->generate($routeName, $routeParameters, Router::ABSOLUTE_URL);
175
176 1
                    $collection->add(new Url($url, $this->getName(), $urlParameters));
177
                }
178
            } else {
179 1
                $url = $this->router->generate($routeName, [], Router::ABSOLUTE_URL);
180 1
                $collection->add(new Url($url, $this->getName()));
181
            }
182
        }
183
184 2
        return $collection;
185
    }
186
187
    /**
188
     * Return a generator containing each set of parameters according to the providers.
189
     */
190 1
    protected function getRouteRequirements(string $routeName): Generator
191
    {
192 1
        foreach ($this->requirementsProviderRegistry->all() as $requirementsProvider) {
193 1
            if (!$requirementsProvider->supports($routeName)) {
194
                continue;
195
            }
196 1
            $requirements = $requirementsProvider->getRequirementsData($routeName);
197
198 1
            foreach ($requirements as $values) {
199 1
                $routeParameters = [];
200
201 1
                foreach ($values as $name => $value) {
202 1
                    $routeParameters[$name] = $value;
203
                }
204 1
                yield $routeParameters;
205
            }
206
        }
207 1
    }
208
209
    /**
210
     * Return true if the given route requires parameters to be generated.
211
     */
212 2
    protected function hasRouteRequirements(Route $route): bool
213
    {
214 2
        if (0 < count($route->getRequirements())) {
215 1
            return true;
216
        }
217
218 1
        if (preg_match('/\{(.*?)\}/', $route->getPath())) {
219
            return true;
220
        }
221
222 1
        return false;
223
    }
224
225 2
    protected function defineContext(): void
226
    {
227 2
        $context = $this->router->getContext();
228 2
        $context->setScheme($this->routingConfiguration['scheme']);
229 2
        $context->setHost($this->routingConfiguration['host']);
230 2
        $context->setBaseUrl($this->routingConfiguration['base_url']);
231
232 2
        if ('https' === $context->getScheme()) {
233
            $context->setHttpsPort($this->routingConfiguration['port']);
234
        } else {
235 2
            $context->setHttpPort($this->routingConfiguration['port']);
236
        }
237 2
    }
238
}
239