Skip to content

Commit 8fa588c

Browse files
committed
Merge branch 'cleanup'
2 parents 8908e6c + e65a854 commit 8fa588c

File tree

8 files changed

+125
-60
lines changed

8 files changed

+125
-60
lines changed

src/Toolkit/Curler/Curler.php

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

33
namespace Salient\Curler;
44

5+
use Psr\Http\Client\ClientExceptionInterface;
56
use Psr\Http\Message\RequestInterface;
67
use Psr\Http\Message\ResponseInterface;
78
use Psr\Http\Message\UriInterface as PsrUriInterface;
@@ -41,6 +42,8 @@
4142
use Salient\Core\Utility\Str;
4243
use Salient\Curler\Exception\CurlErrorException;
4344
use Salient\Curler\Exception\HttpErrorException;
45+
use Salient\Curler\Exception\NetworkException;
46+
use Salient\Curler\Exception\RequestException;
4447
use Salient\Http\Exception\InvalidHeaderException;
4548
use Salient\Http\Exception\StreamEncapsulationException;
4649
use Salient\Http\HasHttpHeaders;
@@ -53,6 +56,7 @@
5356
use CurlHandle;
5457
use Generator;
5558
use Stringable;
59+
use Throwable;
5660

5761
/**
5862
* An HTTP client optimised for RESTful APIs
@@ -116,6 +120,7 @@ class Curler implements CurlerInterface, Buildable
116120
protected bool $ThrowHttpErrors;
117121
protected ?RequestInterface $LastRequest = null;
118122
protected ?HttpResponseInterface $LastResponse = null;
123+
private ?Curler $WithoutThrowHttpErrors = null;
119124
private ?Closure $Closure = null;
120125

121126
private static string $DefaultUserAgent;
@@ -228,6 +233,7 @@ private function __clone()
228233
{
229234
$this->LastRequest = null;
230235
$this->LastResponse = null;
236+
$this->WithoutThrowHttpErrors = null;
231237
$this->Closure = null;
232238
}
233239

@@ -363,7 +369,7 @@ private function process(string $method, ?array $query, $data = false)
363369
$request = $pager->getFirstRequest($request, $this);
364370
}
365371

366-
$this->sendRequest($request);
372+
$this->doSendRequest($request);
367373

368374
if ($method === Method::HEAD) {
369375
return $this->LastResponse->getHttpHeaders();
@@ -436,7 +442,7 @@ private function paginate(string $method, ?array $query, $data = false): Generat
436442
$request = $this->Pager->getFirstRequest($request, $this);
437443
$prev = null;
438444
do {
439-
$this->sendRequest($request);
445+
$this->doSendRequest($request);
440446
$page = $this->Pager->getPage(
441447
$this->getLastResponseBody(),
442448
$request,
@@ -494,7 +500,7 @@ private function processRaw(string $method, string $data, string $mediaType, ?ar
494500
{
495501
$request = $this->createRequest($method, $query, $data);
496502
$request = $request->withHeader(HttpHeader::CONTENT_TYPE, $mediaType);
497-
$this->sendRequest($request);
503+
$this->doSendRequest($request);
498504
return $this->getLastResponseBody();
499505
}
500506

@@ -966,15 +972,45 @@ public function withThrowHttpErrors(bool $throw = true)
966972

967973
/**
968974
* @inheritDoc
969-
*
975+
*/
976+
public function sendRequest(RequestInterface $request): HttpResponseInterface
977+
{
978+
// PSR-18: "A Client MUST NOT treat a well-formed HTTP request or HTTP
979+
// response as an error condition. For example, response status codes in
980+
// the 400 and 500 range MUST NOT cause an exception and MUST be
981+
// returned to the Calling Library as normal."
982+
$curler = $this->WithoutThrowHttpErrors
983+
??= $this->withThrowHttpErrors(false);
984+
try {
985+
try {
986+
return $curler->doSendRequest($request);
987+
} finally {
988+
$this->LastRequest = $curler->LastRequest;
989+
$this->LastResponse = $curler->LastResponse;
990+
}
991+
} catch (ClientExceptionInterface $ex) {
992+
throw $ex;
993+
} catch (CurlErrorException $ex) {
994+
throw $ex->isNetworkError()
995+
? new NetworkException($ex->getMessage(), $this->LastRequest ?? $request, [], $ex)
996+
: new RequestException($ex->getMessage(), $this->LastRequest ?? $request, [], $ex);
997+
} catch (Throwable $ex) {
998+
throw new RequestException($ex->getMessage(), $this->LastRequest ?? $request, [], $ex);
999+
}
1000+
}
1001+
1002+
/**
9701003
* @phpstan-assert !null $this->LastRequest
9711004
* @phpstan-assert !null $this->LastResponse
9721005
*/
973-
public function sendRequest(RequestInterface $request): HttpResponseInterface
1006+
private function doSendRequest(RequestInterface $request): HttpResponseInterface
9741007
{
1008+
$this->LastRequest = null;
1009+
$this->LastResponse = null;
1010+
9751011
return $this->Middleware
9761012
? ($this->Closure ??= $this->getClosure())($request)
977-
: $this->doSendRequest($request);
1013+
: $this->getResponse($request);
9781014
}
9791015

