Skip to content
This repository was archived by the owner on Jun 25, 2025. It is now read-only.

Introduced Twig support #5

Merged
merged 1 commit into from
Aug 15, 2019
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
96 changes: 93 additions & 3 deletions AsyncHttpKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\AsyncEventDispatcherNeededException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\ControllerDoesNotReturnResponseException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable;

/**
* Class AsyncHttpKernel.
Expand Down Expand Up @@ -113,7 +116,7 @@ public function handleAsync(
$type
)
->then(null,
function (Exception $exception) use ($request, $type, $catch) {
function (Throwable $exception) use ($request, $type, $catch) {
if ($exception instanceof RequestExceptionInterface) {
$exception = new BadRequestHttpException($exception->getMessage(), $exception);
}
Expand Down Expand Up @@ -197,11 +200,53 @@ private function callAsyncController(Request $request, int $type): PromiseInterf
->then(function () use ($controller, $arguments) {
return $controller(...$arguments);
})
->then(function ($response) use ($request, $type, $controller) {
if (!$response instanceof Response) {
return $this->callAsyncView($request, $response, $controller, $type);
}

return $response;
})
->then(function ($response) use ($request, $type) {
return $this->filterResponsePromise($response, $request, $type);
});
}

/**
* Call async view.
*
* @param Request $request
* @param mixed $response
* @param callable $controller
* @param int $type
*
* @return PromiseInterface
*/
private function callAsyncView(
Request $request,
$response,
callable $controller,
int $type
): PromiseInterface {
return (new FulfilledPromise())
->then(function () use ($request, $response, $controller, $type) {
$event = new GetResponseForControllerResultEvent($this, $request, $type, $response);
$this->dispatcher->dispatch(KernelEvents::VIEW, $event);

if ($event->hasResponse()) {
return $event->getResponse();
} else {
$msg = sprintf('The controller must return a "Symfony\Component\HttpFoundation\Response" object but it returned %s.', $this->varToString($response));
// the user may have forgotten to return something
if (null === $response) {
$msg .= ' Did you forget to add a return statement somewhere in your controller?';
}

throw new ControllerDoesNotReturnResponseException($msg, $controller, __FILE__, __LINE__ - 17);
}
});
}

/**
* Filters a response object.
*
Expand Down Expand Up @@ -247,7 +292,7 @@ private function finishRequestPromise(Request $request, int $type)
/**
* Handles an exception by trying to convert it to a Response.
*
* @param Exception $exception
* @param Throwable $exception
* @param Request $request
* @param int $type
*
Expand All @@ -256,10 +301,17 @@ private function finishRequestPromise(Request $request, int $type)
* @throws Exception
*/
private function handleExceptionPromise(
Exception $exception,
Throwable $exception,
Request $request,
int $type
): PromiseInterface {
if (!$exception instanceof Exception) {
$exception = new Exception(
$exception->getMessage(),
$exception->getCode()
);
}

$event = new GetResponseForExceptionEvent($this, $request, $type, $exception);

return $this
Expand Down Expand Up @@ -291,4 +343,42 @@ private function handleExceptionPromise(
return $this->filterResponsePromise($response, $request, $type);
});
}

/**
* Returns a human-readable string for the specified variable.
*/
private function varToString($var): string
{
if (\is_object($var)) {
return sprintf('an object of type %s', \get_class($var));
}
if (\is_array($var)) {
$a = [];
foreach ($var as $k => $v) {
$a[] = sprintf('%s => ...', $k);
}

return sprintf('an array ([%s])', mb_substr(implode(', ', $a), 0, 255));
}
if (\is_resource($var)) {
return sprintf('a resource (%s)', get_resource_type($var));
}
if (null === $var) {
return 'null';
}
if (false === $var) {
return 'a boolean value (false)';
}
if (true === $var) {
return 'a boolean value (true)';
}
if (\is_string($var)) {
return sprintf('a string ("%s%s")', mb_substr($var, 0, 255), mb_strlen($var) > 255 ? '...' : '');
}
if (is_numeric($var)) {
return sprintf('a number (%s)', (string) $var);
}

return (string) $var;
}
}
5 changes: 5 additions & 0 deletions Tests/AsyncKernelFunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ protected static function getKernel(): KernelInterface
Controller::class.':getPromiseException',
'promise-exception',
],
[
'/simple-result',
Controller::class.':getSimpleResult',
'simple-result',
],
];

return new AsyncBaseKernel(
Expand Down
10 changes: 10 additions & 0 deletions Tests/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,14 @@ public function getPromiseException(): PromiseInterface
{
return new RejectedPromise(new Exception('E2'));
}

/**
* Return array.
*
* @return array
*/
public function getSimpleResult(): array
{
return ['a', 'b'];
}
}
5 changes: 5 additions & 0 deletions Tests/GetResponsePromiseFunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ public function testSyncKernel()
'EXC',
$exception->getMessage()
);

$this->assertEquals(
404,
$exception->getCode()
);
});

$loop->run();
Expand Down
14 changes: 13 additions & 1 deletion Tests/Listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@

use React\Promise\FulfilledPromise;
use React\Promise\PromiseInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;

/**
Expand Down Expand Up @@ -85,7 +87,7 @@ public function handleGetExceptionA(GetResponseForExceptionEvent $event)
{
return (new FulfilledPromise())
->then(function () use ($event) {
$event->setResponse(new Response('EXC'));
$event->setResponse(new Response('EXC', 404));
});
}

Expand Down Expand Up @@ -124,4 +126,14 @@ public function handleGetResponsePromise3(GetResponseEvent $event)
{
$_GET['partial'] .= '3';
}

/**
* Handle view.
*
* @param GetResponseForControllerResultEvent $event
*/
public function handleView(GetResponseForControllerResultEvent $event)
{
$event->setResponse(new JsonResponse($event->getControllerResult()));
}
}
76 changes: 76 additions & 0 deletions Tests/TwigTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

/*
* This file is part of the Symfony Async Kernel
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Feel free to edit as you please, and have fun.
*
* @author Marc Morera <yuhu@mmoreram.com>
*/

declare(strict_types=1);

namespace Symfony\Component\HttpKernel\Tests;

use Clue\React\Block;
use React\EventLoop\StreamSelectLoop;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* Class TwigTest.
*/
class TwigTest extends AsyncKernelFunctionalTest
{
/**
* Decorate configuration.
*
* @param array $configuration
*
* @return array
*/
protected static function decorateConfiguration(array $configuration): array
{
$configuration = parent::decorateConfiguration($configuration);
$configuration['services']['listener'] = [
'class' => Listener::class,
'tags' => [
[
'name' => 'kernel.event_listener',
'event' => 'kernel.view',
'method' => 'handleView',
],
],
];

return $configuration;
}

/**
* Everything should work as before in the world of sync requests.
*/
public function testSyncKernel()
{
$loop = new StreamSelectLoop();
$request = new Request([], [], [], [], [], [
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/simple-result',
]);

$_GET['partial'] = '';
$promise = self::$kernel
->handleAsync($request)
->then(function (Response $response) {
$this->assertEquals(
json_encode(['a', 'b']),
$response->getContent()
);
});

$loop->run();
Block\await($promise, $loop);
}
}