3131use Symfony \Component \HttpKernel \Event \FilterResponseEvent ;
3232use Symfony \Component \HttpKernel \Event \FinishRequestEvent ;
3333use Symfony \Component \HttpKernel \Event \GetResponseEvent ;
34+ use Symfony \Component \HttpKernel \Event \GetResponseForControllerResultEvent ;
3435use Symfony \Component \HttpKernel \Event \GetResponseForExceptionEvent ;
3536use Symfony \Component \HttpKernel \Exception \AsyncEventDispatcherNeededException ;
3637use Symfony \Component \HttpKernel \Exception \BadRequestHttpException ;
38+ use Symfony \Component \HttpKernel \Exception \ControllerDoesNotReturnResponseException ;
3739use Symfony \Component \HttpKernel \Exception \HttpExceptionInterface ;
3840use Symfony \Component \HttpKernel \Exception \NotFoundHttpException ;
41+ use Throwable ;
3942
4043/**
4144 * Class AsyncHttpKernel.
@@ -113,7 +116,7 @@ public function handleAsync(
113116 $ type
114117 )
115118 ->then (null ,
116- function (Exception $ exception ) use ($ request , $ type , $ catch ) {
119+ function (Throwable $ exception ) use ($ request , $ type , $ catch ) {
117120 if ($ exception instanceof RequestExceptionInterface) {
118121 $ exception = new BadRequestHttpException ($ exception ->getMessage (), $ exception );
119122 }
@@ -197,11 +200,53 @@ private function callAsyncController(Request $request, int $type): PromiseInterf
197200 ->then (function () use ($ controller , $ arguments ) {
198201 return $ controller (...$ arguments );
199202 })
203+ ->then (function ($ response ) use ($ request , $ type , $ controller ) {
204+ if (!$ response instanceof Response) {
205+ return $ this ->callAsyncView ($ request , $ response , $ controller , $ type );
206+ }
207+
208+ return $ response ;
209+ })
200210 ->then (function ($ response ) use ($ request , $ type ) {
201211 return $ this ->filterResponsePromise ($ response , $ request , $ type );
202212 });
203213 }
204214
215+ /**
216+ * Call async view.
217+ *
218+ * @param Request $request
219+ * @param mixed $response
220+ * @param callable $controller
221+ * @param int $type
222+ *
223+ * @return PromiseInterface
224+ */
225+ private function callAsyncView (
226+ Request $ request ,
227+ $ response ,
228+ callable $ controller ,
229+ int $ type
230+ ): PromiseInterface {
231+ return (new FulfilledPromise ())
232+ ->then (function () use ($ request , $ response , $ controller , $ type ) {
233+ $ event = new GetResponseForControllerResultEvent ($ this , $ request , $ type , $ response );
234+ $ this ->dispatcher ->dispatch (KernelEvents::VIEW , $ event );
235+
236+ if ($ event ->hasResponse ()) {
237+ return $ event ->getResponse ();
238+ } else {
239+ $ msg = sprintf ('The controller must return a "Symfony\Component\HttpFoundation\Response" object but it returned %s. ' , $ this ->varToString ($ response ));
240+ // the user may have forgotten to return something
241+ if (null === $ response ) {
242+ $ msg .= ' Did you forget to add a return statement somewhere in your controller? ' ;
243+ }
244+
245+ throw new ControllerDoesNotReturnResponseException ($ msg , $ controller , __FILE__ , __LINE__ - 17 );
246+ }
247+ });
248+ }
249+
205250 /**
206251 * Filters a response object.
207252 *
@@ -247,7 +292,7 @@ private function finishRequestPromise(Request $request, int $type)
247292 /**
248293 * Handles an exception by trying to convert it to a Response.
249294 *
250- * @param Exception $exception
295+ * @param Throwable $exception
251296 * @param Request $request
252297 * @param int $type
253298 *
@@ -256,10 +301,17 @@ private function finishRequestPromise(Request $request, int $type)
256301 * @throws Exception
257302 */
258303 private function handleExceptionPromise (
259- Exception $ exception ,
304+ Throwable $ exception ,
260305 Request $ request ,
261306 int $ type
262307 ): PromiseInterface {
308+ if (!$ exception instanceof Exception) {
309+ $ exception = new Exception (
310+ $ exception ->getMessage (),
311+ $ exception ->getCode ()
312+ );
313+ }
314+
263315 $ event = new GetResponseForExceptionEvent ($ this , $ request , $ type , $ exception );
264316
265317 return $ this
@@ -291,4 +343,42 @@ private function handleExceptionPromise(
291343 return $ this ->filterResponsePromise ($ response , $ request , $ type );
292344 });
293345 }
346+
347+ /**
348+ * Returns a human-readable string for the specified variable.
349+ */
350+ private function varToString ($ var ): string
351+ {
352+ if (\is_object ($ var )) {
353+ return sprintf ('an object of type %s ' , \get_class ($ var ));
354+ }
355+ if (\is_array ($ var )) {
356+ $ a = [];
357+ foreach ($ var as $ k => $ v ) {
358+ $ a [] = sprintf ('%s => ... ' , $ k );
359+ }
360+
361+ return sprintf ('an array ([%s]) ' , mb_substr (implode (', ' , $ a ), 0 , 255 ));
362+ }
363+ if (\is_resource ($ var )) {
364+ return sprintf ('a resource (%s) ' , get_resource_type ($ var ));
365+ }
366+ if (null === $ var ) {
367+ return 'null ' ;
368+ }
369+ if (false === $ var ) {
370+ return 'a boolean value (false) ' ;
371+ }
372+ if (true === $ var ) {
373+ return 'a boolean value (true) ' ;
374+ }
375+ if (\is_string ($ var )) {
376+ return sprintf ('a string ("%s%s") ' , mb_substr ($ var , 0 , 255 ), mb_strlen ($ var ) > 255 ? '... ' : '' );
377+ }
378+ if (is_numeric ($ var )) {
379+ return sprintf ('a number (%s) ' , (string ) $ var );
380+ }
381+
382+ return (string ) $ var ;
383+ }
294384}
0 commit comments