From 36cdd39ef1c2bd899ca69c564bd5252e83c2f789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Planchat?= Date: Tue, 25 Apr 2023 18:06:15 +0200 Subject: [PATCH 01/10] Error handling: Exploded the base exception into several more specialized exception classes --- PSWebServiceLibrary.php | 346 ++++++++++++++++++++++++++++++++----- examples/Create.php | 12 +- examples/CustomersList.php | 4 +- examples/Delete.php | 10 +- examples/Retrieve.php | 6 +- examples/Update.php | 12 +- 6 files changed, 322 insertions(+), 68 deletions(-) diff --git a/PSWebServiceLibrary.php b/PSWebServiceLibrary.php index 90ca603..27fdd08 100644 --- a/PSWebServiceLibrary.php +++ b/PSWebServiceLibrary.php @@ -75,9 +75,7 @@ class PrestaShopWebservice function __construct($url, $key, $debug = true) { if (!extension_loaded('curl')) { - throw new PrestaShopWebserviceException( - 'Please activate the PHP extension \'curl\' to allow use of PrestaShop webservice library' - ); + throw PrestaShopWebserviceMissingPreconditionException::missingExtension('curl'); } $this->url = $url; $this->key = $key; @@ -92,50 +90,66 @@ function __construct($url, $key, $debug = true) * 'response' => CURL response *

* - * @param array $request Response elements of CURL request + * @param array{status_code: string, response: string} $request Response elements of CURL request * * @throws PrestaShopWebserviceException if HTTP status code is not 200 or 201 */ protected function checkStatusCode($request) { - switch ($request['status_code']) { - case 200: - case 201: - break; + $this->assertStatusCode($request['status_code']); + } + + private function assertStatusCode($statusCode) + { + switch ($statusCode) { + case 100: + throw PrestaShopWebserviceStatusException::shouldNotReceive100ContinueStatus(); + case 101: + throw PrestaShopWebserviceStatusException::shouldNotReceive101SwitchingProtocolsStatus(); + case 102: + throw PrestaShopWebserviceStatusException::shouldNotReceive102ProcessingStatus(); + case 103: + throw PrestaShopWebserviceStatusException::shouldNotReceive103EarlyHintsStatus(); case 204: - $error_message = 'No content'; - break; + 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 400: - $error_message = 'Bad Request'; - break; + throw new PrestaShopWebserviceBadRequestException(); case 401: - $error_message = 'Unauthorized'; - break; + throw new PrestaShopWebserviceUnauthorizedException(); + case 403: + throw new PrestaShopWebserviceForbiddenException(); case 404: - $error_message = 'Not Found'; - break; + throw new PrestaShopWebserviceNotFoundException(); case 405: - $error_message = 'Method Not Allowed'; - break; - case 500: - $error_message = 'Internal Server Error'; - break; - default: - throw new PrestaShopWebserviceException( - 'This call to PrestaShop Web Services returned an unexpected HTTP status of:' . $request['status_code'] - ); + throw new PrestaShopWebserviceMethodNotAllowedException(); + case 429: + throw new PrestaShopWebserviceTooManyRequestsException(); + } + if ($statusCode >= 100 && $statusCode < 200 + || $statusCode >= 202 && $statusCode < 300 + || $statusCode >= 300 && $statusCode < 500 + || $statusCode >= 600 + || $statusCode < 100 + ) { + throw new PrestaShopWebserviceClientException('This call to PrestaShop Web Services responded with a non-standard code or a code this client could not handle properly. This can come from your web server or reverse proxy configuration.', $statusCode); } - if (!empty($error_message)) { - $response = $this->parseXML($request['response']); - $errors = $response->children()->children(); - if ($errors && count($errors) > 0) { - foreach ($errors as $error) { - $error_message.= ' - (Code ' . $error->code . '): ' . $error->message; - } - } - $error_label = 'This call to PrestaShop Web Services failed and returned an HTTP status of %d. That means: %s.'; - throw new PrestaShopWebserviceException(sprintf($error_label, $request['status_code'], $error_message)); + if ($statusCode >= 500 && $statusCode < 600) { + throw new PrestaShopWebserviceServerException('This call to PrestaShop Web Services responded with a status code indicating it is not available or under a too heavy load to process your request.', $statusCode); } } @@ -194,7 +208,7 @@ protected function executeRequest($url, $curl_params = array()) $index = strpos($response, "\r\n\r\n"); if ($index === false && $curl_params[CURLOPT_CUSTOMREQUEST] != 'HEAD') { - throw new PrestaShopWebserviceException('Bad HTTP response ' . $response . curl_error($session)); + throw new PrestaShopWebserviceServerException('Bad HTTP response ' . $response . curl_error($session)); } $header = substr($response, 0, $index); @@ -217,7 +231,7 @@ protected function executeRequest($url, $curl_params = array()) version_compare(PrestaShopWebservice::psCompatibleVersionsMin, $headerArray['PSWS-Version']) == 1 || version_compare(PrestaShopWebservice::psCompatibleVersionsMax, $headerArray['PSWS-Version']) == -1 ) { - throw new PrestaShopWebserviceException( + throw new PrestaShopWebserviceMissingPreconditionException( 'This library is not compatible with this version of PrestaShop. Please upgrade/downgrade this library' ); } @@ -229,7 +243,7 @@ protected function executeRequest($url, $curl_params = array()) } $status_code = curl_getinfo($session, CURLINFO_HTTP_CODE); if ($status_code === 0) { - throw new PrestaShopWebserviceException('CURL Error: ' . curl_error($session)); + throw new PrestaShopWebserviceServerException('CURL Error: ' . curl_error($session)); } curl_close($session); if ($this->debug) { @@ -278,11 +292,11 @@ protected function parseXML($response) if (libxml_get_errors()) { $msg = var_export(libxml_get_errors(), true); libxml_clear_errors(); - throw new PrestaShopWebserviceException('HTTP XML response is not parsable: ' . $msg); + throw new PrestaShopWebserviceServerException('HTTP XML response is not parsable: ' . $msg); } return $xml; } else { - throw new PrestaShopWebserviceException('HTTP response is empty'); + throw new PrestaShopWebserviceServerException('HTTP response is empty'); } } @@ -312,7 +326,7 @@ public function add($options) $url .= '&id_group_shop=' . $options['id_group_shop']; } } else { - throw new PrestaShopWebserviceException('Bad parameters given'); + throw PrestaShopWebserviceBadParametersException::badParameters(); } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => $xml)); @@ -374,7 +388,7 @@ public function get($options) $url .= '?' . http_build_query($url_params); } } else { - throw new PrestaShopWebserviceException('Bad parameters given'); + throw PrestaShopWebserviceBadParametersException::badParameters(); } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'GET')); @@ -415,7 +429,7 @@ public function head($options) $url .= '?' . http_build_query($url_params); } } else { - throw new PrestaShopWebserviceException('Bad parameters given'); + throw PrestaShopWebserviceBadParametersException::badParameters(); } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'HEAD', CURLOPT_NOBODY => true)); $this->checkStatusCode($request);// check the response validity @@ -451,7 +465,7 @@ public function edit($options) $url .= '&id_group_shop=' . $options['id_group_shop']; } } else { - throw new PrestaShopWebserviceException('Bad parameters given'); + throw PrestaShopWebserviceBadParametersException::badParameters(); } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_POSTFIELDS => $xml)); @@ -495,7 +509,7 @@ public function delete($options) ? $this->url . '/api/' . $options['resource'] . '/?id=[' . implode(',', $options['id']) . ']' : $this->url . '/api/' . $options['resource'] . '/' . $options['id']; } else { - throw new PrestaShopWebserviceException('Bad parameters given'); + throw PrestaShopWebserviceBadParametersException::badParameters(); } if (isset($options['id_shop'])) { @@ -512,9 +526,249 @@ public function delete($options) } +// Polyfill for PHP 5 +if (!interface_exists('Throwable')) { + interface Throwable + { + } +} + /** * @package PrestaShopWebservice */ -class PrestaShopWebserviceException extends Exception +interface PrestaShopWebserviceException extends \Throwable { } + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceBadParametersException extends \RuntimeException implements PrestaShopWebserviceException { + public static function badParameters($response, $previous = null) + { + return new self('Bad parameters given', $previous); + } +} + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceMissingPreconditionException extends \BadFunctionCallException implements PrestaShopWebserviceException { + public static function missingExtension($extension, $previous = null) + { + return new self( + strtr( + 'Please activate the PHP extension \'%extension%\' to allow use of PrestaShop webservice library', + array( + '%extension%' => $extension, + ) + ), + $previous + ); + } +} + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceBadRequestException extends \RuntimeException implements PrestaShopWebserviceException { + public function __construct($previous = null) + { + parent::__construct('Bad Request', 400, $previous); + } +} + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceUnauthorizedException extends \RuntimeException implements PrestaShopWebserviceException { + public function __construct($previous = null) + { + parent::__construct('Unauthorized', 401, $previous); + } +} + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceForbiddenException extends \RuntimeException implements PrestaShopWebserviceException { + public function __construct($previous = null) + { + parent::__construct('Forbidden', 403, $previous); + } +} + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceNotFoundException extends \RuntimeException implements PrestaShopWebserviceException { + public function __construct($previous = null) + { + parent::__construct('Not Found', 404, $previous); + } +} + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceMethodNotAllowedException extends \RuntimeException implements PrestaShopWebserviceException { + public function __construct($previous = null) + { + parent::__construct('Method Not Allowed', 405, $previous); + } +} + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceTooManyRequestsException extends \RuntimeException implements PrestaShopWebserviceException { + public function __construct($previous = null) + { + parent::__construct('Too Many Requests', 429, $previous); + } +} + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceNoContentException extends \RuntimeException implements PrestaShopWebserviceException { + public function __construct($previous = null) + { + parent::__construct('No Content', 204, $previous); + } +} + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceStatusException extends \RuntimeException implements PrestaShopWebserviceException { + /** + * @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); + } +} + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceClientException extends \RuntimeException implements PrestaShopWebserviceException {} + +/** + * @package PrestaShopWebservice + */ +class PrestaShopWebserviceServerException extends \RuntimeException implements PrestaShopWebserviceException {} diff --git a/examples/Create.php b/examples/Create.php index 1ebf4d8..73eaf64 100644 --- a/examples/Create.php +++ b/examples/Create.php @@ -44,13 +44,13 @@ $xml = $webService->get($opt); $resources = $xml->children()->children(); } -catch (PrestaShopWebserviceException $e) +catch (PrestaShopWebserviceException $exception) { // Here we are dealing with errors - $trace = $e->getTrace(); + $trace = $exception->getTrace(); if ($trace[0]['args'][0] == 404) echo 'Bad ID'; else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$e->getMessage(); + else echo 'Other error
'.$exception->getMessage(); } if (count($_POST) > 0) @@ -70,13 +70,13 @@ echo "Successfully added."; } } - catch (PrestaShopWebserviceException $ex) + catch (PrestaShopWebserviceException $exception) { // Here we are dealing with errors - $trace = $ex->getTrace(); + $trace = $exception->getTrace(); if ($trace[0]['args'][0] == 404) echo 'Bad ID'; else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$ex->getMessage(); + else echo 'Other error
'.$exception->getMessage(); } } diff --git a/examples/CustomersList.php b/examples/CustomersList.php index 241eeb0..3d33e34 100644 --- a/examples/CustomersList.php +++ b/examples/CustomersList.php @@ -47,10 +47,10 @@ // Here we get the elements from children of customers markup "customer" $resources = $xml->customers->children(); } -catch (PrestaShopWebserviceException $e) +catch (PrestaShopWebserviceException $exception) { // Here we are dealing with errors - $trace = $e->getTrace(); + $trace = $exception->getTrace(); if ($trace[0]['args'][0] == 404) echo 'Bad ID'; else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; else echo 'Other error'; diff --git a/examples/Delete.php b/examples/Delete.php index 07171a1..0f521e3 100644 --- a/examples/Delete.php +++ b/examples/Delete.php @@ -50,13 +50,13 @@ // If there's an error we throw an exception echo 'Successfully deleted !'; } - catch (PrestaShopWebserviceException $e) + catch (PrestaShopWebserviceException $exception) { // Here we are dealing with errors - $trace = $e->getTrace(); + $trace = $exception->getTrace(); if ($trace[0]['args'][0] == 404) echo 'Bad ID'; else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$e->getMessage(); + else echo 'Other error
'.$exception->getMessage(); } } else @@ -69,10 +69,10 @@ $xml = $webService->get($opt); $resources = $xml->children()->children(); } - catch (PrestaShopWebserviceException $e) + catch (PrestaShopWebserviceException $exception) { // Here we are dealing with errors - $trace = $e->getTrace(); + $trace = $exception->getTrace(); if ($trace[0]['args'][0] == 404) echo 'Bad ID'; else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; else echo 'Other error'; diff --git a/examples/Retrieve.php b/examples/Retrieve.php index 8771220..00ad2a3 100644 --- a/examples/Retrieve.php +++ b/examples/Retrieve.php @@ -49,13 +49,13 @@ // Here we get the elements from children of customer markup which is children of prestashop root markup $resources = $xml->children()->children(); } -catch (PrestaShopWebserviceException $e) +catch (PrestaShopWebserviceException $exception) { // Here we are dealing with errors - $trace = $e->getTrace(); + $trace = $exception->getTrace(); if ($trace[0]['args'][0] == 404) echo 'Bad ID'; else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$e->getMessage(); + else echo 'Other error
'.$exception->getMessage(); } // We set the Title diff --git a/examples/Update.php b/examples/Update.php index ddb4ac7..ab7914d 100644 --- a/examples/Update.php +++ b/examples/Update.php @@ -45,13 +45,13 @@ // Here we get the elements from children of customer markup which is children of prestashop root markup $resources = $xml->children()->children(); } -catch (PrestaShopWebserviceException $e) +catch (PrestaShopWebserviceException $exception) { // Here we are dealing with errors - $trace = $e->getTrace(); + $trace = $exception->getTrace(); if ($trace[0]['args'][0] == 404) echo 'Bad ID'; else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$e->getMessage(); + else echo 'Other error
'.$exception->getMessage(); } // Second : We update the data and send it to the web service @@ -72,13 +72,13 @@ // if WebService don't throw an exception the action worked well and we don't show the following message echo "Successfully updated."; } - catch (PrestaShopWebserviceException $ex) + catch (PrestaShopWebserviceException $exception) { // Here we are dealing with errors - $trace = $ex->getTrace(); + $trace = $exception->getTrace(); if ($trace[0]['args'][0] == 404) echo 'Bad ID'; else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$ex->getMessage(); + else echo 'Other error
'.$exception->getMessage(); } } From dbc3cf6eeb8faaedfed300a3082ea7ffc7b8b231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Planchat?= Date: Wed, 26 Apr 2023 11:40:35 +0200 Subject: [PATCH 02/10] Error handling: Removed an unnecessary and unused parameter inside an exception constructor --- PSWebServiceLibrary.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PSWebServiceLibrary.php b/PSWebServiceLibrary.php index 27fdd08..22ff061 100644 --- a/PSWebServiceLibrary.php +++ b/PSWebServiceLibrary.php @@ -544,7 +544,7 @@ interface PrestaShopWebserviceException extends \Throwable * @package PrestaShopWebservice */ class PrestaShopWebserviceBadParametersException extends \RuntimeException implements PrestaShopWebserviceException { - public static function badParameters($response, $previous = null) + public static function badParameters($previous = null) { return new self('Bad parameters given', $previous); } From 4f78866cb49c04e7e131a1f5ddb8fcc4531f66d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Planchat?= Date: Wed, 26 Apr 2023 11:46:35 +0200 Subject: [PATCH 03/10] Error handling: Updated the code examples, removing the complex trace analysis to determine the error type --- examples/Create.php | 33 +++++++++++++++++---------------- examples/CustomersList.php | 16 ++++++++-------- examples/Delete.php | 36 ++++++++++++++++++------------------ examples/Retrieve.php | 16 ++++++++-------- examples/Update.php | 34 +++++++++++++++++----------------- 5 files changed, 68 insertions(+), 67 deletions(-) diff --git a/examples/Create.php b/examples/Create.php index 73eaf64..a7dd5f2 100644 --- a/examples/Create.php +++ b/examples/Create.php @@ -43,14 +43,15 @@ else $xml = $webService->get($opt); $resources = $xml->children()->children(); -} -catch (PrestaShopWebserviceException $exception) -{ - // Here we are dealing with errors - $trace = $exception->getTrace(); - if ($trace[0]['args'][0] == 404) echo 'Bad ID'; - else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$exception->getMessage(); + +} catch (PrestaShopWebserviceNotFoundException $exception) { + echo 'Bad ID'; +} catch (PrestaShopWebserviceUnauthorizedException $exception) { + echo 'Bad auth key'; +} catch (PrestaShopWebserviceForbiddenException $exception) { + echo 'Not logged in'; +} catch (PrestaShopWebserviceException $exception) { + echo 'Other error
'.$exception->getMessage(); } if (count($_POST) > 0) @@ -69,14 +70,14 @@ $xml = $webService->add($opt); echo "Successfully added."; } - } - catch (PrestaShopWebserviceException $exception) - { - // Here we are dealing with errors - $trace = $exception->getTrace(); - if ($trace[0]['args'][0] == 404) echo 'Bad ID'; - else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$exception->getMessage(); + } catch (PrestaShopWebserviceNotFoundException $exception) { + echo 'Bad ID'; + } catch (PrestaShopWebserviceUnauthorizedException $exception) { + echo 'Bad auth key'; + } catch (PrestaShopWebserviceForbiddenException $exception) { + echo 'Not logged in'; + } catch (PrestaShopWebserviceException $exception) { + echo 'Other error
'.$exception->getMessage(); } } diff --git a/examples/CustomersList.php b/examples/CustomersList.php index 3d33e34..799565a 100644 --- a/examples/CustomersList.php +++ b/examples/CustomersList.php @@ -46,14 +46,14 @@ // Here we get the elements from children of customers markup "customer" $resources = $xml->customers->children(); -} -catch (PrestaShopWebserviceException $exception) -{ - // Here we are dealing with errors - $trace = $exception->getTrace(); - if ($trace[0]['args'][0] == 404) echo 'Bad ID'; - else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error'; +} catch (PrestaShopWebserviceNotFoundException $exception) { + echo 'Bad ID'; +} catch (PrestaShopWebserviceUnauthorizedException $exception) { + echo 'Bad auth key'; +} catch (PrestaShopWebserviceForbiddenException $exception) { + echo 'Not logged in'; +} catch (PrestaShopWebserviceException $exception) { + echo 'Other error
'.$exception->getMessage(); } // We set the Title diff --git a/examples/Delete.php b/examples/Delete.php index 0f521e3..cfb1a32 100644 --- a/examples/Delete.php +++ b/examples/Delete.php @@ -49,15 +49,15 @@ $webService->delete(array('resource' => 'customers', 'id' => intval($_GET['DeleteID']))); // If there's an error we throw an exception echo 'Successfully deleted !'; - } - catch (PrestaShopWebserviceException $exception) - { - // Here we are dealing with errors - $trace = $exception->getTrace(); - if ($trace[0]['args'][0] == 404) echo 'Bad ID'; - else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$exception->getMessage(); - } + } catch (PrestaShopWebserviceNotFoundException $exception) { + echo 'Bad ID'; + } catch (PrestaShopWebserviceUnauthorizedException $exception) { + echo 'Bad auth key'; + } catch (PrestaShopWebserviceForbiddenException $exception) { + echo 'Not logged in'; + } catch (PrestaShopWebserviceException $exception) { + echo 'Other error
'.$exception->getMessage(); + } } else { @@ -68,15 +68,15 @@ $opt = array('resource' => 'customers'); $xml = $webService->get($opt); $resources = $xml->children()->children(); - } - catch (PrestaShopWebserviceException $exception) - { - // Here we are dealing with errors - $trace = $exception->getTrace(); - if ($trace[0]['args'][0] == 404) echo 'Bad ID'; - else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error'; - } + } catch (PrestaShopWebserviceNotFoundException $exception) { + echo 'Bad ID'; + } catch (PrestaShopWebserviceUnauthorizedException $exception) { + echo 'Bad auth key'; + } catch (PrestaShopWebserviceForbiddenException $exception) { + echo 'Not logged in'; + } catch (PrestaShopWebserviceException $exception) { + echo 'Other error
'.$exception->getMessage(); + } echo '

Customers List

'; echo ''; diff --git a/examples/Retrieve.php b/examples/Retrieve.php index 00ad2a3..b64d726 100644 --- a/examples/Retrieve.php +++ b/examples/Retrieve.php @@ -48,14 +48,14 @@ // Here we get the elements from children of customer markup which is children of prestashop root markup $resources = $xml->children()->children(); -} -catch (PrestaShopWebserviceException $exception) -{ - // Here we are dealing with errors - $trace = $exception->getTrace(); - if ($trace[0]['args'][0] == 404) echo 'Bad ID'; - else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$exception->getMessage(); +} catch (PrestaShopWebserviceNotFoundException $exception) { + echo 'Bad ID'; +} catch (PrestaShopWebserviceUnauthorizedException $exception) { + echo 'Bad auth key'; +} catch (PrestaShopWebserviceForbiddenException $exception) { + echo 'Not logged in'; +} catch (PrestaShopWebserviceException $exception) { + echo 'Other error
'.$exception->getMessage(); } // We set the Title diff --git a/examples/Update.php b/examples/Update.php index ab7914d..3f32b13 100644 --- a/examples/Update.php +++ b/examples/Update.php @@ -44,14 +44,14 @@ // Here we get the elements from children of customer markup which is children of prestashop root markup $resources = $xml->children()->children(); -} -catch (PrestaShopWebserviceException $exception) -{ - // Here we are dealing with errors - $trace = $exception->getTrace(); - if ($trace[0]['args'][0] == 404) echo 'Bad ID'; - else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$exception->getMessage(); +} catch (PrestaShopWebserviceNotFoundException $exception) { + echo 'Bad ID'; +} catch (PrestaShopWebserviceUnauthorizedException $exception) { + echo 'Bad auth key'; +} catch (PrestaShopWebserviceForbiddenException $exception) { + echo 'Not logged in'; +} catch (PrestaShopWebserviceException $exception) { + echo 'Other error
'.$exception->getMessage(); } // Second : We update the data and send it to the web service @@ -71,15 +71,15 @@ $xml = $webService->edit($opt); // if WebService don't throw an exception the action worked well and we don't show the following message echo "Successfully updated."; - } - catch (PrestaShopWebserviceException $exception) - { - // Here we are dealing with errors - $trace = $exception->getTrace(); - if ($trace[0]['args'][0] == 404) echo 'Bad ID'; - else if ($trace[0]['args'][0] == 401) echo 'Bad auth key'; - else echo 'Other error
'.$exception->getMessage(); - } + } catch (PrestaShopWebserviceNotFoundException $exception) { + echo 'Bad ID'; + } catch (PrestaShopWebserviceUnauthorizedException $exception) { + echo 'Bad auth key'; + } catch (PrestaShopWebserviceForbiddenException $exception) { + echo 'Not logged in'; + } catch (PrestaShopWebserviceException $exception) { + echo 'Other error
'.$exception->getMessage(); + } } // UI From 7bdad868715c46c4e7cc1c6fc402cba33d5f1465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Planchat?= Date: Wed, 26 Apr 2023 11:48:47 +0200 Subject: [PATCH 04/10] Error handling: Replaced tabs with spaces in the examples --- examples/Create.php | 82 +++++++++++++++++++------------------- examples/CustomersList.php | 36 ++++++++--------- examples/Delete.php | 70 ++++++++++++++++---------------- examples/Retrieve.php | 74 +++++++++++++++++----------------- examples/Update.php | 80 ++++++++++++++++++------------------- 5 files changed, 171 insertions(+), 171 deletions(-) diff --git a/examples/Create.php b/examples/Create.php index a7dd5f2..2dbad74 100644 --- a/examples/Create.php +++ b/examples/Create.php @@ -36,13 +36,13 @@ // Here we use the WebService to get the schema of "customers" resource try { - $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); - $opt = array('resource' => 'customers'); - if (isset($_GET['Create'])) - $xml = $webService->get(array('url' => PS_SHOP_PATH.'/api/customers?schema=blank')); - else - $xml = $webService->get($opt); - $resources = $xml->children()->children(); + $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); + $opt = array('resource' => 'customers'); + if (isset($_GET['Create'])) + $xml = $webService->get(array('url' => PS_SHOP_PATH.'/api/customers?schema=blank')); + else + $xml = $webService->get($opt); + $resources = $xml->children()->children(); } catch (PrestaShopWebserviceNotFoundException $exception) { echo 'Bad ID'; @@ -57,28 +57,28 @@ if (count($_POST) > 0) { // Here we have XML before update, lets update XML - foreach ($resources as $nodeKey => $node) - { - $resources->$nodeKey = $_POST[$nodeKey]; - } - try - { - $opt = array('resource' => 'customers'); - if ($_GET['Create'] == 'Creating') - { - $opt['postXml'] = $xml->asXML(); - $xml = $webService->add($opt); - echo "Successfully added."; - } - } catch (PrestaShopWebserviceNotFoundException $exception) { + foreach ($resources as $nodeKey => $node) + { + $resources->$nodeKey = $_POST[$nodeKey]; + } + try + { + $opt = array('resource' => 'customers'); + if ($_GET['Create'] == 'Creating') + { + $opt['postXml'] = $xml->asXML(); + $xml = $webService->add($opt); + echo "Successfully added."; + } + } catch (PrestaShopWebserviceNotFoundException $exception) { echo 'Bad ID'; - } catch (PrestaShopWebserviceUnauthorizedException $exception) { + } catch (PrestaShopWebserviceUnauthorizedException $exception) { echo 'Bad auth key'; } catch (PrestaShopWebserviceForbiddenException $exception) { echo 'Not logged in'; } catch (PrestaShopWebserviceException $exception) { - echo 'Other error
'.$exception->getMessage(); - } + echo 'Other error
'.$exception->getMessage(); + } } // We set the Title @@ -89,12 +89,12 @@ // We set a link to go back to list if we are in creation if (isset($_GET['Create'])) - echo 'Return to the list'; + echo 'Return to the list'; if (!isset($_GET['Create'])) - echo ''; + echo ''; else - echo '
'; + echo ''; echo '
'; if (isset($resources)) @@ -103,30 +103,30 @@ echo ''; if (count($_GET) == 0) { - echo ''; + echo ''; - foreach ($resources as $resource) - { - echo ''; - } + foreach ($resources as $resource) + { + echo ''; + } } else { - echo ''; - foreach ($resources as $key => $resource) - { - echo ''; - } + echo ''; + foreach ($resources as $key => $resource) + { + echo ''; + } } } echo '
Id
Id
'.$resource->attributes().'
'.$resource->attributes().'
'.$key.''; - if (isset($_GET['Create'])) - echo ''; - echo '
'.$key.''; + if (isset($_GET['Create'])) + echo ''; + echo '

'; if (isset($_GET['Create'])) - echo ''; + echo ''; ?> diff --git a/examples/CustomersList.php b/examples/CustomersList.php index 799565a..1f9f1fe 100644 --- a/examples/CustomersList.php +++ b/examples/CustomersList.php @@ -28,24 +28,24 @@ */ // Here we define constants /!\ You need to replace this parameters -define('DEBUG', true); // Debug mode -define('PS_SHOP_PATH', 'http://www.myshop.com/'); // Root path of your PrestaShop store -define('PS_WS_AUTH_KEY', 'ZQ88PRJX5VWQHCWE4EE7SQ7HPNX00RAJ'); // Auth key (Get it in your Back Office) +define('DEBUG', true); // Debug mode +define('PS_SHOP_PATH', 'http://www.myshop.com/'); // Root path of your PrestaShop store +define('PS_WS_AUTH_KEY', 'ZQ88PRJX5VWQHCWE4EE7SQ7HPNX00RAJ'); // Auth key (Get it in your Back Office) require_once('../PSWebServiceLibrary.php'); // Here we make the WebService Call try { - $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); - - // Here we set the option array for the Webservice : we want customers resources - $opt['resource'] = 'customers'; - - // Call - $xml = $webService->get($opt); + $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); + + // Here we set the option array for the Webservice : we want customers resources + $opt['resource'] = 'customers'; + + // Call + $xml = $webService->get($opt); - // Here we get the elements from children of customers markup "customer" - $resources = $xml->customers->children(); + // Here we get the elements from children of customers markup "customer" + $resources = $xml->customers->children(); } catch (PrestaShopWebserviceNotFoundException $exception) { echo 'Bad ID'; } catch (PrestaShopWebserviceUnauthorizedException $exception) { @@ -63,12 +63,12 @@ // if $resources is set we can lists element in it otherwise do nothing cause there's an error if (isset($resources)) { - echo 'Id'; - foreach ($resources as $resource) - { - // Iterates on the found IDs - echo ''.$resource->attributes().''; - } + echo 'Id'; + foreach ($resources as $resource) + { + // Iterates on the found IDs + echo ''.$resource->attributes().''; + } } echo ''; ?> diff --git a/examples/Delete.php b/examples/Delete.php index cfb1a32..0b516f8 100644 --- a/examples/Delete.php +++ b/examples/Delete.php @@ -35,20 +35,20 @@ if (isset($_GET['DeleteID'])) { - //Deletion + //Deletion - echo '

Customers Deletion


'; + echo '

Customers Deletion


'; - // We set a link to go back to list - echo 'Return to the list'; + // We set a link to go back to list + echo 'Return to the list'; - try - { - $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); - // Call for a deletion, we specify the resource name and the id of the resource in order to delete the item - $webService->delete(array('resource' => 'customers', 'id' => intval($_GET['DeleteID']))); - // If there's an error we throw an exception - echo 'Successfully deleted !'; + try + { + $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); + // Call for a deletion, we specify the resource name and the id of the resource in order to delete the item + $webService->delete(array('resource' => 'customers', 'id' => intval($_GET['DeleteID']))); + // If there's an error we throw an exception + echo 'Successfully deleted !'; } catch (PrestaShopWebserviceNotFoundException $exception) { echo 'Bad ID'; } catch (PrestaShopWebserviceUnauthorizedException $exception) { @@ -61,13 +61,13 @@ } else { - // Else get customers list - try - { - $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); - $opt = array('resource' => 'customers'); - $xml = $webService->get($opt); - $resources = $xml->children()->children(); + // Else get customers list + try + { + $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); + $opt = array('resource' => 'customers'); + $xml = $webService->get($opt); + $resources = $xml->children()->children(); } catch (PrestaShopWebserviceNotFoundException $exception) { echo 'Bad ID'; } catch (PrestaShopWebserviceUnauthorizedException $exception) { @@ -78,24 +78,24 @@ echo 'Other error
'.$exception->getMessage(); } - echo '

Customers List

'; - echo ''; - if (isset($resources)) - { - echo ''; - if (!isset($DeletionID)) - { - echo ''; + echo '

Customers List

'; + echo '
IdMore
'; + if (isset($resources)) + { + echo ''; + if (!isset($DeletionID)) + { + echo ''; - foreach ($resources as $resource) - { - echo ''; - } - } - echo '
IdMore
'.$resource->attributes().''. - 'Delete'. - '

'; - } + foreach ($resources as $resource) + { + echo ''.$resource->attributes().''. + 'Delete'. + ''; + } + } + echo '
'; + } } ?> diff --git a/examples/Retrieve.php b/examples/Retrieve.php index b64d726..a10ce9c 100644 --- a/examples/Retrieve.php +++ b/examples/Retrieve.php @@ -28,26 +28,26 @@ */ // Here we define constants /!\ You need to replace this parameters -define('DEBUG', true); // Debug mode -define('PS_SHOP_PATH', 'http://www.myshop.com/'); // Root path of your PrestaShop store -define('PS_WS_AUTH_KEY', 'ZQ88PRJX5VWQHCWE4EE7SQ7HPNX00RAJ'); // Auth key (Get it in your Back Office) +define('DEBUG', true); // Debug mode +define('PS_SHOP_PATH', 'http://www.myshop.com/'); // Root path of your PrestaShop store +define('PS_WS_AUTH_KEY', 'ZQ88PRJX5VWQHCWE4EE7SQ7HPNX00RAJ'); // Auth key (Get it in your Back Office) require_once('../PSWebServiceLibrary.php'); // Here we make the WebService Call try { - $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); - // Here we set the option array for the Webservice : we want customers resources - $opt['resource'] = 'customers'; - // We set an id if we want to retrieve infos from a customer - if (isset($_GET['id'])) - $opt['id'] = (int)$_GET['id']; // cast string => int for security measures + $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); + // Here we set the option array for the Webservice : we want customers resources + $opt['resource'] = 'customers'; + // We set an id if we want to retrieve infos from a customer + if (isset($_GET['id'])) + $opt['id'] = (int)$_GET['id']; // cast string => int for security measures - // Call - $xml = $webService->get($opt); + // Call + $xml = $webService->get($opt); - // Here we get the elements from children of customer markup which is children of prestashop root markup - $resources = $xml->children()->children(); + // Here we get the elements from children of customer markup which is children of prestashop root markup + $resources = $xml->children()->children(); } catch (PrestaShopWebserviceNotFoundException $exception) { echo 'Bad ID'; } catch (PrestaShopWebserviceUnauthorizedException $exception) { @@ -61,40 +61,40 @@ // We set the Title echo '

Customers '; if (isset($_GET['id'])) - echo 'Details'; + echo 'Details'; else - echo 'List'; + echo 'List'; echo '

'; // We set a link to go back to list if we are in customer's details if (isset($_GET['id'])) - echo 'Return to the list'; + echo 'Return to the list'; echo ''; // if $resources is set we can lists element in it otherwise do nothing cause there's an error if (isset($resources)) { - if (!isset($_GET['id'])) - { - echo ''; - foreach ($resources as $resource) - { - // Iterates on the found IDs - echo ''; - } - } - else - { - foreach ($resources as $key => $resource) - { - // Iterates on customer's properties - echo ''; - echo ''; - echo ''; - } - } + if (!isset($_GET['id'])) + { + echo ''; + foreach ($resources as $resource) + { + // Iterates on the found IDs + echo ''; + } + } + else + { + foreach ($resources as $key => $resource) + { + // Iterates on customer's properties + echo ''; + echo ''; + echo ''; + } + } } echo '
IdMore
'.$resource->attributes().''. - 'Retrieve'. - '
'.$key.''.$resource.'
IdMore
'.$resource->attributes().''. + 'Retrieve'. + '
'.$key.''.$resource.'
'; ?> diff --git a/examples/Update.php b/examples/Update.php index 3f32b13..fa26c4f 100644 --- a/examples/Update.php +++ b/examples/Update.php @@ -36,14 +36,14 @@ // First : We always get the customer's list or a specific one try { - $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); - $opt = array('resource' => 'customers'); - if (isset($_GET['id'])) - $opt['id'] = $_GET['id']; - $xml = $webService->get($opt); + $webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG); + $opt = array('resource' => 'customers'); + if (isset($_GET['id'])) + $opt['id'] = $_GET['id']; + $xml = $webService->get($opt); - // Here we get the elements from children of customer markup which is children of prestashop root markup - $resources = $xml->children()->children(); + // Here we get the elements from children of customer markup which is children of prestashop root markup + $resources = $xml->children()->children(); } catch (PrestaShopWebserviceNotFoundException $exception) { echo 'Bad ID'; } catch (PrestaShopWebserviceUnauthorizedException $exception) { @@ -57,20 +57,20 @@ // Second : We update the data and send it to the web service if (isset($_GET['id']) && isset($_POST['id'])) // Here we check id cause in every resource there's an id { - // Here we have XML before update, lets update XML with new values - foreach ($resources as $nodeKey => $node) - { - $resources->$nodeKey = $_POST[$nodeKey]; - } - // And call the web service - try - { - $opt = array('resource' => 'customers'); - $opt['putXml'] = $xml->asXML(); - $opt['id'] = $_GET['id']; - $xml = $webService->edit($opt); - // if WebService don't throw an exception the action worked well and we don't show the following message - echo "Successfully updated."; + // Here we have XML before update, lets update XML with new values + foreach ($resources as $nodeKey => $node) + { + $resources->$nodeKey = $_POST[$nodeKey]; + } + // And call the web service + try + { + $opt = array('resource' => 'customers'); + $opt['putXml'] = $xml->asXML(); + $opt['id'] = $_GET['id']; + $xml = $webService->edit($opt); + // if WebService don't throw an exception the action worked well and we don't show the following message + echo "Successfully updated."; } catch (PrestaShopWebserviceNotFoundException $exception) { echo 'Bad ID'; } catch (PrestaShopWebserviceUnauthorizedException $exception) { @@ -92,10 +92,10 @@ // We set a link to go back to list if we are in customer's details if (isset($_GET['id'])) - echo 'Return to the list'; + echo 'Return to the list'; if (isset($_GET['id'])) - echo '
'; + echo ''; echo ''; if (isset($resources)) { @@ -103,32 +103,32 @@ echo ''; if (!isset($_GET['id'])) { - //Show list of customers - echo ''; - foreach ($resources as $resource) - { - echo ''; - } + //Show list of customers + echo ''; + foreach ($resources as $resource) + { + echo ''; + } } else { - //Show customer form - echo ''; - foreach ($resources as $key => $resource) - { - echo ''; - } + //Show customer form + echo ''; + foreach ($resources as $key => $resource) + { + echo ''; + } } } echo '
IdMore
'.$resource->attributes().''. - 'Update '. - '
IdMore
'.$resource->attributes().''. + 'Update '. + '
'.$key.''; - echo ''; - echo '
'.$key.''; + echo ''; + echo '

