WsdlToPhp /
PackageBase
| 1 | <?php |
||
| 2 | |||
| 3 | declare(strict_types=1); |
||
| 4 | |||
| 5 | namespace WsdlToPhp\PackageBase; |
||
| 6 | |||
| 7 | use DOMDocument; |
||
| 8 | use SoapClient; |
||
| 9 | use SoapFault; |
||
| 10 | use SoapHeader; |
||
| 11 | |||
| 12 | abstract class AbstractSoapClientBase implements SoapClientInterface |
||
| 13 | { |
||
| 14 | /** |
||
| 15 | * SoapClient called to communicate with the actual SOAP Service |
||
| 16 | * @var SoapClient|null |
||
| 17 | */ |
||
| 18 | private ?SoapClient $soapClient = null; |
||
| 19 | |||
| 20 | /** |
||
| 21 | * Contains Soap call result |
||
| 22 | * @var mixed |
||
| 23 | */ |
||
| 24 | private $result; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * Contains last errors |
||
| 28 | * @var array |
||
| 29 | */ |
||
| 30 | private array $lastError = []; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * Contains output headers |
||
| 34 | * @var array |
||
| 35 | */ |
||
| 36 | protected array $outputHeaders = []; |
||
| 37 | |||
| 38 | 64 | public function __construct(array $wsdlOptions = []) |
|
| 39 | { |
||
| 40 | 64 | $this->initSoapClient($wsdlOptions); |
|
| 41 | } |
||
| 42 | |||
| 43 | 56 | public function getSoapClient(): ?SoapClient |
|
| 44 | { |
||
| 45 | 56 | return $this->soapClient; |
|
| 46 | } |
||
| 47 | |||
| 48 | 52 | public function setSoapClient(SoapClient $soapClient): SoapClient |
|
| 49 | { |
||
| 50 | 52 | return ($this->soapClient = $soapClient); |
|
| 51 | } |
||
| 52 | |||
| 53 | 64 | public function initSoapClient(array $options): void |
|
| 54 | { |
||
| 55 | 64 | $wsdlOptions = []; |
|
| 56 | 64 | $defaultWsdlOptions = static::getDefaultWsdlOptions(); |
|
| 57 | 64 | foreach ($defaultWsdlOptions as $optionName => $optionValue) { |
|
| 58 | 64 | if (array_key_exists($optionName, $options) && !is_null($options[$optionName])) { |
|
| 59 | 56 | $wsdlOptions[str_replace(self::OPTION_PREFIX, '', $optionName)] = $options[$optionName]; |
|
| 60 | 64 | } elseif (!is_null($optionValue)) { |
|
| 61 | 64 | $wsdlOptions[str_replace(self::OPTION_PREFIX, '', $optionName)] = $optionValue; |
|
| 62 | } |
||
| 63 | } |
||
| 64 | 64 | if (self::canInstantiateSoapClientWithOptions($wsdlOptions)) { |
|
| 65 | 52 | $wsdlUrl = null; |
|
| 66 | 52 | if (array_key_exists(str_replace(self::OPTION_PREFIX, '', self::WSDL_URL), $wsdlOptions)) { |
|
| 67 | 48 | $wsdlUrl = $wsdlOptions[str_replace(self::OPTION_PREFIX, '', self::WSDL_URL)]; |
|
| 68 | 48 | unset($wsdlOptions[str_replace(self::OPTION_PREFIX, '', self::WSDL_URL)]); |
|
| 69 | } |
||
| 70 | 52 | $soapClientClassName = $this->getSoapClientClassName(); |
|
| 71 | 52 | $this->setSoapClient(new $soapClientClassName($wsdlUrl, $wsdlOptions)); |
|
| 72 | } |
||
| 73 | } |
||
| 74 | |||
| 75 | /** |
||
| 76 | * Checks if the provided options are sufficient to instantiate a SoapClient: |
||
| 77 | * - WSDL-mode : only the WSDL is required |
||
| 78 | * - non-WSDL-mode : URI and LOCATION are required, WSDL url can be empty then |
||
| 79 | * @param array $wsdlOptions |
||
| 80 | * @return bool |
||
| 81 | */ |
||
| 82 | 64 | protected static function canInstantiateSoapClientWithOptions(array $wsdlOptions): bool |
|
| 83 | { |
||
| 84 | 64 | return ( |
|
| 85 | 64 | array_key_exists(str_replace(self::OPTION_PREFIX, '', self::WSDL_URL), $wsdlOptions) || |
|
| 86 | 64 | ( |
|
| 87 | 64 | array_key_exists(str_replace(self::OPTION_PREFIX, '', self::WSDL_URI), $wsdlOptions) && |
|
| 88 | 64 | array_key_exists(str_replace(self::OPTION_PREFIX, '', self::WSDL_LOCATION), $wsdlOptions) |
|
| 89 | 64 | ) |
|
| 90 | 64 | ); |
|
| 91 | } |
||
| 92 | |||
| 93 | /** |
||
| 94 | * Returns the SoapClient class name to use to create the instance of the SoapClient. |
||
| 95 | * Be sure that this class inherits from the native PHP SoapClient class and this class has been loaded or can be loaded. |
||
| 96 | * The goal is to allow the override of the SoapClient without having to modify this generated class. |
||
| 97 | * Then the overriding SoapClient class can override for example the SoapClient::__doRequest() method if it is needed. |
||
| 98 | * @param string|null $soapClientClassName |
||
| 99 | * @return string |
||
| 100 | */ |
||
| 101 | 56 | public function getSoapClientClassName(?string $soapClientClassName = null): string |
|
| 102 | { |
||
| 103 | 56 | $className = static::DEFAULT_SOAP_CLIENT_CLASS; |
|
| 104 | 56 | if (!empty($soapClientClassName) && is_subclass_of($soapClientClassName, SoapClient::class)) { |
|
| 105 | 56 | $className = $soapClientClassName; |
|
| 106 | } |
||
| 107 | |||
| 108 | 56 | return $className; |
|
| 109 | } |
||
| 110 | |||
| 111 | /** |
||
| 112 | * Method returning all default SoapClient options values |
||
| 113 | * @return array |
||
| 114 | */ |
||
| 115 | 64 | public static function getDefaultWsdlOptions(): array |
|
| 116 | { |
||
| 117 | 64 | return [ |
|
| 118 | 64 | self::WSDL_AUTHENTICATION => null, |
|
| 119 | 64 | self::WSDL_CACHE_WSDL => WSDL_CACHE_NONE, |
|
| 120 | 64 | self::WSDL_CLASSMAP => null, |
|
| 121 | 64 | self::WSDL_COMPRESSION => null, |
|
| 122 | 64 | self::WSDL_CONNECTION_TIMEOUT => null, |
|
| 123 | 64 | self::WSDL_ENCODING => null, |
|
| 124 | 64 | self::WSDL_EXCEPTIONS => true, |
|
| 125 | 64 | self::WSDL_FEATURES => SOAP_SINGLE_ELEMENT_ARRAYS | SOAP_USE_XSI_ARRAY_TYPE, |
|
| 126 | 64 | self::WSDL_LOCAL_CERT => null, |
|
| 127 | 64 | self::WSDL_LOCATION => null, |
|
| 128 | 64 | self::WSDL_LOGIN => null, |
|
| 129 | 64 | self::WSDL_PASSPHRASE => null, |
|
| 130 | 64 | self::WSDL_PASSWORD => null, |
|
| 131 | 64 | self::WSDL_PROXY_HOST => null, |
|
| 132 | 64 | self::WSDL_PROXY_LOGIN => null, |
|
| 133 | 64 | self::WSDL_PROXY_PASSWORD => null, |
|
| 134 | 64 | self::WSDL_PROXY_PORT => null, |
|
| 135 | 64 | self::WSDL_SOAP_VERSION => null, |
|
| 136 | 64 | self::WSDL_SSL_METHOD => null, |
|
| 137 | 64 | self::WSDL_STREAM_CONTEXT => null, |
|
| 138 | 64 | self::WSDL_STYLE => null, |
|
| 139 | 64 | self::WSDL_TRACE => true, |
|
| 140 | 64 | self::WSDL_TYPEMAP => null, |
|
| 141 | 64 | self::WSDL_URL => null, |
|
| 142 | 64 | self::WSDL_URI => null, |
|
| 143 | 64 | self::WSDL_USE => null, |
|
| 144 | 64 | self::WSDL_USER_AGENT => null, |
|
| 145 | 64 | ]; |
|
| 146 | } |
||
| 147 | |||
| 148 | /** |
||
| 149 | * Allows to set the SoapClient location to call |
||
| 150 | * @param string $location |
||
| 151 | * @return AbstractSoapClientBase |
||
| 152 | */ |
||
| 153 | 2 | public function setLocation(string $location): self |
|
| 154 | { |
||
| 155 | 2 | if ($this->getSoapClient() instanceof SoapClient) { |
|
| 156 | 2 | $this->getSoapClient()->__setLocation($location); |
|
| 157 | } |
||
| 158 | |||
| 159 | 2 | return $this; |
|
| 160 | } |
||
| 161 | |||
| 162 | /** |
||
| 163 | * Returns the last request content as a DOMDocument or as a formatted XML String |
||
| 164 | * @param bool $asDomDocument |
||
| 165 | * @return DOMDocument|string|null |
||
| 166 | */ |
||
| 167 | 4 | public function getLastRequest(bool $asDomDocument = false) |
|
| 168 | { |
||
| 169 | 4 | return $this->getLastXml('__getLastRequest', $asDomDocument); |
|
| 170 | } |
||
| 171 | |||
| 172 | /** |
||
| 173 | * Returns the last response content as a DOMDocument or as a formatted XML String |
||
| 174 | * @param bool $asDomDocument |
||
| 175 | * @return DOMDocument|string|null |
||
| 176 | */ |
||
| 177 | 4 | public function getLastResponse(bool $asDomDocument = false) |
|
| 178 | { |
||
| 179 | 4 | return $this->getLastXml('__getLastResponse', $asDomDocument); |
|
| 180 | } |
||
| 181 | |||
| 182 | /** |
||
| 183 | * @param string $method |
||
| 184 | * @param bool $asDomDocument |
||
| 185 | * @return DOMDocument|string|null |
||
| 186 | */ |
||
| 187 | 8 | protected function getLastXml(string $method, bool $asDomDocument = false) |
|
| 188 | { |
||
| 189 | 8 | $xml = null; |
|
| 190 | 8 | if ($this->getSoapClient() instanceof SoapClient) { |
|
| 191 | 8 | $xml = static::getFormattedXml($this->getSoapClient()->$method(), $asDomDocument); |
|
| 192 | } |
||
| 193 | |||
| 194 | 8 | return $xml; |
|
| 195 | } |
||
| 196 | |||
| 197 | /** |
||
| 198 | * Returns the last request headers used by the SoapClient object as the original value or an array |
||
| 199 | * @param bool $asArray allows to get the headers in an associative array |
||
| 200 | * @return null|string|string[] |
||
| 201 | */ |
||
| 202 | 4 | public function getLastRequestHeaders(bool $asArray = false) |
|
| 203 | { |
||
| 204 | 4 | return $this->getLastHeaders('__getLastRequestHeaders', $asArray); |
|
| 205 | } |
||
| 206 | |||
| 207 | /** |
||
| 208 | * Returns the last response headers used by the SoapClient object as the original value or an array |
||
| 209 | * @param bool $asArray allows to get the headers in an associative array |
||
| 210 | * @return null|string|string[] |
||
| 211 | */ |
||
| 212 | 4 | public function getLastResponseHeaders(bool $asArray = false) |
|
| 213 | { |
||
| 214 | 4 | return $this->getLastHeaders('__getLastResponseHeaders', $asArray); |
|
| 215 | } |
||
| 216 | |||
| 217 | /** |
||
| 218 | * @param string $method |
||
| 219 | * @param bool $asArray allows to get the headers in an associative array |
||
| 220 | * @return null|string|string[] |
||
| 221 | */ |
||
| 222 | 8 | protected function getLastHeaders(string $method, bool $asArray) |
|
| 223 | { |
||
| 224 | 8 | $headers = $this->getSoapClient() instanceof SoapClient ? $this->getSoapClient()->$method() : null; |
|
| 225 | 8 | if (is_string($headers) && $asArray) { |
|
| 226 | 4 | return static::convertStringHeadersToArray($headers); |
|
| 227 | } |
||
| 228 | |||
| 229 | 4 | return $headers; |
|
| 230 | } |
||
| 231 | |||
| 232 | /** |
||
| 233 | * Returns a XML string content as a DOMDocument or as a formatted XML string |
||
| 234 | * @param string|null $string |
||
| 235 | * @param bool $asDomDocument |
||
| 236 | * @return DOMDocument|string|null |
||
| 237 | */ |
||
| 238 | 8 | public static function getFormattedXml(?string $string, bool $asDomDocument = false) |
|
| 239 | { |
||
| 240 | 8 | return Utils::getFormattedXml($string, $asDomDocument); |
|
| 241 | } |
||
| 242 | |||
| 243 | /** |
||
| 244 | * Returns an associative array between the headers name and their respective values |
||
| 245 | * @param string $headers |
||
| 246 | * @return string[] |
||
| 247 | */ |
||
| 248 | 4 | public static function convertStringHeadersToArray(string $headers): array |
|
| 249 | { |
||
| 250 | 4 | $lines = explode("\r\n", $headers); |
|
| 251 | 4 | $headers = []; |
|
| 252 | 4 | foreach ($lines as $line) { |
|
| 253 | 4 | if (strpos($line, ':')) { |
|
| 254 | 4 | $headerParts = explode(':', $line); |
|
| 255 | 4 | $headers[$headerParts[0]] = trim(implode(':', array_slice($headerParts, 1))); |
|
| 256 | } |
||
| 257 | } |
||
| 258 | |||
| 259 | 4 | return $headers; |
|
| 260 | } |
||
| 261 | |||
| 262 | /** |
||
| 263 | * Sets a SoapHeader to send |
||
| 264 | * For more information, please read the online documentation on {@link http://www.php.net/manual/en/class.soapheader.php} |
||
| 265 | * @param string $namespace SoapHeader namespace |
||
| 266 | * @param string $name SoapHeader name |
||
| 267 | * @param mixed $data SoapHeader data |
||
| 268 | * @param bool $mustUnderstand |
||
| 269 | * @param string|null $actor |
||
| 270 | * @return AbstractSoapClientBase |
||
| 271 | */ |
||
| 272 | 6 | public function setSoapHeader(string $namespace, string $name, $data, bool $mustUnderstand = false, ?string $actor = null): self |
|
| 273 | { |
||
| 274 | 6 | if ($this->getSoapClient()) { |
|
| 275 | 6 | $defaultHeaders = (isset($this->getSoapClient()->__default_headers) && is_array($this->getSoapClient()->__default_headers)) ? $this->getSoapClient()->__default_headers : []; |
|
| 276 | 6 | foreach ($defaultHeaders as $index => $soapHeader) { |
|
| 277 | 2 | if ($soapHeader->name === $name) { |
|
| 278 | 2 | unset($defaultHeaders[$index]); |
|
| 279 | 2 | break; |
|
| 280 | } |
||
| 281 | } |
||
| 282 | 6 | $this->getSoapClient()->__setSoapheaders(null); |
|
| 283 | 6 | if (!empty($actor)) { |
|
| 284 | 2 | array_push($defaultHeaders, new SoapHeader($namespace, $name, $data, $mustUnderstand, $actor)); |
|
| 285 | } else { |
||
| 286 | 4 | array_push($defaultHeaders, new SoapHeader($namespace, $name, $data, $mustUnderstand)); |
|
| 287 | } |
||
| 288 | 6 | $this->getSoapClient()->__setSoapheaders($defaultHeaders); |
|
| 289 | } |
||
| 290 | |||
| 291 | 6 | return $this; |
|
| 292 | } |
||
| 293 | |||
| 294 | /** |
||
| 295 | * Sets the SoapClient Stream context HTTP Header name according to its value |
||
| 296 | * If a context already exists, it tries to modify it |
||
| 297 | * It the context does not exist, it then creates it with the header name and its value |
||
| 298 | * @param string $headerName |
||
| 299 | * @param mixed $headerValue |
||
| 300 | * @return bool |
||
| 301 | */ |
||
| 302 | 12 | public function setHttpHeader(string $headerName, $headerValue): bool |
|
| 303 | { |
||
| 304 | 12 | $state = false; |
|
| 305 | 12 | if ($this->getSoapClient() && !empty($headerName)) { |
|
| 306 | 12 | $streamContext = $this->getStreamContext(); |
|
| 307 | 12 | if ($streamContext === null) { |
|
| 308 | $options = []; |
||
| 309 | $options['http'] = []; |
||
| 310 | $options['http']['header'] = ''; |
||
| 311 | } else { |
||
| 312 | 12 | $options = stream_context_get_options($streamContext); |
|
| 313 | 12 | if (!array_key_exists('http', $options) || !is_array($options['http'])) { |
|
| 314 | $options['http'] = []; |
||
| 315 | $options['http']['header'] = ''; |
||
| 316 | 12 | } elseif (!array_key_exists('header', $options['http'])) { |
|
| 317 | $options['http']['header'] = ''; |
||
| 318 | } |
||
| 319 | } |
||
| 320 | 12 | if (count($options) && array_key_exists('http', $options) && is_array($options['http']) && array_key_exists('header', $options['http']) && is_string($options['http']['header'])) { |
|
| 321 | 12 | $lines = explode("\r\n", $options['http']['header']); |
|
| 322 | /** |
||
| 323 | * Ensure there is only one header entry for this header name |
||
| 324 | */ |
||
| 325 | 12 | $newLines = []; |
|
| 326 | 12 | foreach ($lines as $line) { |
|
| 327 | 12 | if (!empty($line) && strpos($line, $headerName) === false) { |
|
| 328 | 12 | array_push($newLines, $line); |
|
| 329 | } |
||
| 330 | } |
||
| 331 | /** |
||
| 332 | * Add new header entry |
||
| 333 | */ |
||
| 334 | 12 | array_push($newLines, "$headerName: $headerValue"); |
|
| 335 | /** |
||
| 336 | * Set the context http header option |
||
| 337 | */ |
||
| 338 | 12 | $options['http']['header'] = implode("\r\n", $newLines); |
|
| 339 | /** |
||
| 340 | * Create context if it does not exist |
||
| 341 | */ |
||
| 342 | 12 | if ($streamContext === null) { |
|
| 343 | $state = is_resource($this->getSoapClient()->_stream_context = stream_context_create($options)); |
||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 344 | } else { |
||
| 345 | /** |
||
| 346 | * Set the new context http header option |
||
| 347 | */ |
||
| 348 | 12 | $state = stream_context_set_option($this->getSoapClient()->_stream_context, 'http', 'header', $options['http']['header']); |
|
| 349 | } |
||
| 350 | } |
||
| 351 | } |
||
| 352 | |||
| 353 | 12 | return $state; |
|
| 354 | } |
||
| 355 | |||
| 356 | /** |
||
| 357 | * Returns current SoapClient::_stream_context resource or null |
||
| 358 | * @return resource|null |
||
| 359 | */ |
||
| 360 | 14 | public function getStreamContext() |
|
| 361 | { |
||
| 362 | 14 | return ($this->getSoapClient() && isset($this->getSoapClient()->_stream_context) && is_resource($this->getSoapClient()->_stream_context)) ? $this->getSoapClient()->_stream_context : null; |
|
| 363 | } |
||
| 364 | |||
| 365 | /** |
||
| 366 | * Returns current SoapClient::_stream_context resource options or empty array |
||
| 367 | * @return array |
||
| 368 | */ |
||
| 369 | 2 | public function getStreamContextOptions(): array |
|
| 370 | { |
||
| 371 | 2 | $options = []; |
|
| 372 | 2 | $context = $this->getStreamContext(); |
|
| 373 | 2 | if ($context !== null) { |
|
| 374 | 2 | $options = stream_context_get_options($context); |
|
| 375 | 2 | if (isset($options['http']['header']) && is_string($options['http']['header'])) { |
|
| 376 | 2 | $options['http']['header'] = array_filter(array_map('trim', explode(PHP_EOL, $options['http']['header']))); |
|
| 377 | } |
||
| 378 | } |
||
| 379 | |||
| 380 | 2 | return $options; |
|
| 381 | } |
||
| 382 | |||
| 383 | /** |
||
| 384 | * Method returning last errors occurred during the calls |
||
| 385 | * @return array |
||
| 386 | */ |
||
| 387 | 2 | public function getLastError(): array |
|
| 388 | { |
||
| 389 | 2 | return $this->lastError; |
|
| 390 | } |
||
| 391 | |||
| 392 | /** |
||
| 393 | * Method saving the last error returned by the SoapClient |
||
| 394 | * @param string $methodName the method called when the error occurred |
||
| 395 | * @param SoapFault $soapFault the fault |
||
| 396 | * @return AbstractSoapClientBase |
||
| 397 | */ |
||
| 398 | 6 | public function saveLastError(string $methodName, SoapFault $soapFault): SoapClientInterface |
|
| 399 | { |
||
| 400 | 6 | $this->lastError[$methodName] = $soapFault; |
|
| 401 | |||
| 402 | 6 | return $this; |
|
| 403 | } |
||
| 404 | |||
| 405 | /** |
||
| 406 | * Method getting the last error for a certain method |
||
| 407 | * @param string $methodName method name to get error from |
||
| 408 | * @return SoapFault|null |
||
| 409 | */ |
||
| 410 | 2 | public function getLastErrorForMethod(string $methodName): ?SoapFault |
|
| 411 | { |
||
| 412 | 2 | return array_key_exists($methodName, $this->lastError) ? $this->lastError[$methodName] : null; |
|
| 413 | } |
||
| 414 | |||
| 415 | /** |
||
| 416 | * Method returning current result from Soap call |
||
| 417 | * @return mixed |
||
| 418 | */ |
||
| 419 | 2 | public function getResult() |
|
| 420 | { |
||
| 421 | 2 | return $this->result; |
|
| 422 | } |
||
| 423 | |||
| 424 | /** |
||
| 425 | * Method setting current result from Soap call |
||
| 426 | * @param mixed $result |
||
| 427 | * @return AbstractSoapClientBase |
||
| 428 | */ |
||
| 429 | 6 | public function setResult($result): SoapClientInterface |
|
| 430 | { |
||
| 431 | 6 | $this->result = $result; |
|
| 432 | |||
| 433 | 6 | return $this; |
|
| 434 | } |
||
| 435 | |||
| 436 | /** |
||
| 437 | * @return array |
||
| 438 | */ |
||
| 439 | 2 | public function getOutputHeaders(): array |
|
| 440 | { |
||
| 441 | 2 | return $this->outputHeaders; |
|
| 442 | } |
||
| 443 | |||
| 444 | /** |
||
| 445 | * Default string representation of current object. Don't want to expose any sensible data |
||
| 446 | * @return string |
||
| 447 | */ |
||
| 448 | 2 | public function __toString(): string |
|
| 449 | { |
||
| 450 | 2 | return get_called_class(); |
|
| 451 | } |
||
| 452 | } |
||
| 453 |