Completed
Push — master ( 2382b9...e5f2b2 )
by Haruaki
02:33
created

CurlSoapClient::__doRequest()   C

Complexity

Conditions 14
Paths 64

Size

Total Lines 47
Code Lines 33

Duplication

Lines 3
Ratio 6.38 %

Code Coverage

Tests 23
CRAP Score 23.2286

Importance

Changes 11
Bugs 0 Features 1
Metric Value
c 11
b 0
f 1
dl 3
loc 47
ccs 23
cts 36
cp 0.6389
rs 5.0622
cc 14
eloc 33
nc 64
nop 5
crap 23.2286

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
 * curlsoapclient - SoapClient with ext-curl. -
4
 *
5
 * @author    aaharu
6
 * @copyright Copyright (c) 2014 aaharu
7
 * @license   MIT License
8
 */
9
10
namespace Aaharu\Soap;
11
12
use SoapClient;
13
14
/**
15
 * @see https://github.com/php/php-src/tree/master/ext/soap
16
 */
17
class CurlSoapClient extends SoapClient
18
{
19
    protected $curl = null; ///< cURL handle
20
    protected $redirect_max; ///< max redirect counts
21
    protected $curl_timeout; ///< cURL request time-out seconds
22
    private $redirect_count = 0;
23
24 12
    public function __construct($wsdl, array $options)
25
    {
26 12
        parent::__construct($wsdl, $options);
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: Aaharu\Soap\CurlSoapClient. 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...
27 12
        $this->redirect_max = 5;
28 12
        if (isset($options['redirect_max'])) {
29 1
            $this->redirect_max = (int)$options['redirect_max'];
30 1
        }
31 12
        $this->curl_timeout = 30;
32 12
        if (isset($options['curl_timeout'])) {
33 1
            $this->curl_timeout = (int)$options['curl_timeout'];
34 1
        }
35 12
        $this->curl = curl_init();
36 12
        $this->_cookies = array();
1 ignored issue
show
Bug introduced by
The property _cookies does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
37 12
    }
38
39 12
    public function __destruct()
40
    {
41 12
        if (isset($this->curl)) {
42 12
            curl_close($this->curl);
43 12
        }
44 12
    }
45
46 1
    public function ___curlSetOpt($option, $value)
47
    {
48 1
        curl_setopt($this->curl, $option, $value);
49 1
    }
50
51 1
    public function __getCookies()
52
    {
53 1
        return $this->_cookies;
54
    }
55
56 1
    public function __setCookie($name, $value = null)
57
    {
58 1
        if (!isset($value)) {
59
            unset($this->_cookies[$name]);
60
            return;
61
        }
62 1
        $this->_cookies[$name] = (array)$value;
63 1
    }
64
65
    /**
66
     * Execute SOAP requests.
67
     *
68
     * @param string $request SOAP request
69
     * @param string $location SOAP address
70
     * @param string $action SOAP action
71
     * @param int $version SOAP version
72
     * @param int $one_way
73
     * @throws \Exception
74
     * @throws \SoapFault
75
     * @return mixed (string) SOAP response / (object) SoapFault object
76
     */
77 12
    public function __doRequest($request, $location, $action, $version, $one_way = 0)
78
    {
79 12
        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
80 12
        curl_setopt($this->curl, CURLOPT_HEADER, true);
81 12
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $request);
82 12
        if (isset($this->trace) && $this->trace) {
1 ignored issue
show
Bug introduced by
The property trace does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
83 3
            curl_setopt($this->curl, CURLINFO_HEADER_OUT, true);
84 3
        }
85
86 12
        $this->___configHeader($action, $version);
87 12
        $this->___configCompression();
88 12
        $this->___configTimeout();
89 12 View Code Duplication
        if (isset($this->_user_agent) && is_string($this->_user_agent) && strlen($this->_user_agent) > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
90 1
            curl_setopt($this->curl, CURLOPT_USERAGENT, $this->_user_agent);
1 ignored issue
show
Bug introduced by
The property _user_agent does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
91 1
        }
92 12
        $this->___configHttpAuthentication();
93 12
        $this->___configProxy();
94 12
        if (isset($this->_ssl_method) && is_int($this->_ssl_method)) {
95
            switch ($this->_ssl_method) {
1 ignored issue
show
Bug introduced by
The property _ssl_method does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
96
                case SOAP_SSL_METHOD_SSLv2:
97
                    curl_setopt($this->curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv2);
98
                    break;
99
                case SOAP_SSL_METHOD_SSLv3:
100
                    curl_setopt($this->curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv3);
101
                    break;
102
                default:
103
                    curl_setopt($this->curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_DEFAULT);
104
                    break;
105
            }
106
        }
107
108
        try {
109 12
            $response = $this->___curlCall($location);
110 12
        } catch (\SoapFault $fault) {
111 7
            if (isset($this->_exceptions) && empty($this->_exceptions)) {
1 ignored issue
show
Bug introduced by
The property _exceptions does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
112
                // if exceptions option is false, return \SoapFault object
113 1
                return $fault;
114
            }
115 6
            throw $fault;
116
        }
117
118 5
        if ($one_way) {
119
            return '';
120
        }
121
122 5
        return $response;
123
    }
