diff --git a/README.md b/README.md index b35459f..2b1d529 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,14 @@ # SlimRedirects -Redirect a request to a different domain. Supports wildcard, regex, http to https redirection. +PSR7 Based Redirect library. Supports wildcard, regex, http to https redirection. Query string handling + +-Trailing slash is ignored for request matching +-Assumes http://localhost is http://localhost/ +-Querystring +--Defaults to combination of request and rule with rule overridding request where matching +--Rule fragment overrides request fragment +--Rule user info overrides request user info + +TODO + +-"/wild/card/\*/?old=querystring" match only when qs present diff --git a/src/RedirectController.php b/src/RedirectController.php index 01ee4d3..b8e9fdb 100644 --- a/src/RedirectController.php +++ b/src/RedirectController.php @@ -6,6 +6,7 @@ use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; +use Psr\Http\Message\UriInterface; use Slim\Psr7\Factory\ResponseFactory; use Slim\Psr7\Factory\ServerRequestFactory; @@ -128,8 +129,7 @@ protected function setRedirects(array $redirects): self $array = []; foreach ($redirects as $redirect) { $r = ($redirect instanceof RedirectRule) ? $redirect : RedirectRule::factory($redirect); - $r->setSource($this->normalizePath($r->getSource())); - $array[$r->getSource()] = $r; + $array[$r->getSourceUri()->getPathNormalized()] = $r; } $this->redirects = $array; return $this; @@ -276,14 +276,6 @@ protected function isExcluded(string $path): bool return in_array($path, $this->getExcludes()); } - protected function normalizePath(string $path): string - { - if ($path == '' || $path === '/') { - return '/'; - } - return rtrim(strtolower($path), '/'); - } - protected function mergeRuleIntoUri(RedirectRule $rule, RedirectUri $uri): RedirectUri { $destination = $rule->getDestinationUri(); @@ -317,7 +309,7 @@ public function redirectProcess(): ?Response { $redirects = $this->getRedirectsFiltered(true); $uri = new RedirectUri($this->getRequest()->getUri(), $this->getResponse()->getStatusCode()); - $path = $this->normalizePath($uri->getPath()); + $path = $uri->getPathNormalized(); $noRedirectsOrExcluded = empty($redirects) || $this->isExcluded($path); $nullOrResponse = null; @@ -353,22 +345,24 @@ public function redirectProcess(): ?Response if (!empty($redirects[$path])) { $redirect = $redirects[$path]; $uri = $this->mergeRuleIntoUri($redirect, $uri); + $typeHandlerCallback = $this->getTypeHandler($redirect->getType()); $uri = $typeHandlerCallback($uri, $redirect, $this->getRequest()); + return $uri->toRedirectResponse(); } $newPath = ''; - foreach ($redirects as $redirect) { + foreach ($redirects as $sourcePath => $redirect) { - $source = $redirect->getSource(); - if (strpos($source, '*') === false) { + $sourcePath = urldecode($sourcePath); + if (strpos($sourcePath, '*') === false) { continue; } // TODO refactor into matching method $destination = $redirect->getDestination(); - $sourcePattern = rtrim(str_replace('*', '(.*)', $source), '/'); + $sourcePattern = rtrim(str_replace('*', '(.*)', $sourcePath), '/'); $sourcePatternRegex = '/^' . str_replace('/', '\/', $sourcePattern) . '/'; $regexPath = preg_replace($sourcePatternRegex, $this->parseDestination($destination), $path); @@ -377,10 +371,11 @@ public function redirectProcess(): ?Response continue; } - $typeHandlerCallback = $this->getTypeHandler($redirect->getType()); $regexRule = clone $redirect; $regexRule = $regexRule->setDestination($regexPath); $uri = $this->mergeRuleIntoUri($regexRule, $uri); + + $typeHandlerCallback = $this->getTypeHandler($redirect->getType()); $uri = $typeHandlerCallback($uri, $regexRule, $this->getRequest()); // the second condition here prevents redirect loops as a result of wildcards. diff --git a/src/RedirectUri.php b/src/RedirectUri.php index 74168be..edbb5ed 100644 --- a/src/RedirectUri.php +++ b/src/RedirectUri.php @@ -58,6 +58,16 @@ public function withStatusCode(int $statusCode) return $this; } + public function getPathNormalized(): string + { + $path = $this->getPath(); + if ($path == '' || $path === '/') { + return '/'; + } + $path = rtrim(strtolower($path), '/'); + return $path; + } + public function toRedirectResponse(ResponseInterface $response = null): ResponseInterface { $factory = new RedirectResponseFactory(); diff --git a/tests/ControllerTest.php b/tests/ControllerTest.php index 4934122..a68411d 100644 --- a/tests/ControllerTest.php +++ b/tests/ControllerTest.php @@ -347,6 +347,18 @@ public function testTrailingSlash() $result = $this->slimRedirect('https://localhost/?query=string', [$rule]); $this->assertEquals($rule['httpStatus'], $result->responseStatus); $this->assertEquals($result->location, 'https://localhost/root/?new=querystring&query=string'); + + $rule = [ + "id" => "1", + "source" => "/wild/card/*/?old=querystring", + "type" => "path", + "destination" => "/root/?new=querystring", + "httpStatus" => 302, + "active" => 1 + ]; + $result = $this->slimRedirect('https://localhost/wild/card/random?query=string', [$rule]); + $this->assertEquals($rule['httpStatus'], $result->responseStatus); + $this->assertEquals($result->location, 'https://localhost/root/?new=querystring&query=string'); } public function testOnlyDestinationHasQs()