RouteCollection.php 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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\Resource\ResourceInterface;
  12. /**
  13. * A RouteCollection represents a set of Route instances.
  14. *
  15. * When adding a route, it overrides existing routes with the
  16. * same name defined in theinstance or its children and parents.
  17. *
  18. * @author Fabien Potencier <fabien@symfony.com>
  19. *
  20. * @api
  21. */
  22. class RouteCollection implements \IteratorAggregate
  23. {
  24. private $routes;
  25. private $resources;
  26. private $prefix;
  27. private $parent;
  28. /**
  29. * Constructor.
  30. *
  31. * @api
  32. */
  33. public function __construct()
  34. {
  35. $this->routes = array();
  36. $this->resources = array();
  37. $this->prefix = '';
  38. }
  39. public function __clone()
  40. {
  41. foreach ($this->routes as $name => $route) {
  42. $this->routes[$name] = clone $route;
  43. if ($route instanceof RouteCollection) {
  44. $this->routes[$name]->setParent($this);
  45. }
  46. }
  47. }
  48. /**
  49. * Gets the parent RouteCollection.
  50. *
  51. * @return RouteCollection The parent RouteCollection
  52. */
  53. public function getParent()
  54. {
  55. return $this->parent;
  56. }
  57. /**
  58. * Sets the parent RouteCollection.
  59. *
  60. * @param RouteCollection $parent The parent RouteCollection
  61. */
  62. public function setParent(RouteCollection $parent)
  63. {
  64. $this->parent = $parent;
  65. }
  66. /**
  67. * Gets the current RouteCollection as an Iterator.
  68. *
  69. * @return \ArrayIterator An \ArrayIterator interface
  70. */
  71. public function getIterator()
  72. {
  73. return new \ArrayIterator($this->routes);
  74. }
  75. /**
  76. * Adds a route.
  77. *
  78. * @param string $name The route name
  79. * @param Route $route A Route instance
  80. *
  81. * @throws \InvalidArgumentException When route name contains non valid characters
  82. *
  83. * @api
  84. */
  85. public function add($name, Route $route)
  86. {
  87. if (!preg_match('/^[a-z0-9A-Z_.]+$/', $name)) {
  88. throw new \InvalidArgumentException(sprintf('The provided route name "%s" contains non valid characters. A route name must only contain digits (0-9), letters (a-z and A-Z), underscores (_) and dots (.).', $name));
  89. }
  90. $parent = $this;
  91. while ($parent->getParent()) {
  92. $parent = $parent->getParent();
  93. }
  94. if ($parent) {
  95. $parent->remove($name);
  96. }
  97. $this->routes[$name] = $route;
  98. }
  99. /**
  100. * Returns the array of routes.
  101. *
  102. * @return array An array of routes
  103. */
  104. public function all()
  105. {
  106. $routes = array();
  107. foreach ($this->routes as $name => $route) {
  108. if ($route instanceof RouteCollection) {
  109. $routes = array_merge($routes, $route->all());
  110. } else {
  111. $routes[$name] = $route;
  112. }
  113. }
  114. return $routes;
  115. }
  116. /**
  117. * Gets a route by name.
  118. *
  119. * @param string $name The route name
  120. *
  121. * @return Route $route A Route instance
  122. */
  123. public function get($name)
  124. {
  125. // get the latest defined route
  126. foreach (array_reverse($this->routes) as $routes) {
  127. if (!$routes instanceof RouteCollection) {
  128. continue;
  129. }
  130. if (null !== $route = $routes->get($name)) {
  131. return $route;
  132. }
  133. }
  134. if (isset($this->routes[$name])) {
  135. return $this->routes[$name];
  136. }
  137. }
  138. /**
  139. * Removes a route by name.
  140. *
  141. * @param string $name The route name
  142. */
  143. public function remove($name)
  144. {
  145. if (isset($this->routes[$name])) {
  146. unset($this->routes[$name]);
  147. }
  148. foreach ($this->routes as $routes) {
  149. if ($routes instanceof RouteCollection) {
  150. $routes->remove($name);
  151. }
  152. }
  153. }
  154. /**
  155. * Adds a route collection to the current set of routes (at the end of the current set).
  156. *
  157. * @param RouteCollection $collection A RouteCollection instance
  158. * @param string $prefix An optional prefix to add before each pattern of the route collection
  159. *
  160. * @api
  161. */
  162. public function addCollection(RouteCollection $collection, $prefix = '')
  163. {
  164. $collection->setParent($this);
  165. $collection->addPrefix($prefix);
  166. // remove all routes with the same name in all existing collections
  167. foreach (array_keys($collection->all()) as $name) {
  168. $this->remove($name);
  169. }
  170. $this->routes[] = $collection;
  171. }
  172. /**
  173. * Adds a prefix to all routes in the current set.
  174. *
  175. * @param string $prefix An optional prefix to add before each pattern of the route collection
  176. *
  177. * @api
  178. */
  179. public function addPrefix($prefix)
  180. {
  181. // a prefix must not end with a slash
  182. $prefix = rtrim($prefix, '/');
  183. if (!$prefix) {
  184. return;
  185. }
  186. // a prefix must start with a slash
  187. if ('/' !== $prefix[0]) {
  188. $prefix = '/'.$prefix;
  189. }
  190. $this->prefix = $prefix.$this->prefix;
  191. foreach ($this->routes as $name => $route) {
  192. if ($route instanceof RouteCollection) {
  193. $route->addPrefix($prefix);
  194. } else {
  195. $route->setPattern($prefix.$route->getPattern());
  196. }
  197. }
  198. }
  199. public function getPrefix()
  200. {
  201. return $this->prefix;
  202. }
  203. /**
  204. * Returns an array of resources loaded to build this collection.
  205. *
  206. * @return ResourceInterface[] An array of resources
  207. */
  208. public function getResources()
  209. {
  210. $resources = $this->resources;
  211. foreach ($this as $routes) {
  212. if ($routes instanceof RouteCollection) {
  213. $resources = array_merge($resources, $routes->getResources());
  214. }
  215. }
  216. return array_unique($resources);
  217. }
  218. /**
  219. * Adds a resource for this collection.
  220. *
  221. * @param ResourceInterface $resource A resource instance
  222. */
  223. public function addResource(ResourceInterface $resource)
  224. {
  225. $this->resources[] = $resource;
  226. }
  227. }