Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new RequestResponseNamedArgs route strategy #3132

Merged
merged 2 commits into from
Jan 14, 2022
Merged
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
49 changes: 49 additions & 0 deletions Slim/Handlers/Strategies/RequestResponseNamedArgs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

/**
* Slim Framework (https://slimframework.com)
*
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
*/

declare(strict_types=1);

namespace Slim\Handlers\Strategies;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Interfaces\InvocationStrategyInterface;
use RuntimeException;

/**
* Route callback strategy with route parameters as individual arguments.
*/
class RequestResponseNamedArgs implements InvocationStrategyInterface
{
public function __construct()
{
if (PHP_VERSION_ID < 80000) {
throw new RuntimeException('Named arguments are only available for PHP >= 8.0.0');
}
}

/**
* Invoke a route callable with request, response and all route parameters
* as individual arguments.
*
* @param callable $callable
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param array<string, string> $routeArguments
*
* @return ResponseInterface
*/
public function __invoke(
callable $callable,
ServerRequestInterface $request,
ResponseInterface $response,
array $routeArguments
): ResponseInterface {
return $callable($request, $response, ...$routeArguments);
}
}
51 changes: 50 additions & 1 deletion tests/AppTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use Slim\Exception\HttpMethodNotAllowedException;
use Slim\Exception\HttpNotFoundException;
use Slim\Handlers\Strategies\RequestResponseArgs;
use Slim\Handlers\Strategies\RequestResponseNamedArgs;
use Slim\Interfaces\CallableResolverInterface;
use Slim\Interfaces\MiddlewareDispatcherInterface;
use Slim\Interfaces\RouteCollectorInterface;
Expand Down Expand Up @@ -1214,7 +1215,7 @@ public function testInvokeWithMatchingRouteWithSetArguments()
$this->assertEquals('Hello World', (string) $response->getBody());
}

public function testInvokeWithMatchingRouteWithNamedParameter()
public function testInvokeWithMatchingRouteWithNamedParameterRequestResponseStrategy()
{
$streamProphecy = $this->prophesize(StreamInterface::class);
$streamProphecy->__toString()->willReturn('');
Expand Down Expand Up @@ -1295,6 +1296,54 @@ public function testInvokeWithMatchingRouteWithNamedParameterRequestResponseArgS
$this->assertEquals('Hello World', (string) $response->getBody());
}

public function testInvokeWithMatchingRouteWithNamedParameterRequestResponseNamedArgsStrategy()
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');
}

$streamProphecy = $this->prophesize(StreamInterface::class);
$streamProphecy->__toString()->willReturn('');
$streamProphecy->write(Argument::type('string'))->will(function ($args) {
$body = $this->reveal()->__toString();
$body .= $args[0];
$this->__toString()->willReturn($body);
});

$responseProphecy = $this->prophesize(ResponseInterface::class);
$responseProphecy->getBody()->willReturn($streamProphecy->reveal());

$responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);
$responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());

$app = new App($responseFactoryProphecy->reveal());
$app->getRouteCollector()->setDefaultInvocationStrategy(new RequestResponseNamedArgs());
$app->get(
'/{greeting}/{name}',
function (ServerRequestInterface $request, ResponseInterface $response, $name, $greeting) {
$response->getBody()->write("{$greeting} {$name}");
return $response;
}
);

$uriProphecy = $this->prophesize(UriInterface::class);
$uriProphecy->getPath()->willReturn('/Hello/World');

$requestProphecy = $this->prophesize(ServerRequestInterface::class);
$requestProphecy->getMethod()->willReturn('GET');
$requestProphecy->getUri()->willReturn($uriProphecy->reveal());
$requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);
$requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {
$this->getAttribute($args[0])->willReturn($args[1]);
return $this;
});

$response = $app->handle($requestProphecy->reveal());

$this->assertInstanceOf(ResponseInterface::class, $response);
$this->assertEquals('Hello World', (string) $response->getBody());
}

