Completed
Push — master ( c312b3...d35487 )
by Haruaki
02:33
created

CurlSoapClient::__doRequest()   D

Complexity

Conditions 10
Paths 16

Size

Total Lines 34
Code Lines 22

Duplication

Lines 3
Ratio 8.82 %

Code Coverage

Tests 22
CRAP Score 10.0082

Importance

Changes 10
Bugs 0 Features 1
Metric Value
c 10
b 0
f 1
dl 3
loc 34
ccs 22
cts 23
cp 0.9565
rs 4.8196
cc 10
eloc 22
nc 16
nop 5
crap 10.0082

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
95
        try {
96 12
            $response = $this->___curlCall($location);
97 12
        } catch (\SoapFault $fault) {
98 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...
99
                // if exceptions option is false, return \SoapFault object
100 1
                return $fault;
101
            }
102 6
            throw $fault;
103
        }
104
105 5
        if ($one_way) {
106
            return '';
107
        }
108
109 5
        return $response;
110
    }
111
112
    /**
113
     * set CURLOPT_HTTPHEADER.
114
     *
115
     * @param string $action SOAP action
116
     * @param int $version SOAP version
117
     * @return void
118
     */
119 12
    private function ___configHeader($action, $version)
120
    {
121 12
        $header = array();
122 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...
123 1
            $header[] = 'Connection: close';
124 1
        } else {
125 11
            $header[] = 'Connection: Keep-Alive';
126
        }
127 12
        if ($version === SOAP_1_2) {
128 1
            $header[] = "Content-Type: application/soap+xml; charset=utf-8; action=\"{$action}\"";
129 1
        } else {
130 11
            $header[] = 'Content-Type: text/xml; charset=utf-8';
131 11
            $header[] = "SOAPAction: \"{$action}\"";
132
        }
133 12
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, $header);
134 12
    }
135
136
    /**
137
     * set CURLOPT_ENCODING.
138
     *
139
     * @return void
140
     */
141 12
    private function ___configCompression()
142
    {
143 12
        if (isset($this->compression)) {
144 3
            if ($this->compression & SOAP_COMPRESSION_ACCEPT) {
145 1
                curl_setopt($this->curl, CURLOPT_ENCODING, '');
146 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...
147 1
                curl_setopt($this->curl, CURLOPT_ENCODING, 'deflate');
148 1
            } else {
149 1
                curl_setopt($this->curl, CURLOPT_ENCODING, 'gzip');
150
            }
151 3
        }
152 12
    }
153
154
    /**
155
     * set CURLOPT_CONNECTTIMEOUT and CURLOPT_TIMEOUT.
156
     *
157
     * @return void
158
     */
159 12
    private function ___configTimeout()
160
    {
161 12
        $connection_timeout = 10; // default
162 12
        if (isset($this->_connection_timeout) && is_int($this->_connection_timeout)) {
163 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...
164 1
        }
165 12
        curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, $connection_timeout);
166 12
        curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->curl_timeout);
167 12
    }
168
169
    /**
170
     * set CURLOPT_HTTPAUTH.
171
     *
172
     * @return void
173
     */
174 12
    private function ___configHttpAuthentication()
175
    {
176 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...
177 12
            isset($this->_password) && is_string($this->_password) && strlen($this->_password) > 0) {
178 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...
179 2
            if (property_exists($this, '_digest')) {
180 1
                curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
181 1
            } else {
182 1
                curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
183
            }
184 2
        }
185 12
    }
186
187
    /**
188
     * set proxy options.
189
     *
190
     * @return void
191
     */
192 12
    private function ___configProxy()
193
    {
194 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...
195
            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...
196
        }
197 12
        if (isset($this->_proxy_port) && is_integer($this->_proxy_port)) {
198
            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...
199
        }
200 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...
201 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...
202
            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...
203
            if (property_exists($this, '_digest')) {
204
                curl_setopt($this->curl, CURLOPT_PROXYAUTH, CURLAUTH_ANYSAFE);
205
            } else {
206
                curl_setopt($this->curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
207
            }
208
        }
