Skip to content

Instantly share code, notes, and snippets.

@jamiehannaford
Created April 30, 2014 17:02
Show Gist options
  • Save jamiehannaford/0a085d4b1507308b0190 to your computer and use it in GitHub Desktop.
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;
}
}
@jamiehannaford
Copy link
Author

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.

@ycombinator
Copy link

In that case, what about creating a default case that returns new self($message, $request, $response);?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment