Completed
Push — master ( bac1bc...5acb47 )
by Ivo
07:35
created

SoapClient   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 273
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 54.17%

Importance

Changes 0
Metric Value
wmc 43
lcom 1
cbo 6
dl 0
loc 273
ccs 91
cts 168
cp 0.5417
rs 8.96
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 35 5
A __call() 0 14 3
A __soapCall() 0 19 3
C __doRequest() 0 62 14
A preCall() 0 6 2
A postCall() 0 14 2
A faultCall() 0 9 2
B resolveLocation() 0 11 7
A setMockRequests() 0 4 1
A setMockResponses() 0 4 1
A setDispatcher() 0 4 1
A handleFault() 0 11 2

How to fix   Complexity   

Complex Class

Complex classes like SoapClient often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SoapClient, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Freshcells\SoapClientBundle\SoapClient;
4
5
use Freshcells\SoapClientBundle\Event\Events;
6
use Freshcells\SoapClientBundle\Event\FaultEvent;
7
use Freshcells\SoapClientBundle\Event\RequestEvent;
8
use Freshcells\SoapClientBundle\Event\ResponseEvent;
9
use Ramsey\Uuid\Uuid;
10
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
11
12
/**
13
 * Class SoapClient
14
 */
15
class SoapClient extends \SoapClient
16
{
17
    /**
18
     * @var array
19
     */
20
    protected $options;
21
    /**
22
     * @var EventDispatcherInterface
23
     */
24
    protected $dispatcher;
25
    /**
26
     * @var array
27
     */
28
    private $mockRequests = [];
29
    /**
30
     * @var array
31
     */
32
    private $mockResponses = [];
33
34
    /**
35
     * SoapClient constructor.
36
     * @param null $wsdl
37
     * @param array|null $options
38
     */
39 6
    public function __construct($wsdl = null, array $options = [])
40
    {
41
42 6
        if (isset($options['mock_requests'])) {
43 6
            $this->setMockRequests($options['mock_requests']);
44 6
            unset($options['mock_requests']);
45
        }
46 6
        if (isset($options['mock_responses'])) {
47 4
            $this->setMockResponses($options['mock_responses']);
48 4
            unset($options['mock_responses']);
49
        }
50
51
        $defaults = array(
52 6
            'compression'        => (SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP),
53 6
            'cache_wsdl'         => WSDL_CACHE_BOTH,
54 6
            'connection_timeout' => 60,
55
            'exceptions'         => true,
56 6
            'features'           => SOAP_SINGLE_ELEMENT_ARRAYS,
57 6
            'soap_version'       => SOAP_1_2,
58
            'trace'              => true,
59 6
            'user_agent'         => 'freshcells/soap-client-bundle',
60
        );
61
62 6
        $options = array_merge($defaults, $options);
63
64 6
        if (!is_null($wsdl)) {
65
            // if option 'location' not set explicit use WSDL URL as service location
66 6
            if (!isset($options['location'])) {
67 6
                $options['location'] = $this->resolveLocation($wsdl);
68
            }
69
        }
70
71 6
        $this->SoapClient($wsdl, $options);
72 6
        $this->options = $options;
73 6
    }
74
75 6
    public function __call($function_name, $arguments)
76
    {
77
        try {
78 6
            $response = parent::__call($function_name, $arguments);
79
            //works only with 'exceptions' => false, we always throw
80 4
            if (is_soap_fault($response)) {
81 4
                throw $response;
82
            }
83 2
        } catch (\Exception $e) {
84 2
            $this->handleFault($function_name, $arguments, $e);
85
        }
86
87 4
        return $response;
88
    }
89
90
    public function __soapCall(
91
        $function_name,
92
        $arguments,
93
        $options = null,
94
        $input_headers = null,
95
        &$output_headers = null
96
    ) {
97
        try {
98
            $response = parent::__soapCall($function_name, $arguments, $options, $input_headers, $output_headers);
99
            //works only with 'exceptions' => false, we always throw
100
            if (is_soap_fault($response)) {
101
                throw $response;
102
            }
103
        } catch (\Exception $e) {
104
            $this->handleFault($function_name, $arguments, $e);
105
        }
106
107
        return $response;
108
    }
109
110
    /**
111
     * @param string $request
112
     * @param string $location
113
     * @param string $action
114
     * @param int $version
115
     * @param null $one_way
116
     * @return bool|string
117
     */
118 4
    public function __doRequest($request, $location, $action, $version, $one_way = null)
119
    {
120 4
        $id = Uuid::uuid1();
121
122 4
        foreach ($this->mockRequests as $key => $mockRequest) {
123 4
            if (is_string($key)) {
124 4
                if (strrpos($action, $key) !== false) {
125 4
                    $request = file_get_contents($mockRequest);
126 4
                    break;
127
                }
128
            } else {
129
                if (is_callable($mockRequest)) {
130
                    if ($requestFilePath = $mockRequest($request, $location, $action, $version, $one_way)) {
131
                        $request = file_get_contents($requestFilePath);
132
                        break;
133
                    }
134
                }
135
            }
136
        }
137
138 4
        $this->preCall($id->toString(), $action, $request);
139
140 4
        foreach ($this->mockResponses as $key => $mockResponse) {
141 4
            if (is_string($key)) {
142 2
                if (strrpos($action, $key) !== false) {
143 2
                    $response = file_get_contents($mockResponse);
144
145 2
                    $this->postCall($id->toString(), $action, $response);
146
147 2
                    return $response;
148
                }
149
            } else {
150 2
                if (is_callable($mockResponse)) {
151 2
                    if ($responseFilePath = $mockResponse($request, $location, $action, $version, $one_way)) {
152 2
                        $response = file_get_contents($responseFilePath);
153
154 2
                        $this->postCall($id->toString(), $action, $response);
155
156 2
                        return $response;
157
                    }
158
                }
159
            }
160
        }
161
162
        /* workaround for working timeout */
163
        $socketTimeout = false;
164
        if (isset($this->options['connection_timeout'])
165
            && (int)$this->options['connection_timeout'] > (int)ini_get('default_socket_timeout')
166
        ) {
167
            $socketTimeout = ini_set('default_socket_timeout', $this->options['connection_timeout']);
168
        }
169
170
        $response = parent::__doRequest($request, $location, $action, $version, $one_way);
171
172
        $this->postCall($id->toString(), $action, $response);
173
174
        if ($socketTimeout !== false) {
175
            ini_set('default_socket_timeout', $socketTimeout);
176
        }
177
178
        return $response;
179
    }
180
181
    /**
182
     * Triggered before a request is executed
183
     *
184
     * @param string $id
185
     * @param string $resource
186
     * @param string $requestContent
187
     */
188 4
    protected function preCall(string $id, string $resource, string $requestContent = null)
189
    {
190 4
        if (null !== $this->dispatcher) {
191 4
            $this->dispatcher->dispatch(Events::REQUEST, new RequestEvent($id, $resource, $requestContent));
192
        }
193 4
    }
194
195
    /**
196
     * @param string $id
197
     * @param string $resource
198
     * @param string $response
199
     */
200 4
    protected function postCall(string $id, string $resource, string $response = null)
201
    {
202 4
        if (null !== $this->dispatcher) {
203 4
            $responseEvent = new ResponseEvent(
204 4
                $id,
205 4
                $resource,
206 4
                $this->__getLastRequest(),
207 4
                $this->__getLastRequestHeaders(),
208 4
                $response,
209 4
                $this->__getLastResponseHeaders()
210
            );
211 4
            $this->dispatcher->dispatch(Events::RESPONSE, $responseEvent);
212
        }
213 4
    }
214
215
    /**
216
     * @param string $id
217
     * @param string $resource
218
     * @param string $requestContent
219
     * @param \Exception $exception
220
     */
221 2
    protected function faultCall(string $id, string $resource, string $requestContent, \Exception $exception)
222
    {
223 2
        if (null !== $this->dispatcher) {
224 2
            $this->dispatcher->dispatch(
225 2
                Events::FAULT,
226 2
                new FaultEvent($id, $exception, new RequestEvent($id, $resource, $requestContent))
227
            );
228
        }
229 2
    }
230
231
    /**
232
     * @param string $wsdl
233
     * @return string
234
     */
235 6
    protected function resolveLocation($wsdl)
236
    {
237 6
        $wsdlUrl = parse_url($wsdl);
238
239 6
        return ((isset($wsdlUrl['scheme'])) ? $wsdlUrl['scheme'].'://' : '')
240 6
            .((isset($wsdlUrl['user'])) ? $wsdlUrl['user']
241 6
                .((isset($wsdlUrl['pass'])) ? ':'.$wsdlUrl['pass'] : '').'@' : '')
242 6
            .((isset($wsdlUrl['host'])) ? $wsdlUrl['host'] : '')
243 6
            .((isset($wsdlUrl['port'])) ? ':'.$wsdlUrl['port'] : '')
244 6
            .((isset($wsdlUrl['path'])) ? $wsdlUrl['path'] : '');
245
    }
246
247
    /**
248
     * @param array $mockRequests
249
     */
250 6
    public function setMockRequests(array $mockRequests)
251
    {
252 6
        $this->mockRequests = $mockRequests;
253 6
    }
254
255
    /**
256
     * @param array $mockResponses
257
     */
258 6
    public function setMockResponses(array $mockResponses)
259
    {
260 6
        $this->mockResponses = $mockResponses;
261 6
    }
262
263
    /**
264
     * @param EventDispatcherInterface $dispatcher
265
     */
266 6
    public function setDispatcher(EventDispatcherInterface $dispatcher)
267
    {
268 6
        $this->dispatcher = $dispatcher;
269 6
    }
270
271
    /**
272
     * @param $function_name
273
     * @param $arguments
274
     * @param $e
275
     */
276 2
    protected function handleFault($function_name, $arguments, $e): void
277
    {
278 2
        $request = $this->__getLastRequest();
279 2
        if ($request === null) { //only dispatch this when no request was fired
280 2
            $request = implode(' ', $arguments);
281 2
            $id      = Uuid::uuid1();
282 2
            $this->faultCall($id->toString(), $function_name, $request, $e);
283
        }
284
285 2
        throw $e;
286
    }
287
}
288