9801016
/**
@@ -983,7 +1019,7 @@ public function sendRequest(RequestInterface $request): HttpResponseInterface
9831019
private function getClosure(): Closure
9841020
{
9851021
$closure = fn(RequestInterface $request): HttpResponseInterface =>
986-
$this->doSendRequest($request);
1022+
$this->getResponse($request);
9871023

9881024
foreach (array_reverse($this->Middleware) as [$middleware]) {
9891025
$closure = $middleware instanceof CurlerMiddlewareInterface
@@ -999,7 +1035,7 @@ private function getClosure(): Closure
9991035
return $closure;
10001036
}
10011037

1002-
private function doSendRequest(RequestInterface $request): HttpResponseInterface
1038+
private function getResponse(RequestInterface $request): HttpResponseInterface
10031039
{
10041040
$uri = $request->getUri()->withFragment('');
10051041
$request = $request->withUri($uri);

src/Toolkit/Curler/Exception/AbstractCurlerException.php renamed to src/Toolkit/Curler/Exception/AbstractRequestException.php

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,38 @@
33
namespace Salient\Curler\Exception;
44

55
use Psr\Http\Message\RequestInterface;
6-
use Salient\Contract\Http\HttpResponseInterface;
76
use Salient\Core\Utility\Format;
87
use Salient\Core\AbstractException;
8+
use Salient\Http\HttpRequest;
99
use Throwable;
1010

11-
abstract class AbstractCurlerException extends AbstractException
11+
abstract class AbstractRequestException extends AbstractException
1212
{
1313
protected RequestInterface $Request;
14-
protected HttpResponseInterface $Response;
1514
/** @var array<string,mixed> */
1615
protected array $Data;
1716

1817
/**
1918
* @param array<string,mixed> $data
2019
*/
2120
public function __construct(
22-
string $message = '',
23-
?RequestInterface $request = null,
24-
?HttpResponseInterface $response = null,
21+
string $message,
22+
RequestInterface $request,
2523
array $data = [],
2624
?Throwable $previous = null
2725
) {
28-
if ($request !== null) {
29-
$this->Request = $request;
30-
}
31-
if ($response !== null) {
32-
$this->Response = $response;
33-
}
26+
$this->Request = $request;
3427
$this->Data = $data;
3528

3629
parent::__construct($message, $previous);
3730
}
3831

3932
/**
40-
* Get the request that triggered the exception, if applicable
33+
* Get the request that triggered the exception
4134
*/
42-
public function getRequest(): ?RequestInterface
35+
public function getRequest(): RequestInterface
4336
{
44-
return $this->Request ?? null;
45-
}
46-
47-
/**
48-
* Get the response that triggered the exception, if applicable
49-
*/
50-
public function getResponse(): ?HttpResponseInterface
51-
{
52-
return $this->Response ?? null;
37+
return $this->Request;
5338
}
5439

