66use FrameworkX \App ;
77use FrameworkX \Container ;
88use FrameworkX \ErrorHandler ;
9+ use FrameworkX \FiberHandler ;
910use FrameworkX \MiddlewareHandler ;
1011use FrameworkX \RouteHandler ;
1112use FrameworkX \SapiHandler ;
2627use React \EventLoop \Loop ;
2728use React \Http \Message \Response ;
2829use React \Http \Message \ServerRequest ;
30+ use React \Promise \Deferred ;
2931use React \Promise \Promise ;
3032use React \Promise \PromiseInterface ;
3133use ReflectionMethod ;
3234use ReflectionProperty ;
35+ use function React \Async \await ;
3336use function React \Promise \reject ;
3437use function React \Promise \resolve ;
3538
@@ -72,6 +75,11 @@ public function testConstructWithMiddlewareAssignsGivenMiddleware()
7275 $ ref ->setAccessible (true );
7376 $ handlers = $ ref ->getValue ($ handler );
7477
78+ if (PHP_VERSION_ID >= 80100 ) {
79+ $ first = array_shift ($ handlers );
80+ $ this ->assertInstanceOf (FiberHandler::class, $ first );
81+ }
82+
7583 $ this ->assertCount (4 , $ handlers );
7684 $ this ->assertInstanceOf (AccessLogHandler::class, $ handlers [0 ]);
7785 $ this ->assertInstanceOf (ErrorHandler::class, $ handlers [1 ]);
@@ -93,6 +101,11 @@ public function testConstructWithContainerAssignsContainerForRouteHandlerOnly()
93101 $ ref ->setAccessible (true );
94102 $ handlers = $ ref ->getValue ($ handler );
95103
104+ if (PHP_VERSION_ID >= 80100 ) {
105+ $ first = array_shift ($ handlers );
106+ $ this ->assertInstanceOf (FiberHandler::class, $ first );
107+ }
108+
96109 $ this ->assertCount (3 , $ handlers );
97110 $ this ->assertInstanceOf (AccessLogHandler::class, $ handlers [0 ]);
98111 $ this ->assertInstanceOf (ErrorHandler::class, $ handlers [1 ]);
@@ -122,6 +135,11 @@ public function testConstructWithContainerAndMiddlewareClassNameAssignsCallableF
122135 $ ref ->setAccessible (true );
123136 $ handlers = $ ref ->getValue ($ handler );
124137
138+ if (PHP_VERSION_ID >= 80100 ) {
139+ $ first = array_shift ($ handlers );
140+ $ this ->assertInstanceOf (FiberHandler::class, $ first );
141+ }
142+
125143 $ this ->assertCount (4 , $ handlers );
126144 $ this ->assertInstanceOf (AccessLogHandler::class, $ handlers [0 ]);
127145 $ this ->assertInstanceOf (ErrorHandler::class, $ handlers [1 ]);
@@ -820,6 +838,57 @@ public function testHandleRequestWithMatchingRouteReturnsPendingPromiseWhenHandl
820838 $ this ->assertFalse ($ resolved );
821839 }
822840
841+ public function testHandleRequestWithMatchingRouteReturnsPromiseResolvingWithResponseWhenHandlerReturnsResponseAfterAwaitingPromiseResolvingWithResponse ()
842+ {
843+ if (PHP_VERSION_ID < 80100 || !function_exists ('React\Async\async ' )) {
844+ $ this ->markTestSkipped ('Requires PHP 8.1+ with react/async 4+ ' );
845+ }
846+
847+ $ app = $ this ->createAppWithoutLogger ();
848+
849+ $ deferred = new Deferred ();
850+
851+ $ app ->get ('/users ' , function () use ($ deferred ) {
852+ return await ($ deferred ->promise ());
853+ });
854+
855+ $ request = new ServerRequest ('GET ' , 'http://localhost/users ' );
856+
857+ // $promise = $app->handleRequest($request);
858+ $ ref = new ReflectionMethod ($ app , 'handleRequest ' );
859+ $ ref ->setAccessible (true );
860+ $ promise = $ ref ->invoke ($ app , $ request );
861+
862+ /** @var PromiseInterface $promise */
863+ $ this ->assertInstanceOf (PromiseInterface::class, $ promise );
864+
865+ $ response = null ;
866+ $ promise ->then (function ($ value ) use (&$ response ) {
867+ $ response = $ value ;
868+ });
869+
870+ $ this ->assertNull ($ response );
871+
872+ $ deferred ->resolve (new Response (
873+ 200 ,
874+ [
875+ 'Content-Type ' => 'text/html '
876+ ],
877+ "OK \n"
878+ ));
879+
880+ // await next tick: https://github.com/reactphp/async/issues/27
881+ await (new Promise (function ($ resolve ) {
882+ Loop::futureTick ($ resolve );
883+ }));
884+
885+ /** @var ResponseInterface $response */
886+ $ this ->assertInstanceOf (ResponseInterface::class, $ response );
887+ $ this ->assertEquals (200 , $ response ->getStatusCode ());
888+ $ this ->assertEquals ('text/html ' , $ response ->getHeaderLine ('Content-Type ' ));
889+ $ this ->assertEquals ("OK \n" , (string ) $ response ->getBody ());
890+ }
891+
823892 public function testHandleRequestWithMatchingRouteAndRouteVariablesReturnsResponseFromHandlerWithRouteVariablesAssignedAsRequestAttributes ()
824893 {
825894 $ app = $ this ->createAppWithoutLogger ();
@@ -1047,6 +1116,58 @@ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWit
10471116 $ this ->assertStringContainsString ("<p>Expected request handler to return <code>Psr\Http\Message\ResponseInterface</code> but got uncaught <code>RuntimeException</code> with message <code>Foo</code> in <code title= \"See " . __FILE__ . " line $ line \">AppTest.php: $ line</code>.</p> \n" , (string ) $ response ->getBody ());
10481117 }
10491118
1119+ public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithInternalServerErrorResponseWhenHandlerThrowsAfterAwaitingPromiseRejectingWithException ()
1120+ {
1121+ if (PHP_VERSION_ID < 80100 || !function_exists ('React\Async\async ' )) {
1122+ $ this ->markTestSkipped ('Requires PHP 8.1+ with react/async 4+ ' );
1123+ }
1124+
1125+ $ app = $ this ->createAppWithoutLogger ();
1126+
1127+ $ deferred = new Deferred ();
1128+
1129+ $ line = __LINE__ + 1 ;
1130+ $ exception = new \RuntimeException ('Foo ' );
1131+
1132+ $ app ->get ('/users ' , function () use ($ deferred ) {
1133+ return await ($ deferred ->promise ());
1134+ });
1135+
1136+ $ request = new ServerRequest ('GET ' , 'http://localhost/users ' );
1137+
1138+ // $promise = $app->handleRequest($request);
1139+ $ ref = new ReflectionMethod ($ app , 'handleRequest ' );
1140+ $ ref ->setAccessible (true );
1141+ $ promise = $ ref ->invoke ($ app , $ request );
1142+
1143+ /** @var PromiseInterface $promise */
1144+ $ this ->assertInstanceOf (PromiseInterface::class, $ promise );
1145+
1146+ $ response = null ;
1147+ $ promise ->then (function ($ value ) use (&$ response ) {
1148+ $ response = $ value ;
1149+ });
1150+
1151+ $ this ->assertNull ($ response );
1152+
1153+ $ deferred ->reject ($ exception );
1154+
1155+ // await next tick: https://github.com/reactphp/async/issues/27
1156+ await (new Promise (function ($ resolve ) {
1157+ Loop::futureTick ($ resolve );
1158+ }));
1159+
1160+ /** @var ResponseInterface $response */
1161+ $ this ->assertInstanceOf (ResponseInterface::class, $ response );
1162+ $ this ->assertEquals (500 , $ response ->getStatusCode ());
1163+ $ this ->assertEquals ('text/html; charset=utf-8 ' , $ response ->getHeaderLine ('Content-Type ' ));
1164+ $ this ->assertStringMatchesFormat ("<!DOCTYPE html> \n<html>%a</html> \n" , (string ) $ response ->getBody ());
1165+
1166+ $ this ->assertStringContainsString ("<title>Error 500: Internal Server Error</title> \n" , (string ) $ response ->getBody ());
1167+ $ this ->assertStringContainsString ("<p>The requested page failed to load, please try again later.</p> \n" , (string ) $ response ->getBody ());
1168+ $ this ->assertStringContainsString ("<p>Expected request handler to return <code>Psr\Http\Message\ResponseInterface</code> but got uncaught <code>RuntimeException</code> with message <code>Foo</code> in <code title= \"See " . __FILE__ . " line $ line \">AppTest.php: $ line</code>.</p> \n" , (string ) $ response ->getBody ());
1169+ }
1170+
10501171 public function testHandleRequestWithMatchingRouteReturnsPromiseWhichFulfillsWithInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichReturnsNull ()
10511172 {
10521173 $ app = $ this ->createAppWithoutLogger ();
@@ -1386,8 +1507,20 @@ private function createAppWithoutLogger(): App
13861507 $ ref ->setAccessible (true );
13871508 $ handlers = $ ref ->getValue ($ middleware );
13881509
1389- unset($ handlers [0 ]);
1390- $ ref ->setValue ($ middleware , array_values ($ handlers ));
1510+ if (PHP_VERSION_ID >= 80100 ) {
1511+ $ first = array_shift ($ handlers );
1512+ $ this ->assertInstanceOf (FiberHandler::class, $ first );
1513+
1514+ $ next = array_shift ($ handlers );
1515+ $ this ->assertInstanceOf (AccessLogHandler::class, $ next );
1516+
1517+ array_unshift ($ handlers , $ next , $ first );
1518+ }
1519+
1520+ $ first = array_shift ($ handlers );
1521+ $ this ->assertInstanceOf (AccessLogHandler::class, $ first );
1522+
1523+ $ ref ->setValue ($ middleware , $ handlers );
13911524
13921525 return $ app ;
13931526 }
0 commit comments