Passed
Pull Request — develop (#46)
by
unknown
47:40
created

AbstractSoapClientBase::setHttpHeader()   C

Complexity

Conditions 15
Paths 7

Size

Total Lines 36
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 15.8857

Importance

Changes 4
Bugs 1 Features 0
Metric Value
cc 15
eloc 19
c 4
b 1
f 0
nc 7
nop 2
dl 0
loc 36
ccs 16
cts 19
cp 0.8421
crap 15.8857
rs 5.9166

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