Skip to content

Commit 078431f

Browse files
authored
Merge pull request #8 from friends-of-reactphp/fibers
Use fibers instead of monkey patching
2 parents cce5223 + e2bdaa7 commit 078431f

11 files changed

+68
-323
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ jobs:
1111
strategy:
1212
matrix:
1313
php:
14-
- 7.2
15-
- 7.1
16-
- 7.0
14+
- 8.1
1715
steps:
1816
- uses: actions/checkout@v2
1917
- uses: shivammathur/setup-php@v2

README.md

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![CI status](https://github.com/friends-of-reactphp/http-middleware-psr15-adapter/workflows/CI/badge.svg)](https://github.com/friends-of-reactphp/http-middleware-psr15-adapter/actions)
44

5-
Wraps PSR-15 middleware into coroutines using [`RecoilPHP`](https://github.com/recoilphp) making them usable within `react/http` as middleware.
5+
Wraps PSR-15 middleware using `async` and `await` from `react/async` utilizing fibers making them usable within `react/http` as middleware.
66

77
# Install
88

@@ -18,27 +18,13 @@ The following usage example uses [`middlewares/redirect`](https://github.com/mid
1818
and using the callback to call several methods on the redirect middleware to change it's behavior:
1919

2020
```php
21-
$loop = Factory::create();
22-
$server = new Server([
21+
$server = new Server(
2322
/** Other middleware */
2423
new PSR15Middleware(
25-
$loop, // The react/event-loop (required)
26-
Redirect::class, // String class name of the middleware (required)
27-
[ // Any constructor arguments (optional)
28-
['/old-url' => '/new-url']
29-
],
30-
function ($redirectMiddleware) {
31-
// This callback is optional, but when used it must return the
32-
// instance passed into it, or a clone of it.
33-
return $redirectMiddleware
34-
->permanent(false)
35-
->query(false)
36-
->method(['GET', 'POST'])
37-
;
38-
}
24+
(new Redirect(['/old-url' => '/new-url']))->permanent(false)->query(false)->method(['GET', 'POST'])
3925
),
4026
/** Other middleware */
41-
]);
27+
);
4228
```
4329

4430
# Grouped Usage
@@ -51,24 +37,10 @@ $loop = Factory::create();
5137
$server = new Server([
5238
/** Other middleware */
5339
(new GroupedPSR15Middleware($loop))->withMiddleware(
54-
Redirect::class,
55-
[
56-
['/old-url' => '/new-url']
57-
],
58-
function ($redirectMiddleware) {
59-
return $redirectMiddleware
60-
->permanent(false)
61-
->query(false)
62-
->method(['GET', 'POST'])
63-
;
64-
}
65-
)->withMiddleware(Expires::class),
40+
(new Redirect(['/old-url' => '/new-url']))->permanent(false)->query(false)->method(['GET', 'POST'])
41+
)->withMiddleware(
42+
new Expires()
43+
),
6644
/** Other middleware */
6745
]);
6846
```
69-
70-
# Warning
71-
72-
This adapter rewrites the code of the PSR-15 middleware during the constructor phase, wrapping all `$delegate->process($request)`
73-
calls into a yield `(yield $delegate->process($request))`. This should work for most middleware but cannot be guaranteed for all.
74-
In case you run into issues please open an issue with the middleware in question you're having problems with.

composer.json

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@
44
"keywords": ["HTTP", "HTTPS", "ReactPHP", "middleware", "PSR15"],
55
"license": "MIT",
66
"require": {
7-
"php": "^7.0",
8-
"recoil/react": "^1.0",
9-
"recoil/recoil": "^1.0",
10-
"nikic/php-parser": "^4.0 || ^3.1",
11-
"psr/http-server-middleware": "^1.0"
7+
"php": "^8.1",
8+
"psr/http-server-middleware": "^1.0",
9+
"react/async": "^4"
1210
},
1311
"require-dev": {
14-
"phpunit/phpunit": "^6.0",
15-
"clue/block-react": "^1.2",
16-
"react/http": "^0.8.0"
12+
"phpunit/phpunit": "^9.5",
13+
"react/http": "^1.5"
1714
},
1815
"autoload": {
1916
"psr-4": {

src/AwaitRequestHandler.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace FriendsOfReact\Http\Middleware\Psr15Adapter;
4+
5+
use Psr\Http\Message\ResponseInterface;
6+
use Psr\Http\Message\ServerRequestInterface;
7+
use Psr\Http\Server\RequestHandlerInterface;
8+
use function React\Async\await;
9+
use function React\Promise\resolve;
10+
11+
/**
12+
* @internal
13+
*/
14+
final class AwaitRequestHandler implements RequestHandlerInterface
15+
{
16+
public function __construct(private \Closure $next) {}
17+
18+
public function handle(ServerRequestInterface $request): ResponseInterface
19+
{
20+
return await(resolve(($this->next)($request)));
21+
}
22+
}

src/GroupedPSR15Middleware.php

Lines changed: 12 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,86 +5,34 @@
55
use Psr\Http\Message\ResponseInterface;
66
use Psr\Http\Message\ServerRequestInterface;
77
use Psr\Http\Server\MiddlewareInterface as PSR15MiddlewareInterface;
8-
use React\EventLoop\LoopInterface;
98
use React\Promise;
10-
use Recoil\React\ReactKernel;
11-
use Throwable;
9+
use function React\Async\async;
1210

1311
final class GroupedPSR15Middleware
1412
{
1513
/**
16-
* @var ReactKernel
14+
* @var array<PSR15MiddlewareInterface>
1715
*/
18-
private $kernel;
16+
private array $middleware = [];
1917

20-
/**
21-
* @var PSR15MiddlewareInterface[]
22-
*/
23-
private $middleware = [];
24-
25-
public function __construct(LoopInterface $loop)
18+
public function withMiddleware(PSR15MiddlewareInterface $middleware): self
2619
{
27-
$this->kernel = ReactKernel::create($loop);
28-
}
29-
30-
public function withMiddleware(string $middleware, array $arguments = [], callable $func = null)
31-
{
32-
if ($func === null) {
33-
$func = function ($middleware) {
34-
return $middleware;
35-
};
36-
}
37-
3820
$clone = clone $this;
39-
$clone->middleware[] = $func(YieldingMiddlewareFactory::construct($middleware, $arguments));
40-
21+
$clone->middleware[] = $middleware;
4122
return $clone;
4223
}
4324

4425
public function __invoke(ServerRequestInterface $request, callable $next): Promise\PromiseInterface
4526
{
27+
return async(function (ServerRequestInterface $request, callable $next): ResponseInterface {
28+
$middleware = array_reverse($this->middleware);
29+
$requestHandler = new AwaitRequestHandler($next);
4630

47-
$stack = $this->createStack($next);
48-
49-
return new Promise\Promise(function ($resolve, $reject) use ($request, $next, $stack) {
50-
$this->kernel->execute(function () use ($resolve, $reject, $request, $next, $stack) {
51-
try {
52-
$response = $stack($request, $next);
53-
if ($response instanceof ResponseInterface) {
54-
$response = Promise\resolve($response);
55-
}
56-
$response = (yield $response);
57-
$resolve($response);
58-
} catch (Throwable $throwable) {
59-
$reject($throwable);
60-
}
61-
});
62-
});
63-
}
64-
65-
private function createStack($next)
66-
{
67-
$stack = function (ServerRequestInterface $request) use ($next) {
68-
$response = $next($request);
69-
if ($response instanceof ResponseInterface) {
70-
$response = Promise\resolve($response);
31+
foreach ($middleware as $mw) {
32+
$requestHandler = new PassThroughRequestHandler(static fn (ServerRequestInterface $request): ResponseInterface => $mw->process($request, $requestHandler));
7133
}
72-
return (yield $response);
73-
};
74-
75-
$middleware = $this->middleware;
76-
$middleware = array_reverse($middleware);
77-
foreach ($middleware as $mw) {
78-
$mwh = $mw;
79-
$stack = function (ServerRequestInterface $request) use ($stack, $mwh) {
80-
$response = $mwh->process($request, new PassThroughRequestHandler($stack));
81-
if ($response instanceof ResponseInterface) {
82-
$response = Promise\resolve($response);
83-
}
84-
return (yield $response);
85-
};
86-
}
8734

88-
return $stack;
35+
return $requestHandler->handle($request);
36+
})($request, $next);
8937
}
9038
}

src/PSR15Middleware.php

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,17 @@
55
use Psr\Http\Message\ResponseInterface;
66
use Psr\Http\Message\ServerRequestInterface;
77
use Psr\Http\Server\MiddlewareInterface as PSR15MiddlewareInterface;
8-
use React\EventLoop\LoopInterface;
9-
use React\Promise;
10-
use Recoil\React\ReactKernel;
11-
use Throwable;
8+
use React\Promise\PromiseInterface;
9+
use function React\Async\async;
1210

1311
final class PSR15Middleware
1412
{
15-
/**
16-
* @var ReactKernel
17-
*/
18-
private $kernel;
13+
public function __construct(private PSR15MiddlewareInterface $middleware) {}
1914

20-
/**
21-
* @var PSR15MiddlewareInterface
22-
*/
23-
private $middleware;
24-
25-
public function __construct(LoopInterface $loop, string $middleware, array $arguments = [], callable $func = null)
26-
{
27-
if ($func === null) {
28-
$func = function ($middleware) {
29-
return $middleware;
30-
};
31-
}
32-
33-
$this->kernel = ReactKernel::create($loop);
34-
$this->middleware = $func(YieldingMiddlewareFactory::construct($middleware, $arguments));
35-
}
36-
37-
public function __invoke(ServerRequestInterface $request, callable $next): Promise\PromiseInterface
15+
public function __invoke(ServerRequestInterface $request, callable $next): PromiseInterface
3816
{
39-
return new Promise\Promise(function ($resolve, $reject) use ($request, $next) {
40-
$this->kernel->execute(function () use ($resolve, $reject, $request, $next) {
41-
try {
42-
$response = $this->middleware->process($request, new RecoilWrappedRequestHandler($next));
43-
if ($response instanceof ResponseInterface) {
44-
$response = Promise\resolve($response);
45-
}
46-
$response = (yield $response);
47-
$resolve($response);
48-
} catch (Throwable $throwable) {
49-
$reject($throwable);
50-
}
51-
});
52-
});
17+
return async(
18+
fn (): ResponseInterface => $this->middleware->process($request, new AwaitRequestHandler($next))
19+
)($request, $next);
5320
}
5421
}

src/PassThroughRequestHandler.php

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,19 @@
22

33
namespace FriendsOfReact\Http\Middleware\Psr15Adapter;
44

5+
use Psr\Http\Message\ResponseInterface;
56
use Psr\Http\Message\ServerRequestInterface;
7+
use Psr\Http\Server\RequestHandlerInterface;
68

79
/**
810
* @internal
911
*/
10-
final class PassThroughRequestHandler
12+
final class PassThroughRequestHandler implements RequestHandlerInterface
1113
{
12-
/**
13-
* @var callable
14-
*/
15-
private $next;
14+
public function __construct(private \Closure $next) {}
1615

17-
/**
18-
* @param callable $next
19-
*/
20-
public function __construct(callable $next)
16+
public function handle(ServerRequestInterface $request): ResponseInterface
2117
{
22-
$this->next = $next;
23-
}
24-
25-
public function handle(ServerRequestInterface $request)
26-
{
27-
$next = $this->next;
28-
return $next($request);
18+
return ($this->next)($request);
2919
}
3020
}

src/RecoilWrappedRequestHandler.php

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)