dolibarr 21.0.0-beta
PSWebServiceLibrary.class.php
1<?php
2/* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
3 */
4/*
5* 2007-2022 PrestaShop SA and Contributors
6*
7* NOTICE OF LICENSE
8*
9* This source file is subject to the Open Software License (OSL 3.0)
10* that is bundled with this package in the file LICENSE.txt.
11* It is also available through the world-wide-web at this URL:
12* https://opensource.org/licenses/osl-3.0.php
13* If you did not receive a copy of the license and are unable to
14* obtain it through the world-wide-web, please send an email
15* to license@prestashop.com so we can send you a copy immediately.
16*
17* DISCLAIMER
18*
19* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
20* versions in the future. If you wish to customize PrestaShop for your
21* needs please refer to https://www.prestashop.com for more information.
22*
23* @author PrestaShop SA <contact@prestashop.com>
24* @copyright 2007-2022 PrestaShop SA
25* @license https://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
26* International Registered Trademark & Property of PrestaShop SA
27* PrestaShop Webservice Library
28* @package PrestaShopWebservice
29*/
30
35{
37 protected $url;
38
40 protected $key;
41
43 protected $debug;
44
46 protected $version;
47
49 const PS_COMPATIBLE_VERSIONS_MIN = '1.4.0.0';
51 const PS_COMPATIBLE_VERSIONS_MAX = '8.1.1';
52
76 public function __construct($url, $key, $debug = true)
77 {
78 if (!extension_loaded('curl')) {
80 'Please activate the PHP extension \'curl\' to allow use of PrestaShop webservice library'
81 );
82 }
83 $this->url = $url;
84 $this->key = $key;
85 $this->debug = $debug;
86 $this->version = 'unknown';
87 }
88
101 protected function checkStatusCode($request)
102 {
103 switch ($request['status_code']) {
104 case 200:
105 case 201:
106 break;
107 case 204:
108 $error_message = 'No content';
109 break;
110 case 400:
111 $error_message = 'Bad Request';
112 break;
113 case 401:
114 $error_message = 'Unauthorized';
115 break;
116 case 404:
117 $error_message = 'Not Found';
118 break;
119 case 405:
120 $error_message = 'Method Not Allowed';
121 break;
122 case 500:
123 $error_message = 'Internal Server Error';
124 break;
125 default:
127 'This call to PrestaShop Web Services returned an unexpected HTTP status of:' . $request['status_code']
128 );
129 }
130
131 if (!empty($error_message)) {
132 $response = $this->parseXML($request['response']);
133 $errors = $response->children()->children();
134 if ($errors && count($errors) > 0) {
135 foreach ($errors as $error) {
136 $error_message .= ' - (Code ' . $error->code . '): ' . $error->message;
137 }
138 }
139 $error_label = 'This call to PrestaShop Web Services failed and returned an HTTP status of %d. That means: %s.';
140 throw new PrestaShopWebserviceException(sprintf($error_label, $request['status_code'], $error_message));
141 }
142 }
143
148 protected function getCurlDefaultParams()
149 {
150 $defaultParams = array(
151 CURLOPT_HEADER => true,
152 CURLOPT_RETURNTRANSFER => true,
153 CURLINFO_HEADER_OUT => true,
154 CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
155 CURLOPT_USERPWD => $this->key . ':',
156 CURLOPT_HTTPHEADER => array('Expect:'),
157 //CURLOPT_SSL_VERIFYPEER => false, // reminder, in dev environment sometimes self-signed certificates are used
158 //CURLOPT_CAINFO => "PATH2CAINFO", // ssl certificate chain checking
159 //CURLOPT_CAPATH => "PATH2CAPATH",
160 );
161 return $defaultParams;
162 }
163
174 public function executeRequest($url, $curl_params = array())
175 {
176 $defaultParams = $this->getCurlDefaultParams();
177
178 dol_syslog("curl_init url=".$url);
179 $session = curl_init($url);
180
181 $curl_options = array();
182 foreach ($defaultParams as $defkey => $defval) {
183 if (isset($curl_params[$defkey])) {
184 $curl_options[$defkey] = $curl_params[$defkey];
185 } else {
186 $curl_options[$defkey] = $defaultParams[$defkey];
187 }
188 }
189 foreach ($curl_params as $defkey => $defval) {
190 if (!isset($curl_options[$defkey])) {
191 $curl_options[$defkey] = $curl_params[$defkey];
192 }
193 }
194
195 dol_syslog("curl curl_options = ".var_export($curl_options, true));
196 curl_setopt_array($session, $curl_options);
197 $response = curl_exec($session);
198
199 $index = strpos($response, "\r\n\r\n");
200 if ($index === false && $curl_params[CURLOPT_CUSTOMREQUEST] != 'HEAD') {
201 throw new PrestaShopWebserviceException('Bad HTTP response ' . $response . curl_error($session));
202 }
203
204 $header = substr($response, 0, $index);
205 $body = substr($response, $index + 4);
206
207 $headerArrayTmp = explode("\n", $header);
208
209 $headerArray = array();
210 foreach ($headerArrayTmp as &$headerItem) {
211 $tmp = explode(':', $headerItem);
212 $tmp = array_map('trim', $tmp);
213 if (count($tmp) == 2) {
214 $headerArray[$tmp[0]] = $tmp[1];
215 }
216 }
217
218 if (array_key_exists('PSWS-Version', $headerArray)) {
219 $this->version = $headerArray['PSWS-Version'];
220 if (
221 version_compare(PrestaShopWebservice::PS_COMPATIBLE_VERSIONS_MIN, $headerArray['PSWS-Version']) == 1 ||
222 version_compare(PrestaShopWebservice::PS_COMPATIBLE_VERSIONS_MAX, $headerArray['PSWS-Version']) == -1
223 ) {
225 'This library is not compatible with this version of PrestaShop. Please upgrade/downgrade this library'
226 );
227 }
228 }
229
230 if ($this->debug) {
231 $this->printDebug('HTTP REQUEST HEADER', curl_getinfo($session, CURLINFO_HEADER_OUT));
232 $this->printDebug('HTTP RESPONSE HEADER', $header);
233 }
234 $status_code = curl_getinfo($session, CURLINFO_HTTP_CODE);
235 if ($status_code === 0) {
236 throw new PrestaShopWebserviceException('CURL Error: ' . curl_error($session));
237 }
238 curl_close($session);
239 if ($this->debug) {
240 if ($curl_params[CURLOPT_CUSTOMREQUEST] == 'PUT' || $curl_params[CURLOPT_CUSTOMREQUEST] == 'POST') {
241 $this->printDebug('XML SENT', urldecode($curl_params[CURLOPT_POSTFIELDS]));
242 }
243 if ($curl_params[CURLOPT_CUSTOMREQUEST] != 'DELETE' && $curl_params[CURLOPT_CUSTOMREQUEST] != 'HEAD') {
244 $this->printDebug('RETURN HTTP BODY', $body);
245 }
246 }
247 return array('status_code' => $status_code, 'response' => $body, 'header' => $header);
248 }
249
257 public function printDebug($title, $content)
258 {
259 if (php_sapi_name() == 'cli') {
260 echo $title . PHP_EOL . $content;
261 } else {
262 echo '<div style="display:table;background:#CCC;font-size:8pt;padding:7px"><h6 style="font-size:9pt;margin:0">'
263 . $title
264 . '</h6><pre>'
265 . htmlentities($content)
266 . '</pre></div>';
267 }
268 }
269
275 public function getVersion()
276 {
277 return $this->version;
278 }
279
288 protected function parseXML($response)
289 {
290 if ($response != '') {
291 libxml_clear_errors();
292 libxml_use_internal_errors(true);
293 if (LIBXML_VERSION < 20900) {
294 // Avoid load of external entities (security problem).
295 // Required only if LIBXML_VERSION < 20900
296 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
297 libxml_disable_entity_loader(true);
298 }
299
300 $xml = simplexml_load_string(trim($response), 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NONET);
301 if (libxml_get_errors()) {
302 $msg = var_export(libxml_get_errors(), true);
303 libxml_clear_errors();
304 throw new PrestaShopWebserviceException('HTTP XML response is not parsable: ' . $msg);
305 }
306 return $xml;
307 } else {
308 throw new PrestaShopWebserviceException('HTTP response is empty');
309 }
310 }
311
324 public function add($options)
325 {
326 $xml = '';
327
328 if (isset($options['resource'], $options['postXml']) || isset($options['url'], $options['postXml'])) {
329 $url = (isset($options['resource']) ? $this->url . '/api/' . $options['resource'] : $options['url']);
330 $xml = $options['postXml'];
331 if (isset($options['id_shop'])) {
332 $url .= '&id_shop=' . $options['id_shop'];
333 }
334 if (isset($options['id_group_shop'])) {
335 $url .= '&id_group_shop=' . $options['id_group_shop'];
336 }
337 } else {
338 throw new PrestaShopWebserviceException('Bad parameters given');
339 }
340 $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => $xml));
341
342 $this->checkStatusCode($request);
343 return $this->parseXML($request['response']);
344 }
345
377 public function get($options)
378 {
379 if (isset($options['url'])) {
380 $url = $options['url'];
381 } elseif (isset($options['resource'])) {
382 $url = $this->url . '/api/' . $options['resource'];
383 $url_params = array();
384 if (isset($options['id'])) {
385 $url .= '/' . $options['id'];
386 }
387
388 $params = array('filter', 'display', 'sort', 'limit', 'id_shop', 'id_group_shop', 'schema', 'language', 'date', 'price');
389 foreach ($params as $p) {
390 foreach ($options as $k => $o) {
391 if (strpos($k, $p) !== false) {
392 $url_params[$k] = $options[$k];
393 }
394 }
395 }
396 if (count($url_params) > 0) {
397 $url .= '?' . http_build_query($url_params);
398 }
399 } else {
400 throw new PrestaShopWebserviceException('Bad parameters given');
401 }
402
403 $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'GET'));
404
405 $this->checkStatusCode($request);// check the response validity
406
407 return $this->parseXML($request['response']);
408 }
409
418 public function head($options)
419 {
420 if (isset($options['url'])) {
421 $url = $options['url'];
422 } elseif (isset($options['resource'])) {
423 $url = $this->url . '/api/' . $options['resource'];
424 $url_params = array();
425 if (isset($options['id'])) {
426 $url .= '/' . $options['id'];
427 }
428
429 $params = array('filter', 'display', 'sort', 'limit');
430 foreach ($params as $p) {
431 foreach ($options as $k => $o) {
432 if (strpos($k, $p) !== false) {
433 $url_params[$k] = $options[$k];
434 }
435 }
436 }
437 if (count($url_params) > 0) {
438 $url .= '?' . http_build_query($url_params);
439 }
440 } else {
441 throw new PrestaShopWebserviceException('Bad parameters given');
442 }
443 $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'HEAD', CURLOPT_NOBODY => true));
444 $this->checkStatusCode($request);// check the response validity
445 return $request['header'];
446 }
447
461 public function edit($options)
462 {
463 $xml = '';
464 if (isset($options['url'])) {
465 $url = $options['url'];
466 } elseif ((isset($options['resource'], $options['id']) || isset($options['url'])) && $options['putXml']) {
467 $url = (isset($options['url']) ? $options['url'] :
468 $this->url . '/api/' . $options['resource'] . '/' . $options['id']);
469 $xml = $options['putXml'];
470 if (isset($options['id_shop'])) {
471 $url .= '&id_shop=' . $options['id_shop'];
472 }
473 if (isset($options['id_group_shop'])) {
474 $url .= '&id_group_shop=' . $options['id_group_shop'];
475 }
476 } else {
477 throw new PrestaShopWebserviceException('Bad parameters given');
478 }
479
480 $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_POSTFIELDS => $xml));
481 $this->checkStatusCode($request);// check the response validity
482 return $this->parseXML($request['response']);
483 }
484
512 public function delete($options)
513 {
514 if (isset($options['url'])) {
515 $url = $options['url'];
516 } elseif (isset($options['resource']) && isset($options['id'])) {
517 $url = (is_array($options['id']))
518 ? $this->url . '/api/' . $options['resource'] . '/?id=[' . implode(',', $options['id']) . ']'
519 : $this->url . '/api/' . $options['resource'] . '/' . $options['id'];
520 } else {
521 throw new PrestaShopWebserviceException('Bad parameters given');
522 }
523
524 if (isset($options['id_shop'])) {
525 $url .= '&id_shop=' . $options['id_shop'];
526 }
527 if (isset($options['id_group_shop'])) {
528 $url .= '&id_group_shop=' . $options['id_group_shop'];
529 }
530
531 $request = $this->executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'DELETE'));
532 $this->checkStatusCode($request);// check the response validity
533 return true;
534 }
535}
536
add($options)
Add (POST) a resource.
printDebug($title, $content)
Output debug info.
edit($options)
Edit (PUT) a resource.
parseXML($response)
Load XML from string.
checkStatusCode($request)
Take the status code and throw an exception if the server didn't return 200 or 201 code.
getCurlDefaultParams()
Provides default parameters for the curl connection(s)
head($options)
Head method (HEAD) a resource.
__construct($url, $key, $debug=true)
PrestaShopWebservice constructor.
executeRequest($url, $curl_params=array())
Handles a CURL request to PrestaShop Webservice.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.