-
-
Save jamiehannaford/0a085d4b1507308b0190 to your computer and use it in GitHub Desktop.
<?php | |
namespace OpenStack\Common\Transport\Exception; | |
use GuzzleHttp\Message\RequestInterface; | |
use GuzzleHttp\Message\ResponseInterface; | |
use OpenStack\Common\Exception; | |
class RequestException extends Exception | |
{ | |
/** @var \GuzzleHttp\Message\RequestInterface */ | |
protected $request; | |
/** @var \GuzzleHttp\Message\ResponseInterface */ | |
protected $response; | |
/** | |
* Construct this exception like any other, but also inject Request and | |
* Response objects in case the user needs them for debugging. | |
* | |
* @param string $errorMessage Human-readable explanation of error | |
* @param RequestInterface $request The failed request | |
* @param ResponseInterface $response The API's response | |
*/ | |
public function __construct($errorMessage, RequestInterface $request, ResponseInterface $response) | |
{ | |
parent::__construct($errorMessage); | |
$this->request = $request; | |
$this->response = $response; | |
} | |
/** | |
* Factory method that creates an appropriate Exception object based on the | |
* Response's status code. The message is constructed here also. | |
* | |
* @param RequestInterface $request The failed request | |
* @param ResponseInterface $response The API's response | |
* @return self | |
*/ | |
public static function create(RequestInterface $request, ResponseInterface $response) | |
{ | |
$label = 'A HTTP error occurred'; | |
$status = $response->getStatusCode(); | |
switch ($status) { | |
case '401': | |
$class = self::prependNamespace('UnauthorizedException'); | |
break; | |
case '403': | |
$class = self::prependNamespace('ForbiddenException'); | |
break; | |
case '404': | |
$class = self::prependNamespace('ResourceNotFoundException'); | |
break; | |
case '405': | |
$class = self::prependNamespace('MethodNotAllowedException'); | |
break; | |
case '409': | |
$class = self::prependNamespace('ConflictException'); | |
break; | |
case '411': | |
$class = self::prependNamespace('LengthRequiredException'); | |
break; | |
case '422': | |
$class = self::prependNamespace('UnprocessableEntityException'); | |
break; | |
case '500': | |
$class = self::prependNamespace('ServerException'); | |
break; | |
} | |
$message = sprintf( | |
"%s\n[Status] %s (%s)\n[Message] %s", $label, | |
$status, $response->getReasonPhrase(), (string) $response->getBody() | |
); | |
// For all other errors, throw a generic Exception | |
if (!isset($class)) { | |
throw new Exception($message); | |
} | |
return new $class($message, $request, $response); | |
} | |
protected static function prependNamespace($class) | |
{ | |
return sprintf("%s\\%s", __NAMESPACE__, $class); | |
} | |
public function getResponse() | |
{ | |
return $this->response; | |
} | |
public function getRequest() | |
{ | |
return $this->request; | |
} | |
} |
Minor suggestion: when I first read self::prependNamespace('UnauthorizedException')
, I interpreted it as "prepend the UnauthorizedException namespace (to something)". Consider renaming prependNamespace
to something like createFullyQualifiedClassNameFrom
or prependNamespaceTo
.
Lines 74 - 82: It might be clearer if the creation of $message
was done before the switch and a default case was added in the switch to throw the generic Exception.
I imagine each of the *Exception
classes mentioned in the switch cases will extend this RequestException
class. I also imagine you would want to disallow instantiation of the RequestException
class itself (that is, one of its subclasses must be instantiated instead). If so, consider making the RequestException
class abstract.
I agree about the method name, I'll change it to self::prependNamespaceTo
.
Adding a default
block won't work because the generic exception class has different constructor args - so it needs to be instantiated in a different way. Another option might be:
return isset($class) ? new $class($message, $request, $response) : Exception($message);
I see what you mean about making the RequestException
abstract - but I'm not sure we'll need to do it. Say for example we encounter a HTTP 4xx
or 5xx
error that does not have its own concrete exception, we'd probably throw a generic RequestException
.
In that case, what about creating a default
case that returns new self($message, $request, $response);
?
One of the problems I've just noticed is that the
$response
and$request
properties reference Guzzle classes, which is incorrect. I can change this to point to our own message classes.