Completed
Push — master ( 1b7bb6...145a7f )
by Ibrahim
01:59
created

Router::attemptCurl()   D

Complexity

Conditions 10
Paths 30

Size

Total Lines 41
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 41
ccs 0
cts 28
cp 0
rs 4.8196
cc 10
eloc 24
nc 30
nop 4
crap 110

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
namespace Yabacon\Paystack\Helpers;
4
5
use \Closure;
6
use \Yabacon\Paystack\Contracts\RouteInterface;
7
8
/**
9
 * Router
10
 * Insert description here
11
 *
12
 * @category
13
 * @package
14
 * @author
15
 * @copyright
16
 * @license
17
 * @version
18
 * @link
19
 * @see
20
 * @since
21
 */
22
class Router
23
{
24
25
    private $route;
26
    private $route_class;
27
    private $secret_key;
28
    private $methods;
29
    private $use_guzzle=false;
30
31
    const ID_KEY = 'id';
32
    const PAYSTACK_API_ROOT = 'https://api.paystack.co';
33
34
    /**
35
 * moveArgsToSentargs
36
 * Insert description here
37
 *
38
 * @param $interface
39
 * @param $payload
40
 * @param $sentargs
41
 *
42
 * @return
43
 *
44
 * @access
45
 * @static
46
 * @see
47
 * @since
48
 */
49
    private function moveArgsToSentargs(
50
        $interface,
51
        &$payload,
52
        &$sentargs
53
    ) {
54
55
56
57
        // check if interface supports args
58
        if (array_key_exists(RouteInterface:: ARGS_KEY, $interface)) {
59
            // to allow args to be specified in the payload, filter them out and put them in sentargs
60
            $sentargs = (!$sentargs) ? [ ] : $sentargs; // Make sure $sentargs is not null
61
            $args = $interface[RouteInterface::ARGS_KEY];
62
            while (list($key, $value) = each($payload)) {
63
                // check that a value was specified
64
                // with a key that was expected as an arg
65
                if (in_array($key, $args)) {
66
                    $sentargs[$key] = $value;
67
                    unset($payload[$key]);
68
                }
69
            }
70
        }
71
    }
72
73
    /**
74
 * putArgsIntoEndpoint
75
 * Insert description here
76
 *
77
 * @param $endpoint
78
 * @param $sentargs
79
 *
80
 * @return
81
 *
82
 * @access
83
 * @static
84
 * @see
85
 * @since
86
 */
87
    private function putArgsIntoEndpoint(&$endpoint, $sentargs)
88
    {
89
        // substitute sentargs in endpoint
90
        while (list($key, $value) = each($sentargs)) {
91
            $endpoint = str_replace('{' . $key . '}', $value, $endpoint);
92
        }
93
    }
94
    
95
    private function attemptGuzzle($method, $endpoint, $headers, $body)
96
    {
97
        if ($this->use_guzzle &&
98
            class_exists('\\GuzzleHttp\\Exception\\BadResponseException') &&
99
            class_exists('\\GuzzleHttp\\Exception\\ClientException') &&
100
            class_exists('\\GuzzleHttp\\Exception\\ConnectException') &&
101
            class_exists('\\GuzzleHttp\\Exception\\RequestException') &&
102
            class_exists('\\GuzzleHttp\\Exception\\ServerException') &&
103
            class_exists('\\GuzzleHttp\\Exception\\TooManyRedirectsException') &&
104
            class_exists('\\GuzzleHttp\\Client') &&
105
            class_exists('\\GuzzleHttp\\Psr7\\Request')) {
106
            $request = new \GuzzleHttp\Psr7\Request(strtoupper($method), $endpoint, $headers, $body);
107
            $client = new \GuzzleHttp\Client();
108
            try {
109
                $response = $client->send($request);
110
            } catch (\Exception $e) {
111
                if (($e instanceof \GuzzleHttp\Exception\BadResponseException
112
                    || $e instanceof \GuzzleHttp\Exception\ClientException
113
                    || $e instanceof \GuzzleHttp\Exception\ConnectException
114
                    || $e instanceof \GuzzleHttp\Exception\RequestException
115
                    || $e instanceof \GuzzleHttp\Exception\ServerException
116
                    || $e instanceof \GuzzleHttp\Exception\TooManyRedirectsException
117
                    ) && $e->hasResponse()) {
118
                    $response = $e->getResponse();
119
                } else {
120
                    throw $e;
121
                }
122
            }
123
            return $response;
124
        } else {
125
            return false;
126
        }
127
    }
128
129
    /**
130
 * callEndpoint
131
 * Insert description here
132
 *
133
 * @param $interface
134
 * @param $payload
135
 * @param $sentargs
136
 *
137
 * @return
138
 *
139
 * @access
140
 * @static
141
 * @see
142
 * @since
143
 */
144
    private function callEndpoint($interface, $payload = [ ], $sentargs = [ ])
145
    {
146
        $endpoint = Router::PAYSTACK_API_ROOT . $interface[RouteInterface::ENDPOINT_KEY];
147
        $method = $interface[RouteInterface::METHOD_KEY];
148
149
        $this->moveArgsToSentargs($interface, $payload, $sentargs);
150
        $this->putArgsIntoEndpoint($endpoint, $sentargs);
151
 
152
        $headers = ["Authorization"=>"Bearer " . $this->secret_key ];
153
        $body = '';
154
        if (($method === RouteInterface::POST_METHOD)
155
            || ($method === RouteInterface::PUT_METHOD)
156
        ) {
157
            $headers["Content-Type"] = "application/json";
158
            $body = json_encode($payload);
159
        } elseif ($method === RouteInterface::GET_METHOD) {
160
            $endpoint = $endpoint . '?' . http_build_query($payload);
161
        }
162
        // Use Guzzle if found, else use Curl
163
        $guzzleResponse = $this->attemptGuzzle($method, $endpoint, $headers, $body);
164
        if ($guzzleResponse !== false) {
165
            return $guzzleResponse;
166
        }
167
        
168
        return $this->attemptCurl($method, $endpoint, $headers, $body);
169
    }
170
171
    private function attemptCurl($method, $endpoint, $headers, $body)
172
    {
173
        //open connection
174
        $ch = \curl_init();
175
        \curl_setopt($ch, \CURLOPT_URL, $endpoint);
176
177
        if ($method === RouteInterface::POST_METHOD || $method === RouteInterface::PUT_METHOD) {
178
            ($method === RouteInterface:: POST_METHOD) && \curl_setopt($ch, \CURLOPT_POST, true);
179
            ($method === RouteInterface ::PUT_METHOD) && \curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, "PUT");
180
181
            \curl_setopt($ch, \CURLOPT_POSTFIELDS, $body);
182
        }
183
        //flatten the headers
184
        $flattened_headers = [];
185
        while (list($key, $value) = each($headers)) {
186
            $flattened_headers[] = $key . ": " . $value;
187
        }
188
        \curl_setopt($ch, \CURLOPT_HTTPHEADER, $flattened_headers);
189
        \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, 1);
