Completed
Push — master ( 10c012...bd9447 )
by Ivo
01:52
created

SoapClient::__construct()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 17
cts 17
cp 1
rs 9.472
c 0
b 0
f 0
cc 3
nc 4
nop 2
crap 3
1
<?php
2
3
namespace Freshcells\SoapClientBundle\SoapClient;
4
5
use Freshcells\SoapClientBundle\Event\Event;
6
use Freshcells\SoapClientBundle\Event\Events;
7
use Freshcells\SoapClientBundle\Event\FaultEvent;
8
use Freshcells\SoapClientBundle\Event\RequestEvent;
9
use Freshcells\SoapClientBundle\Event\ResponseEvent;
10
use Ramsey\Uuid\Uuid;
11
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
12
use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
13
14
/**
15
 * Class SoapClient
16
 */
17
class SoapClient extends \SoapClient implements SoapClientInterface
18
{
19
    /**
20
     * @var array
21
     */
22
    protected $options;
23
    /**
24
     * @var EventDispatcherInterface
25
     */
26
    protected $dispatcher;
27
    /**
28
     * @var array
29
     */
30
    private $mockRequests = [];
31
    /**
32
     * @var array
33
     */
34
    private $mockResponses = [];
35
36
    /**
37
     * SoapClient constructor.
38
     * @param null $wsdl
39
     * @param array|null $options
40
     */
41 18
    public function __construct($wsdl = null, array $options = [])
42
    {
43
44 18
        if (isset($options['mock_requests'])) {
45 12
            $this->setMockRequests($options['mock_requests']);
46 12
            unset($options['mock_requests']);
47
        }
48 18
        if (isset($options['mock_responses'])) {
49 9
            $this->setMockResponses($options['mock_responses']);
50 9
            unset($options['mock_responses']);
51
        }
52
53
        $defaults = array(
54 18
            'compression'        => (SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP),
55 18
            'cache_wsdl'         => WSDL_CACHE_BOTH,
56 18
            'connection_timeout' => 60,
57
            'exceptions'         => true,
58 18
            'features'           => SOAP_SINGLE_ELEMENT_ARRAYS,
59 18
            'soap_version'       => SOAP_1_2,
60
            'trace'              => true,
61 18
            'user_agent'         => 'freshcells/soap-client-bundle',
62
        );
63
64 18
        $options = array_merge($defaults, $options);
65
66 18
        $this->SoapClient($wsdl, $options);
67 18
        $this->options = $options;
68 18
    }
69
70 6
    public function getOptions(): array
71
    {
72 6
        return $this->options;
73
    }
74
75 9
    public function __call($function_name, $arguments)
76
    {
77
        try {
78 9
            $response = parent::__call($function_name, $arguments);
79
            //works only with 'exceptions' => false, we always throw
80 6
            if (is_soap_fault($response)) {
81 6
                throw $response;
82
            }
83 3
        } catch (\Exception $e) {
84 3
            $this->handleFault($function_name, $arguments, $e);
85
        }
86
87 6
        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 6
    public function __doRequest($request, $location, $action, $version, $one_way = null)
119
    {
120 6
        $id = Uuid::uuid1();
121
122 6
        foreach ($this->mockRequests as $key => $mockRequest) {
123 6
            if (is_string($key)) {
124 6
                if (strrpos($action, $key) !== false) {
125 6
                    $request = file_get_contents($mockRequest);
126 6
                    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 6
        $this->preCall($id->toString(), (string)$action, $request);
139
140 6
        foreach ($this->mockResponses as $key => $mockResponse) {
141 6
            if (is_string($key)) {
142 3
                if (strrpos($action, $key) !== false) {
143 3
                    $response = file_get_contents($mockResponse);
144
145 3
                    $this->postCall($id->toString(), $action, $response);
146
147 3
                    return $response;
148
                }
149
            } else {
150 3
                if (is_callable($mockResponse)) {
151 3
                    if ($responseFilePath = $mockResponse($request, $location, $action, $version, $one_way)) {
152 3
                        $response = file_get_contents($responseFilePath);
153
154 3
                        $this->postCall($id->toString(), $action, $response);
155
156 3
                        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(), (string)$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 6
    protected function preCall(string $id, string $resource, string $requestContent = null)
189
    {
190 6
        $this->dispatch(new RequestEvent($id, $resource, $requestContent), Events::REQUEST);
191 6
    }
192
193
    /**
194
     * @param string $id
195
     * @param string $resource
196
     * @param string $response
197
     */
198 6
    protected function postCall(string $id, string $resource, string $response = null)
199
    {
200 6
        $responseEvent = new ResponseEvent(
201 6
            $id,
202 6
            $resource,
203 6
            $this->__getLastRequest(),
204 6
            $this->__getLastRequestHeaders(),
205 6
            $response,
206 6
            $this->__getLastResponseHeaders()
207
        );
208 6
        $this->dispatch($responseEvent, Events::RESPONSE);
209 6
    }
210
211
    /**
212
     * @param string $id
213
     * @param string $resource
214
     * @param string $requestContent
215
     * @param \Exception $exception
216
     */
217 3
    protected function faultCall(string $id, string $resource, string $requestContent, \Exception $exception)
218
    {
219 3
        $this->dispatch(
220 3
            new FaultEvent($id, $exception, new RequestEvent($id, $resource, $requestContent)),
221 3
            Events::FAULT
222
        );
223 3
    }
224
225
    /**
226
     * @param array $mockRequests
227
     */
228 12
    public function setMockRequests(array $mockRequests)
229
    {
230 12
        $this->mockRequests = $mockRequests;
231 12
    }
232
233
    /**
234
     * @param array $mockResponses
235
     */
236 12
    public function setMockResponses(array $mockResponses)
237
    {
238 12
        $this->mockResponses = $mockResponses;
239 12
    }
240
241
    /**
242
     * @param EventDispatcherInterface $dispatcher
243
     * @required
244
     */
245 18
    public function setDispatcher(EventDispatcherInterface $dispatcher)
246
    {
247 18
        if (class_exists(LegacyEventDispatcherProxy::class)) {
248 6
            $this->dispatcher = LegacyEventDispatcherProxy::decorate($dispatcher);
249
        } else {
250 12
            $this->dispatcher = $dispatcher;
251
        }
252 18
    }
253
254
    /**
255
     * @param $function_name
256
     * @param $arguments
257
     * @param $e
258
     */
259 3
    protected function handleFault($function_name, $arguments, $e): void
260
    {
261 3
        $request = $this->__getLastRequest();
262 3
        if ($request === null) { //only dispatch this when no request was fired
263 3
            $request = implode(' ', $arguments);
264 3
            $id      = Uuid::uuid1();
265 3
            $this->faultCall($id->toString(), $function_name, $request, $e);
266
        }
267
268 3
        throw $e;
269
    }
270
271
    /**
272
     * @param Event $event
273
     * @param string $eventName
274
     */
275 9
    private function dispatch(Event $event, $eventName)
276
    {
277 9
        if (null === $this->dispatcher) {
278
            return;
279
        }
280
281
        // LegacyEventDispatcherProxy exists in Symfony >= 4.3
282 9
        if (class_exists(LegacyEventDispatcherProxy::class)) {
283
            // New Symfony 4.3 EventDispatcher signature
284 3
            $this->dispatcher->dispatch($event, $eventName);
0 ignored issues
show
Documentation introduced by
$event is of type object<Freshcells\SoapClientBundle\Event\Event>, but the function expects a object<Symfony\Contracts\EventDispatcher\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
285
        } else {
286
            // Old EventDispatcher signature
287 6
            $this->dispatcher->dispatch($eventName, $event);
0 ignored issues
show
Documentation introduced by
$eventName is of type string, but the function expects a object<Symfony\Contracts\EventDispatcher\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$event is of type object<Freshcells\SoapClientBundle\Event\Event>, but the function expects a null|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
288
        }
289 9
    }
290
}
291