-
-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PSR HTTP message converters for controllers
- Loading branch information
Showing
11 changed files
with
449 additions
and
2 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,4 @@ composer.lock | |
phpunit.xml | ||
.php_cs.cache | ||
.phpunit.result.cache | ||
/Tests/Fixtures/App/var |
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,49 @@ | ||
<?php | ||
|
||
namespace Symfony\Bridge\PsrHttpMessage\ArgumentValueResolver; | ||
|
||
use Psr\Http\Message\MessageInterface; | ||
use Psr\Http\Message\RequestInterface; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; | ||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; | ||
|
||
/** | ||
* Injects the RequestInterface, MessageInterface or ServerRequestInterface when requested. | ||
* | ||
* @author Iltar van der Berg <[email protected]> | ||
* @author Alexander M. Turek <[email protected]> | ||
*/ | ||
final class PsrServerRequestResolver implements ArgumentValueResolverInterface | ||
{ | ||
private const SUPPORTED_TYPES = [ | ||
ServerRequestInterface::class => true, | ||
RequestInterface::class => true, | ||
MessageInterface::class => true, | ||
]; | ||
|
||
private $httpMessageFactory; | ||
|
||
public function __construct(HttpMessageFactoryInterface $httpMessageFactory) | ||
{ | ||
$this->httpMessageFactory = $httpMessageFactory; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function supports(Request $request, ArgumentMetadata $argument): bool | ||
{ | ||
return self::SUPPORTED_TYPES[$argument->getType()] ?? false; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function resolve(Request $request, ArgumentMetadata $argument): \Traversable | ||
{ | ||
yield $this->httpMessageFactory->createRequest($request); | ||
} | ||
} |
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,49 @@ | ||
<?php | ||
|
||
namespace Symfony\Bridge\PsrHttpMessage\EventListener; | ||
|
||
use Psr\Http\Message\ResponseInterface; | ||
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface; | ||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
use Symfony\Component\HttpKernel\Event\ViewEvent; | ||
use Symfony\Component\HttpKernel\KernelEvents; | ||
|
||
/** | ||
* Converts PSR-7 Response to HttpFoundation Response using the bridge. | ||
* | ||
* @author Kévin Dunglas <[email protected]> | ||
* @author Alexander M. Turek <[email protected]> | ||
*/ | ||
final class PsrResponseListener implements EventSubscriberInterface | ||
{ | ||
private $httpFoundationFactory; | ||
|
||
public function __construct(HttpFoundationFactoryInterface $httpFoundationFactory) | ||
{ | ||
$this->httpFoundationFactory = $httpFoundationFactory; | ||
} | ||
|
||
/** | ||
* Do the conversion if applicable and update the response of the event. | ||
*/ | ||
public function onKernelView(ViewEvent $event): void | ||
{ | ||
$controllerResult = $event->getControllerResult(); | ||
|
||
if (!$controllerResult instanceof ResponseInterface) { | ||
return; | ||
} | ||
|
||
$event->setResponse($this->httpFoundationFactory->createResponse($controllerResult)); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public static function getSubscribedEvents(): array | ||
{ | ||
return [ | ||
KernelEvents::VIEW => 'onKernelView', | ||
]; | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
Tests/ArgumentValueResolver/PsrServerRequestResolverTest.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,59 @@ | ||
<?php | ||
|
||
namespace Symfony\Bridge\PsrHttpMessage\Tests\ArgumentValueResolver; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Psr\Http\Message\MessageInterface; | ||
use Psr\Http\Message\RequestInterface; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
use Symfony\Bridge\PsrHttpMessage\ArgumentValueResolver\PsrServerRequestResolver; | ||
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpKernel\Controller\ArgumentResolver; | ||
|
||
/** | ||
* @author Alexander M. Turek <[email protected]> | ||
*/ | ||
final class PsrServerRequestResolverTest extends TestCase | ||
{ | ||
public function testServerRequest() | ||
{ | ||
$symfonyRequest = $this->createMock(Request::class); | ||
$psrRequest = $this->createMock(ServerRequestInterface::class); | ||
|
||
$resolver = $this->bootstrapResolver($symfonyRequest, $psrRequest); | ||
|
||
self::assertSame([$psrRequest], $resolver->getArguments($symfonyRequest, static function (ServerRequestInterface $serverRequest): void {})); | ||
} | ||
|
||
public function testRequest() | ||
{ | ||
$symfonyRequest = $this->createMock(Request::class); | ||
$psrRequest = $this->createMock(ServerRequestInterface::class); | ||
|
||
$resolver = $this->bootstrapResolver($symfonyRequest, $psrRequest); | ||
|
||
self::assertSame([$psrRequest], $resolver->getArguments($symfonyRequest, static function (RequestInterface $request): void {})); | ||
} | ||
|
||
public function testMessage() | ||
{ | ||
$symfonyRequest = $this->createMock(Request::class); | ||
$psrRequest = $this->createMock(ServerRequestInterface::class); | ||
|
||
$resolver = $this->bootstrapResolver($symfonyRequest, $psrRequest); | ||
|
||
self::assertSame([$psrRequest], $resolver->getArguments($symfonyRequest, static function (MessageInterface $request): void {})); | ||
} | ||
|
||
private function bootstrapResolver(Request $symfonyRequest, ServerRequestInterface $psrRequest): ArgumentResolver | ||
{ | ||
$messageFactory = $this->createMock(HttpMessageFactoryInterface::class); | ||
$messageFactory->expects(self::once()) | ||
->method('createRequest') | ||
->with(self::identicalTo($symfonyRequest)) | ||
->willReturn($psrRequest); | ||
|
||
return new ArgumentResolver(null, [new PsrServerRequestResolver($messageFactory)]); | ||
} | ||
} |
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,45 @@ | ||
<?php | ||
|
||
namespace Symfony\Bridge\PsrHttpMessage\Tests\EventListener; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Bridge\PsrHttpMessage\EventListener\PsrResponseListener; | ||
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; | ||
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\Response; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpKernel\Event\ViewEvent; | ||
use Symfony\Component\HttpKernel\HttpKernelInterface; | ||
|
||
/** | ||
* @author Kévin Dunglas <[email protected]> | ||
*/ | ||
class PsrResponseListenerTest extends TestCase | ||
{ | ||
public function testConvertsControllerResult() | ||
{ | ||
$listener = new PsrResponseListener(new HttpFoundationFactory()); | ||
$event = $this->createEventMock(new Response()); | ||
$listener->onKernelView($event); | ||
|
||
self::assertTrue($event->hasResponse()); | ||
} | ||
|
||
public function testDoesNotConvertControllerResult() | ||
{ | ||
$listener = new PsrResponseListener(new HttpFoundationFactory()); | ||
$event = $this->createEventMock([]); | ||
|
||
$listener->onKernelView($event); | ||
self::assertFalse($event->hasResponse()); | ||
|
||
$event = $this->createEventMock(null); | ||
|
||
$listener->onKernelView($event); | ||
self::assertFalse($event->hasResponse()); | ||
} | ||
|
||
private function createEventMock($controllerResult): ViewEvent | ||
{ | ||
return new ViewEvent($this->createMock(HttpKernelInterface::class), new Request(), HttpKernelInterface::MASTER_REQUEST, $controllerResult); | ||
} | ||
} |
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,45 @@ | ||
<?php | ||
|
||
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\App\Controller; | ||
|
||
use Psr\Http\Message\MessageInterface; | ||
use Psr\Http\Message\RequestInterface; | ||
use Psr\Http\Message\ResponseFactoryInterface; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
use Psr\Http\Message\StreamFactoryInterface; | ||
|
||
final class PsrRequestController | ||
{ | ||
private $responseFactory; | ||
private $streamFactory; | ||
|
||
public function __construct(ResponseFactoryInterface $responseFactory, StreamFactoryInterface $streamFactory) | ||
{ | ||
$this->responseFactory = $responseFactory; | ||
$this->streamFactory = $streamFactory; | ||
} | ||
|
||
public function serverRequestAction(ServerRequestInterface $request): ResponseInterface | ||
{ | ||
return $this->responseFactory | ||
->createResponse() | ||
->withBody($this->streamFactory->createStream(sprintf('<html><body>%s</body></html>', $request->getMethod()))); | ||
} | ||
|
||
public function requestAction(RequestInterface $request): ResponseInterface | ||
{ | ||
return $this->responseFactory | ||
->createResponse() | ||
->withStatus(403) | ||
->withBody($this->streamFactory->createStream(sprintf('<html><body>%s %s</body></html>', $request->getMethod(), $request->getBody()->getContents()))); | ||
} | ||
|
||
public function messageAction(MessageInterface $request): ResponseInterface | ||
{ | ||
return $this->responseFactory | ||
->createResponse() | ||
->withStatus(422) | ||
->withBody($this->streamFactory->createStream(sprintf('<html><body>%s</body></html>', $request->getHeader('X-My-Header')[0]))); | ||
} | ||
} |
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,75 @@ | ||
<?php | ||
|
||
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\App; | ||
|
||
use Nyholm\Psr7\Factory\Psr17Factory; | ||
use Psr\Http\Message\ResponseFactoryInterface; | ||
use Psr\Http\Message\ServerRequestFactoryInterface; | ||
use Psr\Http\Message\StreamFactoryInterface; | ||
use Psr\Http\Message\UploadedFileFactoryInterface; | ||
use Psr\Log\NullLogger; | ||
use Symfony\Bridge\PsrHttpMessage\ArgumentValueResolver\PsrServerRequestResolver; | ||
use Symfony\Bridge\PsrHttpMessage\EventListener\PsrResponseListener; | ||
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; | ||
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory; | ||
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface; | ||
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; | ||
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\App\Controller\PsrRequestController; | ||
use Symfony\Bundle\FrameworkBundle\FrameworkBundle; | ||
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; | ||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; | ||
use Symfony\Component\HttpKernel\Kernel as SymfonyKernel; | ||
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; | ||
|
||
class Kernel extends SymfonyKernel | ||
{ | ||
use MicroKernelTrait; | ||
|
||
public function registerBundles(): iterable | ||
{ | ||
yield new FrameworkBundle(); | ||
} | ||
|
||
public function getProjectDir(): string | ||
{ | ||
return __DIR__; | ||
} | ||
|
||
protected function configureRoutes(RoutingConfigurator $routes): void | ||
{ | ||
$routes | ||
->add('server_request', '/server-request')->controller([PsrRequestController::class, 'serverRequestAction'])->methods(['GET']) | ||
->add('request', '/request')->controller([PsrRequestController::class, 'requestAction'])->methods(['POST']) | ||
->add('message', '/message')->controller([PsrRequestController::class, 'messageAction'])->methods(['PUT']) | ||
; | ||
} | ||
|
||
protected function configureContainer(ContainerConfigurator $container): void | ||
{ | ||
$container->extension('framework', [ | ||
'router' => ['utf8' => true], | ||
'test' => true, | ||
]); | ||
|
||
$container->services() | ||
->set('nyholm.psr_factory', Psr17Factory::class) | ||
->alias(ResponseFactoryInterface::class, 'nyholm.psr_factory') | ||
->alias(ServerRequestFactoryInterface::class, 'nyholm.psr_factory') | ||
->alias(StreamFactoryInterface::class, 'nyholm.psr_factory') | ||
->alias(UploadedFileFactoryInterface::class, 'nyholm.psr_factory') | ||
; | ||
|
||
$container->services() | ||
->defaults()->autowire()->autoconfigure() | ||
->set(HttpFoundationFactoryInterface::class, HttpFoundationFactory::class) | ||
->set(HttpMessageFactoryInterface::class, PsrHttpFactory::class) | ||
->set(PsrResponseListener::class) | ||
->set(PsrServerRequestResolver::class) | ||
; | ||
|
||
$container->services() | ||
->set('logger', NullLogger::class) | ||
->set(PsrRequestController::class)->public()->autowire() | ||
; | ||
} | ||
} |
Oops, something went wrong.