'; if (isset($_GET['id'])) - echo '
'; + echo ''; ?> From 0f8d507fdb9210206e836cbccb2a9f343907be52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20PLANCHAT?= Date: Wed, 26 Apr 2023 21:14:09 +0200 Subject: [PATCH 05/10] Apply suggestions from code review Co-authored-by: Pablo Borowicz --- PSWebServiceLibrary.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PSWebServiceLibrary.php b/PSWebServiceLibrary.php index 22ff061..a8519fc 100644 --- a/PSWebServiceLibrary.php +++ b/PSWebServiceLibrary.php @@ -558,7 +558,7 @@ public static function missingExtension($extension, $previous = null) { return new self( strtr( - 'Please activate the PHP extension \'%extension%\' to allow use of PrestaShop webservice library', + 'Please activate the PHP extension "%extension%" to allow use of PrestaShop webservice library', array( '%extension%' => $extension, ) From ab7589dcb41e2f99d76ebcacc4b5d194410ef5fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Planchat?= Date: Wed, 26 Apr 2023 21:17:37 +0200 Subject: [PATCH 06/10] Error handling: Added early returns in the assertStatusCode method Error handling: marked as deprecated the checkStatusCode method --- PSWebServiceLibrary.php | 53 ++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/PSWebServiceLibrary.php b/PSWebServiceLibrary.php index 22ff061..cfeda9c 100644 --- a/PSWebServiceLibrary.php +++ b/PSWebServiceLibrary.php @@ -93,14 +93,28 @@ function __construct($url, $key, $debug = true) * @param array{status_code: string, response: string} $request Response elements of CURL request * * @throws PrestaShopWebserviceException if HTTP status code is not 200 or 201 + * + * @deprecated in favor of the private method assertStatusCode */ protected function checkStatusCode($request) { $this->assertStatusCode($request['status_code']); } + /** + * @param int $statusCode + * + * @return void + * + * @throws PrestaShopWebserviceException if HTTP status code is not 200 or 201 + */ private function assertStatusCode($statusCode) { + if ($statusCode === 200 + || $statusCode === 201) { + return; + } + switch ($statusCode) { case 100: throw PrestaShopWebserviceStatusException::shouldNotReceive100ContinueStatus(); @@ -139,18 +153,12 @@ private function assertStatusCode($statusCode) case 429: throw new PrestaShopWebserviceTooManyRequestsException(); } - if ($statusCode >= 100 && $statusCode < 200 - || $statusCode >= 202 && $statusCode < 300 - || $statusCode >= 300 && $statusCode < 500 - || $statusCode >= 600 - || $statusCode < 100 - ) { - throw new PrestaShopWebserviceClientException('This call to PrestaShop Web Services responded with a non-standard code or a code this client could not handle properly. This can come from your web server or reverse proxy configuration.', $statusCode); - } if ($statusCode >= 500 && $statusCode < 600) { - throw new PrestaShopWebserviceServerException('This call to PrestaShop Web Services responded with a status code indicating it is not available or under a too heavy load to process your request.', $statusCode); + throw PrestaShopWebserviceServerException::withFailingServer($statusCode); } + + throw PrestaShopWebserviceClientException::withUnhandledStatus($statusCode); } /** @@ -330,7 +338,7 @@ public function add($options) } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => $xml)); - $this->checkStatusCode($request); + $this->assertStatusCode($request['status_code']); return $this->parseXML($request['response']); } @@ -393,7 +401,7 @@ public function get($options) $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'GET')); - $this->checkStatusCode($request);// check the response validity + $this->assertStatusCode($request['status_code']);// check the response validity return $this->parseXML($request['response']); } @@ -432,7 +440,7 @@ public function head($options) throw PrestaShopWebserviceBadParametersException::badParameters(); } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'HEAD', CURLOPT_NOBODY => true)); - $this->checkStatusCode($request);// check the response validity + $this->assertStatusCode($request['status_code']);// check the response validity return $request['header']; } @@ -469,7 +477,7 @@ public function edit($options) } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_POSTFIELDS => $xml)); - $this->checkStatusCode($request);// check the response validity + $this->assertStatusCode($request['status_code']);// check the response validity return $this->parseXML($request['response']); } @@ -520,7 +528,7 @@ public function delete($options) } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'DELETE')); - $this->checkStatusCode($request);// check the response validity + $this->assertStatusCode($request['status_code']);// check the response validity return true; } @@ -546,7 +554,7 @@ interface PrestaShopWebserviceException extends \Throwable class PrestaShopWebserviceBadParametersException extends \RuntimeException implements PrestaShopWebserviceException { public static function badParameters($previous = null) { - return new self('Bad parameters given', $previous); + return new self('Bad parameters given', 0, $previous); } } @@ -563,6 +571,7 @@ public static function missingExtension($extension, $previous = null) '%extension%' => $extension, ) ), + 0, $previous ); } @@ -766,9 +775,19 @@ public static function shouldNotReceive308PermanentRedirectStatus($previous = nu /** * @package PrestaShopWebservice */ -class PrestaShopWebserviceClientException extends \RuntimeException implements PrestaShopWebserviceException {} +class PrestaShopWebserviceClientException extends \RuntimeException implements PrestaShopWebserviceException { + public static function withUnhandledStatus($statusCode, $previous = null) + { + return new self('This call to PrestaShop Web Services responded with a non-standard code or a code this client could not handle properly. This can come from your web server or reverse proxy configuration.', $statusCode, $previous); + } +} /** * @package PrestaShopWebservice */ -class PrestaShopWebserviceServerException extends \RuntimeException implements PrestaShopWebserviceException {} +class PrestaShopWebserviceServerException extends \RuntimeException implements PrestaShopWebserviceException { + public static function withFailingServer($statusCode, $previous = null) + { + return new self('This call to PrestaShop Web Services responded with a status code indicating it is not available or under a too heavy load to process your request.', $statusCode, $previous); + } +} From 06b2d1d9a2d5a06cee5dd2cfc1d50855a7a366ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Planchat?= Date: Wed, 26 Apr 2023 21:25:55 +0200 Subject: [PATCH 07/10] Error handling: marked as deprecated the checkStatusCode method --- PSWebServiceLibrary.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PSWebServiceLibrary.php b/PSWebServiceLibrary.php index 8679917..fa06996 100644 --- a/PSWebServiceLibrary.php +++ b/PSWebServiceLibrary.php @@ -98,6 +98,8 @@ function __construct($url, $key, $debug = true) */ protected function checkStatusCode($request) { + @trigger_error(sprintf('Since prestashop/prestashop-webservice-lib 1fcf90ac2001839e94769381e7596aee45423ba7: The %s::checkStatusCode() method is deprecated and should not be used anymore.', __CLASS__), \E_USER_DEPRECATED); + $this->assertStatusCode($request['status_code']); } From fef862746c0bf98230bfe0de3421f72b2d216cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Planchat?= Date: Wed, 26 Apr 2023 22:39:38 +0200 Subject: [PATCH 08/10] Error handling: fixed phpstan level 6 errors --- PSWebServiceLibrary.php | 142 ++++++++++++++++++++++++++++++++++------ examples/Create.php | 4 ++ examples/Update.php | 5 ++ 3 files changed, 132 insertions(+), 19 deletions(-) diff --git a/PSWebServiceLibrary.php b/PSWebServiceLibrary.php index fa06996..cb0be01 100644 --- a/PSWebServiceLibrary.php +++ b/PSWebServiceLibrary.php @@ -90,10 +90,12 @@ function __construct($url, $key, $debug = true) * 'response' => CURL response *

