Skip to content
Draft

5.11 #19062

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG-5.11.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Release Notes for Craft CMS 5.11 (WIP)

### Development
- The `params` argument of the `url()` Twig function now accepts `false` to remove all params from the passed-in URL. ([#19102](https://github.com/craftcms/cms/pull/19102))

### Extensibility
- Added `craft\helpers\UrlHelper::removeParams()`. ([#19102](https://github.com/craftcms/cms/pull/19102))
- Added `craft\helpers\UrlHelper::removeAllParams()`. ([#19102](https://github.com/craftcms/cms/pull/19102))
- The `$params` argument of `craft\helpers\UrlHelper::url()` now accepts `false` to remove all params from the passed-in URL. ([#19102](https://github.com/craftcms/cms/pull/19102))
65 changes: 58 additions & 7 deletions src/helpers/UrlHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,61 @@ public static function urlWithParams(string $url, array|string $params): string
* @since 3.2.2
*/
public static function removeParam(string $url, string $param): string
{
return static::removeParams($url, [$param]);
}

/**
* Removes query string params from a URL.
*
* @param string $url
* @param string[] $params
* @return string
* @since 5.11.0
*/
public static function removeParams(string $url, array $params): string
{
// Extract any params/fragment from the base URL
[$url, $urlParams, $fragment] = self::_extractParams($url);

// Remove the params
foreach ($params as $param) {
unset($urlParams[$param]);
}

// Rebuild
if (($query = static::buildQuery($urlParams)) !== '') {
$url .= '?' . $query;
}
if ($fragment !== null) {
$url .= '#' . $fragment;
}
return $url;
}

/**
* Removes all query string params from a URL.
*
* @param string $url
* @param string[] $except Any params that should be left alone
* @return string
* @since 5.11.0
*/
public static function removeAllParams(string $url, array $except = []): string
{
// Extract any params/fragment from the base URL
[$url, $params, $fragment] = self::_extractParams($url);

// Remove the param
unset($params[$param]);
// Remove the params
if (!empty($except)) {
foreach (array_keys($params) as $param) {
if (!in_array($param, $except)) {
unset($params[$param]);
}
}
} else {
$params = [];
}

// Rebuild
if (($query = static::buildQuery($params)) !== '') {
Expand Down Expand Up @@ -275,18 +324,20 @@ public static function rootRelativeUrl(string $url): string
* Returns either a control panel or a site URL, depending on the request type.
*
* @param string $path
* @param array|string|null $params
* @param array|string|false|null $params The query params to add to the URL. If `false`, any existing params will be removed.
* @param string|null $scheme
* @param bool|null $showScriptName Whether the script name (index.php) should be included in the URL.
* By default (null) it will defer to the `omitScriptNameInUrls` config setting.
* @return string
*/
public static function url(string $path = '', array|string|null $params = null, ?string $scheme = null, ?bool $showScriptName = null): string
public static function url(string $path = '', array|string|false|null $params = null, ?string $scheme = null, ?bool $showScriptName = null): string
{
// Return $path if it appears to be an absolute URL.
if (static::isFullUrl($path)) {
if ($params) {
$path = static::urlWithParams($path, $params);
} elseif ($params === false) {
$path = static::removeAllParams($path);
}

if ($scheme !== null) {
Expand All @@ -312,7 +363,7 @@ public static function url(string $path = '', array|string|null $params = null,
$scheme = 'https';
}

return self::_createUrl($path, $params, $scheme, $cpUrl, showScriptName: $showScriptName);
return self::_createUrl($path, $params ?: null, $scheme, $cpUrl, showScriptName: $showScriptName);
}

/**
Expand Down Expand Up @@ -596,12 +647,12 @@ public static function cpReferralUrl(): ?string
}

// Make sure the CP referred it
if (!str_starts_with($referrer, self::baseCpUrl())) {
if (!str_starts_with($referrer, static::baseCpUrl())) {
return null;
}

// to ensure we're comparing uris strip base cp url and query string from the referrer first
$referrerFullUri = ltrim(StringHelper::removeLeft($referrer, self::baseCpUrl()), '/');
$referrerFullUri = ltrim(StringHelper::removeLeft($referrer, static::baseCpUrl()), '/');
$referrerFullUri = substr($referrerFullUri, 0, strpos($referrerFullUri, '?') ?: null);

// Make sure it didn't refer itself
Expand Down
165 changes: 163 additions & 2 deletions tests/unit/helpers/UrlHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,39 @@ public function testStripQueryString(string $expected, string $url): void
self::assertSame($expected, UrlHelper::stripQueryString($url));
}

/**
* @dataProvider removeParamDataProvider
* @param string $expected
* @param string $url
* @param string $param
*/
public function testRemoveParam(string $expected, string $url, string $param): void
{
self::assertSame($expected, UrlHelper::removeParam($url, $param));
}

/**
* @dataProvider removeParamsDataProvider
* @param string $expected
* @param string $url
* @param string[] $params
*/
public function testRemoveParams(string $expected, string $url, array $params): void
{
self::assertSame($expected, UrlHelper::removeParams($url, $params));
}

/**
* @dataProvider removeAllParamsDataProvider
* @param string $expected
* @param string $url
* @param string[] $except
*/
public function testRemoveAllParams(string $expected, string $url, array $except = []): void
{
self::assertSame($expected, UrlHelper::removeAllParams($url, $except));
}

/**
* @dataProvider encodeParamsDataProvider
*/
Expand Down Expand Up @@ -192,11 +225,11 @@ public function testRootRelativeUrl(string $expected, string $url): void
* @dataProvider urlFunctionDataProvider
* @param string $expected
* @param string $path
* @param array|null $params
* @param array|string|false|null $params
* @param string|null $scheme
* @param bool|null $showScriptName
*/
public function testUrlFunction(string $expected, string $path = '', ?array $params = null, ?string $scheme = null, ?bool $showScriptName = null): void
public function testUrlFunction(string $expected, string $path = '', array|string|false|null $params = null, ?string $scheme = null, ?bool $showScriptName = null): void
{
$scheme ??= 'https';
$expected = $this->_prepExpectedUrl($expected, $scheme);
Expand Down Expand Up @@ -435,6 +468,129 @@ public static function stripQueryStringDataProvider(): array
];
}

/**
* Tests for UrlHelper::removeParam() method
*
* @return array
*/
public static function removeParamDataProvider(): array
{
return [
'basic' => [
self::ABSOLUTE_URL_HTTPS . '?param2=value2',
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param2=value2',
'param1',
],
'last-param' => [
self::ABSOLUTE_URL_HTTPS,
self::ABSOLUTE_URL_HTTPS . '?param1=value1',
'param1',
],
'missing-param' => [
self::ABSOLUTE_URL_HTTPS . '?param1=value1',
self::ABSOLUTE_URL_HTTPS . '?param1=value1',
'param2',
],
'no-params' => [
self::ABSOLUTE_URL_HTTPS,
self::ABSOLUTE_URL_HTTPS,
'param1',
],
'keeps-fragment' => [
self::ABSOLUTE_URL_HTTPS . '?param2=value2#anchor',
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param2=value2#anchor',
'param1',
],
];
}

/**
* Tests for UrlHelper::removeParams() method
*
* @return array
*/
public static function removeParamsDataProvider(): array
{
return [
'multiple' => [
self::ABSOLUTE_URL_HTTPS . '?param2=value2',
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param2=value2&param3=value3',
['param1', 'param3'],
],
'all-params' => [
self::ABSOLUTE_URL_HTTPS,
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param2=value2',
['param1', 'param2'],
],
'missing-params' => [
self::ABSOLUTE_URL_HTTPS . '?param1=value1',
self::ABSOLUTE_URL_HTTPS . '?param1=value1',
['param2', 'param3'],
],
'empty-params' => [
self::ABSOLUTE_URL_HTTPS . '?param1=value1',
self::ABSOLUTE_URL_HTTPS . '?param1=value1',
[],
],
'no-params' => [
self::ABSOLUTE_URL_HTTPS,
self::ABSOLUTE_URL_HTTPS,
['param1'],
],
'keeps-fragment' => [
self::ABSOLUTE_URL_HTTPS . '?param2=value2#anchor',
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param2=value2&param3=value3#anchor',
['param1', 'param3'],
],
];
}

/**
* Tests for UrlHelper::removeAllParams() method
*
* @return array
*/
public static function removeAllParamsDataProvider(): array
{
return [
'basic' => [
self::ABSOLUTE_URL_HTTPS,
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param2=value2',
[],
],
'except' => [
self::ABSOLUTE_URL_HTTPS . '?param2=value2',
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param2=value2&param3=value3',
['param2'],
],
'except-multiple' => [
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param3=value3',
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param2=value2&param3=value3',
['param1', 'param3'],
],
'except-missing' => [
self::ABSOLUTE_URL_HTTPS,
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param2=value2',
['param3'],
],
'no-params' => [
self::ABSOLUTE_URL_HTTPS,
self::ABSOLUTE_URL_HTTPS,
[],
],
'keeps-fragment' => [
self::ABSOLUTE_URL_HTTPS . '#anchor',
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param2=value2#anchor',
[],
],
'except-keeps-fragment' => [
self::ABSOLUTE_URL_HTTPS . '?param2=value2#anchor',
self::ABSOLUTE_URL_HTTPS . '?param1=value1&param2=value2#anchor',
['param2'],
],
];
}

/**
* Tests for UrlHelper::urlWithParams() method
*
Expand Down Expand Up @@ -716,6 +872,11 @@ public static function urlFunctionDataProvider(): array
['returnUrl' => 'https://example.test/admin/entries?site={handle}'],
'https',
],
'remove-params' => [
self::ABSOLUTE_URL_HTTPS,
self::ABSOLUTE_URL_HTTPS . '?x-craft-preview=foo&test=bar',
false,
],
];
}

Expand Down
Loading