A RouteCollection represents a set of Route instances as a tree structure.
When adding a route, it overrides existing routes with the same name defined in the instance or its children and parents.
@author Fabien Potencier <fabien@symfony.com> @author Tobias Schultze <http://tobion.de>
@api
Expanded class hierarchy of RouteCollection
class RouteCollection implements \IteratorAggregate, \Countable {
private $routes;
private $resources;
private $prefix;
private $parent;
/**
* Constructor.
*
* @api
*/
public function __construct() {
$this->routes = array();
$this->resources = array();
$this->prefix = '';
}
public function __clone() {
foreach ($this->routes as $name => $route) {
$this->routes[$name] = clone $route;
if ($route instanceof RouteCollection) {
$this->routes[$name]
->setParent($this);
}
}
}
/**
* Gets the parent RouteCollection.
*
* @return RouteCollection|null The parent RouteCollection or null when it's the root
*/
public function getParent() {
return $this->parent;
}
/**
* Gets the root RouteCollection of the tree.
*
* @return RouteCollection The root RouteCollection
*/
public function getRoot() {
$parent = $this;
while ($parent
->getParent()) {
$parent = $parent
->getParent();
}
return $parent;
}
/**
* Gets the current RouteCollection as an Iterator that includes all routes and child route collections.
*
* @return \ArrayIterator An \ArrayIterator interface
*/
public function getIterator() {
return new \ArrayIterator($this->routes);
}
/**
* Gets the number of Routes in this collection.
*
* @return int The number of routes in this collection, including nested collections
*/
public function count() {
$count = 0;
foreach ($this->routes as $route) {
$count += $route instanceof RouteCollection ? count($route) : 1;
}
return $count;
}
/**
* Adds a route.
*
* @param string $name The route name
* @param Route $route A Route instance
*
* @throws \InvalidArgumentException When route name contains non valid characters
*
* @api
*/
public function add($name, Route $route) {
if (!preg_match('/^[a-z0-9A-Z_.]+$/', $name)) {
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));
}
$this
->remove($name);
$this->routes[$name] = $route;
}
/**
* Returns all routes in this collection and its children.
*
* @return array An array of routes
*/
public function all() {
$routes = array();
foreach ($this->routes as $name => $route) {
if ($route instanceof RouteCollection) {
$routes = array_merge($routes, $route
->all());
}
else {
$routes[$name] = $route;
}
}
return $routes;
}
/**
* Gets a route by name defined in this collection or its children.
*
* @param string $name The route name
*
* @return Route|null A Route instance or null when not found
*/
public function get($name) {
if (isset($this->routes[$name])) {
return $this->routes[$name] instanceof RouteCollection ? null : $this->routes[$name];
}
foreach ($this->routes as $routes) {
if ($routes instanceof RouteCollection && null !== ($route = $routes
->get($name))) {
return $route;
}
}
return null;
}
/**
* Removes a route or an array of routes by name from all connected
* collections (this instance and all parents and children).
*
* @param string|array $name The route name or an array of route names
*/
public function remove($name) {
$root = $this
->getRoot();
foreach ((array) $name as $n) {
$root
->removeRecursively($n);
}
}
/**
* Adds a route collection to the current set of routes (at the end of the current set).
*
* @param RouteCollection $collection A RouteCollection instance
* @param string $prefix An optional prefix to add before each pattern of the route collection
* @param array $defaults An array of default values
* @param array $requirements An array of requirements
* @param array $options An array of options
*
* @throws \InvalidArgumentException When the RouteCollection already exists in the tree
*
* @api
*/
public function addCollection(RouteCollection $collection, $prefix = '', $defaults = array(), $requirements = array(), $options = array()) {
// prevent infinite loops by recursive referencing
$root = $this
->getRoot();
if ($root === $collection || $root
->hasCollection($collection)) {
throw new \InvalidArgumentException('The RouteCollection already exists in the tree.');
}
// remove all routes with the same names in all existing collections
$this
->remove(array_keys($collection
->all()));
$collection
->setParent($this);
// the sub-collection must have the prefix of the parent (current instance) prepended because it does not
// necessarily already have it applied (depending on the order RouteCollections are added to each other)
$collection
->addPrefix($this
->getPrefix() . $prefix, $defaults, $requirements, $options);
$this->routes[] = $collection;
}
/**
* Adds a prefix to all routes in the current set.
*
* @param string $prefix An optional prefix to add before each pattern of the route collection
* @param array $defaults An array of default values
* @param array $requirements An array of requirements
* @param array $options An array of options
*
* @api
*/
public function addPrefix($prefix, $defaults = array(), $requirements = array(), $options = array()) {
// a prefix must not end with a slash
$prefix = rtrim($prefix, '/');
if ('' === $prefix && empty($defaults) && empty($requirements) && empty($options)) {
return;
}
// a prefix must start with a slash
if ('' !== $prefix && '/' !== $prefix[0]) {
$prefix = '/' . $prefix;
}
$this->prefix = $prefix . $this->prefix;
foreach ($this->routes as $route) {
if ($route instanceof RouteCollection) {
$route
->addPrefix($prefix, $defaults, $requirements, $options);
}
else {
$route
->setPattern($prefix . $route
->getPattern());
$route
->addDefaults($defaults);
$route
->addRequirements($requirements);
$route
->addOptions($options);
}
}
}
/**
* Returns the prefix that may contain placeholders.
*
* @return string The prefix
*/
public function getPrefix() {
return $this->prefix;
}
/**
* Returns an array of resources loaded to build this collection.
*
* @return ResourceInterface[] An array of resources
*/
public function getResources() {
$resources = $this->resources;
foreach ($this as $routes) {
if ($routes instanceof RouteCollection) {
$resources = array_merge($resources, $routes
->getResources());
}
}
return array_unique($resources);
}
/**
* Adds a resource for this collection.
*
* @param ResourceInterface $resource A resource instance
*/
public function addResource(ResourceInterface $resource) {
$this->resources[] = $resource;
}
/**
* Sets the parent RouteCollection. It's only used internally from one RouteCollection
* to another. It makes no sense to be available as part of the public API.
*
* @param RouteCollection $parent The parent RouteCollection
*/
private function setParent(RouteCollection $parent) {
$this->parent = $parent;
}
/**
* Removes a route by name from this collection and its children recursively.
*
* @param string $name The route name
*
* @return Boolean true when found
*/
private function removeRecursively($name) {
// It is ensured by the adders (->add and ->addCollection) that there can
// only be one route per name in all connected collections. So we can stop
// iterating recursively on the first hit.
if (isset($this->routes[$name])) {
unset($this->routes[$name]);
return true;
}
foreach ($this->routes as $routes) {
if ($routes instanceof RouteCollection && $routes
->removeRecursively($name)) {
return true;
}
}
return false;
}
/**
* Checks whether the given RouteCollection is already set in any child of the current instance.
*
* @param RouteCollection $collection A RouteCollection instance
*
* @return Boolean
*/
private function hasCollection(RouteCollection $collection) {
foreach ($this->routes as $routes) {
if ($routes === $collection || $routes instanceof RouteCollection && $routes
->hasCollection($collection)) {
return true;
}
}
return false;
}
}
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
RouteCollection:: |
public | function | Constructor. | |
RouteCollection:: |
public | function | ||
RouteCollection:: |
public | function | Gets the parent RouteCollection. | |
RouteCollection:: |
public | function | Gets the root RouteCollection of the tree. | |
RouteCollection:: |
public | function | Gets the current RouteCollection as an Iterator that includes all routes and child route collections. | |
RouteCollection:: |
public | function | Gets the number of Routes in this collection. | |
RouteCollection:: |
public | function | Adds a route. | |
RouteCollection:: |
public | function | Returns all routes in this collection and its children. | |
RouteCollection:: |
public | function | Gets a route by name defined in this collection or its children. | |
RouteCollection:: |
public | function | Removes a route or an array of routes by name from all connected collections (this instance and all parents and children). | |
RouteCollection:: |
public | function | Adds a route collection to the current set of routes (at the end of the current set). | |
RouteCollection:: |
public | function | Adds a prefix to all routes in the current set. | |
RouteCollection:: |
public | function | Returns the prefix that may contain placeholders. | |
RouteCollection:: |
public | function | Returns an array of resources loaded to build this collection. | |
RouteCollection:: |
public | function | Adds a resource for this collection. | |
RouteCollection:: |
private | function | Sets the parent RouteCollection. It's only used internally from one RouteCollection to another. It makes no sense to be available as part of the public API. | |
RouteCollection:: |
private | function | Removes a route by name from this collection and its children recursively. | |
RouteCollection:: |
private | function | Checks whether the given RouteCollection is already set in any child of the current instance. | |
RouteCollection:: |
private | property | ||
RouteCollection:: |
private | property | ||
RouteCollection:: |
private | property | ||
RouteCollection:: |
private | property |