mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-17 23:09:10 +08:00
Merge pull request #10064 from SilverFire/9999-url-validator
Fixed `yii\web\UrlRule` to allow route parameter names with `-`, `_`, `.` characters
This commit is contained in:
@@ -30,6 +30,7 @@ Yii Framework 2 Change Log
|
||||
- Bug #9915: `yii\helpers\ArrayHelper::getValue()` was erroring instead of returning `null` for non-existing object properties (totaldev, samdark)
|
||||
- Bug #9924: Fixed `yii.js` handleAction corrupted parameter values containing quote (") character (silverfire)
|
||||
- Bug #9984: Fixed wrong captcha color in case Imagick is used (DrDeath72)
|
||||
- Bug #9999: Fixed `yii\web\UrlRule` to allow route parameter names with `-`, `_`, `.`characters (silverfire)
|
||||
- Bug #10029: Fixed MaskedInput not working with PJAX (martrix78, samdark)
|
||||
- Bug: Fixed generation of canonical URLs for `ViewAction` pages (samdark)
|
||||
- Enh #3506: Added `\yii\validators\IpValidator` to perform validation of IP addresses and subnets (SilverFire, samdark)
|
||||
|
||||
@@ -104,6 +104,18 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
* @var array list of parameters used in the route.
|
||||
*/
|
||||
private $_routeParams = [];
|
||||
/**
|
||||
* @var array list of placeholders for matching parameters names. Used in [[parseRequest()]], [[createUrl()]]
|
||||
* On the rule initialization, the [[pattern]] parameters names will be replaced with placeholders.
|
||||
* This array contains relations between the original parameters names and their placeholders.
|
||||
* key - placeholder
|
||||
* value - original name
|
||||
*
|
||||
* @see parseRequest()
|
||||
* @see createUrl()
|
||||
* @since 2.0.7
|
||||
*/
|
||||
private $_placeholders = [];
|
||||
|
||||
|
||||
/**
|
||||
@@ -151,7 +163,7 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
$this->pattern = '/' . $this->pattern . '/';
|
||||
}
|
||||
|
||||
if (strpos($this->route, '<') !== false && preg_match_all('/<(\w+)>/', $this->route, $matches)) {
|
||||
if (strpos($this->route, '<') !== false && preg_match_all('/<([\w._-]+)>/', $this->route, $matches)) {
|
||||
foreach ($matches[1] as $name) {
|
||||
$this->_routeParams[$name] = "<$name>";
|
||||
}
|
||||
@@ -166,31 +178,34 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
'(' => '\\(',
|
||||
')' => '\\)',
|
||||
];
|
||||
|
||||
$tr2 = [];
|
||||
if (preg_match_all('/<(\w+):?([^>]+)?>/', $this->pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
|
||||
if (preg_match_all('/<([\w._-]+):?([^>]+)?>/', $this->pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
|
||||
foreach ($matches as $match) {
|
||||
$name = $match[1][0];
|
||||
$pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+';
|
||||
$placeholder = 'a' . hash('crc32b', $name); // placeholder must begin with a letter
|
||||
$this->_placeholders[$placeholder] = $name;
|
||||
if (array_key_exists($name, $this->defaults)) {
|
||||
$length = strlen($match[0][0]);
|
||||
$offset = $match[0][1];
|
||||
if ($offset > 1 && $this->pattern[$offset - 1] === '/' && (!isset($this->pattern[$offset + $length]) || $this->pattern[$offset + $length] === '/')) {
|
||||
$tr["/<$name>"] = "(/(?P<$name>$pattern))?";
|
||||
$tr["/<$name>"] = "(/(?P<$placeholder>$pattern))?";
|
||||
} else {
|
||||
$tr["<$name>"] = "(?P<$name>$pattern)?";
|
||||
$tr["<$name>"] = "(?P<$placeholder>$pattern)?";
|
||||
}
|
||||
} else {
|
||||
$tr["<$name>"] = "(?P<$name>$pattern)";
|
||||
$tr["<$name>"] = "(?P<$placeholder>$pattern)";
|
||||
}
|
||||
if (isset($this->_routeParams[$name])) {
|
||||
$tr2["<$name>"] = "(?P<$name>$pattern)";
|
||||
$tr2["<$name>"] = "(?P<$placeholder>$pattern)";
|
||||
} else {
|
||||
$this->_paramRules[$name] = $pattern === '[^\/]+' ? '' : "#^$pattern$#u";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_template = preg_replace('/<(\w+):?([^>]+)?>/', '<$1>', $this->pattern);
|
||||
$this->_template = preg_replace('/<([\w._-]+):?([^>]+)?>/', '<$1>', $this->pattern);
|
||||
$this->pattern = '#^' . trim(strtr($this->_template, $tr), '/') . '$#u';
|
||||
|
||||
if (!empty($this->_routeParams)) {
|
||||
@@ -216,7 +231,7 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
}
|
||||
|
||||
$pathInfo = $request->getPathInfo();
|
||||
$suffix = (string) ($this->suffix === null ? $manager->suffix : $this->suffix);
|
||||
$suffix = (string)($this->suffix === null ? $manager->suffix : $this->suffix);
|
||||
if ($suffix !== '' && $pathInfo !== '') {
|
||||
$n = strlen($suffix);
|
||||
if (substr_compare($pathInfo, $suffix, -$n, $n) === 0) {
|
||||
@@ -237,6 +252,8 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
if (!preg_match($this->pattern, $pathInfo, $matches)) {
|
||||
return false;
|
||||
}
|
||||
$matches = $this->substitutePlaceholderNames($matches);
|
||||
|
||||
foreach ($this->defaults as $name => $value) {
|
||||
if (!isset($matches[$name]) || $matches[$name] === '') {
|
||||
$matches[$name] = $value;
|
||||
@@ -281,6 +298,7 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
// match the route part first
|
||||
if ($route !== $this->route) {
|
||||
if ($this->_routeRule !== null && preg_match($this->_routeRule, $route, $matches)) {
|
||||
$matches = $this->substitutePlaceholderNames($matches);
|
||||
foreach ($this->_routeParams as $name => $token) {
|
||||
if (isset($this->defaults[$name]) && strcmp($this->defaults[$name], $matches[$name]) === 0) {
|
||||
$tr[$token] = '';
|
||||
@@ -352,4 +370,34 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
{
|
||||
return $this->_paramRules;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over [[_placeholders]] and checks whether each placeholder exists as a key in $matches array.
|
||||
* When found - replaces this placeholder key with a appropriate name of matching parameter.
|
||||
* Used in [[parseRequest()]], [[createUrl()]].
|
||||
*
|
||||
* @param array $matches result of `preg_match()` call
|
||||
* @return array input array with replaced placeholder keys
|
||||
* @see _placeholders
|
||||
* @since 2.0.7
|
||||
*/
|
||||
private function substitutePlaceholderNames (array $matches) {
|
||||
foreach ($this->_placeholders as $placeholder => $name) {
|
||||
if (isset($matches[$placeholder])) {
|
||||
$matches[$name] = $matches[$placeholder];
|
||||
unset($matches[$placeholder]);
|
||||
}
|
||||
}
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of placeholders and original names for matching parameters.
|
||||
* @return array
|
||||
* @since 2.0.7
|
||||
* @see _placeholders
|
||||
*/
|
||||
protected function getPlaceholders() {
|
||||
return $this->_placeholders;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user