2018-02-01 20:01:12 +00:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\Routing ;
use Symfony\Component\Config\Resource\ResourceInterface ;
2022-03-10 11:54:29 +00:00
use Symfony\Component\Routing\Exception\InvalidArgumentException ;
use Symfony\Component\Routing\Exception\RouteCircularReferenceException ;
2018-02-01 20:01:12 +00:00
/**
* A RouteCollection represents a set of Route instances .
*
* When adding a route at the end of the collection , an existing route
* with the same name is removed first . So there can only be one route
* with a given name .
*
* @ author Fabien Potencier < fabien @ symfony . com >
* @ author Tobias Schultze < http :// tobion . de >
2022-03-10 11:54:29 +00:00
*
* @ implements \IteratorAggregate < string , Route >
2018-02-01 20:01:12 +00:00
*/
class RouteCollection implements \IteratorAggregate , \Countable
{
/**
2022-03-10 11:54:29 +00:00
* @ var array < string , Route >
2018-02-01 20:01:12 +00:00
*/
2019-06-11 11:29:32 +00:00
private $routes = [];
2018-02-01 20:01:12 +00:00
/**
2022-03-10 11:54:29 +00:00
* @ var array < string , Alias >
*/
private $aliases = [];
/**
* @ var array < string , ResourceInterface >
2018-02-01 20:01:12 +00:00
*/
2019-06-11 11:29:32 +00:00
private $resources = [];
2018-02-01 20:01:12 +00:00
2022-03-10 11:54:29 +00:00
/**
* @ var array < string , int >
*/
private $priorities = [];
2018-02-01 20:01:12 +00:00
public function __clone ()
{
foreach ( $this -> routes as $name => $route ) {
$this -> routes [ $name ] = clone $route ;
}
2022-03-10 11:54:29 +00:00
foreach ( $this -> aliases as $name => $alias ) {
$this -> aliases [ $name ] = clone $alias ;
}
2018-02-01 20:01:12 +00:00
}
/**
* Gets the current RouteCollection as an Iterator that includes all routes .
*
* It implements \IteratorAggregate .
*
* @ see all ()
*
2022-03-10 11:54:29 +00:00
* @ return \ArrayIterator < string , Route >
2018-02-01 20:01:12 +00:00
*/
2022-03-10 11:54:29 +00:00
#[\ReturnTypeWillChange]
2018-02-01 20:01:12 +00:00
public function getIterator ()
{
2022-03-10 11:54:29 +00:00
return new \ArrayIterator ( $this -> all ());
2018-02-01 20:01:12 +00:00
}
/**
* Gets the number of Routes in this collection .
*
2022-03-10 11:54:29 +00:00
* @ return int
2018-02-01 20:01:12 +00:00
*/
2022-03-10 11:54:29 +00:00
#[\ReturnTypeWillChange]
2018-02-01 20:01:12 +00:00
public function count ()
{
2018-10-14 19:50:32 +00:00
return \count ( $this -> routes );
2018-02-01 20:01:12 +00:00
}
/**
2022-03-10 11:54:29 +00:00
* @ param int $priority
2018-02-01 20:01:12 +00:00
*/
2022-03-10 11:54:29 +00:00
public function add ( string $name , Route $route /*, int $priority = 0*/ )
2018-02-01 20:01:12 +00:00
{
2022-03-10 11:54:29 +00:00
if ( \func_num_args () < 3 && __CLASS__ !== static :: class && __CLASS__ !== ( new \ReflectionMethod ( $this , __FUNCTION__ )) -> getDeclaringClass () -> getName () && ! $this instanceof \PHPUnit\Framework\MockObject\MockObject && ! $this instanceof \Prophecy\Prophecy\ProphecySubjectInterface && ! $this instanceof \Mockery\MockInterface ) {
trigger_deprecation ( 'symfony/routing' , '5.1' , 'The "%s()" method will have a new "int $priority = 0" argument in version 6.0, not defining it is deprecated.' , __METHOD__ );
}
unset ( $this -> routes [ $name ], $this -> priorities [ $name ], $this -> aliases [ $name ]);
2018-02-01 20:01:12 +00:00
$this -> routes [ $name ] = $route ;
2022-03-10 11:54:29 +00:00
if ( $priority = 3 <= \func_num_args () ? func_get_arg ( 2 ) : 0 ) {
$this -> priorities [ $name ] = $priority ;
}
2018-02-01 20:01:12 +00:00
}
/**
* Returns all routes in this collection .
*
2022-03-10 11:54:29 +00:00
* @ return array < string , Route >
2018-02-01 20:01:12 +00:00
*/
public function all ()
{
2022-03-10 11:54:29 +00:00
if ( $this -> priorities ) {
$priorities = $this -> priorities ;
$keysOrder = array_flip ( array_keys ( $this -> routes ));
uksort ( $this -> routes , static function ( $n1 , $n2 ) use ( $priorities , $keysOrder ) {
return (( $priorities [ $n2 ] ? ? 0 ) <=> ( $priorities [ $n1 ] ? ? 0 )) ? : ( $keysOrder [ $n1 ] <=> $keysOrder [ $n2 ]);
});
}
2018-02-01 20:01:12 +00:00
return $this -> routes ;
}
/**
* Gets a route by name .
*
2022-03-10 11:54:29 +00:00
* @ return Route | null
2018-02-01 20:01:12 +00:00
*/
2022-03-10 11:54:29 +00:00
public function get ( string $name )
2018-02-01 20:01:12 +00:00
{
2022-03-10 11:54:29 +00:00
$visited = [];
while ( null !== $alias = $this -> aliases [ $name ] ? ? null ) {
if ( false !== $searchKey = array_search ( $name , $visited )) {
$visited [] = $name ;
throw new RouteCircularReferenceException ( $name , \array_slice ( $visited , $searchKey ));
}
if ( $alias -> isDeprecated ()) {
$deprecation = $alias -> getDeprecation ( $name );
trigger_deprecation ( $deprecation [ 'package' ], $deprecation [ 'version' ], $deprecation [ 'message' ]);
}
$visited [] = $name ;
$name = $alias -> getId ();
}
return $this -> routes [ $name ] ? ? null ;
2018-02-01 20:01:12 +00:00
}
/**
* Removes a route or an array of routes by name from the collection .
*
* @ param string | string [] $name The route name or an array of route names
*/
public function remove ( $name )
{
foreach (( array ) $name as $n ) {
2022-03-10 11:54:29 +00:00
unset ( $this -> routes [ $n ], $this -> priorities [ $n ], $this -> aliases [ $n ]);
2018-02-01 20:01:12 +00:00
}
}
/**
* Adds a route collection at the end of the current set by appending all
* routes of the added collection .
*/
2018-06-13 18:35:28 +00:00
public function addCollection ( self $collection )
2018-02-01 20:01:12 +00:00
{
// we need to remove all routes with the same names first because just replacing them
// would not place the new route at the end of the merged array
foreach ( $collection -> all () as $name => $route ) {
2022-03-10 11:54:29 +00:00
unset ( $this -> routes [ $name ], $this -> priorities [ $name ], $this -> aliases [ $name ]);
2018-02-01 20:01:12 +00:00
$this -> routes [ $name ] = $route ;
2022-03-10 11:54:29 +00:00
if ( isset ( $collection -> priorities [ $name ])) {
$this -> priorities [ $name ] = $collection -> priorities [ $name ];
}
}
foreach ( $collection -> getAliases () as $name => $alias ) {
unset ( $this -> routes [ $name ], $this -> priorities [ $name ], $this -> aliases [ $name ]);
$this -> aliases [ $name ] = $alias ;
2018-02-01 20:01:12 +00:00
}
foreach ( $collection -> getResources () as $resource ) {
$this -> addResource ( $resource );
}
}
/**
* Adds a prefix to the path of all child routes .
*/
2022-03-10 11:54:29 +00:00
public function addPrefix ( string $prefix , array $defaults = [], array $requirements = [])
2018-02-01 20:01:12 +00:00
{
$prefix = trim ( trim ( $prefix ), '/' );
if ( '' === $prefix ) {
return ;
}
foreach ( $this -> routes as $route ) {
$route -> setPath ( '/' . $prefix . $route -> getPath ());
$route -> addDefaults ( $defaults );
$route -> addRequirements ( $requirements );
}
}
2018-10-14 19:50:32 +00:00
/**
* Adds a prefix to the name of all the routes within in the collection .
*/
public function addNamePrefix ( string $prefix )
{
2019-06-11 11:29:32 +00:00
$prefixedRoutes = [];
2022-03-10 11:54:29 +00:00
$prefixedPriorities = [];
$prefixedAliases = [];
2018-10-14 19:50:32 +00:00
foreach ( $this -> routes as $name => $route ) {
$prefixedRoutes [ $prefix . $name ] = $route ;
2022-03-10 11:54:29 +00:00
if ( null !== $canonicalName = $route -> getDefault ( '_canonical_route' )) {
$route -> setDefault ( '_canonical_route' , $prefix . $canonicalName );
}
if ( isset ( $this -> priorities [ $name ])) {
$prefixedPriorities [ $prefix . $name ] = $this -> priorities [ $name ];
2018-10-14 19:50:32 +00:00
}
}
2022-03-10 11:54:29 +00:00
foreach ( $this -> aliases as $name => $alias ) {
$prefixedAliases [ $prefix . $name ] = $alias -> withId ( $prefix . $alias -> getId ());
}
2018-10-14 19:50:32 +00:00
$this -> routes = $prefixedRoutes ;
2022-03-10 11:54:29 +00:00
$this -> priorities = $prefixedPriorities ;
$this -> aliases = $prefixedAliases ;
2018-10-14 19:50:32 +00:00
}
2018-02-01 20:01:12 +00:00
/**
* Sets the host pattern on all routes .
*/
2022-03-10 11:54:29 +00:00
public function setHost ( ? string $pattern , array $defaults = [], array $requirements = [])
2018-02-01 20:01:12 +00:00
{
foreach ( $this -> routes as $route ) {
$route -> setHost ( $pattern );
$route -> addDefaults ( $defaults );
$route -> addRequirements ( $requirements );
}
}
/**
* Sets a condition on all routes .
*
* Existing conditions will be overridden .
*/
2022-03-10 11:54:29 +00:00
public function setCondition ( ? string $condition )
2018-02-01 20:01:12 +00:00
{
foreach ( $this -> routes as $route ) {
$route -> setCondition ( $condition );
}
}
/**
* Adds defaults to all routes .
*
* An existing default value under the same name in a route will be overridden .
*/
public function addDefaults ( array $defaults )
{
if ( $defaults ) {
foreach ( $this -> routes as $route ) {
$route -> addDefaults ( $defaults );
}
}
}
/**
* Adds requirements to all routes .
*
* An existing requirement under the same name in a route will be overridden .
*/
public function addRequirements ( array $requirements )
{
if ( $requirements ) {
foreach ( $this -> routes as $route ) {
$route -> addRequirements ( $requirements );
}
}
}
/**
* Adds options to all routes .
*
* An existing option value under the same name in a route will be overridden .
*/
public function addOptions ( array $options )
{
if ( $options ) {
foreach ( $this -> routes as $route ) {
$route -> addOptions ( $options );
}
}
}
/**
* Sets the schemes ( e . g . 'https' ) all child routes are restricted to .
*
* @ param string | string [] $schemes The scheme or an array of schemes
*/
public function setSchemes ( $schemes )
{
foreach ( $this -> routes as $route ) {
$route -> setSchemes ( $schemes );
}
}
/**
* Sets the HTTP methods ( e . g . 'POST' ) all child routes are restricted to .
*
* @ param string | string [] $methods The method or an array of methods
*/
public function setMethods ( $methods )
{
foreach ( $this -> routes as $route ) {
$route -> setMethods ( $methods );
}
}
/**
* Returns an array of resources loaded to build this collection .
*
2022-03-10 11:54:29 +00:00
* @ return ResourceInterface []
2018-02-01 20:01:12 +00:00
*/
public function getResources ()
{
return array_values ( $this -> resources );
}
/**
* Adds a resource for this collection . If the resource already exists
* it is not added .
*/
public function addResource ( ResourceInterface $resource )
{
$key = ( string ) $resource ;
if ( ! isset ( $this -> resources [ $key ])) {
$this -> resources [ $key ] = $resource ;
}
}
2022-03-10 11:54:29 +00:00
/**
* Sets an alias for an existing route .
*
* @ param string $name The alias to create
* @ param string $alias The route to alias
*
* @ throws InvalidArgumentException if the alias is for itself
*/
public function addAlias ( string $name , string $alias ) : Alias
{
if ( $name === $alias ) {
throw new InvalidArgumentException ( sprintf ( 'Route alias "%s" can not reference itself.' , $name ));
}
unset ( $this -> routes [ $name ], $this -> priorities [ $name ]);
return $this -> aliases [ $name ] = new Alias ( $alias );
}
/**
* @ return array < string , Alias >
*/
public function getAliases () : array
{
return $this -> aliases ;
}
public function getAlias ( string $name ) : ? Alias
{
return $this -> aliases [ $name ] ? ? null ;
}
2018-02-01 20:01:12 +00:00
}