-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[Routing] Support UTF-8 in paths and parameters #19562
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,13 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt | |
*/ | ||
protected $logger; | ||
|
||
/** | ||
* The output encoding used when generating URLs (path, query string and fragment). | ||
* | ||
* @var string | ||
*/ | ||
protected $charset; | ||
|
||
/** | ||
* This array defines the characters (besides alphanumeric ones) that will not be percent-encoded in the path segment of the generated URL. | ||
* | ||
|
@@ -81,12 +88,14 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt | |
* @param RouteCollection $routes A RouteCollection instance | ||
* @param RequestContext $context The context | ||
* @param LoggerInterface|null $logger A logger instance | ||
* @param string $charset The encoding used in the generated URLs | ||
*/ | ||
public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null) | ||
public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null, $charset = 'UTF-8') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. forgot to upgrade the docblock with new arg |
||
{ | ||
$this->routes = $routes; | ||
$this->context = $context; | ||
$this->logger = $logger; | ||
$this->charset = $charset; | ||
} | ||
|
||
/** | ||
|
@@ -158,7 +167,7 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa | |
if ('variable' === $token[0]) { | ||
if (!$optional || !array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) { | ||
// check requirement | ||
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#', $mergedParams[$token[3]])) { | ||
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#u', $mergedParams[$token[3]])) { | ||
if ($this->strictRequirements) { | ||
throw new InvalidParameterException(strtr($message, array('{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]))); | ||
} | ||
|
@@ -185,7 +194,7 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa | |
} | ||
|
||
// the contexts base URL is already encoded (see Symfony\Component\HttpFoundation\Request) | ||
$url = strtr(rawurlencode($url), $this->decodedChars); | ||
$url = strtr(rawurlencode($this->fromUtf8($url)), $this->decodedChars); | ||
|
||
// the path segments "." and ".." are interpreted as relative reference when resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3 | ||
// so we need to encode them as they are not used for this purpose here | ||
|
@@ -266,19 +275,45 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa | |
$fragment = isset($extra['_fragment']) ? $extra['_fragment'] : ''; | ||
unset($extra['_fragment']); | ||
|
||
if ($extra && $query = http_build_query($extra, '', '&')) { | ||
// "/" and "?" can be left decoded for better user experience, see | ||
// ignore null values | ||
$extra = array_filter($extra, function ($value) { return null !== $value; }); | ||
|
||
if ($extra) { | ||
if ($this->charset != 'UTF-8') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
https://symfony.com/doc/current/contributing/code/standards.html#structure
|
||
$extra = array_combine( | ||
array_map(array($this, 'fromUtf8'), array_keys($extra)), | ||
array_map(array($this, 'fromUtf8'), $extra) | ||
); | ||
} | ||
$query = http_build_query($extra, '', '&'); | ||
// "/" and "?" can be left decoded for better user experience, see | ||
// http://tools.ietf.org/html/rfc3986#section-3.4 | ||
$url .= '?'.strtr($query, array('%2F' => '/')); | ||
} | ||
|
||
if ('' !== $fragment) { | ||
$url .= '#'.strtr(rawurlencode($fragment), array('%2F' => '/', '%3F' => '?')); | ||
$url .= '#'.strtr(rawurlencode($this->fromUtf8($fragment)), array('%2F' => '/', '%3F' => '?')); | ||
} | ||
|
||
return $url; | ||
} | ||
|
||
/** | ||
* Converts a string from UTF-8 to the encoding used in URLs. | ||
* | ||
* @param string $string A UTF-8-encoded string | ||
* | ||
* @return string A string in the encoded used in URLs | ||
*/ | ||
protected function fromUtf8($string) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. docblock documenting arg and return value |
||
{ | ||
if ('UTF-8' === $this->charset) { | ||
return $string; | ||
} | ||
|
||
return mb_convert_encoding($string, $this->charset, 'UTF-8'); | ||
} | ||
|
||
/** | ||
* Returns the target path as relative reference from the base path. | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to which version will this PR be merged to? 4.x?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Because the regexp generated by RouteCompiler now has the
u
flag, this is a BC-breaking change. See my question regarding that issue here.