209 12
    }
210
211
    /**
212
     * Request cURL.
213
     *
214
     * @param[in] string $location SOAP address
215
     * @throws \SoapFault
216
     * @return mixed response body
217
     */
218 12
    private function ___curlCall($location)
219
    {
220 12
        curl_setopt($this->curl, CURLOPT_URL, $location);
221
222 12
        if (!empty($this->_cookies)) {
223 1
            $cookies = array();
224 1
            foreach ($this->_cookies as $cookie_name => $cookie_value) {
225 1
                $cookies[] = $cookie_name . '=' . $cookie_value[0];
226 1
            }
227 1
            curl_setopt($this->curl, CURLOPT_COOKIE, implode('; ', $cookies));
228 1
        }
229
230 12
        $response = curl_exec($this->curl);
231 12
        if ($response === false) {
232 2
            throw new \SoapFault(
233 2
                'HTTP',
234 2
                'Error Fetching http, ' . curl_error($this->curl) . ' (' . curl_errno($this->curl) . ')'
235 2
            );
236
        }
237
238 10
        $header_size = curl_getinfo($this->curl, CURLINFO_HEADER_SIZE);
239 10
        $response_header = substr($response, 0, $header_size);
240 10
        $response_body = substr($response, $header_size);
241 10
        $http_code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
242
243 10
        if (isset($this->trace) && $this->trace) {
244 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...
245 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...
246 3
        }
247
248 10
        if ($http_code >= 300 && $http_code < 400) {
249 4
            $tmp = stristr($response_header, 'Location:');
250 4
            $line_end = strpos($tmp, "\n"); // "\r" will be trimmed
251 4
            if ($line_end === false) {
252 1
                throw new \SoapFault('HTTP', 'Error Redirecting, No Location');
253
            }
254 3
            $new_location = trim(substr($tmp, 9, $line_end - 9));
255 3
            $url = parse_url($new_location);
256 3
            if ($url === false ||
257 3
                empty($url['scheme']) ||
258 2
                preg_match('/^https?$/i', $url['scheme']) !== 1
259 3
            ) {
260 1
                throw new \SoapFault('HTTP', 'Error Redirecting, Invalid Location');
261
            }
262 2
            if (++$this->redirect_count > $this->redirect_max) {
263 1
                throw new \SoapFault('HTTP', 'Redirection limit reached, aborting');
264
            }
265 2
            return $this->___curlCall($new_location);
266
        }
267
268 7
        if ($http_code >= 400) {
269 3
            $is_error = false;
270 3
            $response_length = strlen($response_body);
271 3
            if ($response_length === 0) {
272 1
                $is_error = true;
273 3
            } elseif ($response_length > 0) {
274 2
                $is_xml = false;
275 2
                $content_type = curl_getinfo($this->curl, CURLINFO_CONTENT_TYPE);
276 2
                if ($content_type !== null) {
277 2
                    $separator_position = strpos($content_type, ';');
278 2
                    if ($separator_position !== false) {
279 1
                        $content_type = substr($content_type, 0, $separator_position);
280 1
                    }
281 2
                    if ($content_type === 'text/xml' || $content_type === 'application/soap+xml') {
282 1
                        $is_xml = true;
283 1
                    }
284 2
                }
285 2
                if (!$is_xml) {
286 1
                    $str = ltrim($response_body);
287 1
                    if (strncmp($str, '<?xml', 5)) {
288 1
                        $is_error = true;
289 1
                    }
290 1
                }
291 2
            }
292
293 3
            if ($is_error) {
294 2
                $string_http_code = (string)$http_code;
295 2
                $code_position = strpos($response_header, $string_http_code);
296 2
                $tmp = substr($response_header, $code_position + strlen($string_http_code));
297 2
                $http_message = trim(strstr($tmp, "\n", true));
298 2
                throw new \SoapFault('HTTP', $http_message);
299
            }
300 1
        }
301
302 5
        return $response_body;
303
    }
304
}
305