-
Notifications
You must be signed in to change notification settings - Fork 638
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16355 from craftcms/feature/redirects
Redirects via config file
- Loading branch information
Showing
7 changed files
with
353 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
* | ||
* @author Pixel & Tonic, Inc. <[email protected]> | ||
* @since 3.5.13 | ||
* @deprecated in 5.6.0. `config/redirects.php` should be used instead. | ||
*/ | ||
class RedirectController extends Controller | ||
{ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
/** | ||
* @link https://craftcms.com/ | ||
* @copyright Copyright (c) Pixel & Tonic, Inc. | ||
* @license https://craftcms.github.io/license/ | ||
*/ | ||
|
||
namespace craft\events; | ||
|
||
use craft\base\Event; | ||
use craft\web\RedirectRule; | ||
|
||
/** | ||
* RedirectEvent class. | ||
* | ||
* @author Pixel & Tonic, Inc. <[email protected]> | ||
* @since 5.6.0 | ||
*/ | ||
class RedirectEvent extends Event | ||
{ | ||
public RedirectRule $rule; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
<?php | ||
/** | ||
* @link https://craftcms.com/ | ||
* @copyright Copyright (c) Pixel & Tonic, Inc. | ||
* @license https://craftcms.github.io/license/ | ||
*/ | ||
|
||
namespace craft\web; | ||
|
||
use Closure; | ||
use Craft; | ||
use Illuminate\Support\Collection; | ||
use League\Uri\Http; | ||
use yii\base\Component; | ||
|
||
/** | ||
* Class RedirectRule | ||
* | ||
* @author Pixel & Tonic, Inc. <[email protected]> | ||
* @since 5.6.0 | ||
*/ | ||
class RedirectRule extends Component | ||
{ | ||
/** | ||
* @event \yii\base\Event The event that is triggered before redirecting the request. | ||
*/ | ||
public const EVENT_BEFORE_REDIRECT = 'beforeRedirect'; | ||
|
||
public string $to; | ||
public string $from; | ||
public int $statusCode = 302; | ||
public bool $caseSensitive = false; | ||
private Closure $_match; | ||
private array $regexTokens = []; | ||
|
||
public function __invoke(): void | ||
{ | ||
$url = $this->getMatch(); | ||
|
||
if ($url === null) { | ||
return; | ||
} | ||
|
||
if ($this->hasEventHandlers(self::EVENT_BEFORE_REDIRECT)) { | ||
$this->trigger(self::EVENT_BEFORE_REDIRECT); | ||
} | ||
|
||
Craft::$app->getResponse()->redirect($url, $this->statusCode); | ||
Craft::$app->end(); | ||
} | ||
|
||
public function getMatch(): ?string | ||
{ | ||
if (isset($this->_match)) { | ||
return ($this->_match)(Http::new(Craft::$app->getRequest()->getAbsoluteUrl())); | ||
} | ||
|
||
$subject = Craft::$app->getRequest()->getFullPath(); | ||
|
||
if (str_contains($this->from, '<')) { | ||
if (preg_match( | ||
$this->toRegexPattern($this->from), | ||
$subject, | ||
$matches, | ||
)) { | ||
return $this->replaceParams($this->to, $matches); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
if ($this->caseSensitive) { | ||
return strcmp($this->from, $subject) === 0 ? $this->to : null; | ||
} | ||
|
||
return strcasecmp($this->from, $subject) === 0 ? $this->to : null; | ||
} | ||
|
||
public function setMatch(callable $match): void | ||
{ | ||
$this->_match = $match; | ||
} | ||
|
||
private function replaceParams(string $value, array $params): string | ||
{ | ||
$params = Collection::make($params) | ||
->mapWithKeys(fn($item, $key) => ["<$key>" => $item]); | ||
|
||
return strtr($value, $params->all()); | ||
} | ||
|
||
private function toRegexPattern(string $from): string | ||
{ | ||
// Tokenize the patterns first, so we only escape regex chars outside of patterns | ||
$tokenizedPattern = preg_replace_callback('/<([\w._-]+):?([^>]+)?>/', function($match) { | ||
$name = $match[1]; | ||
$pattern = strtr($match[2] ?? '[^\/]+', UrlRule::regexTokens()); | ||
$token = "<$name>"; | ||
$this->regexTokens[$token] = "(?P<$name>$pattern)"; | ||
|
||
return $token; | ||
}, $from); | ||
|
||
$replacements = array_merge($this->regexTokens, [ | ||
'.' => '\\.', | ||
'*' => '\\*', | ||
'$' => '\\$', | ||
'[' => '\\[', | ||
']' => '\\]', | ||
'(' => '\\(', | ||
')' => '\\)', | ||
]); | ||
|
||
$pattern = strtr($tokenizedPattern, $replacements); | ||
$flags = $this->caseSensitive ? 'u' : 'iu'; | ||
|
||
return "`^{$pattern}$`{$flags}"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.