-
Notifications
You must be signed in to change notification settings - Fork 308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exploded the base exception into several more specialized exception class #89
base: master
Are you sure you want to change the base?
Exploded the base exception into several more specialized exception class #89
Conversation
…ized exception classes
… exception constructor
… analysis to determine the error type
PSWebServiceLibrary.php
Outdated
* @package PrestaShopWebservice | ||
*/ | ||
class PrestaShopWebserviceBadRequestException extends \RuntimeException implements PrestaShopWebserviceException { | ||
public function __construct($previous = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding the Throwable type hints for this parameter everywhere
public function __construct($previous = null) | |
public function __construct(Throwable $previous = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doing so will conflict with the PHP minimum version constraint being 5.3.
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive100ContinueStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(100, 'Continue', $previous); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this function and the following be overly specific and redundant, forcing the use of different switch/case
Making shouldNotReceiveStatus()
public would reduce the amount of repetitive code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those methods were created to get the status text, as the client does not provide it in the $response
array. Not doing so would require to provide a very generic exception message in all those return codes.
All those cases may not be produced by Prestashop's API itself, but they could be produced by the underlying infrastructure and networks between the API and the API client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah but you don't need one factory method per message, you can handle it this way:
class PrestaShopWebserviceStatusException extends \RuntimeException implements PrestaShopWebserviceException {
const STATUS_MESSAGES = array(
100 => 'Continue',
101 => 'Switching protocols',
// ...
);
/**
* @param int $code
* @param ?\Throwable $previous
* @return self
*/
public static function shouldNotReceiveStatus($code, $previous = null)
{
$status = (isset(self::STATUS_MESSAGES[$code])) ? self::STATUS_MESSAGES[$code] : 'Nonstandard status';
return new self(
strtr(
'This call to PrestaShop Web Services responded with status `%code% %status%`, this client was not designed to process this kind of response. This can come from your web server or reverse proxy configuration.',
array(
'%code%' => $code,
'%status%' => $status,
)
),
$code,
$previous
);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having one factory per message is a practice I use, and personal preference making the code self-descriptive with a minimal cyclomatic complexity.
If you prefer the option with the array, I'll change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Array in constants are available since PHP 5.6, we would need to use static property instead of constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having one factory per message is a practice I use
I think that's generally a good practice when you're throwing exceptions in different scenarios. But in this case, you're just transforming a plethora of error codes into instances of the same exception: you're just transforming an error message from the server into an exception, and only the message changes. When handling a new scenario requires you to change two files to add a new function and a new line in a switch, it's usually a code smell.
Array in constants are available since PHP 5.6
Honestly I don't think anyone who's running PHP < 7 these days will update anything at all on their stack. I wouldn't be against upping the minimum requirements to PHP 7.1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[...] in this case, you're just transforming a plethora of error codes into instances of the same exception: you're just transforming an error message from the server into an exception, and only the message changes. When handling a new scenario requires you to change two files to add a new function and a new line in a switch, it's usually a code smell.
I'm sorry but I'm not sure what you are asking me to do to avoid code smells.
Do you want me to use an array of error messages with a unique static factory method?
This will not change anything about the switch statement and the number of files to change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what Pablo proposed was to use a generic static function able to build the error messages based on the status code This way you don't need a big switch in PsWebServiceLibrary::assertStatusCode
the switch is only handled in this exception class And if you use an array to store the messages even the switch part can be avoided
Also I understand the limitation about const in PHP 5.6, what Pablo suggested I think is that maybe we could release a new major version of this library based on PHP 7 to use more modern code As he said people still relying on PHP 5 have a very old environment and are not likely to update their libraries regularly so this wouldn't be a problem
Co-authored-by: Pablo Borowicz <[email protected]>
Error handling: marked as deprecated the checkStatusCode method
…nto feature/better-error-handling
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive100ContinueStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(100, 'Continue', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive101SwitchingProtocolsStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(101, 'Switching Protocols', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive102ProcessingStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(102, 'Processing', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive103EarlyHintsStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(103, 'Early Hints', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive300MultipleChoicesStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(300, 'Multiple Choices', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive301MovedPermanentlyStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(301, 'Moved Permanently', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive302FoundStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(302, 'Found', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive303SeeOtherStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(303, 'See Other', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive304NotModifiedStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(304, 'Not Modified', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive307TemporaryRedirectStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(307, 'Temporary Redirect', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive308PermanentRedirectStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(308, 'Permanent Redirect', $previous); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** | |
* @param int $code | |
* @param string $status | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
private static function shouldNotReceiveStatus($code, $status, $previous = null) | |
{ | |
return new self( | |
strtr( | |
'This call to PrestaShop Web Services responded with status `%code% %status%`, this client was not designed to process this kind of response. This can come from your web server or reverse proxy configuration.', | |
array( | |
'%code%' => $code, | |
'%status%' => $status, | |
) | |
), | |
$code, | |
$previous | |
); | |
} | |
/** | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
public static function shouldNotReceive100ContinueStatus($previous = null) | |
{ | |
return self::shouldNotReceiveStatus(100, 'Continue', $previous); | |
} | |
/** | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
public static function shouldNotReceive101SwitchingProtocolsStatus($previous = null) | |
{ | |
return self::shouldNotReceiveStatus(101, 'Switching Protocols', $previous); | |
} | |
/** | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
public static function shouldNotReceive102ProcessingStatus($previous = null) | |
{ | |
return self::shouldNotReceiveStatus(102, 'Processing', $previous); | |
} | |
/** | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
public static function shouldNotReceive103EarlyHintsStatus($previous = null) | |
{ | |
return self::shouldNotReceiveStatus(103, 'Early Hints', $previous); | |
} | |
/** | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
public static function shouldNotReceive300MultipleChoicesStatus($previous = null) | |
{ | |
return self::shouldNotReceiveStatus(300, 'Multiple Choices', $previous); | |
} | |
/** | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
public static function shouldNotReceive301MovedPermanentlyStatus($previous = null) | |
{ | |
return self::shouldNotReceiveStatus(301, 'Moved Permanently', $previous); | |
} | |
/** | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
public static function shouldNotReceive302FoundStatus($previous = null) | |
{ | |
return self::shouldNotReceiveStatus(302, 'Found', $previous); | |
} | |
/** | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
public static function shouldNotReceive303SeeOtherStatus($previous = null) | |
{ | |
return self::shouldNotReceiveStatus(303, 'See Other', $previous); | |
} | |
/** | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
public static function shouldNotReceive304NotModifiedStatus($previous = null) | |
{ | |
return self::shouldNotReceiveStatus(304, 'Not Modified', $previous); | |
} | |
/** | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
public static function shouldNotReceive307TemporaryRedirectStatus($previous = null) | |
{ | |
return self::shouldNotReceiveStatus(307, 'Temporary Redirect', $previous); | |
} | |
/** | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
public static function shouldNotReceive308PermanentRedirectStatus($previous = null) | |
{ | |
return self::shouldNotReceiveStatus(308, 'Permanent Redirect', $previous); | |
} | |
/** | |
* @param int $code | |
* @param string $status | |
* @param ?\Throwable $previous | |
* @return self | |
*/ | |
private static function shouldNotReceiveStatus($code, $status, $previous = null) | |
{ | |
static $messages = array( | |
100 => 'Continue', | |
101 => 'Switching protocols', | |
102 => 'Processing', | |
103 => 'Early Hints', | |
300 => 'Multiple Choices', | |
301 => 'Moved Permanently', | |
302 => 'Found', | |
303 => 'See Other', | |
304 => 'Not Modified', | |
307 => 'Temporary Redirect', | |
308 => 'Permanent Redirect' | |
); | |
return new self( | |
strtr( | |
'This call to PrestaShop Web Services responded with status `%code% %status%`, this client was not designed to process this kind of response. This can come from your web server or reverse proxy configuration.', | |
array( | |
'%code%' => $code, | |
'%status%' => $status, | |
) | |
), | |
$code, | |
$previous | |
); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a nice simplification of the code, however $messages
is not used in the message output yet And it should probably be a const
on the class instead of a static variable You can then get the message via self::STATUS_MESSAGES[$code]
thus only one parameter would be needed for this method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option with an unique factory method on PrestaShopWebserviceStatusException
throw PrestaShopWebserviceStatusException::shouldNotReceive100ContinueStatus(); | ||
case 101: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive101SwitchingProtocolsStatus(); | ||
case 102: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive102ProcessingStatus(); | ||
case 103: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive103EarlyHintsStatus(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throw PrestaShopWebserviceStatusException::shouldNotReceive100ContinueStatus(); | |
case 101: | |
throw PrestaShopWebserviceStatusException::shouldNotReceive101SwitchingProtocolsStatus(); | |
case 102: | |
throw PrestaShopWebserviceStatusException::shouldNotReceive102ProcessingStatus(); | |
case 103: | |
throw PrestaShopWebserviceStatusException::shouldNotReceive103EarlyHintsStatus(); |
throw new PrestaShopWebserviceNoContentException(); | ||
case 300: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive300MultipleChoicesStatus(); | ||
case 301: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive301MovedPermanentlyStatus(); | ||
case 302: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive302FoundStatus(); | ||
case 303: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive303SeeOtherStatus(); | ||
case 304: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive304NotModifiedStatus(); | ||
case 307: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive307TemporaryRedirectStatus(); | ||
case 308: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive308PermanentRedirectStatus(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throw new PrestaShopWebserviceNoContentException(); | |
case 300: | |
throw PrestaShopWebserviceStatusException::shouldNotReceive300MultipleChoicesStatus(); | |
case 301: | |
throw PrestaShopWebserviceStatusException::shouldNotReceive301MovedPermanentlyStatus(); | |
case 302: | |
throw PrestaShopWebserviceStatusException::shouldNotReceive302FoundStatus(); | |
case 303: | |
throw PrestaShopWebserviceStatusException::shouldNotReceive303SeeOtherStatus(); | |
case 304: | |
throw PrestaShopWebserviceStatusException::shouldNotReceive304NotModifiedStatus(); | |
case 307: | |
throw PrestaShopWebserviceStatusException::shouldNotReceive307TemporaryRedirectStatus(); | |
case 308: | |
throw PrestaShopWebserviceStatusException::shouldNotReceive308PermanentRedirectStatus(); | |
case 101: | |
case 102: | |
case 103: | |
case 300: | |
case 301: | |
case 302: | |
case 303: | |
case 304: | |
case 307: | |
case 308: | |
throw PrestaShopWebserviceStatusException::shouldNotReceiveStatus($statusCode); | |
case 204: | |
throw new PrestaShopWebserviceNoContentException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that having a dedicated method for each status code may be overkill You could either use a generic shouldNotReceiveStatus
method as proposed here, or at least a shouldNotReceiveRedirectionStatus
for all redirection status maybe?
SAme thing could be done for all the 10*
status codes
display the server's error message to give more context to the API user
…, not the error message (which is a lot less helpful when troubleshooting), separate multiple error messages with " - "
} | ||
|
||
/** | ||
* @param int $statusCode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @param int $statusCode | |
* @param array{status_code: int} $request |
case 102: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive102ProcessingStatus(); | ||
case 103: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive103EarlyHintsStatus(); | ||
case 204: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is 204 really an error? Usually it's a valid HTTP code, but maybe you throw an exception here because the webservice never use this in theory?
throw new PrestaShopWebserviceNoContentException(); | ||
case 300: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive300MultipleChoicesStatus(); | ||
case 301: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive301MovedPermanentlyStatus(); | ||
case 302: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive302FoundStatus(); | ||
case 303: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive303SeeOtherStatus(); | ||
case 304: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive304NotModifiedStatus(); | ||
case 307: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive307TemporaryRedirectStatus(); | ||
case 308: | ||
throw PrestaShopWebserviceStatusException::shouldNotReceive308PermanentRedirectStatus(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that having a dedicated method for each status code may be overkill You could either use a generic shouldNotReceiveStatus
method as proposed here, or at least a shouldNotReceiveRedirectionStatus
for all redirection status maybe?
SAme thing could be done for all the 10*
status codes
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive100ContinueStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(100, 'Continue', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive101SwitchingProtocolsStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(101, 'Switching Protocols', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive102ProcessingStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(102, 'Processing', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive103EarlyHintsStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(103, 'Early Hints', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive300MultipleChoicesStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(300, 'Multiple Choices', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive301MovedPermanentlyStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(301, 'Moved Permanently', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive302FoundStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(302, 'Found', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive303SeeOtherStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(303, 'See Other', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive304NotModifiedStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(304, 'Not Modified', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive307TemporaryRedirectStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(307, 'Temporary Redirect', $previous); | ||
} | ||
|
||
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive308PermanentRedirectStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(308, 'Permanent Redirect', $previous); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a nice simplification of the code, however $messages
is not used in the message output yet And it should probably be a const
on the class instead of a static variable You can then get the message via self::STATUS_MESSAGES[$code]
thus only one parameter would be needed for this method
/** | ||
* @param ?\Throwable $previous | ||
* @return self | ||
*/ | ||
public static function shouldNotReceive100ContinueStatus($previous = null) | ||
{ | ||
return self::shouldNotReceiveStatus(100, 'Continue', $previous); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what Pablo proposed was to use a generic static function able to build the error messages based on the status code This way you don't need a big switch in PsWebServiceLibrary::assertStatusCode
the switch is only handled in this exception class And if you use an array to store the messages even the switch part can be avoided
Also I understand the limitation about const in PHP 5.6, what Pablo suggested I think is that maybe we could release a new major version of this library based on PHP 7 to use more modern code As he said people still relying on PHP 5 have a very old environment and are not likely to update their libraries regularly so this wouldn't be a problem
$resources = $xml->children()->children(); | ||
|
||
} catch (PrestaShopWebserviceNotFoundException $exception) { | ||
echo 'Bad ID'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a suggestion but maybe this could be implemented by the PrestaShopWebserviceException
, maybe not getMessage
but an alternative one like getApiErrorMessage
this way you could simplify this try/catch
try {
...
} catch (PrestaShopWebserviceException $e) {
echo $e->getApiErrorMessage();
exit;
}
And leave all the logic about error messages in the exception instead of duplicating it here The base class can implement a generic message base on $this->getMessage
while the specific children classes can override the method with a custom message
But it's just a suggestion, not mandatory if you don't think it's relevant
PrestaShopWebserviceException
exception should now throw a more specialized exception implementingPrestaShopWebserviceException
(which has now become an interface.