1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\Routing; 13 14use Symfony\Component\Config\Resource\ResourceInterface; 15 16/** 17 * A RouteCollection represents a set of Route instances. 18 * 19 * When adding a route at the end of the collection, an existing route 20 * with the same name is removed first. So there can only be one route 21 * with a given name. 22 * 23 * @author Fabien Potencier <fabien@symfony.com> 24 * @author Tobias Schultze <http://tobion.de> 25 */ 26class RouteCollection implements \IteratorAggregate, \Countable 27{ 28 /** 29 * @var Route[] 30 */ 31 private $routes = []; 32 33 /** 34 * @var array 35 */ 36 private $resources = []; 37 38 public function __clone() 39 { 40 foreach ($this->routes as $name => $route) { 41 $this->routes[$name] = clone $route; 42 } 43 } 44 45 /** 46 * Gets the current RouteCollection as an Iterator that includes all routes. 47 * 48 * It implements \IteratorAggregate. 49 * 50 * @see all() 51 * 52 * @return \ArrayIterator|Route[] An \ArrayIterator object for iterating over routes 53 */ 54 public function getIterator() 55 { 56 return new \ArrayIterator($this->routes); 57 } 58 59 /** 60 * Gets the number of Routes in this collection. 61 * 62 * @return int The number of routes 63 */ 64 public function count() 65 { 66 return \count($this->routes); 67 } 68 69 public function add(string $name, Route $route) 70 { 71 unset($this->routes[$name]); 72 73 $this->routes[$name] = $route; 74 } 75 76 /** 77 * Returns all routes in this collection. 78 * 79 * @return Route[] An array of routes 80 */ 81 public function all() 82 { 83 return $this->routes; 84 } 85 86 /** 87 * Gets a route by name. 88 * 89 * @return Route|null A Route instance or null when not found 90 */ 91 public function get(string $name) 92 { 93 return isset($this->routes[$name]) ? $this->routes[$name] : null; 94 } 95 96 /** 97 * Removes a route or an array of routes by name from the collection. 98 * 99 * @param string|string[] $name The route name or an array of route names 100 */ 101 public function remove($name) 102 { 103 foreach ((array) $name as $n) { 104 unset($this->routes[$n]); 105 } 106 } 107 108 /** 109 * Adds a route collection at the end of the current set by appending all 110 * routes of the added collection. 111 */ 112 public function addCollection(self $collection) 113 { 114 // we need to remove all routes with the same names first because just replacing them 115 // would not place the new route at the end of the merged array 116 foreach ($collection->all() as $name => $route) { 117 unset($this->routes[$name]); 118 $this->routes[$name] = $route; 119 } 120 121 foreach ($collection->getResources() as $resource) { 122 $this->addResource($resource); 123 } 124 } 125 126 /** 127 * Adds a prefix to the path of all child routes. 128 */ 129 public function addPrefix(string $prefix, array $defaults = [], array $requirements = []) 130 { 131 $prefix = trim(trim($prefix), '/'); 132 133 if ('' === $prefix) { 134 return; 135 } 136 137 foreach ($this->routes as $route) { 138 $route->setPath('/'.$prefix.$route->getPath()); 139 $route->addDefaults($defaults); 140 $route->addRequirements($requirements); 141 } 142 } 143 144 /** 145 * Adds a prefix to the name of all the routes within in the collection. 146 */ 147 public function addNamePrefix(string $prefix) 148 { 149 $prefixedRoutes = []; 150 151 foreach ($this->routes as $name => $route) { 152 $prefixedRoutes[$prefix.$name] = $route; 153 if (null !== $name = $route->getDefault('_canonical_route')) { 154 $route->setDefault('_canonical_route', $prefix.$name); 155 } 156 } 157 158 $this->routes = $prefixedRoutes; 159 } 160 161 /** 162 * Sets the host pattern on all routes. 163 */ 164 public function setHost(?string $pattern, array $defaults = [], array $requirements = []) 165 { 166 foreach ($this->routes as $route) { 167 $route->setHost($pattern); 168 $route->addDefaults($defaults); 169 $route->addRequirements($requirements); 170 } 171 } 172 173 /** 174 * Sets a condition on all routes. 175 * 176 * Existing conditions will be overridden. 177 */ 178 public function setCondition(?string $condition) 179 { 180 foreach ($this->routes as $route) { 181 $route->setCondition($condition); 182 } 183 } 184 185 /** 186 * Adds defaults to all routes. 187 * 188 * An existing default value under the same name in a route will be overridden. 189 */ 190 public function addDefaults(array $defaults) 191 { 192 if ($defaults) { 193 foreach ($this->routes as $route) { 194 $route->addDefaults($defaults); 195 } 196 } 197 } 198 199 /** 200 * Adds requirements to all routes. 201 * 202 * An existing requirement under the same name in a route will be overridden. 203 */ 204 public function addRequirements(array $requirements) 205 { 206 if ($requirements) { 207 foreach ($this->routes as $route) { 208 $route->addRequirements($requirements); 209 } 210 } 211 } 212 213 /** 214 * Adds options to all routes. 215 * 216 * An existing option value under the same name in a route will be overridden. 217 */ 218 public function addOptions(array $options) 219 { 220 if ($options) { 221 foreach ($this->routes as $route) { 222 $route->addOptions($options); 223 } 224 } 225 } 226 227 /** 228 * Sets the schemes (e.g. 'https') all child routes are restricted to. 229 * 230 * @param string|string[] $schemes The scheme or an array of schemes 231 */ 232 public function setSchemes($schemes) 233 { 234 foreach ($this->routes as $route) { 235 $route->setSchemes($schemes); 236 } 237 } 238 239 /** 240 * Sets the HTTP methods (e.g. 'POST') all child routes are restricted to. 241 * 242 * @param string|string[] $methods The method or an array of methods 243 */ 244 public function setMethods($methods) 245 { 246 foreach ($this->routes as $route) { 247 $route->setMethods($methods); 248 } 249 } 250 251 /** 252 * Returns an array of resources loaded to build this collection. 253 * 254 * @return ResourceInterface[] An array of resources 255 */ 256 public function getResources() 257 { 258 return array_values($this->resources); 259 } 260 261 /** 262 * Adds a resource for this collection. If the resource already exists 263 * it is not added. 264 */ 265 public function addResource(ResourceInterface $resource) 266 { 267 $key = (string) $resource; 268 269 if (!isset($this->resources[$key])) { 270 $this->resources[$key] = $resource; 271 } 272 } 273} 274