Skip to content

Commit c251911

Browse files
committed
feat: implement interceptors in the runtime library
Signed-off-by: Mark Sagi-Kazar <[email protected]>
1 parent da32286 commit c251911

File tree

4 files changed

+129
-0
lines changed

4 files changed

+129
-0
lines changed

lib/src/Interceptor.php

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Twirp;
6+
7+
/**
8+
* Interceptor is a form of middleware for Twirp requests, that can be installed on both
9+
* clients and servers.
10+
*
11+
* Just like http middleware, interceptors can mutate requests and responses.
12+
* This can enable some powerful integrations, but it should be used with much care
13+
* because it may result in code that is very hard to debug.
14+
*/
15+
interface Interceptor
16+
{
17+
/**
18+
* Intercept a request and return an alternate handler (method).
19+
*
20+
* The returned method can either wrap the original or can be an entirely new one.
21+
*/
22+
public function intercept(Method $method): Method;
23+
}

lib/src/InterceptorChain.php

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Twirp;
6+
7+
/**
8+
* Chain multiple Interceptors into a single Interceptor.
9+
*/
10+
final class InterceptorChain implements Interceptor
11+
{
12+
/**
13+
* @var Interceptor[]
14+
*/
15+
private $interceptors = [];
16+
17+
public function __construct(Interceptor ...$interceptors)
18+
{
19+
$this->interceptors = $interceptors;
20+
}
21+
22+
/**
23+
* {@inheritdoc}
24+
*/
25+
public function intercept(Method $method): Method
26+
{
27+
foreach ($this->interceptors as $interceptor) {
28+
$method = $interceptor->intercept($method);
29+
}
30+
31+
return $method;
32+
}
33+
}

lib/src/Method.php

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Twirp;
6+
7+
/**
8+
* Method is a generic representation of a Twirp-generated RPC method.
9+
*
10+
* It is used to define Interceptors.
11+
*/
12+
interface Method
13+
{
14+
/**
15+
* Generic representation of the underlying method call.
16+
*
17+
* Since PHP doesn't support function types, it is defined as an interface.
18+
*/
19+
public function call(array $ctx, $request): string;
20+
}

lib/tests/InterceptorChainTest.php

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Twirp;
6+
7+
use Prophecy\PhpUnit\ProphecyTrait;
8+
use Prophecy\Prophecy\ObjectProphecy;
9+
use Twirp\InterceptorChain;
10+
use Twirp\Interceptor;
11+
use Twirp\Method;
12+
13+
final class InterceptorChainTest extends \PHPUnit\Framework\TestCase
14+
{
15+
use ProphecyTrait;
16+
17+
/**
18+
* @var InterceptorChain
19+
*/
20+
private $interceptor;
21+
22+
/**
23+
* @var Interceptor|ObjectProphecy
24+
*/
25+
private $interceptor1;
26+
27+
/**
28+
* @var Interceptor|ObjectProphecy
29+
*/
30+
private $interceptor2;
31+
32+
public function setUp(): void
33+
{
34+
$this->interceptor1 = $this->prophesize(Interceptor::class);
35+
$this->interceptor2 = $this->prophesize(Interceptor::class);
36+
37+
$this->interceptor = new InterceptorChain($this->interceptor1->reveal(), $this->interceptor2->reveal());
38+
}
39+
40+
/**
41+
* @test
42+
*/
43+
public function it_intercepts_a_method(): void
44+
{
45+
$method1 = $this->prophesize(Method::class)->reveal();
46+
$method2 = $this->prophesize(Method::class)->reveal();
47+
48+
$this->interceptor1->intercept($method1)->willReturn($method2);
49+
$this->interceptor2->intercept($method2)->willReturn($method2);
50+
51+
self::assertSame($method2, $this->interceptor->intercept($method1));
52+
}
53+
}

0 commit comments

Comments
 (0)