Completed
Pull Request — 23 (#431)
by Harald
10:27
created

Http   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 183
Duplicated Lines 20.77 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 38
loc 183
rs 8.2769
c 0
b 0
f 0
wmc 41
lcom 1
cbo 6

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A delete() 9 9 2
A get() 9 9 2
B post() 10 10 5
B put() 10 10 5
A _buildXml() 0 4 2
A _getHeaders() 0 7 1
A _getAuthorization() 0 18 3
A useClientCredentials() 0 4 1
A _doRequest() 0 4 1
D _doUrlRequest() 0 64 12
B getCaFile() 0 23 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Http 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 Http, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Braintree;
3
4
/**
5
 * Braintree HTTP Client
6
 * processes Http requests using curl
7
 *
8
 * @copyright  2015 Braintree, a division of PayPal, Inc.
9
 */
10
class Http
11
{
12
    protected $_config;
13
    private $_useClientCredentials = false;
14
15
    public function __construct($config)
16
    {
17
        $this->_config = $config;
18
    }
19
20 View Code Duplication
    public function delete($path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
21
    {
22
        $response = $this->_doRequest('DELETE', $path);
23
        if($response['status'] === 200) {
24
            return true;
25
        } else {
26
            Util::throwStatusCodeException($response['status']);
27
        }
28
    }
29
30 View Code Duplication
    public function get($path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
31
    {
32
        $response = $this->_doRequest('GET', $path);
33
        if ($response['status'] === 200) {
34
            return Xml::buildArrayFromXml($response['body']);
35
        } else {
36
            Util::throwStatusCodeException($response['status']);
37
        }
38
    }
39
40 View Code Duplication
    public function post($path, $params = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
41
    {
42
        $response = $this->_doRequest('POST', $path, $this->_buildXml($params));
43
        $responseCode = $response['status'];
44
        if($responseCode === 200 || $responseCode === 201 || $responseCode === 422 || $responseCode == 400) {
45
            return Xml::buildArrayFromXml($response['body']);
46
        } else {
47
            Util::throwStatusCodeException($responseCode);
48
        }
49
    }
50
51 View Code Duplication
    public function put($path, $params = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
52
    {
53
        $response = $this->_doRequest('PUT', $path, $this->_buildXml($params));
54
        $responseCode = $response['status'];
55
        if($responseCode === 200 || $responseCode === 201 || $responseCode === 422 || $responseCode == 400) {
56
            return Xml::buildArrayFromXml($response['body']);
57
        } else {
58
            Util::throwStatusCodeException($responseCode);
59
        }
60
    }
61
62
    private function _buildXml($params)
63
    {
64
        return empty($params) ? null : Xml::buildXmlFromArray($params);
65
    }
66
67
    private function _getHeaders()
68
    {
69
        return [
70
            'Accept: application/xml',
71
            'Content-Type: application/xml',
72
        ];
73
    }
74
75
    private function _getAuthorization()
76
    {
77
        if ($this->_useClientCredentials) {
78
            return [
79
                'user' => $this->_config->getClientId(),
80
                'password' => $this->_config->getClientSecret(),
81
            ];
82
        } else if ($this->_config->isAccessToken()) {
83
            return [
84
                'token' => $this->_config->getAccessToken(),
85
            ];
86
        } else {
87
            return [
88
                'user' => $this->_config->getPublicKey(),
89
                'password' => $this->_config->getPrivateKey(),
90
            ];
91
        }
92
    }
93
94
    public function useClientCredentials()
95
    {
96
        $this->_useClientCredentials = true;
97
    }
98
99
    private function _doRequest($httpVerb, $path, $requestBody = null)
100
    {
101
        return $this->_doUrlRequest($httpVerb, $this->_config->baseUrl() . $path, $requestBody);
102
    }
103
104
    public function _doUrlRequest($httpVerb, $url, $requestBody = null)
105
    {
106
        $curl = curl_init();
107
        curl_setopt($curl, CURLOPT_TIMEOUT, $this->_config->timeout());
108
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $httpVerb);
109
        curl_setopt($curl, CURLOPT_URL, $url);
110
        curl_setopt($curl, CURLOPT_ENCODING, 'gzip');
111
112
        $headers = $this->_getHeaders($curl);
0 ignored issues
show
Unused Code introduced by
The call to Http::_getHeaders() has too many arguments starting with $curl.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
113
        $headers[] = 'User-Agent: Braintree PHP Library ' . Version::get();
114
        $headers[] = 'X-ApiVersion: ' . Configuration::API_VERSION;
115
116
        $authorization = $this->_getAuthorization();
117
        if (isset($authorization['user'])) {
118
            curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
119
            curl_setopt($curl, CURLOPT_USERPWD, $authorization['user'] . ':' . $authorization['password']);
120
        } else if (isset($authorization['token'])) {
121
            $headers[] = 'Authorization: Bearer ' . $authorization['token'];
122
        }
123
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
124
125
        // curl_setopt($curl, CURLOPT_VERBOSE, true);
126
        if ($this->_config->sslOn()) {
127
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
128
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
129
            curl_setopt($curl, CURLOPT_CAINFO, $this->getCaFile());
130
        }
131
132
        if(!empty($requestBody)) {
133
            curl_setopt($curl, CURLOPT_POSTFIELDS, $requestBody);
134
        }
135
136
        if($this->_config->isUsingProxy()) {
137
            $proxyHost = $this->_config->getProxyHost();
138
            $proxyPort = $this->_config->getProxyPort();
139
            $proxyType = $this->_config->getProxyType();
140
            $proxyUser = $this->_config->getProxyUser();
141
            $proxyPwd= $this->_config->getProxyPassword();
142
            curl_setopt($curl, CURLOPT_PROXY, $proxyHost . ':' . $proxyPort);
143
            if(!empty($proxyType)) {
144
                curl_setopt($curl, CURLOPT_PROXYTYPE, $proxyType);
145
            }
146
            if($this->_config->isAuthenticatedProxy()) {
147
                curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyUser . ':' . $proxyPwd);
148
            }
149
        }
150
151
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
152
        $response = curl_exec($curl);
153
        $httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
154
        $error_code = curl_errno($curl);
155
156
        if ($error_code == 28 && $httpStatus == 0) {
157
            throw new Exception\Timeout();
158
        }
159
160
        curl_close($curl);
161
        if ($this->_config->sslOn()) {
162
            if ($httpStatus == 0) {
163
                throw new Exception\SSLCertificate();
164
            }
165
        }
166
        return ['status' => $httpStatus, 'body' => $response];
167
    }
168
169
    private function getCaFile()
170
    {
171
        static $memo;
172
173
        if ($memo === null) {
174
            $caFile = $this->_config->caFile();
175
176
            if (substr($caFile, 0, 7) !== 'phar://') {
177
                return $caFile;
178
            }
179
180
            $extractedCaFile = sys_get_temp_dir() . '/api_braintreegateway_com.ca.crt';
181
182
            if (!file_exists($extractedCaFile) || sha1_file($extractedCaFile) != sha1_file($caFile)) {
183
                if (!copy($caFile, $extractedCaFile)) {
184
                    throw new Exception\SSLCaFileNotFound();
185
                }
186
            }
187
            $memo = $extractedCaFile;
188
        }
189
190
        return $memo;
191
    }
192
}
193
class_alias('Braintree\Http', 'Braintree_Http');
194