You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

364 lines
9.4 KiB

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