Skip to content

Commit

Permalink
Add support for creating protocol-relative URLs.
Browse files Browse the repository at this point in the history
Improve phpdoc for protocol-relative URL creation.
Remove useless `is_string()` check.
  • Loading branch information
rob006 authored and SilverFire committed Nov 29, 2016
1 parent f23dc5a commit 2d91723
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 36 deletions.
1 change: 1 addition & 0 deletions framework/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Yii Framework 2 Change Log
- Enh #12015: Changed visibility `yii\db\ActiveQueryTrait::createModels()` from private to protected (ArekX, dynasource)
- Enh #12619: Added catch `Throwable` in `yii\base\ErrorHandler::handleException()` (rob006)
- 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 #12738: Added support for creating protocol-relative URLs in `UrlManager::createAbsoluteUrl()` and `Url` helper methods (rob006)
- Enh #12748: Added Migration tool automatic generation reference column for foreignKey (MKiselev)
- Enh #12748: Migration generator now tries to fetch reference column name for foreignKey from schema if it's not set explicitly (MKiselev)
- Enh #12750: `yii\widgets\ListView::itemOptions` can be a closure now (webdevsega, silverfire)
Expand Down
88 changes: 58 additions & 30 deletions framework/helpers/BaseUrl.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ class BaseUrl
* @param bool|string $scheme the URI scheme to use in the generated URL:
*
* - `false` (default): generating a relative URL.
* - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::hostInfo]].
* - string: generating an absolute URL with the specified scheme (either `http` or `https`).
* - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
* - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string
* for protocol-relative URL).
*
* @return string the generated URL
* @throws InvalidParamException a relative route is given while there is no active controller
Expand All @@ -97,7 +98,7 @@ public static function toRoute($route, $scheme = false)
$route = (array) $route;
$route[0] = static::normalizeRoute($route[0]);