190
        \curl_setopt($ch, \CURLOPT_SSLVERSION, 6);
191
192
        $response = \curl_exec($ch);
193
        
194
        if (\curl_errno($ch)) {   // should be 0
195
            // curl ended with an error
196
            $cerr = \curl_error($ch);
197
            \curl_close($ch);
198
            throw new \Exception("Curl failed with response: '" . $cerr . "'.");
199
        }
200
201
        // Decode JSON from Paystack:
202
        $resp = \json_decode($response);
203
        \curl_close($ch);
204
205
        if (json_last_error() !== JSON_ERROR_NONE || !$resp->status) {
206
            throw new \Exception("Paystack Request failed with response: '" .
207
            ((json_last_error() === JSON_ERROR_NONE) ? $resp->message : $response) . "'.");
208
        }
209
210
        return $resp;
211
    }
212
    
213
    /**
214
 * __call
215
 * Insert description here
216
 *
217
 * @param $methd
218
 * @param $sentargs
219
 *
220
 * @return
221
 *
222
 * @access
223
 * @static
224
 * @see
225
 * @since
226
 */
227
    public function __call($methd, $sentargs)
228
    {
229
        $method = ($methd === 'list' ? 'getList' : $methd );
230
        if (array_key_exists($method, $this->methods) && is_callable($this->methods[$method])) {
231
            return call_user_func_array($this->methods[$method], $sentargs);
232
        } else {
233
            // User attempted to call a function that does not exist
234
            throw new \Exception('Function "' . $method . '" does not exist for "' . $this->route . '".');
235
        }
236
    }
237
238
    /**
239
 * A magic resource object that can make method calls to API
240
 *
241
 * @param $route
242
 * @param $paystackObj - A Yabacon\Paystack Object
243
 */
244
    public function __construct($route, $paystackObj)
245
    {
246
        $this->route = strtolower($route);
247
        $this->route_class = 'Yabacon\\Paystack\\Routes\\' . ucwords($route);
248
        $this->secret_key = $paystackObj->secret_key;
249
        $this->use_guzzle = $paystackObj->use_guzzle;
250
251
        $mets = get_class_methods($this->route_class);
252
        if (empty($mets)) {
253
            throw new \InvalidArgumentException('Class "' . $this->route . '" does not exist.');
254
        }
255
        // add methods to this object per method, except root
256
        foreach ($mets as $mtd) {
257
            if ($mtd === 'root') {
258
                // skip root method
259
                continue;
260
            }
261
            /**
262
 * array
263
 * Insert description here
264
 *
265
 * @param $params
266
 * @param array
267
 * @param $sentargs
268
 *
269
 * @return
270
 *
271
 * @access
272
 * @static
273
 * @see
274
 * @since
275
 */
276
            $mtdFunc = function (
277
                array $params = [ ],
278
                array $sentargs = [ ]
279
            ) use ($mtd) {
280
                $interface = call_user_func($this->route_class . '::' . $mtd);
281
                // TODO: validate params and sentargs against definitions
282
                return $this->callEndpoint($interface, $params, $sentargs);
283
            };
284
            $this->methods[$mtd] = \Closure::bind($mtdFunc, $this, get_class());
285
        }
286
    }
287
}
288