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.
Passed
Branch scrutinizer-fixes (60c229)
by Thijs
02:43
created

SoapClient::__soapCall()   A

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 1
Bugs 0 Features 0
Metric Value
dl 0
loc 18
ccs 7
cts 7
cp 1
rs 9.4285
c 1
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
18
/**
19
 * PHP SoapClient with curl for HTTP transport.
20
 *
21
 * Extends the native PHP SoapClient. Adds PSR7 Client (Guzzle) for making the calls for better timeout management.
22
 * Also optional loggerInterface (middleware client) helps with tracing and debugging calls.
23
 */
24
class SoapClient extends \SoapClient implements ClientInterface
25
{
26
    use LoggerAwareTrait;
27
28
    const PROTOCOL = 'soap';
29
    
30
    /**
31
     * @var ConverterInterface
32
     */
33
    private $converter;
34
    
35
    /**
36
     * Guzzle Client for the SOAP calls.
37
     *
38
     * @var HttpClient
39
     */
40
    private $httpClient;
41
42
    /**
43
     * @var Manager
44
     */
45
    private $manager;
46
47
    /**
48
     * Soap settings.
49
     *
50
     * @var SoapSettings;
51
     */
52
    private $settings;
53
    
54
    /**
55
     * Content types for SOAP versions.
56
     *
57
     * @var array(string=>string)
58
     */
59
    protected static $versionToContentTypeMap = [
60
        SOAP_1_1 => 'text/xml; charset=utf-8',
61
        SOAP_1_2 => 'application/soap+xml; charset=utf-8',
62
    ];
63
64
    /**
65
     * SoapClient constructor.
66
     *
67
     * @param SoapSettings $settings
68
     * @param Manager      $manager
69
     * @param HttpClient   $client
70
     *
71
     * @throws NoServerAvailableException
72
     * @throws \InvalidArgumentException
73
     */
74 13
    public function __construct(SoapSettings $settings, Manager $manager, HttpClient $client)
75
    {
76 13
        $this->settings = $settings;
77 13
        $this->manager = $manager;
78 13
        $this->httpClient = $client;
79
80
        // throws an Exception when no endpoint is met
81 13
        $active = $this->manager->getActiveEndpoint();
82 13
        $this->log('Initial endpoint is ' . (string)$active->getUri(), LogLevel::INFO);
83
84
        // initiate the native PHP SoapClient for fetching all the WSDL stuff
85 13
        parent::__construct((string)$active->getUri()->withQuery('wsdl'), $this->settings->toArray());
1 ignored issue
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...
86 13
    }
87
88
    /**
89
     * Triggers the SOAP request over HTTP.
90
     * Sent request by cURL instead of native SOAP request.
91
     *
92
     * @param string      $request
93
     * @param string      $location
94
     * @param string      $action
95
     * @param int         $version
96
     * @param string|null $one_way
97
     *
98
     * @return string The XML SOAP response.
99
     * @throws WsClientException
100
     * @throws NoServerAvailableException
101
     * @throws \InvalidArgumentException
102
     * @throws \SoapFault
103
     */
104 6
    public function __doRequest($request, $location, $action, $version, $one_way = null)
105
    {
106 6
        $active = $this->manager->getActiveEndpoint();
107
        try {
108 6
            $response = $this->doHttpRequest($request, (string)$active->getUri(), $action);
109 2
            $this->manager->updateLastConnected();
110
111 2
            return $response;
112
        } // when a connection failed try the next server, else return the response
113 5
        catch (ConnectException $exception) {
114 2
            $active->setStatus('error');
115 2
            $this->log('Endpoint is not responding', 'error', ['endpoint' => $active]);
116
117 2
            return $this->__doRequest($request, $location, $action, $version, $one_way);
118
        }
119
    }
120
121
    /**
122
     * Proxy function to SoapCall
123
     *
124
     * @param array $args
125
     *
126
     * @return mixed
127
     * @throws \Exception
128
     * @throws \SoapFault
129
     */
130 1
    public function call(array $args = [])
131
    {
132 1
        $args += ['functionName' => ''];
133
        
134 1
        $functionName = $args['functionName'];
135 1
        unset($args['functionName']);
136
137 1
        return $this->__soapCall($functionName, $args);
138
    }
139
140
    /**
141
     * Determine the SOAPHeaders for given version.
142
     *
143
     * @param string $action
144
     *
145
     * @return array
146
     */
147 6
    private function createHeaders($action)
148
    {
149 6
        $headers = ['Content-Type' => self::$versionToContentTypeMap[$this->settings->getSoapVersion()]];
150 6
        if ($this->settings->getSoapVersion() === SOAP_1_1) {
151 6
            $headers['SOAPAction'] = $action;
152 6
        }
153
154 6
        return $headers;
155
    }
156
157
    /**
158
     * Determines methods.
159
     * For Soap it's either GET or POST.
160
     *
161
     * @param mixed $request
162
     *
163
     * @return string
164
     */
165 6
    private static function determineMethod($request)
166
    {
167 6
        return ($request === null || (is_string($request) && trim($request) === '')) ? 'GET' : 'POST';
168
    }
169
170
    /**
171
     * Http version of doRequest.
172
     *
173
     * @param string $requestBody
174
     * @param string $location
175
     * @param string $action
176
     *
177
     * @return string
178
     * @throws WsClientException
179
     * @throws \SoapFault
180
     * @throws \InvalidArgumentException
181
     * @todo move exception handler to middleware, find solution error suppressing
182
     */
183 6
    private function doHttpRequest($requestBody, $location, $action)
184
    {
185
        // get soap details for request
186 6
        $headers = $this->createHeaders($action);
187 6
        $method = self::determineMethod($requestBody);
188
189
        // try to fire a request and return it
190
        try {
191 6
            $requestObj = new Request($method, $location, $headers, $requestBody);
192 6
            $response = $this->httpClient->send($requestObj);
193
            // Throw a SoapFault if the response was received, but it can't be read into valid XML
194 4
            if ($response->getStatusCode() > 399 && @simplexml_load_string((string)$response->getBody()) === false) {
195 2
                throw new \SoapFault('Server', 'Invalid SoapResponse');
196
            }
197
            
198 2
            return (string)$response->getBody();
199 5
        } catch (ClientException $e) {
200
            // if a client exception is thrown, the guzzle instance, is configured to throw exceptions
201 1
            $code = ($e->getResponse() !== null) ? 'Server' : 'Client.Input';
202 1
            throw new \SoapFault('Client', $e->getMessage(), null, $code);
203
        }
204
    }
205
206
    /**
207
     * @return ConverterInterface
208
     */
209 5
    public function getConverter()
210
    {
211 5
        return $this->converter;
212
    }
213
214
    /**
215
     * @param ConverterInterface $converter
216
     */
217 8
    public function setConverter($converter)
218
    {
219 8
        $this->converter = $converter;
220 8
    }
221
222
    /**
223
     * @return httpClient
0 ignored issues
show
Documentation introduced by
Should the return type not be HttpClient?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
224
     */
225 1
    public function getHttpClient()
226
    {
227 1
        return $this->httpClient;
228
    }
229
230
    /**
231
     * Return this connector over which a connection is established.
232
     *
233
     * @return string
234
     */
235 3
    public function getProtocolName()
236
    {
237 3
        return static::PROTOCOL;
238
    }
239
240
    /**
241
     * Log message.
242
     *
243
     * @param string $message
244
     * @param int    $level
245
     * @param array  $context
246
     */
247 13
    public function log($message, $level, array $context = [])
248
    {
249 13
        if ($this->logger instanceof LoggerInterface) {
250 7
            $this->logger->log($level, $message, $context);
251 7
        }
252 13
    }
253
254
    /**
255
     * Prepares the soapCall.
256
     *
257
     * @param string     $function_name
258
     * @param array      $arguments
259
     * @param array      $options
260
     * @param array      $input_headers
261
     * @param array|null $output_headers
262
     *
263
     * @return mixed
264
     * @throws \Exception|\SoapFault
265
     */
266 7
    public function __soapCall(
267
        $function_name,
268
        $arguments = [],
269
        $options = [],
270
        $input_headers = [],
271
        &$output_headers = null
272
    ) {
273 7
        $this->log('Called:' . $function_name, LogLevel::INFO, ['arguments' => $arguments]);
274
275
        try {
276 7
            return parent::__soapCall($function_name, $arguments, $options, $input_headers, $output_headers);
277 5
        } catch (\SoapFault $fault) {
278 4
            if ($this->getConverter() !== null) {
279 2
                throw $this->getConverter()->convertToException($fault);
280
            }
281 2
            throw $fault;
282
        }
283
    }
284
}
285