1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
|
4
|
|
|
namespace Bpost\BpostApiClient; |
5
|
|
|
|
6
|
|
|
use Bpost\BpostApiClient\Bpack247\Customer; |
7
|
|
|
use Bpost\BpostApiClient\Exception\BpostApiResponseException\BpostApiBusinessException; |
8
|
|
|
use Bpost\BpostApiClient\Exception\BpostApiResponseException\BpostApiSystemException; |
9
|
|
|
use Bpost\BpostApiClient\Exception\BpostApiResponseException\BpostCurlException; |
10
|
|
|
use Bpost\BpostApiClient\Exception\BpostApiResponseException\BpostInvalidResponseException; |
11
|
|
|
use DOMDocument; |
12
|
|
|
use SimpleXMLElement; |
13
|
|
|
use CurlHandle; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* bPost Bpack24/7 class |
17
|
|
|
* |
18
|
|
|
* @author Tijs Verkoyen <[email protected]> |
19
|
|
|
* |
20
|
|
|
* @version 3.0.0 |
21
|
|
|
* |
22
|
|
|
* @copyright Copyright (c), Tijs Verkoyen. All rights reserved. |
23
|
|
|
* @license BSD License |
24
|
|
|
*/ |
25
|
|
|
class Bpack247 |
26
|
|
|
{ |
27
|
|
|
/** URL for the API */ |
28
|
|
|
public const API_URL = 'http://www.bpack247.be/BpostRegistrationWebserviceREST/servicecontroller.svc'; |
29
|
|
|
|
30
|
|
|
/** current version */ |
31
|
|
|
public const VERSION = '3.0.0'; |
32
|
|
|
|
33
|
|
|
private string $accountId; |
34
|
|
|
private string $passPhrase; |
35
|
|
|
|
36
|
|
|
/** @var CurlHandle|null */ |
37
|
|
|
private ?CurlHandle $curl = null; |
38
|
|
|
|
39
|
|
|
/** * The port to use. */ |
40
|
|
|
private ?int $port = null; |
41
|
|
|
|
42
|
|
|
/** Timeout in seconds */ |
43
|
|
|
private int $timeOut = 30; |
44
|
|
|
|
45
|
|
|
private string $userAgent = ''; |
46
|
|
|
|
47
|
|
|
public function __construct(string $accountId, string $passPhrase) |
48
|
|
|
{ |
49
|
|
|
$this->accountId = $accountId; |
50
|
|
|
$this->passPhrase = $passPhrase; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Make the call |
55
|
|
|
* |
56
|
|
|
* @throws BpostApiBusinessException |
57
|
|
|
* @throws BpostApiSystemException |
58
|
|
|
* @throws BpostCurlException |
59
|
|
|
* @throws BpostInvalidResponseException |
60
|
|
|
*/ |
61
|
|
|
private function doCall(string $url, ?string $body = null, string $method = 'GET'): SimpleXMLElement |
62
|
|
|
{ |
63
|
|
|
$headers = [ |
64
|
|
|
'Authorization: Basic ' . $this->getAuthorizationHeader(), |
65
|
|
|
]; |
66
|
|
|
|
67
|
|
|
$options = [ |
68
|
|
|
CURLOPT_URL => self::API_URL . $url, |
69
|
|
|
CURLOPT_USERAGENT => $this->getUserAgent(), |
70
|
|
|
CURLOPT_RETURNTRANSFER => true, |
71
|
|
|
CURLOPT_TIMEOUT => $this->getTimeOut(), |
72
|
|
|
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, |
73
|
|
|
CURLOPT_HTTPHEADER => $headers, |
74
|
|
|
]; |
75
|
|
|
|
76
|
|
|
if ($this->port !== null) { |
77
|
|
|
$options[CURLOPT_PORT] = $this->port; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
if ($method === 'POST') { |
81
|
|
|
$options[CURLOPT_POST] = true; |
82
|
|
|
$options[CURLOPT_POSTFIELDS] = $body ?? ''; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
$this->curl = curl_init(); |
|
|
|
|
86
|
|
|
curl_setopt_array($this->curl, $options); |
87
|
|
|
|
88
|
|
|
try { |
89
|
|
|
$response = curl_exec($this->curl); |
90
|
|
|
$info = curl_getinfo($this->curl); |
91
|
|
|
|
92
|
|
|
$errorNumber = curl_errno($this->curl); |
93
|
|
|
$errorMessage = curl_error($this->curl); |
94
|
|
|
|
95
|
|
|
if ($errorNumber !== 0) { |
96
|
|
|
throw new BpostCurlException($errorMessage, $errorNumber); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
$httpCode = (int)($info['http_code'] ?? 0); |
100
|
|
|
|
101
|
|
|
// Non 200 => tenter de parser l'erreur métier pour renvoyer l’exception dédiée |
102
|
|
|
if (!in_array($httpCode, [0, 200], true)) { |
103
|
|
|
$xml = @simplexml_load_string((string)$response); |
104
|
|
|
|
105
|
|
|
if ( |
106
|
|
|
$xml !== false |
107
|
|
|
&& ($xml->getName() === 'businessException' || $xml->getName() === 'systemException') |
108
|
|
|
) { |
109
|
|
|
$message = (string) ($xml->message ?? ''); |
110
|
|
|
$code = isset($xml->code) ? (int) $xml->code : 0; |
111
|
|
|
|
112
|
|
|
if ($xml->getName() === 'businessException') { |
113
|
|
|
throw new BpostApiBusinessException($message, $code); |
114
|
|
|
} |
115
|
|
|
throw new BpostApiSystemException($message, $code); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
throw new BpostInvalidResponseException('', $httpCode); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
// 200: parser XML |
122
|
|
|
$xml = simplexml_load_string((string)$response); |
123
|
|
|
if ($xml === false) { |
124
|
|
|
// pas de XML valide alors que 200 => considérer comme réponse invalide |
125
|
|
|
throw new BpostInvalidResponseException('Empty or invalid XML body', 200); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
if ($xml->getName() === 'businessException') { |
129
|
|
|
$message = (string) ($xml->message ?? ''); |
130
|
|
|
$code = (int) ($xml->code ?? 0); |
131
|
|
|
throw new BpostApiBusinessException($message, $code); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
return $xml; |
135
|
|
|
} finally { |
136
|
|
|
if (is_resource($this->curl) || $this->curl instanceof CurlHandle) { |
137
|
|
|
curl_close($this->curl); |
138
|
|
|
} |
139
|
|
|
$this->curl = null; |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Generate the secret string for the Authorization header |
145
|
|
|
*/ |
146
|
|
|
private function getAuthorizationHeader(): string |
147
|
|
|
{ |
148
|
|
|
return base64_encode($this->accountId . ':' . $this->passPhrase); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* After this time the request will stop. |
153
|
|
|
*/ |
154
|
|
|
public function setTimeOut(int $seconds): void |
155
|
|
|
{ |
156
|
|
|
$this->timeOut = $seconds; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
public function getTimeOut(): int |
160
|
|
|
{ |
161
|
|
|
return $this->timeOut; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Get the useragent that will be used. |
166
|
|
|
* Our version will be prepended to yours. |
167
|
|
|
* It will look like: "PHP Bpost/<version> <your-user-agent>" |
168
|
|
|
* |
169
|
|
|
* @return string |
170
|
|
|
*/ |
171
|
|
|
public function getUserAgent(): string |
172
|
|
|
{ |
173
|
|
|
$extra = trim($this->userAgent); |
174
|
|
|
return sprintf('PHP Bpost Bpack247/%s%s', self::VERSION, $extra !== '' ? ' ' . $extra : ''); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Set your application user-agent, e.g. "MyApp/1.2.3" |
179
|
|
|
*/ |
180
|
|
|
public function setUserAgent(string $userAgent): void |
181
|
|
|
{ |
182
|
|
|
$this->userAgent = $userAgent; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
// Webservice methods |
186
|
|
|
/** |
187
|
|
|
* @throws BpostApiBusinessException |
188
|
|
|
* @throws BpostApiSystemException |
189
|
|
|
* @throws BpostCurlException |
190
|
|
|
* @throws BpostInvalidResponseException |
191
|
|
|
*/ |
192
|
|
|
public function createMember(Customer $customer): SimpleXMLElement |
193
|
|
|
{ |
194
|
|
|
$url = '/customer'; |
195
|
|
|
|
196
|
|
|
$document = new DOMDocument('1.0', 'utf-8'); |
197
|
|
|
$document->preserveWhiteSpace = false; |
198
|
|
|
$document->formatOutput = true; |
199
|
|
|
|
200
|
|
|
$document->appendChild($customer->toXML($document)); |
201
|
|
|
|
202
|
|
|
return $this->doCall($url, $document->saveXML(), 'POST'); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Retrieve member information |
207
|
|
|
* |
208
|
|
|
* @throws BpostApiBusinessException |
209
|
|
|
* @throws BpostApiSystemException |
210
|
|
|
* @throws BpostCurlException |
211
|
|
|
* @throws BpostInvalidResponseException |
212
|
|
|
* @throws Exception\XmlException\BpostXmlNoUserIdFoundException |
213
|
|
|
*/ |
214
|
|
|
public function getMember(string $id): Customer |
215
|
|
|
{ |
216
|
|
|
$xml = $this->doCall('/customer/' . $id); |
217
|
|
|
return Customer::createFromXML($xml); |
218
|
|
|
} |
219
|
|
|
} |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.