124
125
    /**
126
     * set CURLOPT_HTTPHEADER.
127
     *
128
     * @param string $action SOAP action
129
     * @param int $version SOAP version
130
     * @return void
131
     */
132 12
    private function ___configHeader($action, $version)
133
    {
134 12
        $header = array();
135 12
        if (isset($this->_keep_alive) && empty($this->_keep_alive)) {
1 ignored issue
show
Bug introduced by
The property _keep_alive does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
136 1
            $header[] = 'Connection: close';
137 1
        } else {
138 11
            $header[] = 'Connection: Keep-Alive';
139
        }
140 12
        if ($version === SOAP_1_2) {
141 1
            $header[] = "Content-Type: application/soap+xml; charset=utf-8; action=\"{$action}\"";
142 1
        } else {
143 11
            $header[] = 'Content-Type: text/xml; charset=utf-8';
144 11
            $header[] = "SOAPAction: \"{$action}\"";
145
        }
146 12
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, $header);
147 12
    }
148
149
    /**
150
     * set CURLOPT_ENCODING.
151
     *
152
     * @return void
153
     */
154 12
    private function ___configCompression()
155
    {
156 12
        if (isset($this->compression)) {
157 3
            if ($this->compression & SOAP_COMPRESSION_ACCEPT) {
158 1
                curl_setopt($this->curl, CURLOPT_ENCODING, '');
159 3
            } elseif ($this->compression & SOAP_COMPRESSION_DEFLATE) {
1 ignored issue
show
Bug introduced by
The property compression does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
160 1
                curl_setopt($this->curl, CURLOPT_ENCODING, 'deflate');
161 1
            } else {
162 1
                curl_setopt($this->curl, CURLOPT_ENCODING, 'gzip');
163
            }
164 3
        }
165 12
    }
166
167
    /**
168
     * set CURLOPT_CONNECTTIMEOUT and CURLOPT_TIMEOUT.
169
     *
170
     * @return void
171
     */
172 12
    private function ___configTimeout()
173
    {
174 12
        $connection_timeout = 10; // default
175 12
        if (isset($this->_connection_timeout) && is_int($this->_connection_timeout)) {
176 1
            $connection_timeout = $this->_connection_timeout;
1 ignored issue
show
Bug introduced by
The property _connection_timeout does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
177 1
        }
178 12
        curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, $connection_timeout);
179 12
        curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->curl_timeout);
180 12
    }
181
182
    /**
183
     * set CURLOPT_HTTPAUTH.
184
     *
185
     * @return void
186
     */
187 12
    private function ___configHttpAuthentication()
188
    {
189 12 View Code Duplication
        if (isset($this->_login) && is_string($this->_login) && strlen($this->_login) > 0 &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
190 12
            isset($this->_password) && is_string($this->_password) && strlen($this->_password) > 0) {
191 2
            curl_setopt($this->curl, CURLOPT_USERPWD, $this->_login . ':' . $this->_password);
2 ignored issues
show
Bug introduced by
The property _login does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property _password does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
192 2
            if (property_exists($this, '_digest')) {
193 1
                curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
194 1
            } else {
195 1
                curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
196
            }
197 2
        }
198 12
    }
199
200
    /**
201
     * set proxy options.
202
     *
203
     * @return void
204
     */
205 12
    private function ___configProxy()
