Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Symfony\Component\Routing;
|
Chris@0
|
13
|
Chris@0
|
14 use Symfony\Component\Config\Exception\FileLoaderLoadException;
|
Chris@0
|
15 use Symfony\Component\Config\Loader\LoaderInterface;
|
Chris@0
|
16 use Symfony\Component\Config\Resource\ResourceInterface;
|
Chris@0
|
17
|
Chris@0
|
18 /**
|
Chris@0
|
19 * Helps add and import routes into a RouteCollection.
|
Chris@0
|
20 *
|
Chris@0
|
21 * @author Ryan Weaver <ryan@knpuniversity.com>
|
Chris@0
|
22 */
|
Chris@0
|
23 class RouteCollectionBuilder
|
Chris@0
|
24 {
|
Chris@0
|
25 /**
|
Chris@0
|
26 * @var Route[]|RouteCollectionBuilder[]
|
Chris@0
|
27 */
|
Chris@0
|
28 private $routes = array();
|
Chris@0
|
29
|
Chris@0
|
30 private $loader;
|
Chris@0
|
31 private $defaults = array();
|
Chris@0
|
32 private $prefix;
|
Chris@0
|
33 private $host;
|
Chris@0
|
34 private $condition;
|
Chris@0
|
35 private $requirements = array();
|
Chris@0
|
36 private $options = array();
|
Chris@0
|
37 private $schemes;
|
Chris@0
|
38 private $methods;
|
Chris@0
|
39 private $resources = array();
|
Chris@0
|
40
|
Chris@0
|
41 /**
|
Chris@0
|
42 * @param LoaderInterface $loader
|
Chris@0
|
43 */
|
Chris@0
|
44 public function __construct(LoaderInterface $loader = null)
|
Chris@0
|
45 {
|
Chris@0
|
46 $this->loader = $loader;
|
Chris@0
|
47 }
|
Chris@0
|
48
|
Chris@0
|
49 /**
|
Chris@0
|
50 * Import an external routing resource and returns the RouteCollectionBuilder.
|
Chris@0
|
51 *
|
Chris@0
|
52 * $routes->import('blog.yml', '/blog');
|
Chris@0
|
53 *
|
Chris@0
|
54 * @param mixed $resource
|
Chris@0
|
55 * @param string|null $prefix
|
Chris@0
|
56 * @param string $type
|
Chris@0
|
57 *
|
Chris@0
|
58 * @return self
|
Chris@0
|
59 *
|
Chris@0
|
60 * @throws FileLoaderLoadException
|
Chris@0
|
61 */
|
Chris@0
|
62 public function import($resource, $prefix = '/', $type = null)
|
Chris@0
|
63 {
|
Chris@0
|
64 /** @var RouteCollection $collection */
|
Chris@0
|
65 $collection = $this->load($resource, $type);
|
Chris@0
|
66
|
Chris@0
|
67 // create a builder from the RouteCollection
|
Chris@0
|
68 $builder = $this->createBuilder();
|
Chris@0
|
69 foreach ($collection->all() as $name => $route) {
|
Chris@0
|
70 $builder->addRoute($route, $name);
|
Chris@0
|
71 }
|
Chris@0
|
72
|
Chris@0
|
73 foreach ($collection->getResources() as $resource) {
|
Chris@0
|
74 $builder->addResource($resource);
|
Chris@0
|
75 }
|
Chris@0
|
76
|
Chris@0
|
77 // mount into this builder
|
Chris@0
|
78 $this->mount($prefix, $builder);
|
Chris@0
|
79
|
Chris@0
|
80 return $builder;
|
Chris@0
|
81 }
|
Chris@0
|
82
|
Chris@0
|
83 /**
|
Chris@0
|
84 * Adds a route and returns it for future modification.
|
Chris@0
|
85 *
|
Chris@0
|
86 * @param string $path The route path
|
Chris@0
|
87 * @param string $controller The route's controller
|
Chris@0
|
88 * @param string|null $name The name to give this route
|
Chris@0
|
89 *
|
Chris@0
|
90 * @return Route
|
Chris@0
|
91 */
|
Chris@0
|
92 public function add($path, $controller, $name = null)
|
Chris@0
|
93 {
|
Chris@0
|
94 $route = new Route($path);
|
Chris@0
|
95 $route->setDefault('_controller', $controller);
|
Chris@0
|
96 $this->addRoute($route, $name);
|
Chris@0
|
97
|
Chris@0
|
98 return $route;
|
Chris@0
|
99 }
|
Chris@0
|
100
|
Chris@0
|
101 /**
|
Chris@0
|
102 * Returns a RouteCollectionBuilder that can be configured and then added with mount().
|
Chris@0
|
103 *
|
Chris@0
|
104 * @return self
|
Chris@0
|
105 */
|
Chris@0
|
106 public function createBuilder()
|
Chris@0
|
107 {
|
Chris@0
|
108 return new self($this->loader);
|
Chris@0
|
109 }
|
Chris@0
|
110
|
Chris@0
|
111 /**
|
Chris@0
|
112 * Add a RouteCollectionBuilder.
|
Chris@0
|
113 *
|
Chris@0
|
114 * @param string $prefix
|
Chris@0
|
115 * @param RouteCollectionBuilder $builder
|
Chris@0
|
116 */
|
Chris@0
|
117 public function mount($prefix, RouteCollectionBuilder $builder)
|
Chris@0
|
118 {
|
Chris@0
|
119 $builder->prefix = trim(trim($prefix), '/');
|
Chris@0
|
120 $this->routes[] = $builder;
|
Chris@0
|
121 }
|
Chris@0
|
122
|
Chris@0
|
123 /**
|
Chris@0
|
124 * Adds a Route object to the builder.
|
Chris@0
|
125 *
|
Chris@0
|
126 * @param Route $route
|
Chris@0
|
127 * @param string|null $name
|
Chris@0
|
128 *
|
Chris@0
|
129 * @return $this
|
Chris@0
|
130 */
|
Chris@0
|
131 public function addRoute(Route $route, $name = null)
|
Chris@0
|
132 {
|
Chris@0
|
133 if (null === $name) {
|
Chris@0
|
134 // used as a flag to know which routes will need a name later
|
Chris@0
|
135 $name = '_unnamed_route_'.spl_object_hash($route);
|
Chris@0
|
136 }
|
Chris@0
|
137
|
Chris@0
|
138 $this->routes[$name] = $route;
|
Chris@0
|
139
|
Chris@0
|
140 return $this;
|
Chris@0
|
141 }
|
Chris@0
|
142
|
Chris@0
|
143 /**
|
Chris@0
|
144 * Sets the host on all embedded routes (unless already set).
|
Chris@0
|
145 *
|
Chris@0
|
146 * @param string $pattern
|
Chris@0
|
147 *
|
Chris@0
|
148 * @return $this
|
Chris@0
|
149 */
|
Chris@0
|
150 public function setHost($pattern)
|
Chris@0
|
151 {
|
Chris@0
|
152 $this->host = $pattern;
|
Chris@0
|
153
|
Chris@0
|
154 return $this;
|
Chris@0
|
155 }
|
Chris@0
|
156
|
Chris@0
|
157 /**
|
Chris@0
|
158 * Sets a condition on all embedded routes (unless already set).
|
Chris@0
|
159 *
|
Chris@0
|
160 * @param string $condition
|
Chris@0
|
161 *
|
Chris@0
|
162 * @return $this
|
Chris@0
|
163 */
|
Chris@0
|
164 public function setCondition($condition)
|
Chris@0
|
165 {
|
Chris@0
|
166 $this->condition = $condition;
|
Chris@0
|
167
|
Chris@0
|
168 return $this;
|
Chris@0
|
169 }
|
Chris@0
|
170
|
Chris@0
|
171 /**
|
Chris@0
|
172 * Sets a default value that will be added to all embedded routes (unless that
|
Chris@0
|
173 * default value is already set).
|
Chris@0
|
174 *
|
Chris@0
|
175 * @param string $key
|
Chris@0
|
176 * @param mixed $value
|
Chris@0
|
177 *
|
Chris@0
|
178 * @return $this
|
Chris@0
|
179 */
|
Chris@0
|
180 public function setDefault($key, $value)
|
Chris@0
|
181 {
|
Chris@0
|
182 $this->defaults[$key] = $value;
|
Chris@0
|
183
|
Chris@0
|
184 return $this;
|
Chris@0
|
185 }
|
Chris@0
|
186
|
Chris@0
|
187 /**
|
Chris@0
|
188 * Sets a requirement that will be added to all embedded routes (unless that
|
Chris@0
|
189 * requirement is already set).
|
Chris@0
|
190 *
|
Chris@0
|
191 * @param string $key
|
Chris@0
|
192 * @param mixed $regex
|
Chris@0
|
193 *
|
Chris@0
|
194 * @return $this
|
Chris@0
|
195 */
|
Chris@0
|
196 public function setRequirement($key, $regex)
|
Chris@0
|
197 {
|
Chris@0
|
198 $this->requirements[$key] = $regex;
|
Chris@0
|
199
|
Chris@0
|
200 return $this;
|
Chris@0
|
201 }
|
Chris@0
|
202
|
Chris@0
|
203 /**
|
Chris@0
|
204 * Sets an opiton that will be added to all embedded routes (unless that
|
Chris@0
|
205 * option is already set).
|
Chris@0
|
206 *
|
Chris@0
|
207 * @param string $key
|
Chris@0
|
208 * @param mixed $value
|
Chris@0
|
209 *
|
Chris@0
|
210 * @return $this
|
Chris@0
|
211 */
|
Chris@0
|
212 public function setOption($key, $value)
|
Chris@0
|
213 {
|
Chris@0
|
214 $this->options[$key] = $value;
|
Chris@0
|
215
|
Chris@0
|
216 return $this;
|
Chris@0
|
217 }
|
Chris@0
|
218
|
Chris@0
|
219 /**
|
Chris@0
|
220 * Sets the schemes on all embedded routes (unless already set).
|
Chris@0
|
221 *
|
Chris@0
|
222 * @param array|string $schemes
|
Chris@0
|
223 *
|
Chris@0
|
224 * @return $this
|
Chris@0
|
225 */
|
Chris@0
|
226 public function setSchemes($schemes)
|
Chris@0
|
227 {
|
Chris@0
|
228 $this->schemes = $schemes;
|
Chris@0
|
229
|
Chris@0
|
230 return $this;
|
Chris@0
|
231 }
|
Chris@0
|
232
|
Chris@0
|
233 /**
|
Chris@0
|
234 * Sets the methods on all embedded routes (unless already set).
|
Chris@0
|
235 *
|
Chris@0
|
236 * @param array|string $methods
|
Chris@0
|
237 *
|
Chris@0
|
238 * @return $this
|
Chris@0
|
239 */
|
Chris@0
|
240 public function setMethods($methods)
|
Chris@0
|
241 {
|
Chris@0
|
242 $this->methods = $methods;
|
Chris@0
|
243
|
Chris@0
|
244 return $this;
|
Chris@0
|
245 }
|
Chris@0
|
246
|
Chris@0
|
247 /**
|
Chris@0
|
248 * Adds a resource for this collection.
|
Chris@0
|
249 *
|
Chris@0
|
250 * @param ResourceInterface $resource
|
Chris@0
|
251 *
|
Chris@0
|
252 * @return $this
|
Chris@0
|
253 */
|
Chris@0
|
254 private function addResource(ResourceInterface $resource)
|
Chris@0
|
255 {
|
Chris@0
|
256 $this->resources[] = $resource;
|
Chris@0
|
257
|
Chris@0
|
258 return $this;
|
Chris@0
|
259 }
|
Chris@0
|
260
|
Chris@0
|
261 /**
|
Chris@0
|
262 * Creates the final RouteCollection and returns it.
|
Chris@0
|
263 *
|
Chris@0
|
264 * @return RouteCollection
|
Chris@0
|
265 */
|
Chris@0
|
266 public function build()
|
Chris@0
|
267 {
|
Chris@0
|
268 $routeCollection = new RouteCollection();
|
Chris@0
|
269
|
Chris@0
|
270 foreach ($this->routes as $name => $route) {
|
Chris@0
|
271 if ($route instanceof Route) {
|
Chris@0
|
272 $route->setDefaults(array_merge($this->defaults, $route->getDefaults()));
|
Chris@0
|
273 $route->setOptions(array_merge($this->options, $route->getOptions()));
|
Chris@0
|
274
|
Chris@0
|
275 foreach ($this->requirements as $key => $val) {
|
Chris@0
|
276 if (!$route->hasRequirement($key)) {
|
Chris@0
|
277 $route->setRequirement($key, $val);
|
Chris@0
|
278 }
|
Chris@0
|
279 }
|
Chris@0
|
280
|
Chris@0
|
281 if (null !== $this->prefix) {
|
Chris@0
|
282 $route->setPath('/'.$this->prefix.$route->getPath());
|
Chris@0
|
283 }
|
Chris@0
|
284
|
Chris@0
|
285 if (!$route->getHost()) {
|
Chris@0
|
286 $route->setHost($this->host);
|
Chris@0
|
287 }
|
Chris@0
|
288
|
Chris@0
|
289 if (!$route->getCondition()) {
|
Chris@0
|
290 $route->setCondition($this->condition);
|
Chris@0
|
291 }
|
Chris@0
|
292
|
Chris@0
|
293 if (!$route->getSchemes()) {
|
Chris@0
|
294 $route->setSchemes($this->schemes);
|
Chris@0
|
295 }
|
Chris@0
|
296
|
Chris@0
|
297 if (!$route->getMethods()) {
|
Chris@0
|
298 $route->setMethods($this->methods);
|
Chris@0
|
299 }
|
Chris@0
|
300
|
Chris@0
|
301 // auto-generate the route name if it's been marked
|
Chris@0
|
302 if ('_unnamed_route_' === substr($name, 0, 15)) {
|
Chris@0
|
303 $name = $this->generateRouteName($route);
|
Chris@0
|
304 }
|
Chris@0
|
305
|
Chris@0
|
306 $routeCollection->add($name, $route);
|
Chris@0
|
307 } else {
|
Chris@0
|
308 /* @var self $route */
|
Chris@0
|
309 $subCollection = $route->build();
|
Chris@0
|
310 $subCollection->addPrefix($this->prefix);
|
Chris@0
|
311
|
Chris@0
|
312 $routeCollection->addCollection($subCollection);
|
Chris@0
|
313 }
|
Chris@0
|
314
|
Chris@0
|
315 foreach ($this->resources as $resource) {
|
Chris@0
|
316 $routeCollection->addResource($resource);
|
Chris@0
|
317 }
|
Chris@0
|
318 }
|
Chris@0
|
319
|
Chris@0
|
320 return $routeCollection;
|
Chris@0
|
321 }
|
Chris@0
|
322
|
Chris@0
|
323 /**
|
Chris@0
|
324 * Generates a route name based on details of this route.
|
Chris@0
|
325 *
|
Chris@0
|
326 * @return string
|
Chris@0
|
327 */
|
Chris@0
|
328 private function generateRouteName(Route $route)
|
Chris@0
|
329 {
|
Chris@0
|
330 $methods = implode('_', $route->getMethods()).'_';
|
Chris@0
|
331
|
Chris@0
|
332 $routeName = $methods.$route->getPath();
|
Chris@0
|
333 $routeName = str_replace(array('/', ':', '|', '-'), '_', $routeName);
|
Chris@0
|
334 $routeName = preg_replace('/[^a-z0-9A-Z_.]+/', '', $routeName);
|
Chris@0
|
335
|
Chris@0
|
336 // Collapse consecutive underscores down into a single underscore.
|
Chris@0
|
337 $routeName = preg_replace('/_+/', '_', $routeName);
|
Chris@0
|
338
|
Chris@0
|
339 return $routeName;
|
Chris@0
|
340 }
|
Chris@0
|
341
|
Chris@0
|
342 /**
|
Chris@0
|
343 * Finds a loader able to load an imported resource and loads it.
|
Chris@0
|
344 *
|
Chris@0
|
345 * @param mixed $resource A resource
|
Chris@0
|
346 * @param string|null $type The resource type or null if unknown
|
Chris@0
|
347 *
|
Chris@0
|
348 * @return RouteCollection
|
Chris@0
|
349 *
|
Chris@0
|
350 * @throws FileLoaderLoadException If no loader is found
|
Chris@0
|
351 */
|
Chris@0
|
352 private function load($resource, $type = null)
|
Chris@0
|
353 {
|
Chris@0
|
354 if (null === $this->loader) {
|
Chris@0
|
355 throw new \BadMethodCallException('Cannot import other routing resources: you must pass a LoaderInterface when constructing RouteCollectionBuilder.');
|
Chris@0
|
356 }
|
Chris@0
|
357
|
Chris@0
|
358 if ($this->loader->supports($resource, $type)) {
|
Chris@0
|
359 return $this->loader->load($resource, $type);
|
Chris@0
|
360 }
|
Chris@0
|
361
|
Chris@0
|
362 if (null === $resolver = $this->loader->getResolver()) {
|
Chris@0
|
363 throw new FileLoaderLoadException($resource);
|
Chris@0
|
364 }
|
Chris@0
|
365
|
Chris@0
|
366 if (false === $loader = $resolver->resolve($resource, $type)) {
|
Chris@0
|
367 throw new FileLoaderLoadException($resource);
|
Chris@0
|
368 }
|
Chris@0
|
369
|
Chris@0
|
370 return $loader->load($resource, $type);
|
Chris@0
|
371 }
|
Chris@0
|
372 }
|