Skip to content

Commit

Permalink
Merge pull request #265 from clue-labs/promise-v3
Browse files Browse the repository at this point in the history
Update to require Promise v3
  • Loading branch information
clue authored Nov 21, 2024
2 parents cd7bb12 + a0b0158 commit 0e6bdee
Show file tree
Hide file tree
Showing 6 changed files with 4 additions and 71 deletions.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"nikic/fast-route": "^1.3",
"react/async": "^4 || ^3",
"react/http": "^1.10",
"react/promise": "^3 || ^2.10",
"react/socket": "^1.13"
"react/promise": "^3",
"react/socket": "^1.14"
},
"require-dev": {
"phpstan/phpstan": "1.10.47 || 1.4.10",
Expand Down
3 changes: 0 additions & 3 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,3 @@ parameters:
- tests/

reportUnmatchedIgnoredErrors: false
ignoreErrors:
# ignore generic usage like `PromiseInterface<ResponseInterface>` until fixed upstream
- '/^PHPDoc tag @return contains generic type React\\Promise\\PromiseInterface<Psr\\Http\\Message\\ResponseInterface> but interface React\\Promise\\PromiseInterface is not generic\.$/'
12 changes: 2 additions & 10 deletions src/ErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,8 @@ public function __invoke(ServerRequestInterface $request, callable $next)
} else {
return $this->errorInvalidResponse($response);
}
}, function ($e) {
// Promise rejected, always a `\Throwable` as of Promise v3
assert($e instanceof \Throwable || !\method_exists(PromiseInterface::class, 'catch')); // @phpstan-ignore-line

if ($e instanceof \Throwable) {
return $this->errorInvalidException($e);
} else { // @phpstan-ignore-line
// @phpstan-ignore-next-line
return $this->errorInvalidResponse(\React\Promise\reject($e)); // @codeCoverageIgnore
}
}, function (\Throwable $e) {
return $this->errorInvalidException($e);
});
} elseif ($response instanceof \Generator) {
return $this->coroutine($response);
Expand Down
26 changes: 0 additions & 26 deletions tests/AppTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1272,32 +1272,6 @@ public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhe
$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());
}

public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsPromiseWhichRejectsWithNull(): void
{
if (method_exists(PromiseInterface::class, 'catch')) {
$this->markTestSkipped('Only supported for legacy Promise v2, Promise v3 always rejects with Throwable');
}

$app = $this->createAppWithoutLogger();

$app->get('/users', function () {
return reject(null); // @phpstan-ignore-line
});

$request = new ServerRequest('GET', 'http://localhost/users');

$response = $app($request);
assert($response instanceof ResponseInterface);

$this->assertEquals(500, $response->getStatusCode());
$this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type'));
$this->assertStringMatchesFormat("<!DOCTYPE html>\n<html>%a</html>\n", (string) $response->getBody());

$this->assertStringContainsString("<title>Error 500: Internal Server Error</title>\n", (string) $response->getBody());
$this->assertStringContainsString("<p>The requested page failed to load, please try again later.</p>\n", (string) $response->getBody());
$this->assertStringContainsString("<p>Expected request handler to return <code>Psr\Http\Message\ResponseInterface</code> but got <code>React\Promise\RejectedPromise</code>.</p>\n", (string) $response->getBody());
}

public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsCoroutineWhichYieldsRejectedPromise(): void
{
$app = $this->createAppWithoutLogger();
Expand Down
27 changes: 0 additions & 27 deletions tests/ErrorHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -271,33 +271,6 @@ public function testInvokeWithHandlerReturningPromiseResolvingWithNullReturnsPro
$this->assertEquals(500, $response->getStatusCode());
}

public function testInvokeWithHandlerReturningPromiseRejectingWithNullReturnsPromiseResolvingWithError500Response(): void
{
if (method_exists(PromiseInterface::class, 'catch')) {
$this->markTestSkipped('Only supported for legacy Promise v2, Promise v3 always rejects with Throwable');
}

$handler = new ErrorHandler();

$request = new ServerRequest('GET', 'http://example.com/');

$promise = $handler($request, function () {
return reject(null); // @phpstan-ignore-line
});

/** @var PromiseInterface<ResponseInterface> $promise */
$this->assertInstanceOf(PromiseInterface::class, $promise);

$response = null;
$promise->then(function ($value) use (&$response) {
$response = $value;
});

/** @var ResponseInterface $response */
$this->assertInstanceOf(ResponseInterface::class, $response);
$this->assertEquals(500, $response->getStatusCode());
}

public function testInvokeWithHandlerReturningGeneratorYieldingNullReturnsGeneratorYieldingError500Response(): void
{
$handler = new ErrorHandler();
Expand Down
3 changes: 0 additions & 3 deletions tests/Io/ReactiveHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ public function testRunWillListenForHttpRequestAndSendBackHttpResponseOverSocket
$connector = new Connector();
$promise = $connector->connect($addr);

/** @var \React\Promise\PromiseInterface<ConnectionInterface> $promise */
$promise->then(function (ConnectionInterface $connection): void {
$connection->on('data', function (string $data): void {
$this->assertEquals("HTTP/1.0 200 OK\r\nContent-Length: 3\r\n\r\nOK\n", $data);
Expand Down Expand Up @@ -217,7 +216,6 @@ public function testRunWillOnlyRestartLoopAfterAwaitingWhenFibersAreNotAvailable
$logger->expects($this->never())->method('log');
}

/** @var \React\Promise\PromiseInterface<ConnectionInterface> $promise */
$promise->then(function (ConnectionInterface $connection): void {
$connection->on('data', function (string $data): void {
$this->assertEquals("HTTP/1.0 200 OK\r\nContent-Length: 3\r\n\r\nOK\n", $data);
Expand Down Expand Up @@ -279,7 +277,6 @@ public function testRunWillReportHttpErrorForInvalidClientRequest(): void
$connector = new Connector();
$promise = $connector->connect($addr);

/** @var \React\Promise\PromiseInterface<ConnectionInterface> $promise */
$promise->then(function (ConnectionInterface $connection) use ($logger): void {
$logger->expects($this->once())->method('log')->with($this->matchesRegularExpression('/^HTTP error: .*$/'));
$connection->write("not a valid HTTP request\r\n\r\n");
Expand Down

0 comments on commit 0e6bdee

Please sign in to comment.