GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

SoapClient::__soapCall()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 13
nc 3
nop 5
crap 3
1
<?php
2
3
namespace WebservicesNl\Protocol\Soap\Client;
4
5
use GuzzleHttp\Client as HttpClient;
6
use GuzzleHttp\Exception\ClientException;
7
use GuzzleHttp\Exception\ConnectException;
8
use GuzzleHttp\Psr7\Request;
9
use Psr\Log\LoggerAwareTrait;
10
use Psr\Log\LoggerInterface;
11
use Psr\Log\LogLevel;
12
use WebservicesNl\Common\Endpoint\Manager;
13
use WebservicesNl\Common\Exception\ClientException as WsClientException;
14
use WebservicesNl\Common\Exception\Server\NoServerAvailableException;
15
use WebservicesNl\Connector\Client\ClientInterface;
16
use WebservicesNl\Protocol\Soap\Exception\ConverterInterface;
17
use WebservicesNl\Utils\ArrayUtils;
18
19
/**
20
 * PHP SoapClient with curl for HTTP transport.
21
 *
22
 * Extends the native PHP SoapClient. Adds PSR7 Client (Guzzle) for making the calls for better timeout management.
23
 * Also optional loggerInterface (middleware client) helps with tracing and debugging calls.
24
 */
25
class SoapClient extends \SoapClient implements ClientInterface
26
{
27
    use LoggerAwareTrait;
28
29
    const PROTOCOL = 'soap';
30
31
    /**
32
     * @var ConverterInterface
33
     */
34
    private $converter;
35
36
    /**
37
     * Guzzle Client for the SOAP calls.
38
     *
39
     * @var HttpClient
40
     */
41
    private $httpClient;
42
43
    /**
44
     * @var Manager
45
     */
46
    private $manager;
47
48
    /**
49
     * Soap settings.
50
     *
51
     * @var SoapSettings;
52
     */
53
    private $settings;
54
55
    /**
56
     * Content types for SOAP versions.
57
     *
58
     * @var array(string=>string)
59
     */
60
    protected static $versionToContentTypeMap = [
61
        SOAP_1_1 => 'text/xml; charset=utf-8',
62
        SOAP_1_2 => 'application/soap+xml; charset=utf-8',
63
    ];
64
65
    /**
66
     * SoapClient constructor.
67
     *
68
     * @param SoapSettings $settings
69
     * @param Manager      $manager
70
     * @param HttpClient   $client
71
     *
72
     * @throws NoServerAvailableException
73
     * @throws \InvalidArgumentException
74
     * @throws \Ddeboer\Transcoder\Exception\ExtensionMissingException
75
     * @throws \Ddeboer\Transcoder\Exception\UnsupportedEncodingException
76
     */
77 14
    public function __construct(SoapSettings $settings, Manager $manager, HttpClient $client)
78
    {
79 14
        $this->settings = $settings;
80 14
        $this->manager = $manager;
81 14
        $this->httpClient = $client;
82
83
        // throws an Exception when no endpoint is met
84 14
        $active = $this->manager->getActiveEndpoint();
85 14
        $this->log('Initial endpoint is ' . (string) $active->getUri(), LogLevel::INFO);
86
87
        // initiate the native PHP SoapClient for fetching all the WSDL stuff
88 14
        $soapSettings = ArrayUtils::toUnderscore($this->settings->toArray());
89 14
        parent::__construct((string) $active->getUri()->withQuery('wsdl'), array_filter($soapSettings));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class SoapClient as the method __construct() does only exist in the following sub-classes of SoapClient: WebservicesNl\Protocol\Soap\Client\SoapClient. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
90 14
    }
91
92
    /**
93
     * Triggers the SOAP request over HTTP.
94
     * Sent request by cURL instead of native SOAP request.
95
     *
96
     * @param string      $request
97
     * @param string      $location
98
     * @param string      $action
99
     * @param int         $version
100
     * @param string|null $one_way
101
     *
102
     * @return string The XML SOAP response.
103
     * @throws WsClientException
104
     * @throws NoServerAvailableException
105
     * @throws \InvalidArgumentException
106
     * @throws \SoapFault
107
     */
108 6
    public function __doRequest($request, $location, $action, $version, $one_way = null)
109
    {
110 6
        $active = $this->manager->getActiveEndpoint();
111
        try {
112 6
            $response = $this->doHttpRequest($request, (string) $active->getUri(), $action);
113 2
            $this->manager->updateLastConnected();
114
115 2
            return $response;
116
        } // when a connection failed try the next server, else return the response
117 5
        catch (ConnectException $exception) {
118 2
            $active->setStatus('error');
119 2
            $this->log('Endpoint is not responding', 'error', ['endpoint' => $active]);
120
121 2
            return $this->__doRequest($request, $location, $action, $version, $one_way);
122
        }
123
    }
124
125
    /**
126
     * Proxy function to SoapCall.
127
     *
128
     * @param array $args
129
     *
130
     * @return mixed
131
     * @throws \Exception
132
     * @throws \SoapFault
133
     */
134 1
    public function call(array $args = [])
135
    {
136 1
        $args += ['functionName' => ''];
137
138 1
        $functionName = $args['functionName'];
139 1
        unset($args['functionName']);
140
141 1
        return $this->__soapCall($functionName, $args);
142
    }
143
144
    /**
145
     * Determine the SOAPHeaders for given version.
146
     *
147
     * @param string $action
148
     *
149
     * @return array
150
     */
151 6
    private function createHeaders($action)
152
    {
153 6
        $headers = ['Content-Type' => self::$versionToContentTypeMap[$this->settings->getSoapVersion()]];
154 6
        if ($this->settings->getSoapVersion() === SOAP_1_1) {
155 6
            $headers['SOAPAction'] = $action;
156 6
        }
157
158 6
        return $headers;
159
    }
160
161
    /**
162
     * Determines methods.
163
     * For Soap it's either GET or POST.
164
     *
165
     * @param string|null $request
166
     *
167
     * @return string
168
     */
169 6
    private static function determineMethod($request)
170
    {
171 6
        return ($request === null || (is_string($request) && trim($request) === '')) ? 'GET' : 'POST';
172
    }
173
174
    /**
175
     * Http version of doRequest.
176
     *
177
     * @param string $requestBody
178
     * @param string $location
179
     * @param string $action
180
     *
181
     * @return string
182
     * @throws WsClientException
183
     * @throws \SoapFault
184
     * @throws \InvalidArgumentException
185
     * @todo move exception handler to middleware, find solution error suppressing
186
     */
187 6
    private function doHttpRequest($requestBody, $location, $action)
188
    {
189
        // get soap details for request
190 6
        $headers = $this->createHeaders($action);
191 6
        $method = self::determineMethod($requestBody);
192
193
        // try to fire a request and return it
194
        try {
195 6
            $requestObj = new Request($method, $location, $headers, $requestBody);
196 6
            $response = $this->httpClient->send($requestObj);
197
            // Throw a SoapFault if the response was received, but it can't be read into valid XML
198 4
            if ($response->getStatusCode() > 399 && @simplexml_load_string((string) $response->getBody()) === false) {
199 2
                throw new \SoapFault('Server', 'Invalid SoapResponse');
200
            }
201
202 2
            return (string) $response->getBody();
203 5
        } catch (ClientException $e) {
204
            // if a client exception is thrown, the guzzle instance, is configured to throw exceptions
205 1
            $code = ($e->getResponse() !== null) ? 'Server' : 'Client.Input';
206 1
            throw new \SoapFault('Client', $e->getMessage(), null, $code);
207
        }
208
    }
209
210
    /**
211
     * @return ConverterInterface
212
     */
213 5
    public function getConverter()
214
    {
215 5
        return $this->converter;
216
    }
217
218
    /**
219
     * @param ConverterInterface $converter
220
     */
221 9
    public function setConverter($converter)
222
    {
223 9
        $this->converter = $converter;
224 9
    }
225
226
    /**
227
     * @return HttpClient
228
     */
229 1
    public function getHttpClient()
230
    {
231 1
        return $this->httpClient;
232
    }
233
234
    /**
235
     * Return this connector over which a connection is established.
236
     *
237
     * @return string
238
     */
239 4
    public function getProtocolName()
240
    {
241 4
        return static::PROTOCOL;
242
    }
243
244
    /**
245
     * Log message.
246
     *
247
     * @param string $message
248
     * @param int    $level
249
     * @param array  $context
250
     */
251 14
    public function log($message, $level, array $context = [])
252
    {
253 14
        if ($this->logger instanceof LoggerInterface) {
254 7
            $this->logger->log($level, $message, $context);
255 7
        }
256 14
    }
257
258
    /**
259
     * Prepares the soapCall.
260
     *
261
     * @param string     $function_name
262
     * @param array      $arguments
263
     * @param array      $options
264
     * @param array      $input_headers
265
     * @param array|null $output_headers
266
     *
267
     * @return mixed
268
     * @throws \Exception|\SoapFault
269
     */
270 7
    public function __soapCall(
271
        $function_name,
272
        $arguments = [],
273
        $options = [],
274
        $input_headers = [],
275
        &$output_headers = null
276
    ) {
277 7
        $this->log('Called:' . $function_name, LogLevel::INFO, ['arguments' => $arguments]);
278
279
        try {
280 7
            return parent::__soapCall($function_name, $arguments, $options, $input_headers, $output_headers);
281 5
        } catch (\SoapFault $fault) {
282 4
            if ($this->getConverter() !== null) {
283 2
                throw $this->getConverter()->convertToException($fault);
284
            }
285 2
            throw $fault;
286
        }
287
    }
288
}
289