if ($scheme) {
if ($scheme !== false) {
return static::getUrlManager()->createAbsoluteUrl($route, is_string($scheme) ? $scheme : null);
} else {
return static::getUrlManager()->createUrl($route);
Expand Down Expand Up @@ -160,8 +161,8 @@ protected static function normalizeRoute($route)
* - an empty string: the currently requested URL will be returned;
* - a normal string: it will be returned as is.
*
* When `$scheme` is specified (either a string or true), an absolute URL with host info (obtained from
* [[\yii\web\UrlManager::hostInfo]]) will be returned. If `$url` is already an absolute URL, its scheme
* When `$scheme` is specified (either a string or `true`), an absolute URL with host info (obtained from
* [[\yii\web\UrlManager::$hostInfo]]) will be returned. If `$url` is already an absolute URL, its scheme
* will be replaced with the specified one.
*
* Below are some examples of using this method:
Expand Down Expand Up @@ -190,15 +191,19 @@ protected static function normalizeRoute($route)
*
* // https://www.example.com/images/logo.gif
* echo Url::to('@web/images/logo.gif', 'https');
*
* // //www.example.com/images/logo.gif
* echo Url::to('@web/images/logo.gif', '');
* ```
*
*
* @param array|string $url the parameter to be used to generate a valid URL
* @param bool|string $scheme the URI scheme to use in the generated URL:
*
* - `false` (default): generating a relative URL.
* - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::hostInfo]].
* - string: generating an absolute URL with the specified scheme (either `http` or `https`).
* - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
* - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string
* for protocol-relative URL).
*
* @return string the generated URL
* @throws InvalidParamException a relative route is given while there is no active controller
Expand All @@ -214,23 +219,46 @@ public static function to($url = '', $scheme = false)
$url = Yii::$app->getRequest()->getUrl();
}

if (!$scheme) {
if ($scheme === false) {
return $url;
}

if (strncmp($url, '//', 2) === 0) {
// e.g. //hostname/path/to/resource
return is_string($scheme) ? "$scheme:$url" : $url;
}

if (($pos = strpos($url, ':')) === false || !ctype_alpha(substr($url, 0, $pos))) {
// turn relative URL into absolute
$url = static::getUrlManager()->getHostInfo() . '/' . ltrim($url, '/');
}

if (is_string($scheme) && ($pos = strpos($url, ':')) !== false) {
// replace the scheme with the specified one
$url = $scheme . substr($url, $pos);
return static::ensureScheme($url, $scheme);
}

/**
* Normalize URL by ensuring that it use specified scheme.
*
* If URL is relative or scheme is not string, normalization is skipped.
*
* @param string $url the URL to process
* @param string $scheme the URI scheme used in URL (e.g. `http` or `https`). Use empty string to
* create protocol-relative URL (e.g. `//example.com/path`)
* @return string the processed URL
* @since 2.0.11
*/
public static function ensureScheme($url, $scheme)
{
if (static::isRelative($url) || !is_string($scheme)) {
return $url;
}

if (substr($url, 0, 2) === '//') {
// e.g. //example.com/path/to/resource
return $scheme === '' ? $url : "$scheme:$url";
}

if (($pos = strpos($url, '://')) !== false) {
if ($scheme === '') {
$url = substr($url, $pos + 1);
} else {
$url = $scheme . substr($url, $pos);
}
}

return $url;
Expand All @@ -241,19 +269,19 @@ public static function to($url = '', $scheme = false)
* @param bool|string $scheme the URI scheme to use in the returned base URL:
*
* - `false` (default): returning the base URL without host info.
* - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::hostInfo]].
* - string: returning an absolute base URL with the specified scheme (either `http` or `https`).
* - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
* - string: returning an absolute base URL with the specified scheme (either `http`, `https` or empty string
* for protocol-relative URL).
* @return string
*/
public static function base($scheme = false)
{
$url = static::getUrlManager()->getBaseUrl();
if ($scheme) {
if ($scheme !== false) {
$url = static::getUrlManager()->getHostInfo() . $url;
if (is_string($scheme) && ($pos = strpos($url, '://')) !== false) {
$url = $scheme . substr($url, $pos);
}
$url = static::ensureScheme($url, $scheme);
}

return $url;
}

Expand Down Expand Up @@ -320,20 +348,19 @@ public static function canonical()
* @param bool|string $scheme the URI scheme to use for the returned URL:
*
* - `false` (default): returning a relative URL.
* - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::hostInfo]].
* - string: returning an absolute URL with the specified scheme (either `http` or `https`).
* - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
* - string: returning an absolute URL with the specified scheme (either `http`, `https` or empty string
* for protocol-relative URL).
*
* @return string home URL
*/
public static function home($scheme = false)
{
$url = Yii::$app->getHomeUrl();

if ($scheme) {
if ($scheme !== false) {
$url = static::getUrlManager()->getHostInfo() . $url;
if (is_string($scheme) && ($pos = strpos($url, '://')) !== false) {
$url = $scheme . substr($url, $pos);
}
$url = static::ensureScheme($url, $scheme);
}

return $url;
Expand Down Expand Up @@ -387,8 +414,9 @@ public static function isRelative($url)
* @param bool|string $scheme the URI scheme to use in the generated URL:
*
* - `false` (default): generating a relative URL.
* - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::hostInfo]].
* - string: generating an absolute URL with the specified scheme (either `http` or `https`).
* - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
* - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string
* for protocol-relative URL).
*
* @return string the generated URL
* @since 2.0.3
Expand Down
11 changes: 5 additions & 6 deletions framework/web/UrlManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\caching\Cache;
use yii\helpers\Url;

/**
* UrlManager handles HTTP request parsing and creation of URLs based on a set of rules.
Expand Down Expand Up @@ -452,8 +453,9 @@ protected function setRuleToCache($cacheKey, UrlRuleInterface $rule)
*
* @param string|array $params use a string to represent a route (e.g. `site/index`),
* or an array to represent a route with query parameters (e.g. `['site/index', 'param1' => 'value1']`).
* @param string $scheme the scheme to use for the url (either `http` or `https`). If not specified
* the scheme of the current request will be used.
* @param string|null $scheme the scheme to use for the URL (either `http`, `https` or empty string
* for protocol-relative URL).
* If not specified the scheme of the current request will be used.
* @return string the created URL
* @see createUrl()
*/
Expand All @@ -464,11 +466,8 @@ public function createAbsoluteUrl($params, $scheme = null)
if (strpos($url, '://') === false) {
$url = $this->getHostInfo() . $url;
}
if (is_string($scheme) && ($pos = strpos($url, '://')) !== false) {
$url = $scheme . substr($url, $pos);
}

