-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
new redirection response type and handler
- Loading branch information
Showing
5 changed files
with
375 additions
and
7 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 |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<?php | ||
|
||
/* | ||
* slim-routing (https://github.com/juliangut/slim-routing). | ||
* Slim framework routing. | ||
* | ||
* @license BSD-3-Clause | ||
* @link https://github.com/juliangut/slim-routing | ||
* @author Julián Gutiérrez <[email protected]> | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Jgut\Slim\Routing\Response\Handler; | ||
|
||
use InvalidArgumentException; | ||
use Jgut\Slim\Routing\Response\RedirectResponse; | ||
use Jgut\Slim\Routing\Response\ResponseType; | ||
use Psr\Http\Message\ResponseFactoryInterface; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Slim\Interfaces\RouteCollectorInterface; | ||
|
||
final class RedirectResponseHandler extends AbstractResponseHandler | ||
{ | ||
private const NOT_MODIFIED_STATUS = 304; | ||
|
||
public function __construct( | ||
ResponseFactoryInterface $responseFactory, | ||
private RouteCollectorInterface $routeCollector, | ||
) { | ||
parent::__construct($responseFactory); | ||
} | ||
|
||
public function handle(ResponseType $responseType): ResponseInterface | ||
{ | ||
if (!$responseType instanceof RedirectResponse) { | ||
throw new InvalidArgumentException( | ||
sprintf('Response type should be an instance of %s.', RedirectResponse::class), | ||
); | ||
} | ||
|
||
if ($responseType->getStatus() === self::NOT_MODIFIED_STATUS) { | ||
return $this->getResponse($responseType) | ||
->withStatus(304); | ||
} | ||
|
||
$location = $responseType->getLocation(); | ||
if (!str_starts_with($location, '/') && filter_var($location, \FILTER_VALIDATE_URL) === false) { | ||
$location = $this->routeCollector | ||
->getRouteParser() | ||
->urlFor( | ||
$location, | ||
array_map( | ||
static fn(int|float|string|null $data): string => (string) $data, | ||
$responseType->getData(), | ||
), | ||
array_map( | ||
static fn(int|float|string|null $param): string => (string) $param, | ||
$responseType->getQueryParams(), | ||
), | ||
); | ||
} | ||
|
||
return $this->getResponse($responseType) | ||
->withStatus($responseType->getStatus()) | ||
->withHeader('Location', $location); | ||
} | ||
} |
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,131 @@ | ||
<?php | ||
|
||
/* | ||
* slim-routing (https://github.com/juliangut/slim-routing). | ||
* Slim framework routing. | ||
* | ||
* @license BSD-3-Clause | ||
* @link https://github.com/juliangut/slim-routing | ||
* @author Julián Gutiérrez <[email protected]> | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Jgut\Slim\Routing\Response; | ||
|
||
use Psr\Http\Message\ServerRequestInterface; | ||
|
||
final class RedirectResponse extends AbstractResponse | ||
{ | ||
private function __construct( | ||
private string $location, | ||
private int $status, | ||
ServerRequestInterface $request, | ||
/** | ||
* @var array<string, int|float|string|null> | ||
*/ | ||
private array $data = [], | ||
/** | ||
* @var array<string, int|float|string|null> | ||
*/ | ||
private array $queryParams = [], | ||
) { | ||
parent::__construct($request); | ||
} | ||
|
||
/** | ||
* @param array<string, int|float|string|null> $data | ||
* @param array<string, int|float|string|null> $queryParams | ||
*/ | ||
public static function movedPermanently( | ||
string $location, | ||
ServerRequestInterface $request, | ||
array $data = [], | ||
array $queryParams = [], | ||
): self { | ||
return new self($location, 301, $request, $data, $queryParams); | ||
} | ||
|
||
/** | ||
* @param array<string, int|float|string|null> $data | ||
* @param array<string, int|float|string|null> $queryParams | ||
*/ | ||
public static function found( | ||
string $location, | ||
ServerRequestInterface $request, | ||
array $data = [], | ||
array $queryParams = [], | ||
): self { | ||
return new self($location, 302, $request, $data, $queryParams); | ||
} | ||
|
||
/** | ||
* @param array<string, int|float|string|null> $data | ||
* @param array<string, int|float|string|null> $queryParams | ||
*/ | ||
public static function seeOther( | ||
string $location, | ||
ServerRequestInterface $request, | ||
array $data = [], | ||
array $queryParams = [], | ||
): self { | ||
return new self($location, 303, $request, $data, $queryParams); | ||
} | ||
|
||
public static function notModified(ServerRequestInterface $request): self | ||
{ | ||
return new self('', 304, $request); | ||
} | ||
|
||
/** | ||
* @param array<string, int|float|string|null> $data | ||
* @param array<string, int|float|string|null> $queryParams | ||
*/ | ||
public static function temporaryRedirect( | ||
string $location, | ||
ServerRequestInterface $request, | ||
array $data = [], | ||
array $queryParams = [], | ||
): self { | ||
return new self($location, 307, $request, $data, $queryParams); | ||
} | ||
|
||
/** | ||
* @param array<string, int|float|string|null> $data | ||
* @param array<string, int|float|string|null> $queryParams | ||
*/ | ||
public static function permanentRedirect( | ||
string $location, | ||
ServerRequestInterface $request, | ||
array $data = [], | ||
array $queryParams = [], | ||
): self { | ||
return new self($location, 308, $request, $data, $queryParams); | ||
} | ||
|
||
public function getLocation(): string | ||
{ | ||
return $this->location; | ||
} | ||
|
||
public function getStatus(): int | ||
{ | ||
return $this->status; | ||
} | ||
|
||
/** | ||
* @return array<string, int|float|string|null> | ||
*/ | ||
public function getData(): array | ||
{ | ||
return $this->data; | ||
} | ||
|
||
/** | ||
* @return array<string, int|float|string|null> | ||
*/ | ||
public function getQueryParams(): array | ||
{ | ||
return $this->queryParams; | ||
} | ||
} |
118 changes: 118 additions & 0 deletions
118
tests/Routing/Response/Handler/RedirectResponseHandlerTest.php
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,118 @@ | ||
<?php | ||
|
||
/* | ||
* slim-routing (https://github.com/juliangut/slim-routing). | ||
* Slim framework routing. | ||
* | ||
* @license BSD-3-Clause | ||
* @link https://github.com/juliangut/slim-routing | ||
* @author Julián Gutiérrez <[email protected]> | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Jgut\Slim\Routing\Tests\Response\Handler; | ||
|
||
use InvalidArgumentException; | ||
use Jgut\Slim\Routing\Response\Handler\RedirectResponseHandler; | ||
use Jgut\Slim\Routing\Response\RedirectResponse; | ||
use Jgut\Slim\Routing\Tests\Stubs\ResponseStub; | ||
use Laminas\Diactoros\ResponseFactory; | ||
use PHPUnit\Framework\TestCase; | ||
use Psr\Http\Message\ResponseFactoryInterface; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
use Slim\Routing\RouteCollector; | ||
use Slim\Routing\RouteParser; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
class RedirectResponseHandlerTest extends TestCase | ||
{ | ||
protected ServerRequestInterface $request; | ||
|
||
protected function setUp(): void | ||
{ | ||
$this->request = $this->getMockBuilder(ServerRequestInterface::class) | ||
->getMock(); | ||
} | ||
|
||
public function testInvalidResponseType(): void | ||
{ | ||
$this->expectException(InvalidArgumentException::class); | ||
$this->expectExceptionMessage( | ||
'Response type should be an instance of Jgut\Slim\Routing\Response\RedirectResponse', | ||
); | ||
|
||
$responseFactory = $this->getMockBuilder(ResponseFactoryInterface::class) | ||
->getMock(); | ||
$routeCollector = $this->getMockBuilder(RouteCollector::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
(new RedirectResponseHandler($responseFactory, $routeCollector))->handle(new ResponseStub($this->request)); | ||
} | ||
|
||
public function testNotModified(): void | ||
{ | ||
$responseFactory = new ResponseFactory(); | ||
$routeCollector = $this->getMockBuilder(RouteCollector::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$response = (new RedirectResponseHandler($responseFactory, $routeCollector)) | ||
->handle(RedirectResponse::notModified($this->request)); | ||
|
||
static::assertEquals(304, $response->getStatusCode()); | ||
static::assertEquals('', $response->getHeaderLine('Location')); | ||
} | ||
|
||
public function testUrlRedirect(): void | ||
{ | ||
$responseFactory = new ResponseFactory(); | ||
$routeCollector = $this->getMockBuilder(RouteCollector::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$response = (new RedirectResponseHandler($responseFactory, $routeCollector)) | ||
->handle(RedirectResponse::permanentRedirect('https://example.com', $this->request)); | ||
|
||
static::assertEquals('https://example.com', $response->getHeaderLine('Location')); | ||
} | ||
|
||
public function testPathRedirect(): void | ||
{ | ||
$responseFactory = new ResponseFactory(); | ||
$routeCollector = $this->getMockBuilder(RouteCollector::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$response = (new RedirectResponseHandler($responseFactory, $routeCollector)) | ||
->handle(RedirectResponse::permanentRedirect('/home', $this->request)); | ||
|
||
static::assertEquals('/home', $response->getHeaderLine('Location')); | ||
} | ||
|
||
public function testRouteRedirect(): void | ||
{ | ||
$responseFactory = new ResponseFactory(); | ||
$routeParser = $this->getMockBuilder(RouteParser::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
$routeParser | ||
->method('urlFor') | ||
->with('home') | ||
->willReturn('https://example.com/home'); | ||
$routeCollector = $this->getMockBuilder(RouteCollector::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
$routeCollector | ||
->method('getRouteParser') | ||
->willReturn($routeParser); | ||
|
||
$response = (new RedirectResponseHandler($responseFactory, $routeCollector)) | ||
->handle(RedirectResponse::permanentRedirect('home', $this->request)); | ||
|
||
static::assertEquals('https://example.com/home', $response->getHeaderLine('Location')); | ||
} | ||
} |
Oops, something went wrong.