Skip to content

Commit 26c7029

Browse files
[9.x] Http client - allow to provide closure as "throwif" condition (#45251)
* Option to use closure as throwif conditon * Fix facade dockblock * add consistency to throw and throw if * update tests Co-authored-by: Taylor Otwell <[email protected]>
1 parent 1544f6f commit 26c7029

File tree

4 files changed

+128
-6
lines changed

4 files changed

+128
-6
lines changed

src/Illuminate/Http/Client/PendingRequest.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,13 @@ class PendingRequest
105105
*/
106106
protected $throwCallback;
107107

108+
/**
109+
* A callback to check if an exception should be thrown when a server or client error occurs.
110+
*
111+
* @var \Closure
112+
*/
113+
protected $throwIfCallback;
114+
108115
/**
109116
* The number of times to try the request.
110117
*
@@ -599,12 +606,17 @@ public function throw(callable $callback = null)
599606
/**
600607
* Throw an exception if a server or client error occurred and the given condition evaluates to true.
601608
*
602-
* @param bool $condition
609+
* @param callable|bool $condition
610+
* @param callable|null $throwCallback
603611
* @return $this
604612
*/
605613
public function throwIf($condition)
606614
{
607-
return $condition ? $this->throw() : $this;
615+
if (is_callable($condition)) {
616+
$this->throwIfCallback = $condition;
617+
}
618+
619+
return $condition ? $this->throw(func_get_args()[1] ?? null) : $this;
608620
}
609621

610622
/**
@@ -797,7 +809,9 @@ public function send(string $method, string $url, array $options = [])
797809
throw $exception;
798810
}
799811

800-
if ($this->throwCallback) {
812+
if ($this->throwCallback &&
813+
($this->throwIfCallback === null ||
814+
call_user_func($this->throwIfCallback, $response))) {
801815
$response->throw($this->throwCallback);
802816
}
803817

src/Illuminate/Http/Client/Response.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,14 +329,15 @@ public function throw()
329329
/**
330330
* Throw an exception if a server or client error occurred and the given condition evaluates to true.
331331
*
332-
* @param bool $condition
332+
* @param \Closure|bool $condition
333+
* @param \Closure|null $throwCallback
333334
* @return $this
334335
*
335336
* @throws \Illuminate\Http\Client\RequestException
336337
*/
337338
public function throwIf($condition)
338339
{
339-
return $condition ? $this->throw() : $this;
340+
return value($condition, $this) ? $this->throw(func_get_args()[1] ?? null) : $this;
340341
}
341342

342343
/**

src/Illuminate/Support/Facades/Http.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
* @method static \Illuminate\Http\Client\PendingRequest withMiddleware(callable $middleware)
5050
* @method static \Illuminate\Http\Client\PendingRequest beforeSending(callable $callback)
5151
* @method static \Illuminate\Http\Client\PendingRequest throw(callable|null $callback = null)
52-
* @method static \Illuminate\Http\Client\PendingRequest throwIf(bool $condition)
52+
* @method static \Illuminate\Http\Client\PendingRequest throwIf(callable|bool $condition, callable|null $throwCallback)
5353
* @method static \Illuminate\Http\Client\PendingRequest throwUnless(bool $condition)
5454
* @method static \Illuminate\Http\Client\PendingRequest dump()
5555
* @method static \Illuminate\Http\Client\PendingRequest dd()

tests/Http/HttpClientTest.php

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,63 @@ public function testRequestExceptionIsNotThrownIfTheThrowIfOnThePendingRequestIs
14951495
$this->assertSame(403, $response->status());
14961496
}
14971497

1498+
public function testRequestExceptionIsThrownIfTheThrowIfClosureOnThePendingRequestReturnsTrue()
1499+
{
1500+
$this->factory->fake([
1501+
'*' => $this->factory->response(['error'], 403),
1502+
]);
1503+
1504+
$exception = null;
1505+
1506+
$hitThrowCallback = false;
1507+
1508+
try {
1509+
$this->factory
1510+
->throwIf(function ($response) {
1511+
$this->assertInstanceOf(Response::class, $response);
1512+
$this->assertSame(403, $response->status());
1513+
1514+
return true;
1515+
}, function ($response, $e) use (&$hitThrowCallback) {
1516+
$this->assertInstanceOf(Response::class, $response);
1517+
$this->assertSame(403, $response->status());
1518+
1519+
$this->assertInstanceOf(RequestException::class, $e);
1520+
$hitThrowCallback = true;
1521+
})
1522+
->get('http://foo.com/get');
1523+
} catch (RequestException $e) {
1524+
$exception = $e;
1525+
}
1526+
1527+
$this->assertNotNull($exception);
1528+
$this->assertInstanceOf(RequestException::class, $exception);
1529+
$this->assertTrue($hitThrowCallback);
1530+
}
1531+
1532+
public function testRequestExceptionIsNotThrownIfTheThrowIfClosureOnThePendingRequestReturnsFalse()
1533+
{
1534+
$this->factory->fake([
1535+
'*' => $this->factory->response(['error'], 403),
1536+
]);
1537+
1538+
$hitThrowCallback = false;
1539+
1540+
$response = $this->factory
1541+
->throwIf(function ($response) {
1542+
$this->assertInstanceOf(Response::class, $response);
1543+
$this->assertSame(403, $response->status());
1544+
1545+
return false;
1546+
}, function ($response, $e) use (&$hitThrowCallback) {
1547+
$hitThrowCallback = true;
1548+
})
1549+
->get('http://foo.com/get');
1550+
1551+
$this->assertSame(403, $response->status());
1552+
$this->assertFalse($hitThrowCallback);
1553+
}
1554+
14981555
public function testRequestExceptionIsThrownWithCallbackIfThePendingRequestIsSetToThrowOnFailure()
14991556
{
15001557
$this->factory->fake([
@@ -1603,6 +1660,56 @@ public function testRequestExceptionIsNotThrownIfConditionIsNotSatisfied()
16031660
$this->assertSame('{"result":{"foo":"bar"}}', $response->body());
16041661
}
16051662

1663+
public function testRequestExceptionIsThrowIfConditionClosureIsSatisfied()
1664+
{
1665+
$this->factory->fake([
1666+
'*' => $this->factory::response('', 400),
1667+
]);
1668+
1669+
$exception = null;
1670+
1671+
$hitThrowCallback = false;
1672+
1673+
try {
1674+
$this->factory->get('http://foo.com/api')->throwIf(function ($response) {
1675+
$this->assertSame(400, $response->status());
1676+
1677+
return true;
1678+
}, function ($response, $e) use (&$hitThrowCallback) {
1679+
$this->assertSame(400, $response->status());
1680+
$this->assertInstanceOf(RequestException::class, $e);
1681+
1682+
$hitThrowCallback = true;
1683+
});
1684+
} catch (RequestException $e) {
1685+
$exception = $e;
1686+
}
1687+
1688+
$this->assertNotNull($exception);
1689+
$this->assertInstanceOf(RequestException::class, $exception);
1690+
$this->assertTrue($hitThrowCallback);
1691+
}
1692+
1693+
public function testRequestExceptionIsNotThrownIfConditionClosureIsNotSatisfied()
1694+
{
1695+
$this->factory->fake([
1696+
'*' => $this->factory::response(['result' => ['foo' => 'bar']], 400),
1697+
]);
1698+
1699+
$hitThrowCallback = false;
1700+
1701+
$response = $this->factory->get('http://foo.com/api')->throwIf(function ($response) {
1702+
$this->assertSame(400, $response->status());
1703+
1704+
return false;
1705+
}, function ($response, $e) use (&$hitThrowCallback) {
1706+
$hitThrowCallback = true;
1707+
});
1708+
1709+
$this->assertSame('{"result":{"foo":"bar"}}', $response->body());
1710+
$this->assertFalse($hitThrowCallback);
1711+
}
1712+
16061713
public function testItCanEnforceFaking()
16071714
{
16081715
$this->factory->preventStrayRequests();

0 commit comments

Comments
 (0)