Passed
Push — develop ( 7d53db...1113ca )
by Mikaël
06:37 queued 05:04
created

AbstractSoapClientBase::setHttpHeader()   C

Complexity

Conditions 16
Paths 13

Size

Total Lines 52
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 16.3934

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 16
eloc 27
c 3
b 0
f 0
nc 13
nop 2
dl 0
loc 52
ccs 23
cts 26
cp 0.8846
crap 16.3934
rs 5.5666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 59
    public function __construct(array $wsdlOptions = [])
39
    {
40 59
        $this->initSoapClient($wsdlOptions);
41
    }
42
43 51
    public function getSoapClient(): ?SoapClient
44
    {
45 51
        return $this->soapClient;
46
    }
47
48 47
    public function setSoapClient(SoapClient $soapClient): SoapClient
49
    {
50 47
        return ($this->soapClient = $soapClient);
51
    }
52
53 59
    public function initSoapClient(array $options): void
54
    {
55 59
        $wsdlOptions = [];
56 59
        $defaultWsdlOptions = static::getDefaultWsdlOptions();
57 59
        foreach ($defaultWsdlOptions as $optionName => $optionValue) {
58 59
            if (array_key_exists($optionName, $options) && !is_null($options[$optionName])) {
59 51
                $wsdlOptions[str_replace(self::OPTION_PREFIX, '', $optionName)] = $options[$optionName];
60 59
            } elseif (!is_null($optionValue)) {
61 59
                $wsdlOptions[str_replace(self::OPTION_PREFIX, '', $optionName)] = $optionValue;
62
            }
63
        }
64 59
        if (self::canInstantiateSoapClientWithOptions($wsdlOptions)) {
65 47
            $wsdlUrl = null;
66 47
            if (array_key_exists(str_replace(self::OPTION_PREFIX, '', self::WSDL_URL), $wsdlOptions)) {
67 43
                $wsdlUrl = $wsdlOptions[str_replace(self::OPTION_PREFIX, '', self::WSDL_URL)];
68 43
                unset($wsdlOptions[str_replace(self::OPTION_PREFIX, '', self::WSDL_URL)]);
69
            }
70 47
            $soapClientClassName = $this->getSoapClientClassName();
71 47
            $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 59
    protected static function canInstantiateSoapClientWithOptions(array $wsdlOptions): bool
83
    {
84
        return (
85 59
            array_key_exists(str_replace(self::OPTION_PREFIX, '', self::WSDL_URL), $wsdlOptions) ||
86
            (
87 16
                array_key_exists(str_replace(self::OPTION_PREFIX, '', self::WSDL_URI), $wsdlOptions) &&
88 59
                array_key_exists(str_replace(self::OPTION_PREFIX, '', self::WSDL_LOCATION), $wsdlOptions)
89
            )
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
     * @param string|null $soapClientClassName
99
     * @return string
100
     */
101 51
    public function getSoapClientClassName(?string $soapClientClassName = null): string
102
    {
103 51
        $className = self::DEFAULT_SOAP_CLIENT_CLASS;
104 51
        if (!empty($soapClientClassName) && is_subclass_of($soapClientClassName, SoapClient::class)) {
105 51
            $className = $soapClientClassName;
106
        }
107
108 51
        return $className;
109
    }
110
111
    /**
112
     * Method returning all default SoapClient options values
113
     * @return array
114
     */
115 59
    public static function getDefaultWsdlOptions(): array
116
    {
117
        return [
118 59
            self::WSDL_AUTHENTICATION => null,
119
            self::WSDL_CACHE_WSDL => WSDL_CACHE_NONE,
120
            self::WSDL_CLASSMAP => null,
121
            self::WSDL_COMPRESSION => null,
122
            self::WSDL_CONNECTION_TIMEOUT => null,
123
            self::WSDL_ENCODING => null,
124
            self::WSDL_EXCEPTIONS => true,
125 59
            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 1
    public function setLocation(string $location): self
154
    {
155 1
        if ($this->getSoapClient() instanceof SoapClient) {
156 1
            $this->getSoapClient()->__setLocation($location);
157
        }
158
159 1
        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 3
    public function setSoapHeader(string $namespace, string $name, $data, bool $mustUnderstand = false, ?string $actor = null): self
273
    {
274 3
        if ($this->getSoapClient()) {
275 3
            $defaultHeaders = (isset($this->getSoapClient()->__default_headers) && is_array($this->getSoapClient()->__default_headers)) ? $this->getSoapClient()->__default_headers : [];
276 3
            foreach ($defaultHeaders as $index => $soapHeader) {
277 1
                if ($soapHeader->name === $name) {
278 1
                    unset($defaultHeaders[$index]);
279 1
                    break;
280
                }
281
            }
282 3
            $this->getSoapClient()->__setSoapheaders(null);
283 3
            if (!empty($actor)) {
284 1
                array_push($defaultHeaders, new SoapHeader($namespace, $name, $data, $mustUnderstand, $actor));
285
            } else {
286 2
                array_push($defaultHeaders, new SoapHeader($namespace, $name, $data, $mustUnderstand));
287
            }
288 3
            $this->getSoapClient()->__setSoapheaders($defaultHeaders);
289
        }
290
291 3
        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 6
                $options = [];
309 6
                $options['http'] = [];
310 6
                $options['http']['header'] = '';
311
            } else {
312 10
                $options = stream_context_get_options($streamContext);
313 10
                if (!array_key_exists('http', $options) || !is_array($options['http'])) {
314
                    $options['http'] = [];
315
                    $options['http']['header'] = '';
316 10
                } 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 10
                        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 6
                    $state = is_resource($this->getSoapClient()->_stream_context = stream_context_create($options));
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 10
                    $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