* - * @param array{status_code: string, response: string} $request Response elements of CURL request + * @param array{status_code: int, response: string} $request Response elements of CURL request * * @throws PrestaShopWebserviceException if HTTP status code is not 200 or 201 * + * @return void + * * @deprecated in favor of the private method assertStatusCode */ protected function checkStatusCode($request) @@ -165,7 +167,7 @@ private function assertStatusCode($statusCode) /** * Provides default parameters for the curl connection(s) - * @return array Default parameters for curl connection(s) + * @return array Default parameters for curl connection(s) */ protected function getCurlDefaultParams() { @@ -189,7 +191,7 @@ protected function getCurlDefaultParams() * @param string $url Resource name * @param mixed $curl_params CURL parameters (sent to curl_set_opt) * - * @return array status_code, response, header + * @return array{status_code: int, response: string, header: string} * * @throws PrestaShopWebserviceException */ @@ -198,6 +200,9 @@ protected function executeRequest($url, $curl_params = array()) $defaultParams = $this->getCurlDefaultParams(); $session = curl_init($url); + if ($session === false) { + throw PrestaShopWebserviceClientException::curlSessionInitializationFailure(); + } $curl_options = array(); foreach ($defaultParams as $defkey => $defval) { @@ -215,15 +220,22 @@ protected function executeRequest($url, $curl_params = array()) curl_setopt_array($session, $curl_options); $response = curl_exec($session); + if (!is_string($response)) { + throw PrestaShopWebserviceClientException::curlRequestExecutionFailure(); + } $index = strpos($response, "\r\n\r\n"); - if ($index === false && $curl_params[CURLOPT_CUSTOMREQUEST] != 'HEAD') { - throw new PrestaShopWebserviceServerException('Bad HTTP response ' . $response . curl_error($session)); + if ($curl_params[CURLOPT_CUSTOMREQUEST] === 'HEAD') { + $index = strlen($response); + $header = $response; + $body = ''; + } else if ($index === false) { + throw PrestaShopWebserviceServerException::badResponseFormat(curl_error($session)); + } else { + $header = substr($response, 0, $index); + $body = substr($response, $index + 4); } - $header = substr($response, 0, $index); - $body = substr($response, $index + 4); - $headerArrayTmp = explode("\n", $header); $headerArray = array(); @@ -267,6 +279,13 @@ protected function executeRequest($url, $curl_params = array()) return array('status_code' => $status_code, 'response' => $body, 'header' => $header); } + /** + * @param string $title + * + * @param mixed $content + * + * @return void + */ public function printDebug($title, $content) { if (php_sapi_name() == 'cli') { @@ -280,6 +299,9 @@ public function printDebug($title, $content) } } + /** + * @return string + */ public function getVersion() { return $this->version; @@ -299,7 +321,7 @@ protected function parseXML($response) libxml_clear_errors(); libxml_use_internal_errors(true); $xml = simplexml_load_string(trim($response), 'SimpleXMLElement', LIBXML_NOCDATA); - if (libxml_get_errors()) { + if (libxml_get_errors() || $xml === false) { $msg = var_export(libxml_get_errors(), true); libxml_clear_errors(); throw new PrestaShopWebserviceServerException('HTTP XML response is not parsable: ' . $msg); @@ -317,15 +339,13 @@ protected function parseXML($response) * 'postXml' => Full XML string to add resource

* Examples are given in the tutorial

* - * @param array $options + * @param array> $options * * @return SimpleXMLElement status_code, response * @throws PrestaShopWebserviceException */ public function add($options) { - $xml = ''; - if (isset($options['resource'], $options['postXml']) || isset($options['url'], $options['postXml'])) { $url = (isset($options['resource']) ? $this->url . '/api/' . $options['resource'] : $options['url']); $xml = $options['postXml']; @@ -370,7 +390,7 @@ public function add($options) * ?> * * - * @param array $options Array representing resource to get. + * @param array> $options Array representing resource to get. * * @return SimpleXMLElement status_code, response * @throws PrestaShopWebserviceException @@ -411,10 +431,10 @@ public function get($options) /** * Head method (HEAD) a resource * - * @param array $options Array representing resource for head request. + * @param array> $options Array representing resource for head request. * - * @return SimpleXMLElement status_code, response - * @throws PrestaShopWebserviceException + * @return string + * @throws PrestaShopWebserviceBadParametersException */ public function head($options) { @@ -454,7 +474,7 @@ public function head($options) * 'putXml' => Modified XML string of a resource

* Examples are given in the tutorial

* - * @param array $options Array representing resource to edit. + * @param array> $options Array representing resource to edit. * * @return SimpleXMLElement * @throws PrestaShopWebserviceException @@ -505,10 +525,11 @@ public function edit($options) * ?> * * - * @param array $options Array representing resource to delete. + * @param array> $options Array representing resource to delete. * * @return bool - * @throws PrestaShopWebserviceException + * + * @throws \PrestaShopWebserviceBadParametersException */ public function delete($options) { @@ -540,6 +561,14 @@ public function delete($options) if (!interface_exists('Throwable')) { interface Throwable { + public function getMessage(); + public function getCode(); + public function getFile(); + public function getLine(); + public function getTrace(); + public function getTraceAsString(); + public function getPrevious(); + public function __toString(); } } @@ -554,6 +583,10 @@ interface PrestaShopWebserviceException extends \Throwable * @package PrestaShopWebservice */ class PrestaShopWebserviceBadParametersException extends \RuntimeException implements PrestaShopWebserviceException { + /** + * @param ?\Throwable $previous + * @return PrestaShopWebserviceBadParametersException + */ public static function badParameters($previous = null) { return new self('Bad parameters given', 0, $previous); @@ -564,6 +597,11 @@ public static function badParameters($previous = null) * @package PrestaShopWebservice */ class PrestaShopWebserviceMissingPreconditionException extends \BadFunctionCallException implements PrestaShopWebserviceException { + /** + * @param string $extension + * @param ?\Throwable $previous + * @return PrestaShopWebserviceMissingPreconditionException + */ public static function missingExtension($extension, $previous = null) { return new self( @@ -583,6 +621,10 @@ public static function missingExtension($extension, $previous = null) * @package PrestaShopWebservice */ class PrestaShopWebserviceBadRequestException extends \RuntimeException implements PrestaShopWebserviceException { + /** + * @param ?\Throwable $previous + * @return self + */ public function __construct($previous = null) { parent::__construct('Bad Request', 400, $previous); @@ -593,6 +635,10 @@ public function __construct($previous = null) * @package PrestaShopWebservice */ class PrestaShopWebserviceUnauthorizedException extends \RuntimeException implements PrestaShopWebserviceException { + /** + * @param ?\Throwable $previous + * @return self + */ public function __construct($previous = null) { parent::__construct('Unauthorized', 401, $previous); @@ -603,6 +649,10 @@ public function __construct($previous = null) * @package PrestaShopWebservice */ class PrestaShopWebserviceForbiddenException extends \RuntimeException implements PrestaShopWebserviceException { + /** + * @param ?\Throwable $previous + * @return self + */ public function __construct($previous = null) { parent::__construct('Forbidden', 403, $previous); @@ -613,6 +663,10 @@ public function __construct($previous = null) * @package PrestaShopWebservice */ class PrestaShopWebserviceNotFoundException extends \RuntimeException implements PrestaShopWebserviceException { + /** + * @param \Throwable $previous + * @return self + */ public function __construct($previous = null) { parent::__construct('Not Found', 404, $previous); @@ -623,6 +677,10 @@ public function __construct($previous = null) * @package PrestaShopWebservice */ class PrestaShopWebserviceMethodNotAllowedException extends \RuntimeException implements PrestaShopWebserviceException { + /** + * @param ?\Throwable $previous + * @return self + */ public function __construct($previous = null) { parent::__construct('Method Not Allowed', 405, $previous); @@ -633,6 +691,10 @@ public function __construct($previous = null) * @package PrestaShopWebservice */ class PrestaShopWebserviceTooManyRequestsException extends \RuntimeException implements PrestaShopWebserviceException { + /** + * @param ?\Throwable $previous + * @return self + */ public function __construct($previous = null) { parent::__construct('Too Many Requests', 429, $previous); @@ -643,6 +705,10 @@ public function __construct($previous = null) * @package PrestaShopWebservice */ class PrestaShopWebserviceNoContentException extends \RuntimeException implements PrestaShopWebserviceException { + /** + * @param ?\Throwable $previous + * @return self + */ public function __construct($previous = null) { parent::__construct('No Content', 204, $previous); @@ -778,18 +844,56 @@ public static function shouldNotReceive308PermanentRedirectStatus($previous = nu * @package PrestaShopWebservice */ class PrestaShopWebserviceClientException extends \RuntimeException implements PrestaShopWebserviceException { + /** + * @param int $statusCode + * @param ?\Throwable $previous + * @return self + */ public static function withUnhandledStatus($statusCode, $previous = null) { return new self('This call to PrestaShop Web Services responded with a non-standard code or a code this client could not handle properly. This can come from your web server or reverse proxy configuration.', $statusCode, $previous); } + + /** + * @param ?\Throwable $previous + * @return self + */ + public static function curlSessionInitializationFailure($previous = null) + { + return new self('The CURL session could not be initialized.', 0, $previous); + } + + /** + * @param ?\Throwable $previous + * @return self + */ + public static function curlRequestExecutionFailure($previous = null) + { + return new self('The CURL request could not be executed.', 0, $previous); + } } /** * @package PrestaShopWebservice */ class PrestaShopWebserviceServerException extends \RuntimeException implements PrestaShopWebserviceException { + /** + * @param int $statusCode + * @param ?\Throwable $previous + * @return self + */ public static function withFailingServer($statusCode, $previous = null) { return new self('This call to PrestaShop Web Services responded with a status code indicating it is not available or under a too heavy load to process your request.', $statusCode, $previous); } + + /** + * @param string $errorMessage + * @param ?\Throwable $previous + * @return self + */ + public static function badResponseFormat($errorMessage, $previous = null) + { + return new self('Bad HTTP response, the response format is invalid: '. $errorMessage, 0, $previous); + } } diff --git a/examples/Create.php b/examples/Create.php index 2dbad74..0e9fd36 100644 --- a/examples/Create.php +++ b/examples/Create.php @@ -46,12 +46,16 @@ } catch (PrestaShopWebserviceNotFoundException $exception) { echo 'Bad ID'; + exit; } catch (PrestaShopWebserviceUnauthorizedException $exception) { echo 'Bad auth key'; + exit; } catch (PrestaShopWebserviceForbiddenException $exception) { echo 'Not logged in'; + exit; } catch (PrestaShopWebserviceException $exception) { echo 'Other error
'.$exception->getMessage(); + exit; } if (count($_POST) > 0) diff --git a/examples/Update.php b/examples/Update.php index fa26c4f..07ed597 100644 --- a/examples/Update.php +++ b/examples/Update.php @@ -44,14 +44,19 @@ // Here we get the elements from children of customer markup which is children of prestashop root markup $resources = $xml->children()->children(); + } catch (PrestaShopWebserviceNotFoundException $exception) { echo 'Bad ID'; + exit; } catch (PrestaShopWebserviceUnauthorizedException $exception) { echo 'Bad auth key'; + exit; } catch (PrestaShopWebserviceForbiddenException $exception) { echo 'Not logged in'; + exit; } catch (PrestaShopWebserviceException $exception) { echo 'Other error
'.$exception->getMessage(); + exit; } // Second : We update the data and send it to the web service From bbdcec130b3cdbd86524df683350dca6749c0b7e Mon Sep 17 00:00:00 2001 From: clementzarch Date: Tue, 2 May 2023 10:06:34 +0200 Subject: [PATCH 09/10] display the server's error message to give more context to the API user --- PSWebServiceLibrary.php | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/PSWebServiceLibrary.php b/PSWebServiceLibrary.php index cb0be01..e334496 100644 --- a/PSWebServiceLibrary.php +++ b/PSWebServiceLibrary.php @@ -102,7 +102,7 @@ protected function checkStatusCode($request) { @trigger_error(sprintf('Since prestashop/prestashop-webservice-lib 1fcf90ac2001839e94769381e7596aee45423ba7: The %s::checkStatusCode() method is deprecated and should not be used anymore.', __CLASS__), \E_USER_DEPRECATED); - $this->assertStatusCode($request['status_code']); + $this->assertStatusCode($request); } /** @@ -112,13 +112,23 @@ protected function checkStatusCode($request) * * @throws PrestaShopWebserviceException if HTTP status code is not 200 or 201 */ - private function assertStatusCode($statusCode) + private function assertStatusCode($request) { + $statusCode = $request['status_code']; + if ($statusCode === 200 || $statusCode === 201) { return; } + $errorMessage = ''; + $errors = $this->parseXML($request['response'])->children()->children(); + if ($errors && count($errors) > 0) { + foreach ($errors as $error) { + $errorMessage.= $error->message . '. '; + } + } + switch ($statusCode) { case 100: throw PrestaShopWebserviceStatusException::shouldNotReceive100ContinueStatus(); @@ -145,7 +155,7 @@ private function assertStatusCode($statusCode) case 308: throw PrestaShopWebserviceStatusException::shouldNotReceive308PermanentRedirectStatus(); case 400: - throw new PrestaShopWebserviceBadRequestException(); + throw new PrestaShopWebserviceBadRequestException($errorMessage); case 401: throw new PrestaShopWebserviceUnauthorizedException(); case 403: @@ -159,7 +169,7 @@ private function assertStatusCode($statusCode) } if ($statusCode >= 500 && $statusCode < 600) { - throw PrestaShopWebserviceServerException::withFailingServer($statusCode); + throw PrestaShopWebserviceServerException::withFailingServer($errorMessage, $statusCode); } throw PrestaShopWebserviceClientException::withUnhandledStatus($statusCode); @@ -360,7 +370,7 @@ public function add($options) } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => $xml)); - $this->assertStatusCode($request['status_code']); + $this->assertStatusCode($request); return $this->parseXML($request['response']); } @@ -423,7 +433,7 @@ public function get($options) $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'GET')); - $this->assertStatusCode($request['status_code']);// check the response validity + $this->assertStatusCode($request);// check the response validity return $this->parseXML($request['response']); } @@ -462,7 +472,7 @@ public function head($options) throw PrestaShopWebserviceBadParametersException::badParameters(); } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'HEAD', CURLOPT_NOBODY => true)); - $this->assertStatusCode($request['status_code']);// check the response validity + $this->assertStatusCode($request);// check the response validity return $request['header']; } @@ -499,7 +509,7 @@ public function edit($options) } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_POSTFIELDS => $xml)); - $this->assertStatusCode($request['status_code']);// check the response validity + $this->assertStatusCode($request);// check the response validity return $this->parseXML($request['response']); } @@ -551,7 +561,7 @@ public function delete($options) } $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'DELETE')); - $this->assertStatusCode($request['status_code']);// check the response validity + $this->assertStatusCode($request);// check the response validity return true; } @@ -622,12 +632,13 @@ public static function missingExtension($extension, $previous = null) */ class PrestaShopWebserviceBadRequestException extends \RuntimeException implements PrestaShopWebserviceException { /** + * @param string $errorMessage * @param ?\Throwable $previous * @return self */ - public function __construct($previous = null) + public function __construct($errorMessage, $previous = null) { - parent::__construct('Bad Request', 400, $previous); + parent::__construct('Bad Request: '. $errorMessage, 400, $previous); } } @@ -878,13 +889,14 @@ public static function curlRequestExecutionFailure($previous = null) */ class PrestaShopWebserviceServerException extends \RuntimeException implements PrestaShopWebserviceException { /** + * @param string $errorMessage * @param int $statusCode * @param ?\Throwable $previous * @return self */ - public static function withFailingServer($statusCode, $previous = null) + public static function withFailingServer($errorMessage, $statusCode, $previous = null) { - return new self('This call to PrestaShop Web Services responded with a status code indicating it is not available or under a too heavy load to process your request.', $statusCode, $previous); + return new self('This call to PrestaShop Web Services responded with a status code indicating it is not available or under a too heavy load to process your request: '. $errorMessage, $statusCode, $previous); } /** From c82b9e40ada7c55d89062031e0de10aecf221850 Mon Sep 17 00:00:00 2001 From: clementzarch Date: Wed, 10 May 2023 10:08:00 +0200 Subject: [PATCH 10/10] when the response is not parsable as an XML, display the raw response, not the error message (which is a lot less helpful when troubleshooting), separate multiple error messages with " - " --- PSWebServiceLibrary.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PSWebServiceLibrary.php b/PSWebServiceLibrary.php index e334496..38d600a 100644 --- a/PSWebServiceLibrary.php +++ b/PSWebServiceLibrary.php @@ -125,7 +125,7 @@ private function assertStatusCode($request) $errors = $this->parseXML($request['response'])->children()->children(); if ($errors && count($errors) > 0) { foreach ($errors as $error) { - $errorMessage.= $error->message . '. '; + $errorMessage.= $error->message . ' - '; } } @@ -334,7 +334,7 @@ protected function parseXML($response) if (libxml_get_errors() || $xml === false) { $msg = var_export(libxml_get_errors(), true); libxml_clear_errors(); - throw new PrestaShopWebserviceServerException('HTTP XML response is not parsable: ' . $msg); + throw new PrestaShopWebserviceServerException('HTTP XML response is not parsable: ' . $response); } return $xml; } else {