return $url;
return Url::ensureScheme($url, $scheme);
}

/**
Expand Down
6 changes: 6 additions & 0 deletions tests/framework/helpers/UrlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ public function testToRoute()
$this->assertEquals('/base/index.php?r=', Url::toRoute('/'));
$this->assertEquals('http://example.com/base/index.php?r=page%2Fview', Url::toRoute('', true));
$this->assertEquals('https://example.com/base/index.php?r=page%2Fview', Url::toRoute('', 'https'));
$this->assertEquals('//example.com/base/index.php?r=page%2Fview', Url::toRoute('', ''));

// If the route contains no slashes at all, it is considered to be an action ID of the current controller and
// will be prepended with uniqueId;
$this->assertEquals('/base/index.php?r=page%2Fedit', Url::toRoute('edit'));
$this->assertEquals('/base/index.php?r=page%2Fedit&id=20', Url::toRoute(['edit', 'id' => 20]));
$this->assertEquals('http://example.com/base/index.php?r=page%2Fedit&id=20', Url::toRoute(['edit', 'id' => 20], true));
$this->assertEquals('https://example.com/base/index.php?r=page%2Fedit&id=20', Url::toRoute(['edit', 'id' => 20], 'https'));
$this->assertEquals('//example.com/base/index.php?r=page%2Fedit&id=20', Url::toRoute(['edit', 'id' => 20], ''));

// If the route has no leading slash, it is considered to be a route relative
// to the current module and will be prepended with the module's uniqueId.
Expand All @@ -86,6 +88,7 @@ public function testToRoute()
$this->assertEquals('/base/index.php?r=stats%2Fuser%2Fview&id=42', Url::toRoute(['user/view', 'id' => 42]));
$this->assertEquals('http://example.com/base/index.php?r=stats%2Fuser%2Fview&id=42', Url::toRoute(['user/view', 'id' => 42], true));
$this->assertEquals('https://example.com/base/index.php?r=stats%2Fuser%2Fview&id=42', Url::toRoute(['user/view', 'id' => 42], 'https'));
$this->assertEquals('//example.com/base/index.php?r=stats%2Fuser%2Fview&id=42', Url::toRoute(['user/view', 'id' => 42], ''));

// alias support
\Yii::setAlias('@userView', 'user/view');
Expand Down Expand Up @@ -181,6 +184,7 @@ public function testTo()
$this->assertEquals('#test', Url::to('@web5'));
$this->assertEquals('http://example.com/#test', Url::to('@web5', true));
$this->assertEquals('https://example.com/#test', Url::to('@web5', 'https'));
$this->assertEquals('//example.com/#test', Url::to('@web5', ''));

//In case there is no controller, throw an exception
$this->removeMockedAction();
Expand Down Expand Up @@ -228,13 +232,15 @@ public function testBase()
$this->assertEquals('/base', Url::base());
$this->assertEquals('http://example.com/base', Url::base(true));
$this->assertEquals('https://example.com/base', Url::base('https'));
$this->assertEquals('//example.com/base', Url::base(''));
}

public function testHome()
{
$this->assertEquals('/base/index.php', Url::home());
$this->assertEquals('http://example.com/base/index.php', Url::home(true));
$this->assertEquals('https://example.com/base/index.php', Url::home('https'));
$this->assertEquals('//example.com/base/index.php', Url::home(''));
}

public function testCanonical()
Expand Down
6 changes: 6 additions & 0 deletions tests/framework/web/UrlManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,15 @@ public function testCreateAbsoluteUrl()
$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post'], 'https');
$this->assertEquals('https://www.example.com?r=post%2Fview&id=1&title=sample+post', $url);

$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post'], '');
$this->assertEquals('//www.example.com?r=post%2Fview&id=1&title=sample+post', $url);

$manager->hostInfo = 'https://www.example.com';
$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post'], 'http');
$this->assertEquals('http://www.example.com?r=post%2Fview&id=1&title=sample+post', $url);

$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post'], '');
$this->assertEquals('//www.example.com?r=post%2Fview&id=1&title=sample+post', $url);
}

public function testCreateAbsoluteUrlWithSuffix()
Expand Down

0 comments on commit 2d91723

Please sign in to comment.