Skip to content

Commit 5d712fb

Browse files
[12.x] Introduce FluentPromise to allow for cleaner chaining in Pool (#57967)
* laravel fluent promise * docblocks * protected * Update PendingRequest.php * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent 4b881cf commit 5d712fb

File tree

3 files changed

+155
-2
lines changed

3 files changed

+155
-2
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
namespace Illuminate\Http\Client;
4+
5+
use GuzzleHttp\Promise\PromiseInterface;
6+
use Illuminate\Support\Traits\ForwardsCalls;
7+
8+
/**
9+
* A decorated Promise which allows for chaining callbacks.
10+
*/
11+
class FluentPromise implements PromiseInterface
12+
{
13+
use ForwardsCalls;
14+
15+
/**
16+
* Create a new fluent promise instance.
17+
*
18+
* @param \GuzzleHttp\Promise\PromiseInterface $guzzlePromise
19+
*/
20+
public function __construct(protected PromiseInterface $guzzlePromise)
21+
{
22+
}
23+
24+
#[\Override]
25+
public function then(?callable $onFulfilled = null, ?callable $onRejected = null): PromiseInterface
26+
{
27+
return $this->__call('then', [$onFulfilled, $onRejected]);
28+
}
29+
30+
#[\Override]
31+
public function otherwise(callable $onRejected): PromiseInterface
32+
{
33+
return $this->__call('otherwise', [$onRejected]);
34+
}
35+
36+
#[\Override]
37+
public function resolve($value): void
38+
{
39+
$this->guzzlePromise->resolve($value);
40+
}
41+
42+
#[\Override]
43+
public function reject($reason): void
44+
{
45+
$this->guzzlePromise->reject($reason);
46+
}
47+
48+
#[\Override]
49+
public function cancel(): void
50+
{
51+
$this->guzzlePromise->cancel();
52+
}
53+
54+
#[\Override]
55+
public function wait(bool $unwrap = true)
56+
{
57+
return $this->__call('wait', [$unwrap]);
58+
}
59+
60+
#[\Override]
61+
public function getState(): string
62+
{
63+
return $this->guzzlePromise->getState();
64+
}
65+
66+
/**
67+
* Get the underlying Guzzle promise.
68+
*
69+
* @return \GuzzleHttp\Promise\PromiseInterface
70+
*/
71+
public function getGuzzlePromise(): PromiseInterface
72+
{
73+
return $this->guzzlePromise;
74+
}
75+
76+
/**
77+
* Proxy requests to the underlying promise interface and update the local promise.
78+
*
79+
* @param string $method
80+
* @param array $parameters
81+
* @return mixed
82+
*/
83+
public function __call($method, $parameters)
84+
{
85+
$result = $this->forwardCallTo($this->guzzlePromise, $method, $parameters);
86+
87+
if (! $result instanceof PromiseInterface) {
88+
return $result;
89+
}
90+
91+
$this->guzzlePromise = $result;
92+
93+
return $this;
94+
}
95+
}

src/Illuminate/Http/Client/PendingRequest.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use GuzzleHttp\HandlerStack;
1313
use GuzzleHttp\Middleware;
1414
use GuzzleHttp\Promise\EachPromise;
15+
use GuzzleHttp\Promise\PromiseInterface;
1516
use GuzzleHttp\UriTemplate\UriTemplate;
1617
use Illuminate\Contracts\Support\Arrayable;
1718
use Illuminate\Http\Client\Events\ConnectionFailed;
@@ -1197,7 +1198,7 @@ protected function handlePromiseResponse(Response|ConnectionException|TransferEx
11971198
* @param string $method
11981199
* @param string $url
11991200
* @param array $options
1200-
* @return \Psr\Http\Message\MessageInterface|\GuzzleHttp\Promise\PromiseInterface
1201+
* @return \Psr\Http\Message\MessageInterface|\Illuminate\Http\Client\FluentPromise
12011202
*
12021203
* @throws \Exception
12031204
*/
@@ -1220,7 +1221,13 @@ protected function sendRequest(string $method, string $url, array $options = [])
12201221
'on_stats' => $onStats,
12211222
], $options));
12221223

1223-
return $this->buildClient()->$clientMethod($method, $url, $mergedOptions);
1224+
$result = $this->buildClient()->$clientMethod($method, $url, $mergedOptions);
1225+
1226+
if ($result instanceof PromiseInterface && ! $result instanceof FluentPromise) {
1227+
$result = new FluentPromise($result);
1228+
}
1229+
1230+
return $result;
12241231
}
12251232

12261233
/**

tests/Integration/Http/HttpClientTest.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
namespace Illuminate\Tests\Integration\Http;
44

55
use Illuminate\Http\Client\Events\RequestSending;
6+
use Illuminate\Http\Client\PendingRequest;
7+
use Illuminate\Http\Client\Pool;
8+
use Illuminate\Http\Client\Response;
69
use Illuminate\Support\Collection;
710
use Illuminate\Support\Facades\Event;
811
use Illuminate\Support\Facades\Facade;
@@ -37,4 +40,52 @@ public function testGlobalMiddlewarePersistsAfterFacadeFlush(): void
3740

3841
$this->assertCount(2, Http::getGlobalMiddleware());
3942
}
43+
44+
public function testPoolCanForwardToUnderlyingPromise()
45+
{
46+
Http::fake([
47+
'https://laravel.com*' => Http::response('Laravel'),
48+
'https://forge.laravel.com*' => Http::response('Forge'),
49+
'https://nightwatch.laravel.com*' => Http::response('Tim n Jess'),
50+
]);
51+
52+
$responses = Http::pool(function (Pool $pool) {
53+
$pool->as('laravel')->get('https://laravel.com');
54+
55+
$pool->as('forge')
56+
->get('https://forge.laravel.com')
57+
->then(function (Response $response): int {
58+
return strlen($response->getBody());
59+
});
60+
61+
$pool->as('nightwatch')
62+
->get('https://nightwatch.laravel.com')
63+
->then(fn (): int => 1)
64+
->then(fn ($i): int => $i + 199);
65+
}, 3);
66+
67+
$this->assertInstanceOf(Response::class, $responses['laravel']);
68+
$this->assertEquals(5, $responses['forge']);
69+
$this->assertEquals(200, $responses['nightwatch']);
70+
71+
$this->assertCount(3, Http::recorded());
72+
}
73+
74+
public function testForwardsCallsToPromise()
75+
{
76+
Http::fake(['*' => Http::response('faked response')]);
77+
78+
$myFakedResponse = null;
79+
$r = Http::async()
80+
->get('https://laravel.com')
81+
->then(function (Response $response) use (&$myFakedResponse): string {
82+
$myFakedResponse = $response->getBody();
83+
84+
return 'stub';
85+
})
86+
->wait();
87+
88+
$this->assertEquals('faked response', $myFakedResponse);
89+
$this->assertEquals('stub', $r);
90+
}
4091
}

0 commit comments

Comments
 (0)