5540
/**
@@ -68,10 +53,8 @@ public function getData(): array
6853
public function getMetadata(): array
6954
{
7055
return [
71-
'Response' =>
72-
(string) ($this->Response ?? '<no response>'),
7356
'Request' =>
74-
(string) ($this->Response ?? '<no request>'),
57+
(string) HttpRequest::fromPsr7($this->Request),
7558
'Data' =>
7659
$this->Data
7760
? Format::array($this->Data)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Salient\Curler\Exception;
4+
5+
use Psr\Http\Message\RequestInterface;
6+
use Salient\Contract\Http\HttpResponseInterface;
7+
use Throwable;
8+
9+
abstract class AbstractResponseException extends AbstractRequestException
10+
{
11+
protected HttpResponseInterface $Response;
12+
13+
/**
14+
* @param array<string,mixed> $data
15+
*/
16+
public function __construct(
17+
string $message,
18+
RequestInterface $request,
19+
HttpResponseInterface $response,
20+
array $data = [],
21+
?Throwable $previous = null
22+
) {
23+
$this->Response = $response;
24+
25+
parent::__construct($message, $request, $data, $previous);
26+
}
27+
28+
/**
29+
* Get the response that triggered the exception
30+
*/
31+
public function getResponse(): HttpResponseInterface
32+
{
33+
return $this->Response;
34+
}
35+
36+
/**
37+
* @inheritDoc
38+
*/
39+
public function getMetadata(): array
40+
{
41+
return [
42+
'Response' =>
43+
(string) $this->Response,
44+
] + parent::getMetadata();
45+
}
46+
}

src/Toolkit/Curler/Exception/CurlErrorException.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
namespace Salient\Curler\Exception;
44

5-
use Psr\Http\Client\NetworkExceptionInterface;
65
use Psr\Http\Message\RequestInterface;
76

8-
class CurlErrorException extends AbstractCurlerException implements NetworkExceptionInterface
7+
class CurlErrorException extends AbstractRequestException
98
{
109
protected int $CurlError;
1110

@@ -22,7 +21,6 @@ public function __construct(
2221
parent::__construct(
2322
sprintf('cURL error %d: %s', $curlError, curl_strerror($curlError)),
2423
$request,
25-
null,
2624
$data,
2725
);
2826
}
@@ -36,10 +34,13 @@ public function getCurlError(): int
3634
}
3735

3836
/**
39-
* Get the request that triggered the exception
37+
* Check if the exception was caused by a network error
4038
*/
41-
public function getRequest(): RequestInterface
39+
public function isNetworkError(): bool
4240
{
43-
return $this->Request;
41+
return $this->CurlError === \CURLE_COULDNT_RESOLVE_HOST
42+
|| $this->CurlError === \CURLE_COULDNT_CONNECT
43+
|| $this->CurlError === \CURLE_OPERATION_TIMEOUTED
44+
|| $this->CurlError === \CURLE_SSL_CONNECT_ERROR;
4445
}
4546
}

src/Toolkit/Curler/Exception/HttpErrorException.php

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use Salient\Core\Utility\Arr;
88
use Throwable;
99

10-
class HttpErrorException extends AbstractCurlerException
10+
class HttpErrorException extends AbstractResponseException
1111
{
1212
protected int $StatusCode;
1313

@@ -34,22 +34,6 @@ public function __construct(
3434
);
3535
}
3636

37-
/**
38-
* Get the request that triggered the exception
39-
*/
40-
public function getRequest(): RequestInterface
41-
{
42-
return $this->Request;
43-
}
44-
45-
/**
46-
* Get the response that triggered the exception
47-
*/
48-
public function getResponse(): HttpResponseInterface
49-
{
50-
return $this->Response;
51-
}
52-
5337
/**
5438
* Get the exception's underlying HTTP status code
5539
*/
@@ -62,8 +46,9 @@ public function getStatusCode(): int
6246
* Check if the exception's underlying HTTP status code is 404 (Not Found)
6347
* or 410 (Gone)
6448
*/
65-
public function isNotFound(): bool
49+
public function isNotFoundError(): bool
6650
{
67-
return $this->StatusCode === 404 || $this->StatusCode === 410;
51+
return $this->StatusCode === 404
52+
|| $this->StatusCode === 410;
6853
}
6954
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Salient\Curler\Exception;
4+
5+
use Psr\Http\Client\NetworkExceptionInterface;
6+
7+
class NetworkException extends AbstractRequestException implements NetworkExceptionInterface {}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Salient\Curler\Exception;
4+
5+
use Psr\Http\Client\RequestExceptionInterface;
6+
7+
class RequestException extends AbstractRequestException implements RequestExceptionInterface {}

src/Toolkit/Sync/HttpSyncDefinition.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ function (array $match) use (
673673
} catch (HttpErrorException $ex) {
674674
if ($operation === OP::READ
675675
&& $id !== null
676-
&& $ex->isNotFound()) {
676+
&& $ex->isNotFoundError()) {
677677
throw new SyncEntityNotFoundException(
678678
$this->Provider,
679679
$this->Entity,

0 commit comments

Comments
 (0)