2010-02-17 13:53:31 +00:00
< ? php
/*
2011-01-15 13:29:43 +00:00
* This file is part of the Symfony package .
2010-02-17 13:53:31 +00:00
*
2011-03-06 11:40:06 +00:00
* ( c ) Fabien Potencier < fabien @ symfony . com >
2010-02-17 13:53:31 +00:00
*
2011-01-15 13:29:43 +00:00
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
2010-02-17 13:53:31 +00:00
*/
2011-01-15 13:29:43 +00:00
namespace Symfony\Component\Routing ;
2011-02-10 15:15:51 +00:00
use Symfony\Component\Config\Resource\ResourceInterface ;
2011-01-15 13:29:43 +00:00
2010-02-17 13:53:31 +00:00
/**
2012-04-01 11:32:28 +01:00
* A RouteCollection represents a set of Route instances as a tree structure .
2010-02-17 13:53:31 +00:00
*
2011-09-30 08:20:51 +01:00
* When adding a route , it overrides existing routes with the
2012-04-01 11:32:28 +01:00
* same name defined in the instance or its children and parents .
2011-09-30 08:20:51 +01:00
*
2011-03-06 11:40:06 +00:00
* @ author Fabien Potencier < fabien @ symfony . com >
2012-04-01 11:32:28 +01:00
* @ author Tobias Schultze < http :// tobion . de >
2011-06-14 14:35:32 +01:00
*
* @ api
2010-02-17 13:53:31 +00:00
*/
2012-06-11 01:37:14 +01:00
class RouteCollection implements \IteratorAggregate , \Countable
2010-02-17 13:53:31 +00:00
{
2011-03-23 18:24:18 +00:00
private $routes ;
private $resources ;
2011-04-25 16:39:10 +01:00
private $prefix ;
2011-09-30 08:20:51 +01:00
private $parent ;
2010-02-17 13:53:31 +00:00
2010-05-06 12:25:53 +01:00
/**
* Constructor .
2011-06-14 14:35:32 +01:00
*
* @ api
2010-05-06 12:25:53 +01:00
*/
public function __construct ()
2010-02-17 13:53:31 +00:00
{
2010-05-06 12:25:53 +01:00
$this -> routes = array ();
$this -> resources = array ();
2011-04-25 16:39:10 +01:00
$this -> prefix = '' ;
}
2011-10-24 10:39:31 +01:00
public function __clone ()
{
foreach ( $this -> routes as $name => $route ) {
$this -> routes [ $name ] = clone $route ;
if ( $route instanceof RouteCollection ) {
$this -> routes [ $name ] -> setParent ( $this );
}
}
}
2011-09-30 08:20:51 +01:00
/**
* Gets the parent RouteCollection .
*
2012-04-01 11:32:28 +01:00
* @ return RouteCollection | null The parent RouteCollection or null when it ' s the root
2011-09-30 08:20:51 +01:00
*/
public function getParent ()
{
return $this -> parent ;
}
/**
2012-04-01 11:32:28 +01:00
* Gets the root RouteCollection of the tree .
2011-09-30 08:20:51 +01:00
*
2012-04-01 11:32:28 +01:00
* @ return RouteCollection The root RouteCollection
2011-09-30 08:20:51 +01:00
*/
2012-04-01 11:32:28 +01:00
public function getRoot ()
2011-09-30 08:20:51 +01:00
{
2012-04-01 11:32:28 +01:00
$parent = $this ;
while ( $parent -> getParent ()) {
$parent = $parent -> getParent ();
}
return $parent ;
2011-09-30 08:20:51 +01:00
}
/**
2012-04-03 08:18:44 +01:00
* Gets the current RouteCollection as an Iterator that includes all routes and child route collections .
2011-09-30 08:20:51 +01:00
*
* @ return \ArrayIterator An \ArrayIterator interface
*/
2011-04-25 16:39:10 +01:00
public function getIterator ()
{
return new \ArrayIterator ( $this -> routes );
2010-02-17 13:53:31 +00:00
}
2012-06-11 01:37:14 +01:00
/**
* Gets the number of Routes in this collection .
*
* @ return int The number of routes in this collection , including nested collections
*/
public function count ()
{
2012-06-25 12:50:03 +01:00
$count = 0 ;
foreach ( $this -> routes as $route ) {
$count += $route instanceof RouteCollection ? count ( $route ) : 1 ;
}
return $count ;
2012-06-11 01:37:14 +01:00
}
2010-05-06 12:25:53 +01:00
/**
* Adds a route .
*
* @ param string $name The route name
* @ param Route $route A Route instance
*
* @ throws \InvalidArgumentException When route name contains non valid characters
2011-06-14 14:35:32 +01:00
*
* @ api
2010-05-06 12:25:53 +01:00
*/
2010-11-23 08:42:19 +00:00
public function add ( $name , Route $route )
2010-05-06 12:25:53 +01:00
{
2010-12-12 07:53:18 +00:00
if ( ! preg_match ( '/^[a-z0-9A-Z_.]+$/' , $name )) {
2012-04-06 10:45:36 +01:00
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 ));
2010-05-06 12:25:53 +01:00
}
2010-02-17 13:53:31 +00:00
2012-04-01 11:32:28 +01:00
$this -> remove ( $name );
2011-09-30 08:20:51 +01:00
2010-05-06 12:25:53 +01:00
$this -> routes [ $name ] = $route ;
}
2010-02-17 13:53:31 +00:00
2010-05-06 12:25:53 +01:00
/**
2012-04-01 11:32:28 +01:00
* Returns all routes in this collection and its children .
2010-05-06 12:25:53 +01:00
*
* @ return array An array of routes
*/
2010-11-23 08:42:19 +00:00
public function all ()
2010-02-17 13:53:31 +00:00
{
2011-04-25 16:39:10 +01:00
$routes = array ();
foreach ( $this -> routes as $name => $route ) {
if ( $route instanceof RouteCollection ) {
$routes = array_merge ( $routes , $route -> all ());
} else {
$routes [ $name ] = $route ;
}
}
return $routes ;
2010-02-17 13:53:31 +00:00
}
2010-05-06 12:25:53 +01:00
/**
2012-04-01 11:32:28 +01:00
* Gets a route by name defined in this collection or its children .
2010-05-06 12:25:53 +01:00
*
2012-04-01 11:32:28 +01:00
* @ param string $name The route name
2010-05-06 12:25:53 +01:00
*
2012-04-01 11:32:28 +01:00
* @ return Route | null A Route instance or null when not found
2010-05-06 12:25:53 +01:00
*/
2010-11-23 08:42:19 +00:00
public function get ( $name )
2010-05-06 12:25:53 +01:00
{
2012-04-01 11:32:28 +01:00
if ( isset ( $this -> routes [ $name ])) {
return $this -> routes [ $name ] instanceof RouteCollection ? null : $this -> routes [ $name ];
}
2011-04-25 16:39:10 +01:00
2012-04-01 11:32:28 +01:00
foreach ( $this -> routes as $routes ) {
if ( $routes instanceof RouteCollection && null !== $route = $routes -> get ( $name )) {
2011-04-25 16:39:10 +01:00
return $route ;
}
}
2012-04-01 11:32:28 +01:00
return null ;
2010-05-06 12:25:53 +01:00
}
2010-02-17 13:53:31 +00:00
2011-09-30 08:20:51 +01:00
/**
2012-04-01 11:32:28 +01:00
* Removes a route or an array of routes by name from all connected
* collections ( this instance and all parents and children ) .
2011-09-30 08:20:51 +01:00
*
2012-04-01 11:32:28 +01:00
* @ param string | array $name The route name or an array of route names
2011-09-30 08:20:51 +01:00
*/
public function remove ( $name )
{
2012-04-01 11:32:28 +01:00
$root = $this -> getRoot ();
2011-09-30 08:20:51 +01:00
2012-04-01 11:32:28 +01:00
foreach (( array ) $name as $n ) {
$root -> removeRecursively ( $n );
2011-09-30 08:20:51 +01:00
}
}
2010-05-06 12:25:53 +01:00
/**
* Adds a route collection to the current set of routes ( at the end of the current set ) .
*
2011-10-23 10:56:23 +01:00
* @ 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
2012-04-04 02:35:48 +01:00
* @ param array $options An array of options
2011-06-14 14:35:32 +01:00
*
2012-04-01 11:32:28 +01:00
* @ throws \InvalidArgumentException When the RouteCollection already exists in the tree
*
2011-06-14 14:35:32 +01:00
* @ api
2010-05-06 12:25:53 +01:00
*/
2012-04-04 02:35:48 +01:00
public function addCollection ( RouteCollection $collection , $prefix = '' , $defaults = array (), $requirements = array (), $options = array ())
2010-02-17 13:53:31 +00:00
{
2012-04-01 11:32:28 +01:00
// 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.' );
2011-09-30 08:20:51 +01:00
}
2012-04-01 11:32:28 +01:00
// 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 );
2011-04-25 16:39:10 +01:00
$this -> routes [] = $collection ;
2010-02-17 13:53:31 +00:00
}
2010-05-06 12:25:53 +01:00
/**
* Adds a prefix to all routes in the current set .
*
2011-10-23 10:56:23 +01:00
* @ 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
2012-04-04 02:35:48 +01:00
* @ param array $options An array of options
2011-06-14 14:35:32 +01:00
*
* @ api
2010-05-06 12:25:53 +01:00
*/
2012-04-04 02:35:48 +01:00
public function addPrefix ( $prefix , $defaults = array (), $requirements = array (), $options = array ())
2010-02-17 13:53:31 +00:00
{
2012-10-06 00:21:17 +01:00
$prefix = trim ( trim ( $prefix ), '/' );
2011-04-26 13:26:29 +01:00
2012-04-01 11:32:28 +01:00
if ( '' === $prefix && empty ( $defaults ) && empty ( $requirements ) && empty ( $options )) {
return ;
}
2012-10-06 00:21:17 +01:00
// a prefix must start with a single slash and must not end with a slash
if ( '' !== $prefix ) {
$this -> prefix = '/' . $prefix . $this -> prefix ;
2011-04-26 13:26:29 +01:00
}
2012-04-01 11:32:28 +01:00
foreach ( $this -> routes as $route ) {
2011-05-04 22:29:47 +01:00
if ( $route instanceof RouteCollection ) {
2012-10-06 00:21:17 +01:00
// we add the slashes so the prefix is not lost by trimming in the sub-collection
$route -> addPrefix ( '/' . $prefix . '/' , $defaults , $requirements , $options );
2011-05-04 22:29:47 +01:00
} else {
2012-10-06 00:21:17 +01:00
if ( '' !== $prefix ) {
$route -> setPattern ( '/' . $prefix . $route -> getPattern ());
}
2011-10-23 10:56:23 +01:00
$route -> addDefaults ( $defaults );
$route -> addRequirements ( $requirements );
2012-04-04 02:35:48 +01:00
$route -> addOptions ( $options );
2011-05-04 22:29:47 +01:00
}
2010-05-06 12:25:53 +01:00
}
2010-02-17 13:53:31 +00:00
}
2012-04-01 11:32:28 +01:00
/**
* Returns the prefix that may contain placeholders .
*
* @ return string The prefix
*/
2011-04-25 16:39:10 +01:00
public function getPrefix ()
{
return $this -> prefix ;
}
2010-05-06 12:25:53 +01:00
/**
* Returns an array of resources loaded to build this collection .
*
* @ return ResourceInterface [] An array of resources
*/
public function getResources ()
{
2011-04-25 16:39:10 +01:00
$resources = $this -> resources ;
foreach ( $this as $routes ) {
if ( $routes instanceof RouteCollection ) {
$resources = array_merge ( $resources , $routes -> getResources ());
}
}
return array_unique ( $resources );
2010-05-06 12:25:53 +01:00
}
2010-02-17 13:53:31 +00:00
2010-05-06 12:25:53 +01:00
/**
* Adds a resource for this collection .
*
* @ param ResourceInterface $resource A resource instance
*/
public function addResource ( ResourceInterface $resource )
{
$this -> resources [] = $resource ;
}
2012-04-01 11:32:28 +01:00
/**
* 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
2012-07-28 23:02:29 +01:00
// iterating recursively on the first hit.
2012-04-01 11:32:28 +01:00
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 ;
}
2010-02-17 13:53:31 +00:00
}