diff --git a/spec/Exception/BatchExceptionSpec.php b/spec/Exception/BatchExceptionSpec.php new file mode 100644 index 0000000..f8cc048 --- /dev/null +++ b/spec/Exception/BatchExceptionSpec.php @@ -0,0 +1,41 @@ +beConstructedWith([$e], [$response]); + } + + function it_is_initializable() + { + $this->shouldHaveType('Http\Client\Exception\BatchException'); + } + + function it_is_a_transfer_exception() + { + $this->shouldHaveType('Http\Client\Exception\TransferException'); + } + + function it_has_exceptions(TransferException $e, TransferException $e2) + { + $this->getExceptions()->shouldReturn([$e]); + $this->hasException($e)->shouldReturn(true); + $this->hasException($e2)->shouldReturn(false); + $this->hasExceptions()->shouldReturn(true); + } + + function it_has_responses(ResponseInterface $response, ResponseInterface $response2) + { + $this->getResponses()->shouldReturn([$response]); + $this->hasResponse($response)->shouldReturn(true); + $this->hasResponse($response2)->shouldReturn(false); + $this->hasResponses()->shouldReturn(true); + } +} diff --git a/spec/Exception/ClientExceptionSpec.php b/spec/Exception/ClientExceptionSpec.php new file mode 100644 index 0000000..706f62f --- /dev/null +++ b/spec/Exception/ClientExceptionSpec.php @@ -0,0 +1,27 @@ +getStatusCode()->willReturn(400); + + $this->beConstructedWith('message', $request, $response); + } + + function it_is_initializable() + { + $this->shouldHaveType('Http\Client\Exception\ClientException'); + } + + function it_is_http_exception() + { + $this->shouldHaveType('Http\Client\Exception\HttpException'); + } +} diff --git a/spec/Exception/HttpExceptionSpec.php b/spec/Exception/HttpExceptionSpec.php new file mode 100644 index 0000000..9bd4392 --- /dev/null +++ b/spec/Exception/HttpExceptionSpec.php @@ -0,0 +1,71 @@ +getStatusCode()->willReturn(400); + + $this->beConstructedWith('message', $request, $response); + } + + function it_is_initializable() + { + $this->shouldHaveType('Http\Client\Exception\HttpException'); + } + + function it_is_request_exception() + { + $this->shouldHaveType('Http\Client\Exception\RequestException'); + } + + function it_has_a_response(ResponseInterface $response) + { + $this->getResponse()->shouldReturn($response); + } + + function it_creates_a_client_exception(RequestInterface $request, ResponseInterface $response) + { + $request->getRequestTarget()->willReturn('/uri'); + $request->getMethod()->willReturn('GET'); + $response->getStatusCode()->willReturn(404); + $response->getReasonPhrase()->willReturn('Not Found'); + + $e = $this->create($request, $response); + + $e->shouldHaveType('Http\Client\Exception\ClientException'); + $e->getMessage()->shouldReturn('Client error [url] /uri [http method] GET [status code] 404 [reason phrase] Not Found'); + } + + function it_creates_a_server_exception(RequestInterface $request, ResponseInterface $response) + { + $request->getRequestTarget()->willReturn('/uri'); + $request->getMethod()->willReturn('GET'); + $response->getStatusCode()->willReturn(500); + $response->getReasonPhrase()->willReturn('Internal Server Error'); + + $e = $this->create($request, $response); + + $e->shouldHaveType('Http\Client\Exception\ServerException'); + $e->getMessage()->shouldReturn('Server error [url] /uri [http method] GET [status code] 500 [reason phrase] Internal Server Error'); + } + + function it_creates_an_http_exception(RequestInterface $request, ResponseInterface $response) + { + $request->getRequestTarget()->willReturn('/uri'); + $request->getMethod()->willReturn('GET'); + $response->getStatusCode()->willReturn(100); + $response->getReasonPhrase()->willReturn('Continue'); + + $e = $this->create($request, $response); + + $e->shouldHaveType('Http\Client\Exception\HttpException'); + $e->getMessage()->shouldReturn('Unsuccessful response [url] /uri [http method] GET [status code] 100 [reason phrase] Continue'); + } +} diff --git a/spec/Exception/NetworkExceptionSpec.php b/spec/Exception/NetworkExceptionSpec.php new file mode 100644 index 0000000..a8e0eb6 --- /dev/null +++ b/spec/Exception/NetworkExceptionSpec.php @@ -0,0 +1,24 @@ +beConstructedWith('message', $request); + } + + function it_is_initializable() + { + $this->shouldHaveType('Http\Client\Exception\NetworkException'); + } + + function it_is_request_exception() + { + $this->shouldHaveType('Http\Client\Exception\RequestException'); + } +} diff --git a/spec/Exception/RequestExceptionSpec.php b/spec/Exception/RequestExceptionSpec.php new file mode 100644 index 0000000..9779280 --- /dev/null +++ b/spec/Exception/RequestExceptionSpec.php @@ -0,0 +1,39 @@ +beConstructedWith('message', $request); + } + + function it_is_initializable() + { + $this->shouldHaveType('Http\Client\Exception\RequestException'); + } + + function it_has_a_request(RequestInterface $request) + { + $this->getRequest()->shouldReturn($request); + } + + function it_wraps_an_exception(RequestInterface $request) + { + $e = new \Exception('message'); + + $requestException = $this->wrapException($request, $e); + + $requestException->getMessage()->shouldReturn('message'); + } + + function it_does_not_wrap_if_request_exception(RequestInterface $request, RequestException $requestException) + { + $this->wrapException($request, $requestException)->shouldReturn($requestException); + } +} diff --git a/spec/Exception/ServerExceptionSpec.php b/spec/Exception/ServerExceptionSpec.php new file mode 100644 index 0000000..1005c23 --- /dev/null +++ b/spec/Exception/ServerExceptionSpec.php @@ -0,0 +1,27 @@ +getStatusCode()->willReturn(500); + + $this->beConstructedWith('message', $request, $response); + } + + function it_is_initializable() + { + $this->shouldHaveType('Http\Client\Exception\ServerException'); + } + + function it_is_http_exception() + { + $this->shouldHaveType('Http\Client\Exception\HttpException'); + } +} diff --git a/spec/Exception/TransferExceptionSpec.php b/spec/Exception/TransferExceptionSpec.php new file mode 100644 index 0000000..bd5d39f --- /dev/null +++ b/spec/Exception/TransferExceptionSpec.php @@ -0,0 +1,14 @@ +shouldHaveType('Http\Client\Exception\TransferException'); + } +} diff --git a/src/Exception/BatchException.php b/src/Exception/BatchException.php new file mode 100644 index 0000000..1f71000 --- /dev/null +++ b/src/Exception/BatchException.php @@ -0,0 +1,62 @@ + + */ +final class BatchException extends TransferException +{ + /** + * @var TransferException[] + */ + private $exceptions; + + /** + * @param TransferException[] $exceptions + */ + public function __construct(array $exceptions = []) + { + parent::__construct('An error occurred when sending multiple requests.'); + + foreach ($exceptions as $e) { + if (!$e instanceof TransferException) { + throw new InvalidArgumentException('Exception is not an instanceof Http\Client\Exception\TransferException'); + } + } + + $this->exceptions = $exceptions; + } + + /** + * Returns all exceptions + * + * @return TransferException[] + */ + public function getExceptions() + { + return $this->exceptions; + } + + /** + * Checks if a specific exception exists + * + * @param TransferException $exception + * + * @return boolean TRUE if there is the exception else FALSE. + */ + public function hasException(TransferException $exception) + { + return array_search($exception, $this->exceptions, true) !== false; + } + + /** + * Checks if any exception exists + * + * @return boolean + */ + public function hasExceptions() + { + return !empty($this->exceptions); + } +} diff --git a/src/Exception/ClientException.php b/src/Exception/ClientException.php new file mode 100644 index 0000000..454e386 --- /dev/null +++ b/src/Exception/ClientException.php @@ -0,0 +1,12 @@ + + */ +final class ClientException extends HttpException +{ +} diff --git a/src/Exception/HttpClientException.php b/src/Exception/HttpClientException.php deleted file mode 100644 index 97a6b99..0000000 --- a/src/Exception/HttpClientException.php +++ /dev/null @@ -1,82 +0,0 @@ - - */ -class HttpClientException extends RuntimeException -{ - /** - * @var RequestInterface|null - */ - private $request; - - /** - * @var ResponseInterface|null - */ - private $response; - - /** - * Returns the request - * - * @return RequestInterface|null - */ - public function getRequest() - { - return $this->request; - } - - /** - * Checks if there is a request - * - * @return boolean - */ - public function hasRequest() - { - return isset($this->request); - } - - /** - * Sets the request - * - * @param RequestInterface|null $request - */ - public function setRequest(RequestInterface $request = null) - { - $this->request = $request; - } - - /** - * Returns the response - * - * @return ResponseInterface|null - */ - public function getResponse() - { - return $this->response; - } - - /** - * Checks if there is a response - * - * @return boolean - */ - public function hasResponse() - { - return isset($this->response); - } - - /** - * Sets the response - * - * @param ResponseInterface|null $response - */ - public function setResponse(ResponseInterface $response = null) - { - $this->response = $response; - } -} diff --git a/src/Exception/HttpException.php b/src/Exception/HttpException.php new file mode 100644 index 0000000..5e47b07 --- /dev/null +++ b/src/Exception/HttpException.php @@ -0,0 +1,86 @@ + + */ +class HttpException extends RequestException +{ + /** + * @var ResponseInterface + */ + protected $response; + + /** + * @param string $message + * @param RequestInterface $request + * @param ResponseInterface $response + * @param \Exception|null $previous + */ + public function __construct( + $message, + RequestInterface $request, + ResponseInterface $response, + \Exception $previous = null + ) { + $this->response = $response; + $this->code = $response->getStatusCode(); + + + parent::__construct($message, $request, $previous); + } + + /** + * Returns the response + * + * @return ResponseInterface + */ + public function getResponse() + { + return $this->response; + } + + /** + * Factory method to create a new exception with a normalized error message + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param \Exception|null $previous + * + * @return HttpException + */ + public static function create(RequestInterface $request, ResponseInterface $response, \Exception $previous = null) + { + $code = $response->getStatusCode(); + + if ($code >= 400 && $code < 500) { + $message = 'Client error'; + $className = __NAMESPACE__ . '\\ClientException'; + } elseif ($code >= 500 && $code < 600) { + $message = 'Server error'; + $className = __NAMESPACE__ . '\\ServerException'; + } else { + $message = 'Unsuccessful response'; + $className = __CLASS__; + } + + $message = sprintf( + '%s [url] %s [http method] %s [status code] %s [reason phrase] %s', + $message, + $request->getRequestTarget(), + $request->getMethod(), + $response->getStatusCode(), + $response->getReasonPhrase() + ); + + return new $className($message, $request, $response, $previous); + } +} diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index e761c49..9d9f938 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -11,5 +11,4 @@ */ class InvalidArgumentException extends \InvalidArgumentException implements Exception { - } diff --git a/src/Exception/MultiHttpClientException.php b/src/Exception/MultiHttpClientException.php deleted file mode 100644 index 64456f2..0000000 --- a/src/Exception/MultiHttpClientException.php +++ /dev/null @@ -1,225 +0,0 @@ - - */ -class MultiHttpClientException extends RuntimeException -{ - /** - * @var HttpClientException[] - */ - private $exceptions; - - /** - * @var ResponseInterface[] - */ - private $responses; - - /** - * @param HttpClientException[] $exceptions - * @param ResponseInterface[] $responses - */ - public function __construct(array $exceptions = [], array $responses = []) - { - parent::__construct('An error occurred when sending multiple requests.'); - - $this->setExceptions($exceptions); - $this->setResponses($responses); - } - - /** - * Returns all exceptions - * - * @return HttpClientException[] - */ - public function getExceptions() - { - return $this->exceptions; - } - - /** - * Checks if a specific exception exists - * - * @param HttpClientException $exception - * - * @return boolean TRUE if there is the exception else FALSE. - */ - public function hasException(HttpClientException $exception) - { - return array_search($exception, $this->exceptions, true) !== false; - } - - /** - * Checks if any exception exists - * - * @return boolean - */ - public function hasExceptions() - { - return !empty($this->exceptions); - } - - /** - * Sets the exceptions - * - * @param HttpClientException[] $exceptions - */ - public function setExceptions(array $exceptions) - { - $this->clearExceptions(); - $this->addExceptions($exceptions); - } - - /** - * Adds an exception - * - * @param HttpClientException $exception - */ - public function addException(HttpClientException $exception) - { - $this->exceptions[] = $exception; - } - - /** - * Adds some exceptions - * - * @param HttpClientException[] $exceptions - */ - public function addExceptions(array $exceptions) - { - foreach ($exceptions as $exception) { - $this->addException($exception); - } - } - - /** - * Removes an exception - * - * @param HttpClientException $exception - */ - public function removeException(HttpClientException $exception) - { - unset($this->exceptions[array_search($exception, $this->exceptions, true)]); - $this->exceptions = array_values($this->exceptions); - } - - /** - * Removes some exceptions - * - * @param HttpClientException[] $exceptions - */ - public function removeExceptions(array $exceptions) - { - foreach ($exceptions as $exception) { - $this->removeException($exception); - } - } - - /** - * Clears all exceptions - */ - public function clearExceptions() - { - $this->exceptions = []; - } - - /** - * Returns all responses - * - * @return ResponseInterface[] - */ - public function getResponses() - { - return $this->responses; - } - - /** - * Checks if a specific response exists - * - * @param ResponseInterface $response - * - * @return boolean - */ - public function hasResponse(ResponseInterface $response) - { - return array_search($response, $this->responses, true) !== false; - } - - /** - * Checks if any response exists - * - * @return boolean - */ - public function hasResponses() - { - return !empty($this->responses); - } - - /** - * Sets the responses - * - * @param ResponseInterface[] $responses - */ - public function setResponses(array $responses) - { - $this->clearResponses(); - $this->addResponses($responses); - } - - /** - * Adds a response - * - * @param ResponseInterface $response - */ - public function addResponse(ResponseInterface $response) - { - $this->responses[] = $response; - } - - /** - * Adds some responses - * - * @param ResponseInterface[] $responses - */ - public function addResponses(array $responses) - { - foreach ($responses as $response) { - $this->addResponse($response); - } - } - - /** - * Removes a response - * - * @param ResponseInterface $response - */ - public function removeResponse(ResponseInterface $response) - { - unset($this->responses[array_search($response, $this->responses, true)]); - $this->responses = array_values($this->responses); - } - - /** - * Removes some responses - * - * @param ResponseInterface[] $responses - */ - public function removeResponses(array $responses) - { - foreach ($responses as $response) { - $this->removeResponse($response); - } - } - - /** - * Clears all responses - */ - public function clearResponses() - { - $this->responses = []; - } -} diff --git a/src/Exception/NetworkException.php b/src/Exception/NetworkException.php new file mode 100644 index 0000000..21ea391 --- /dev/null +++ b/src/Exception/NetworkException.php @@ -0,0 +1,12 @@ + + */ +class NetworkException extends RequestException +{ +} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php new file mode 100644 index 0000000..c4e74a3 --- /dev/null +++ b/src/Exception/RequestException.php @@ -0,0 +1,57 @@ + + */ +class RequestException extends TransferException +{ + /** + * @var RequestInterface + */ + private $request; + + /** + * @param string $message + * @param RequestInterface $request + * @param \Exception|null $previous + */ + public function __construct($message, RequestInterface $request, \Exception $previous = null) + { + $this->request = $request; + + parent::__construct($message, 0, $previous); + } + + /** + * Returns the request + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } + + /** + * @param RequestInterface $request + * @param \Exception $e + * + * @return RequestException + */ + public static function wrapException(RequestInterface $request, \Exception $e) + { + if (!$e instanceof RequestException) { + $e = new RequestException($e->getMessage(), $request, $e); + } + + return $e; + } +} diff --git a/src/Exception/RuntimeException.php b/src/Exception/RuntimeException.php index b8a7fc5..4bffb40 100644 --- a/src/Exception/RuntimeException.php +++ b/src/Exception/RuntimeException.php @@ -11,5 +11,4 @@ */ class RuntimeException extends \RuntimeException implements Exception { - } diff --git a/src/Exception/ServerException.php b/src/Exception/ServerException.php new file mode 100644 index 0000000..42c7b4b --- /dev/null +++ b/src/Exception/ServerException.php @@ -0,0 +1,12 @@ + + */ +final class ServerException extends HttpException +{ +} diff --git a/src/Exception/TransferException.php b/src/Exception/TransferException.php new file mode 100644 index 0000000..b8a7ec7 --- /dev/null +++ b/src/Exception/TransferException.php @@ -0,0 +1,12 @@ + + */ +class TransferException extends RuntimeException +{ +}