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\Generator ;
use Symfony\Component\Routing\Route ;
use Symfony\Component\Routing\RouteCollection ;
2011-04-20 12:54:39 +01:00
use Symfony\Component\Routing\RequestContext ;
2011-05-13 16:46:31 +01:00
use Symfony\Component\Routing\Exception\InvalidParameterException ;
2011-05-17 15:51:56 +01:00
use Symfony\Component\Routing\Exception\RouteNotFoundException ;
2011-05-13 16:46:31 +01:00
use Symfony\Component\Routing\Exception\MissingMandatoryParametersException ;
2012-06-09 15:43:04 +01:00
use Symfony\Component\HttpKernel\Log\LoggerInterface ;
2011-01-15 13:29:43 +00:00
2010-02-17 13:53:31 +00:00
/**
2012-04-12 03:08:51 +01:00
* UrlGenerator generates a URL based on a set of routes .
2010-02-17 13:53:31 +00:00
*
2011-03-06 11:40:06 +00:00
* @ author Fabien Potencier < fabien @ symfony . com >
2011-06-14 14:35:32 +01:00
*
* @ api
2010-02-17 13:53:31 +00:00
*/
class UrlGenerator implements UrlGeneratorInterface
{
2010-05-06 12:25:53 +01:00
protected $context ;
2012-06-09 15:43:04 +01:00
protected $strictParameters = true ;
protected $logger ;
2012-05-04 14:28:36 +01:00
/**
* This array defines the characters ( besides alphanumeric ones ) that will not be percent - encoded in the path segment of the generated URL .
*
* PHP ' s rawurlencode () encodes all chars except " a-zA-Z0-9-._~ " according to RFC 3986. But we want to allow some chars
* to be used in their literal form ( reasons below ) . Other chars inside the path must of course be encoded , e . g .
* " ? " and " # " ( would be interpreted wrongly as query and fragment identifier ),
* " ' " and " " " (are used as delimiters in HTML).
*/
2011-07-07 09:17:17 +01:00
protected $decodedChars = array (
2012-05-04 14:28:36 +01:00
// the slash can be used to designate a hierarchical structure and we want allow using it with this meaning
// some webservers don't allow the slash in encoded form in the path for security reasons anyway
// see http://stackoverflow.com/questions/4069002/http-400-if-2f-part-of-get-url-in-jboss
2011-07-07 09:17:17 +01:00
'%2F' => '/' ,
2012-05-04 14:28:36 +01:00
// the following chars are general delimiters in the URI specification but have only special meaning in the authority component
// so they can safely be used in the path in unencoded form
'%40' => '@' ,
'%3A' => ':' ,
// these chars are only sub-delimiters that have no predefined meaning and can therefore be used literally
// so URI producing applications can use these chars to delimit subcomponents in a path segment without being encoded for better readability
'%3B' => ';' ,
'%2C' => ',' ,
'%3D' => '=' ,
'%2B' => '+' ,
'%21' => '!' ,
'%2A' => '*' ,
'%7C' => '|' ,
2011-07-07 08:38:15 +01:00
);
2011-03-23 18:24:18 +00:00
2011-07-26 06:57:40 +01:00
protected $routes ;
2010-05-06 12:25:53 +01:00
/**
* Constructor .
*
2011-04-23 16:05:44 +01:00
* @ param RouteCollection $routes A RouteCollection instance
* @ param RequestContext $context The context
2012-06-09 15:43:04 +01:00
* @ param LoggerInterface $logger A logger instance
2011-06-14 14:35:32 +01:00
*
* @ api
2010-05-06 12:25:53 +01:00
*/
2012-06-09 15:43:04 +01:00
public function __construct ( RouteCollection $routes , RequestContext $context , LoggerInterface $logger = null )
2010-02-17 13:53:31 +00:00
{
2010-05-06 12:25:53 +01:00
$this -> routes = $routes ;
$this -> context = $context ;
2012-06-09 15:43:04 +01:00
$this -> logger = $logger ;
2010-02-17 13:53:31 +00:00
}
2011-01-24 15:59:32 +00:00
/**
2012-05-05 00:43:00 +01:00
* { @ inheritdoc }
2011-01-24 15:59:32 +00:00
*/
2011-04-20 12:54:39 +01:00
public function setContext ( RequestContext $context )
2011-01-24 15:59:32 +00:00
{
$this -> context = $context ;
}
2011-04-21 20:20:27 +01:00
/**
2012-05-05 00:43:00 +01:00
* { @ inheritdoc }
2011-04-21 20:20:27 +01:00
*/
public function getContext ()
{
return $this -> context ;
}
2010-05-06 12:25:53 +01:00
/**
2012-06-09 15:43:04 +01:00
* Enables or disables the exception on incorrect parameters .
*
* @ param Boolean $enabled
*/
public function setStrictParameters ( $enabled )
{
$this -> strictParameters = $enabled ;
}
/**
* Gets the strict check of incorrect parameters .
*
* @ return Boolean
*/
public function getStrictParameters ()
{
return $this -> strictParameters ;
}
2010-05-06 12:25:53 +01:00
/**
2011-11-08 07:47:17 +00:00
* { @ inheritDoc }
2010-05-06 12:25:53 +01:00
*/
2011-07-26 06:57:40 +01:00
public function generate ( $name , $parameters = array (), $absolute = false )
2010-02-17 13:53:31 +00:00
{
2010-11-23 08:42:19 +00:00
if ( null === $route = $this -> routes -> get ( $name )) {
2011-05-17 15:51:56 +01:00
throw new RouteNotFoundException ( sprintf ( 'Route "%s" does not exist.' , $name ));
2010-05-06 12:25:53 +01:00
}
2010-02-17 13:53:31 +00:00
2012-04-12 03:08:51 +01:00
// the Route has a cache of its own and is not recompiled as long as it does not get modified
$compiledRoute = $route -> compile ();
2010-02-17 13:53:31 +00:00
2012-04-12 03:08:51 +01:00
return $this -> doGenerate ( $compiledRoute -> getVariables (), $route -> getDefaults (), $route -> getRequirements (), $compiledRoute -> getTokens (), $parameters , $name , $absolute );
2010-02-17 13:53:31 +00:00
}
2010-05-06 12:25:53 +01:00
/**
2011-11-08 07:47:17 +00:00
* @ throws MissingMandatoryParametersException When route has some missing mandatory parameters
* @ throws InvalidParameterException When a parameter value is not correct
2010-05-06 12:25:53 +01:00
*/
protected function doGenerate ( $variables , $defaults , $requirements , $tokens , $parameters , $name , $absolute )
2010-02-17 13:53:31 +00:00
{
2011-04-25 11:03:41 +01:00
$variables = array_flip ( $variables );
2011-04-21 08:52:35 +01:00
$originParameters = $parameters ;
2011-04-20 22:01:05 +01:00
$parameters = array_replace ( $this -> context -> getParameters (), $parameters );
$tparams = array_replace ( $defaults , $parameters );
2010-05-06 12:25:53 +01:00
// all params must be given
2010-05-07 15:09:11 +01:00
if ( $diff = array_diff_key ( $variables , $tparams )) {
2011-05-26 11:54:21 +01:00
throw new MissingMandatoryParametersException ( sprintf ( 'The "%s" route has some missing mandatory parameters ("%s").' , $name , implode ( '", "' , array_keys ( $diff ))));
2010-02-17 13:53:31 +00:00
}
2010-05-06 12:25:53 +01:00
$url = '' ;
$optional = true ;
2010-05-07 15:09:11 +01:00
foreach ( $tokens as $token ) {
2010-05-08 14:32:30 +01:00
if ( 'variable' === $token [ 0 ]) {
2011-07-19 18:18:29 +01:00
if ( false === $optional || ! array_key_exists ( $token [ 3 ], $defaults ) || ( isset ( $parameters [ $token [ 3 ]]) && ( string ) $parameters [ $token [ 3 ]] != ( string ) $defaults [ $token [ 3 ]])) {
2011-05-03 13:48:08 +01:00
if ( ! $isEmpty = in_array ( $tparams [ $token [ 3 ]], array ( null , '' , false ), true )) {
// check requirement
if ( $tparams [ $token [ 3 ]] && ! preg_match ( '#^' . $token [ 2 ] . '$#' , $tparams [ $token [ 3 ]])) {
2012-06-09 15:43:04 +01:00
$message = sprintf ( 'Parameter "%s" for route "%s" must match "%s" ("%s" given).' , $token [ 3 ], $name , $token [ 2 ], $tparams [ $token [ 3 ]]);
if ( $this -> strictParameters ) {
throw new InvalidParameterException ( $message );
}
if ( $this -> logger ) {
$this -> logger -> err ( $message );
}
return null ;
2011-05-03 13:48:08 +01:00
}
2010-05-06 12:25:53 +01:00
}
2011-05-03 13:48:08 +01:00
if ( ! $isEmpty || ! $optional ) {
2012-05-04 14:28:36 +01:00
$url = $token [ 1 ] . $tparams [ $token [ 3 ]] . $url ;
2011-04-18 14:35:05 +01:00
}
2010-05-06 12:25:53 +01:00
$optional = false ;
}
2010-05-07 15:09:11 +01:00
} elseif ( 'text' === $token [ 0 ]) {
2011-04-25 11:03:41 +01:00
$url = $token [ 1 ] . $url ;
2010-05-06 12:25:53 +01:00
$optional = false ;
}
2010-02-17 13:53:31 +00:00
}
2010-05-07 15:09:11 +01:00
if ( ! $url ) {
2010-05-06 12:25:53 +01:00
$url = '/' ;
}
2010-02-17 13:53:31 +00:00
2012-05-04 14:28:36 +01:00
// do not encode the contexts base url as it is already encoded (see Symfony\Component\HttpFoundation\Request)
$url = $this -> context -> getBaseUrl () . strtr ( rawurlencode ( $url ), $this -> decodedChars );
2010-05-06 12:25:53 +01:00
// add a query string if needed
2011-07-22 17:51:08 +01:00
$extra = array_diff_key ( $originParameters , $variables , $defaults );
2012-04-23 08:55:54 +01:00
if ( $extra && $query = http_build_query ( $extra , '' , '&' )) {
2011-07-22 17:51:08 +01:00
$url .= '?' . $query ;
2010-05-06 12:25:53 +01:00
}
2010-02-17 13:53:31 +00:00
2011-04-20 12:54:39 +01:00
if ( $this -> context -> getHost ()) {
$scheme = $this -> context -> getScheme ();
2011-04-19 23:25:45 +01:00
if ( isset ( $requirements [ '_scheme' ]) && ( $req = strtolower ( $requirements [ '_scheme' ])) && $scheme != $req ) {
$absolute = true ;
$scheme = $req ;
}
if ( $absolute ) {
$port = '' ;
2011-04-20 12:54:39 +01:00
if ( 'http' === $scheme && 80 != $this -> context -> getHttpPort ()) {
$port = ':' . $this -> context -> getHttpPort ();
} elseif ( 'https' === $scheme && 443 != $this -> context -> getHttpsPort ()) {
$port = ':' . $this -> context -> getHttpsPort ();
2011-04-19 23:25:45 +01:00
}
2011-04-20 12:54:39 +01:00
$url = $scheme . '://' . $this -> context -> getHost () . $port . $url ;
2011-02-03 15:21:41 +00:00
}
2010-05-06 12:25:53 +01:00
}
2010-02-17 13:53:31 +00:00
2010-05-06 12:25:53 +01:00
return $url ;
}
2010-02-17 13:53:31 +00:00
}