Passed
Pull Request — develop (#50)
by
unknown
59:22 queued 14:05
created

AbstractSoapClientBase::setHttpHeader()   C

Complexity

Conditions 15
Paths 7

Size

Total Lines 36
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 16.0422

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