mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-03 22:32:40 +08:00
Merge PR #12697
* erickskrauch-12691-url-rule-host-without-protocol: added test for #12697 Used non-regex solution for trimming slashes Prefer strpos to substr Implementation of support UrlRule with relative host definition fixes #12691
This commit is contained in:
@ -79,6 +79,7 @@ Yii Framework 2 Change Log
|
||||
- Enh #12419: Added ability to remove root tag and object tags for `yii\web\XmlResponseFormatter` (mhthnz, samdark)
|
||||
- Enh #12619: Added catch `Throwable` in `yii\base\ErrorHandler::handleException()`, transactions and simlar places where consistency must be kept after exception (rob006, cebe)
|
||||
- Enh #12659: Suggest alternatives when console command was not found (mdmunir, cebe)
|
||||
- Enh #12691: Added support for protocol-relative URLs in `yii\web\UrlRule::$pattern` (erickskrauch)
|
||||
- Enh #12726: `yii\base\Application::$version` converted to `yii\base\Module::$version` virtual property, allowing to specify version as a PHP callback (klimov-paul)
|
||||
- Enh #12732: Added `is_dir()` validation to `yii\helpers\BaseFileHelper::findFiles()` method (zalatov, silverfire)
|
||||
- Enh #12738: Added support for creating protocol-relative URLs in `UrlManager::createAbsoluteUrl()` and `Url` helper methods (rob006)
|
||||
|
||||
@ -396,6 +396,12 @@ class UrlManager extends Component
|
||||
} else {
|
||||
return $url . $baseUrl . $anchor;
|
||||
}
|
||||
} elseif (strpos($url, '//') === 0) {
|
||||
if ($baseUrl !== '' && ($pos = strpos($url, '/', 2)) !== false) {
|
||||
return substr($url, 0, $pos) . $baseUrl . substr($url, $pos) . $anchor;
|
||||
} else {
|
||||
return $url . $baseUrl . $anchor;
|
||||
}
|
||||
} else {
|
||||
return "$baseUrl/{$url}{$anchor}";
|
||||
}
|
||||
@ -475,7 +481,12 @@ class UrlManager extends Component
|
||||
$params = (array) $params;
|
||||
$url = $this->createUrl($params);
|
||||
if (strpos($url, '://') === false) {
|
||||
$url = $this->getHostInfo() . $url;
|
||||
$hostInfo = $this->getHostInfo();
|
||||
if (strpos($url, '//') === 0) {
|
||||
$url = substr($hostInfo, 0, strpos($hostInfo, '://')) . ':' . $url;
|
||||
} else {
|
||||
$url = $hostInfo . $url;
|
||||
}
|
||||
}
|
||||
|
||||
return Url::ensureScheme($url, $scheme);
|
||||
|
||||
@ -178,7 +178,7 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
$this->name = $this->pattern;
|
||||
}
|
||||
|
||||
$this->pattern = trim($this->pattern, '/');
|
||||
$this->pattern = $this->trimSlashes($this->pattern);
|
||||
$this->route = trim($this->route, '/');
|
||||
|
||||
if ($this->host !== null) {
|
||||
@ -195,6 +195,12 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
} else {
|
||||
$this->host = $this->pattern;
|
||||
}
|
||||
} elseif (strpos($this->pattern, '//') === 0) {
|
||||
if (($pos2 = strpos($this->pattern, '/', $pos + 2)) !== false) {
|
||||
$this->host = substr($this->pattern, 0, $pos2);
|
||||
} else {
|
||||
$this->host = $this->pattern;
|
||||
}
|
||||
} else {
|
||||
$this->pattern = '/' . $this->pattern . '/';
|
||||
}
|
||||
@ -244,6 +250,11 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
$this->_template = preg_replace('/<([\w._-]+):?([^>]+)?>/', '<$1>', $this->pattern);
|
||||
$this->pattern = '#^' . trim(strtr($this->_template, $tr), '/') . '$#u';
|
||||
|
||||
// if host starts with relative scheme, then insert pattern to match any
|
||||
if (strpos($this->host, '//') === 0) {
|
||||
$this->pattern = substr_replace($this->pattern, '[\w]+://', 2, 0);
|
||||
}
|
||||
|
||||
if (!empty($this->_routeParams)) {
|
||||
$this->_routeRule = '#^' . strtr($this->route, $tr2) . '$#u';
|
||||
}
|
||||
@ -415,7 +426,7 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
}
|
||||
}
|
||||
|
||||
$url = trim(strtr($this->_template, $tr), '/');
|
||||
$url = $this->trimSlashes(strtr($this->_template, $tr));
|
||||
if ($this->host !== null) {
|
||||
$pos = strpos($url, '/', 8);
|
||||
if ($pos !== false) {
|
||||
@ -467,4 +478,18 @@ class UrlRule extends Object implements UrlRuleInterface
|
||||
}
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim slashes in passed string. If string begins with '//', two slashes are left as is
|
||||
* in the beginning of a string.
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
private function trimSlashes($string) {
|
||||
if (strpos($string, '//') === 0) {
|
||||
return '//' . trim($string, '/');
|
||||
}
|
||||
return trim($string, '/');
|
||||
}
|
||||
}
|
||||
|
||||
@ -472,7 +472,7 @@ class UrlManagerCreateUrlTest extends TestCase
|
||||
[
|
||||
'pattern' => 'post/<id>/<title>',
|
||||
'route' => 'post/view',
|
||||
'host' => 'http://<lang:en|fr>.example.com', // TODO variation of scheme https://github.com/yiisoft/yii2/issues/12691
|
||||
'host' => 'http://<lang:en|fr>.example.com',
|
||||
],
|
||||
// note: baseUrl is not included in the pattern
|
||||
'http://www.example.com/login' => 'site/login',
|
||||
@ -520,6 +520,86 @@ class UrlManagerCreateUrlTest extends TestCase
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test rules that have host info in the patterns, that are protocol relative.
|
||||
* @dataProvider absolutePatternsVariations
|
||||
* @see https://github.com/yiisoft/yii2/issues/12691
|
||||
*/
|
||||
public function testProtocolRelativeAbsolutePattern($showScriptName, $prefix, $config)
|
||||
{
|
||||
$config['rules'] = [
|
||||
[
|
||||
'pattern' => 'post/<id>/<title>',
|
||||
'route' => 'post/view',
|
||||
'host' => '//<lang:en|fr>.example.com',
|
||||
],
|
||||
// note: baseUrl is not included in the pattern
|
||||
'//www.example.com/login' => 'site/login',
|
||||
'//app.example.com' => 'app/index',
|
||||
'//app2.example.com/' => 'app2/index',
|
||||
];
|
||||
$manager = $this->getUrlManager($config, $showScriptName);
|
||||
// first rule matches
|
||||
$urlParams = ['post/view', 'id' => 1, 'title' => 'sample post', 'lang' => 'en'];
|
||||
$expected = "//en.example.com$prefix/post/1/sample+post";
|
||||
$this->assertEquals($expected, $manager->createUrl($urlParams));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams, true));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams, 'http'));
|
||||
$this->assertEquals("https:$expected", $manager->createAbsoluteUrl($urlParams, 'https'));
|
||||
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams, '')); // protocol relative Url
|
||||
|
||||
$urlParams = ['post/view', 'id' => 1, 'title' => 'sample post', 'lang' => 'en', '#' => 'testhash'];
|
||||
$expected = "//en.example.com$prefix/post/1/sample+post#testhash";
|
||||
$this->assertEquals($expected, $manager->createUrl($urlParams));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams));
|
||||
|
||||
// second rule matches
|
||||
$urlParams = ['site/login'];
|
||||
$expected = "//www.example.com$prefix/login";
|
||||
$this->assertEquals($expected, $manager->createUrl($urlParams));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams, true));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams, 'http'));
|
||||
$this->assertEquals("https:$expected", $manager->createAbsoluteUrl($urlParams, 'https'));
|
||||
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams, '')); // protocol relative Url
|
||||
|
||||
// third rule matches
|
||||
$urlParams = ['app/index'];
|
||||
$expected = "//app.example.com$prefix";
|
||||
$this->assertEquals($expected, $manager->createUrl($urlParams));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams, true));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams, 'http'));
|
||||
$this->assertEquals("https:$expected", $manager->createAbsoluteUrl($urlParams, 'https'));
|
||||
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams, '')); // protocol relative Url
|
||||
|
||||
// fourth rule matches
|
||||
$urlParams = ['app2/index'];
|
||||
$expected = "//app2.example.com$prefix";
|
||||
$this->assertEquals($expected, $manager->createUrl($urlParams));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams, true));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams, 'http'));
|
||||
$this->assertEquals("https:$expected", $manager->createAbsoluteUrl($urlParams, 'https'));
|
||||
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams, '')); // protocol relative Url
|
||||
|
||||
// none of the rules matches
|
||||
$urlParams = ['post/index', 'page' => 1];
|
||||
$this->assertEquals("$prefix/post/index?page=1", $manager->createUrl($urlParams));
|
||||
$expected = "//www.example.com$prefix/post/index?page=1";
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams, true));
|
||||
$this->assertEquals("http:$expected", $manager->createAbsoluteUrl($urlParams, 'http'));
|
||||
$this->assertEquals("https:$expected", $manager->createAbsoluteUrl($urlParams, 'https'));
|
||||
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams, '')); // protocol relative Url
|
||||
|
||||
$urlParams = ['post/index', 'page' => 1, '#' => 'testhash'];
|
||||
$this->assertEquals("$prefix/post/index?page=1#testhash", $manager->createUrl($urlParams));
|
||||
$expected = "http://www.example.com$prefix/post/index?page=1#testhash";
|
||||
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams));
|
||||
}
|
||||
|
||||
public function multipleHostsRulesDataProvider()
|
||||
{
|
||||
return [
|
||||
|
||||
@ -670,6 +670,31 @@ class UrlRuleTest extends TestCase
|
||||
['post/index', ['page' => 1, 'tag' => 'a', 'lang' => 'en'], 'http://en.example.com/post/a'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'with relative host info',
|
||||
[
|
||||
'pattern' => 'post/<page:\d+>/<tag>',
|
||||
'route' => 'post/index',
|
||||
'defaults' => ['page' => 1],
|
||||
'host' => '//<lang:en|fr>.example.com',
|
||||
],
|
||||
[
|
||||
['post/index', ['page' => 1, 'tag' => 'a'], false],
|
||||
['post/index', ['page' => 1, 'tag' => 'a', 'lang' => 'en'], '//en.example.com/post/a'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'with relative host info in pattern',
|
||||
[
|
||||
'pattern' => '//<lang:en|fr>.example.com/post/<page:\d+>/<tag>',
|
||||
'route' => 'post/index',
|
||||
'defaults' => ['page' => 1],
|
||||
],
|
||||
[
|
||||
['post/index', ['page' => 1, 'tag' => 'a'], false],
|
||||
['post/index', ['page' => 1, 'tag' => 'a', 'lang' => 'en'], '//en.example.com/post/a'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'with unicode',
|
||||
[
|
||||
@ -1037,6 +1062,31 @@ class UrlRuleTest extends TestCase
|
||||
['2', ['post/index', ['page' => 2]]],
|
||||
],
|
||||
],
|
||||
[
|
||||
'with relative host info',
|
||||
[
|
||||
'pattern' => 'post/<page:\d+>',
|
||||
'route' => 'post/index',
|
||||
'host' => '//<lang:en|fr>.example.com',
|
||||
],
|
||||
[
|
||||
['post/1', ['post/index', ['page' => '1', 'lang' => 'en']]],
|
||||
['post/a', false],
|
||||
['post/1/a', false],
|
||||
],
|
||||
],
|
||||
[
|
||||
'with relative host info in pattern',
|
||||
[
|
||||
'pattern' => '//<lang:en|fr>.example.com/post/<page:\d+>',
|
||||
'route' => 'post/index',
|
||||
],
|
||||
[
|
||||
['post/1', ['post/index', ['page' => '1', 'lang' => 'en']]],
|
||||
['post/a', false],
|
||||
['post/1/a', false],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user