Passed
Push — develop ( de55f2...b27c00 )
by Mikaël
48:31 queued 02:41
created

AbstractSoapClientBase::setSoapClient()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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