public function testInvokeWithMatchingRouteWithNamedParameterOverwritesSetArgument()
{
$streamProphecy = $this->prophesize(StreamInterface::class);
Expand Down
161 changes: 161 additions & 0 deletions tests/Handlers/Strategies/RequestResponseNamedArgsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php

/**
* Slim Framework (https://slimframework.com)
*
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
*/

declare(strict_types=1);

namespace Slim\Tests\Handlers\Strategies;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use RuntimeException;
use Slim\Handlers\Strategies\RequestResponseNamedArgs;
use Slim\Tests\TestCase;

class RequestResponseNamedArgsTest extends TestCase
{
private ServerRequestInterface $request;
private ResponseInterface $response;

public function setUp(): void
{
$this->request = $this->createMock(ServerRequestInterface::class);
$this->response = $this->createMock(ResponseInterface::class);
}

public function testCreatingRequestResponseNamedArgsThrowsRuntimeExceptionForPHPOlderThan80()
{
if (PHP_VERSION_ID >= 80000) {
$this->markTestSkipped('Test only valid for PHP versions older than 8.0');
}

$this->expectException(RuntimeException::class);
new RequestResponseNamedArgs();
}

public function testCallingWithEmptyArguments()
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');
}

$args = [];
$invocationStrategy = new RequestResponseNamedArgs();

$callback = function ($request, $response) {
$this->assertSame($this->request, $request);
$this->assertSame($this->response, $response);

return $response;
};

$this->assertSame($this->response, $invocationStrategy($callback, $this->request, $this->response, $args));
}

public function testCallingWithKnownArguments()
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');
}

$args = [
'name' => 'world',
'greeting' => 'hello',
];

$invocationStrategy = new RequestResponseNamedArgs();

$callback = function ($request, $response, $greeting, $name) use ($args) {
$this->assertSame($this->request, $request);
$this->assertSame($this->response, $response);
$this->assertSame($greeting, $args['greeting']);
$this->assertSame($name, $args['name']);

return $response;
};

$this->assertSame($this->response, $invocationStrategy($callback, $this->request, $this->response, $args));
}

public function testCallingWithOptionalArguments()
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');
}

$args = [
'name' => 'world',
];

$invocationStrategy = new RequestResponseNamedArgs();

$callback = function ($request, $response, $greeting = 'Hello', $name = 'Rob') use ($args) {
$this->assertSame($this->request, $request);
$this->assertSame($this->response, $response);
$this->assertSame($greeting, 'Hello');
$this->assertSame($name, $args['name']);

return $response;
};

$this->assertSame($this->response, $invocationStrategy($callback, $this->request, $this->response, $args));
}

public function testCallingWithUnknownAndVariadic()
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');
}

$args = [
'name' => 'world',
'greeting' => 'hello',
];

$invocationStrategy = new RequestResponseNamedArgs();

$callback = function ($request, $response, ...$arguments) use ($args) {
$this->assertSame($this->request, $request);
$this->assertSame($this->response, $response);
$this->assertSame($args, $arguments);

return $response;
};

$this->assertSame($this->response, $invocationStrategy($callback, $this->request, $this->response, $args));
}

public function testCallingWithMixedKnownAndUnknownParametersAndVariadic()
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');
}

$known = [
'name' => 'world',
'greeting' => 'hello',
];
$unknown = [
'foo' => 'foo',
'bar' => 'bar',
];
$args = array_merge($known, $unknown);
$invocationStrategy = new RequestResponseNamedArgs();

$callback = function ($request, $response, $name, $greeting, ...$arguments) use ($known, $unknown) {
$this->assertSame($this->request, $request);
$this->assertSame($this->response, $response);
$this->assertSame($name, $known['name']);
$this->assertSame($greeting, $known['greeting']);
$this->assertSame($unknown, $arguments);

return $response;
};

$this->assertSame($this->response, $invocationStrategy($callback, $this->request, $this->response, $args));
}
}