diff --git a/src/Illuminate/Http/Client/PendingRequest.php b/src/Illuminate/Http/Client/PendingRequest.php index f1966e880480..8971b125514b 100644 --- a/src/Illuminate/Http/Client/PendingRequest.php +++ b/src/Illuminate/Http/Client/PendingRequest.php @@ -105,6 +105,13 @@ class PendingRequest */ protected $throwCallback; + /** + * A callback to check if an exception should be thrown when a server or client error occurs. + * + * @var \Closure + */ + protected $throwIfCallback; + /** * The number of times to try the request. * @@ -599,12 +606,17 @@ public function throw(callable $callback = null) /** * Throw an exception if a server or client error occurred and the given condition evaluates to true. * - * @param bool $condition + * @param callable|bool $condition + * @param callable|null $throwCallback * @return $this */ public function throwIf($condition) { - return $condition ? $this->throw() : $this; + if (is_callable($condition)) { + $this->throwIfCallback = $condition; + } + + return $condition ? $this->throw(func_get_args()[1] ?? null) : $this; } /** @@ -797,7 +809,9 @@ public function send(string $method, string $url, array $options = []) throw $exception; } - if ($this->throwCallback) { + if ($this->throwCallback && + ($this->throwIfCallback === null || + call_user_func($this->throwIfCallback, $response))) { $response->throw($this->throwCallback); } diff --git a/src/Illuminate/Http/Client/Response.php b/src/Illuminate/Http/Client/Response.php index 233653617808..a28ccc0e45fe 100644 --- a/src/Illuminate/Http/Client/Response.php +++ b/src/Illuminate/Http/Client/Response.php @@ -329,14 +329,15 @@ public function throw() /** * Throw an exception if a server or client error occurred and the given condition evaluates to true. * - * @param bool $condition + * @param \Closure|bool $condition + * @param \Closure|null $throwCallback * @return $this * * @throws \Illuminate\Http\Client\RequestException */ public function throwIf($condition) { - return $condition ? $this->throw() : $this; + return value($condition, $this) ? $this->throw(func_get_args()[1] ?? null) : $this; } /** diff --git a/src/Illuminate/Support/Facades/Http.php b/src/Illuminate/Support/Facades/Http.php index 94165eb3dab6..77b3c072476f 100644 --- a/src/Illuminate/Support/Facades/Http.php +++ b/src/Illuminate/Support/Facades/Http.php @@ -49,7 +49,7 @@ * @method static \Illuminate\Http\Client\PendingRequest withMiddleware(callable $middleware) * @method static \Illuminate\Http\Client\PendingRequest beforeSending(callable $callback) * @method static \Illuminate\Http\Client\PendingRequest throw(callable|null $callback = null) - * @method static \Illuminate\Http\Client\PendingRequest throwIf(bool $condition) + * @method static \Illuminate\Http\Client\PendingRequest throwIf(callable|bool $condition, callable|null $throwCallback) * @method static \Illuminate\Http\Client\PendingRequest throwUnless(bool $condition) * @method static \Illuminate\Http\Client\PendingRequest dump() * @method static \Illuminate\Http\Client\PendingRequest dd() diff --git a/tests/Http/HttpClientTest.php b/tests/Http/HttpClientTest.php index c6fdcb5bcca2..ee2a906d43dd 100644 --- a/tests/Http/HttpClientTest.php +++ b/tests/Http/HttpClientTest.php @@ -1495,6 +1495,63 @@ public function testRequestExceptionIsNotThrownIfTheThrowIfOnThePendingRequestIs $this->assertSame(403, $response->status()); } + public function testRequestExceptionIsThrownIfTheThrowIfClosureOnThePendingRequestReturnsTrue() + { + $this->factory->fake([ + '*' => $this->factory->response(['error'], 403), + ]); + + $exception = null; + + $hitThrowCallback = false; + + try { + $this->factory + ->throwIf(function ($response) { + $this->assertInstanceOf(Response::class, $response); + $this->assertSame(403, $response->status()); + + return true; + }, function ($response, $e) use (&$hitThrowCallback) { + $this->assertInstanceOf(Response::class, $response); + $this->assertSame(403, $response->status()); + + $this->assertInstanceOf(RequestException::class, $e); + $hitThrowCallback = true; + }) + ->get('http://foo.com/get'); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNotNull($exception); + $this->assertInstanceOf(RequestException::class, $exception); + $this->assertTrue($hitThrowCallback); + } + + public function testRequestExceptionIsNotThrownIfTheThrowIfClosureOnThePendingRequestReturnsFalse() + { + $this->factory->fake([ + '*' => $this->factory->response(['error'], 403), + ]); + + $hitThrowCallback = false; + + $response = $this->factory + ->throwIf(function ($response) { + $this->assertInstanceOf(Response::class, $response); + $this->assertSame(403, $response->status()); + + return false; + }, function ($response, $e) use (&$hitThrowCallback) { + $hitThrowCallback = true; + }) + ->get('http://foo.com/get'); + + $this->assertSame(403, $response->status()); + $this->assertFalse($hitThrowCallback); + } + public function testRequestExceptionIsThrownWithCallbackIfThePendingRequestIsSetToThrowOnFailure() { $this->factory->fake([ @@ -1603,6 +1660,56 @@ public function testRequestExceptionIsNotThrownIfConditionIsNotSatisfied() $this->assertSame('{"result":{"foo":"bar"}}', $response->body()); } + public function testRequestExceptionIsThrowIfConditionClosureIsSatisfied() + { + $this->factory->fake([ + '*' => $this->factory::response('', 400), + ]); + + $exception = null; + + $hitThrowCallback = false; + + try { + $this->factory->get('http://foo.com/api')->throwIf(function ($response) { + $this->assertSame(400, $response->status()); + + return true; + }, function ($response, $e) use (&$hitThrowCallback) { + $this->assertSame(400, $response->status()); + $this->assertInstanceOf(RequestException::class, $e); + + $hitThrowCallback = true; + }); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNotNull($exception); + $this->assertInstanceOf(RequestException::class, $exception); + $this->assertTrue($hitThrowCallback); + } + + public function testRequestExceptionIsNotThrownIfConditionClosureIsNotSatisfied() + { + $this->factory->fake([ + '*' => $this->factory::response(['result' => ['foo' => 'bar']], 400), + ]); + + $hitThrowCallback = false; + + $response = $this->factory->get('http://foo.com/api')->throwIf(function ($response) { + $this->assertSame(400, $response->status()); + + return false; + }, function ($response, $e) use (&$hitThrowCallback) { + $hitThrowCallback = true; + }); + + $this->assertSame('{"result":{"foo":"bar"}}', $response->body()); + $this->assertFalse($hitThrowCallback); + } + public function testItCanEnforceFaking() { $this->factory->preventStrayRequests();