206
    {
207 12 View Code Duplication
        if (isset($this->_proxy_host) && is_string($this->_proxy_host) && strlen($this->_proxy_host) > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
208
            curl_setopt($this->curl, CURLOPT_PROXY, $this->_proxy_host);
1 ignored issue
show
Bug introduced by
The property _proxy_host does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
209
        }
210 12
        if (isset($this->_proxy_port) && is_integer($this->_proxy_port)) {
211
            curl_setopt($this->curl, CURLOPT_PROXYPORT, $this->_proxy_port);
1 ignored issue
show
Bug introduced by
The property _proxy_port does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
212
        }
213 12 View Code Duplication
        if (isset($this->_proxy_login) && is_string($this->_proxy_login) && strlen($this->_proxy_login) > 0 &&
1 ignored issue
show
Bug introduced by
The property _proxy_login does not seem to exist. Did you mean _login?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
214 12
            isset($this->_proxy_password) && is_string($this->_proxy_password) && strlen($this->_proxy_password) > 0) {
1 ignored issue
show
Bug introduced by
The property _proxy_password does not seem to exist. Did you mean _password?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
215
            curl_setopt($this->curl, CURLOPT_PROXYUSERPWD, $this->_proxy_login . ':' . $this->_proxy_password);
2 ignored issues
show
Bug introduced by
The property _proxy_login does not seem to exist. Did you mean _login?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
Bug introduced by
The property _proxy_password does not seem to exist. Did you mean _password?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
216
            if (property_exists($this, '_digest')) {
217
                curl_setopt($this->curl, CURLOPT_PROXYAUTH, CURLAUTH_ANYSAFE);
218
            } else {
219
                curl_setopt($this->curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
220
            }
221
        }
222 12
    }
223
224
    /**
225
     * Request cURL.
226
     *
227
     * @param[in] string $location SOAP address
228
     * @throws \SoapFault
229
     * @return mixed response body
230
     */
231 12
    private function ___curlCall($location)
232
    {
233 12
        curl_setopt($this->curl, CURLOPT_URL, $location);
234
235 12
        if (!empty($this->_cookies)) {
236 1
            $cookies = array();
237 1
            foreach ($this->_cookies as $cookie_name => $cookie_value) {
238 1
                $cookies[] = $cookie_name . '=' . $cookie_value[0];
239 1
            }
240 1
            curl_setopt($this->curl, CURLOPT_COOKIE, implode('; ', $cookies));
241 1
        }
242
243 12
        $response = curl_exec($this->curl);
244 12
        if ($response === false) {
245 2
            throw new \SoapFault(
246 2
                'HTTP',
247 2
                'Error Fetching http, ' . curl_error($this->curl) . ' (' . curl_errno($this->curl) . ')'
248 2
            );
249
        }
250
251 10
        $header_size = curl_getinfo($this->curl, CURLINFO_HEADER_SIZE);
252 10
        $response_header = substr($response, 0, $header_size);
253 10
        $response_body = substr($response, $header_size);
254 10
        $http_code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
255
256 10
        if (isset($this->trace) && $this->trace) {
257 3
            $this->__last_request_headers = curl_getinfo($this->curl, CURLINFO_HEADER_OUT);
1 ignored issue
show
Bug introduced by
The property __last_request_headers does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
258 3
            $this->__last_response_headers = $response_header;
1 ignored issue
show
Bug introduced by
The property __last_response_headers does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
259 3
        }
260
261 10
        if ($http_code >= 300 && $http_code < 400) {
262 4
            $tmp = stristr($response_header, 'Location:');
263 4
            $line_end = strpos($tmp, "\n"); // "\r" will be trimmed
264 4
            if ($line_end === false) {
265 1
                throw new \SoapFault('HTTP', 'Error Redirecting, No Location');
266
            }
267 3
            $new_location = trim(substr($tmp, 9, $line_end - 9));
268 3
            $url = parse_url($new_location);
269 3
            if ($url === false ||
270 3
                empty($url['scheme']) ||
271 2
                preg_match('/^https?$/i', $url['scheme']) !== 1
272 3
            ) {
273 1
                throw new \SoapFault('HTTP', 'Error Redirecting, Invalid Location');
274
            }
275 2
            if (++$this->redirect_count > $this->redirect_max) {
276 1
                throw new \SoapFault('HTTP', 'Redirection limit reached, aborting');
277
            }
278 2
            return $this->___curlCall($new_location);
279
        }
280
281 7
        if ($http_code >= 400) {
282 3
            $is_error = false;
283 3
            $response_length = strlen($response_body);
284 3
            if ($response_length === 0) {
285 1
                $is_error = true;
286 3
            } elseif ($response_length > 0) {
287 2
                $is_xml = false;
288 2
                $content_type = curl_getinfo($this->curl, CURLINFO_CONTENT_TYPE);
289 2
                if ($content_type !== null) {
290 2
                    $separator_position = strpos($content_type, ';');
291 2
                    if ($separator_position !== false) {
292 1
                        $content_type = substr($content_type, 0, $separator_position);
293 1
                    }
294 2
                    if ($content_type === 'text/xml' || $content_type === 'application/soap+xml') {
295 1
                        $is_xml = true;
296 1
                    }
297 2
                }
298 2
                if (!$is_xml) {
299 1
                    $str = ltrim($response_body);
300 1
                    if (strncmp($str, '<?xml', 5)) {
301 1
                        $is_error = true;
302 1
                    }
303 1
                }
304 2
            }
305
306 3
            if ($is_error) {
307 2
                $string_http_code = (string)$http_code;
308 2
                $code_position = strpos($response_header, $string_http_code);
309 2
                $tmp = substr($response_header, $code_position + strlen($string_http_code));
310 2
                $http_message = trim(strstr($tmp, "\n", true));
311 2
                throw new \SoapFault('HTTP', $http_message);
312
            }
313 1
        }
314
315 5
        return $response_body;
316
    }
317
}
318