Completed
Push — master ( 2d5249...c54b34 )
by Ibrahim
02:37
created

Caller   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 22.58%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 41
lcom 1
cbo 3
dl 0
loc 146
ccs 21
cts 93
cp 0.2258
rs 8.2769
c 1
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B moveArgsToSentargs() 0 20 5
A putArgsIntoEndpoint() 0 7 2
D attemptGuzzle() 0 34 18
B callEndpoint() 0 26 5
D attemptCurl() 0 43 10

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
namespace Yabacon\Paystack\Helpers;
4
5
use \Closure;
6
use \Yabacon\Paystack\Contracts\RouteInterface;
7
8
class Caller
9
{
10
    private $use_guzzle=false;
11
    private $secret_key;
12
13 2
    public function __construct($paystackObj)
14
    {
15 2
        $this->secret_key = $paystackObj->secret_key;
16 2
        $this->use_guzzle = $paystackObj->use_guzzle;
17 2
    }
18
19 1
    public function moveArgsToSentargs(
20
        $interface,
21
        &$payload,
22
        &$sentargs
23
    ) {
24
        // check if interface supports args
25 1
        if (array_key_exists(RouteInterface:: ARGS_KEY, $interface)) {
26
            // to allow args to be specified in the payload, filter them out and put them in sentargs
27 1
            $sentargs = (!$sentargs) ? [ ] : $sentargs; // Make sure $sentargs is not null
28 1
            $args = $interface[RouteInterface::ARGS_KEY];
29 1
            while (list($key, $value) = each($payload)) {
30
                // check that a value was specified
31
                // with a key that was expected as an arg
32 1
                if (in_array($key, $args)) {
33 1
                    $sentargs[$key] = $value;
34 1
                    unset($payload[$key]);
35 1
                }
36 1
            }
37 1
        }
38 1
    }
39
40 1
    public function putArgsIntoEndpoint(&$endpoint, $sentargs)
41
    {
42
        // substitute sentargs in endpoint
43 1
        while (list($key, $value) = each($sentargs)) {
44 1
            $endpoint = str_replace('{' . $key . '}', $value, $endpoint);
45 1
        }
46 1
    }
47
    
48
    private function attemptGuzzle($method, $endpoint, $headers, $body)
49
    {
50
        if ($this->use_guzzle 
51
            && class_exists('\\GuzzleHttp\\Exception\\BadResponseException') 
52
            && class_exists('\\GuzzleHttp\\Exception\\ClientException') 
53
            && class_exists('\\GuzzleHttp\\Exception\\ConnectException') 
54
            && class_exists('\\GuzzleHttp\\Exception\\RequestException') 
55
            && class_exists('\\GuzzleHttp\\Exception\\ServerException') 
56
            && class_exists('\\GuzzleHttp\\Exception\\TooManyRedirectsException') 
57
            && class_exists('\\GuzzleHttp\\Client') 
58
            && class_exists('\\GuzzleHttp\\Psr7\\Request')
59
        ) {
60
            $request = new \GuzzleHttp\Psr7\Request(strtoupper($method), $endpoint, $headers, $body);
61
            $client = new \GuzzleHttp\Client();
62
            try {
63
                $response = $client->send($request);
64
            } catch (\Exception $e) {
65
                if (($e instanceof \GuzzleHttp\Exception\BadResponseException
66
                    || $e instanceof \GuzzleHttp\Exception\ClientException
67
                    || $e instanceof \GuzzleHttp\Exception\ConnectException
68
                    || $e instanceof \GuzzleHttp\Exception\RequestException
69
                    || $e instanceof \GuzzleHttp\Exception\ServerException
70
                    || $e instanceof \GuzzleHttp\Exception\TooManyRedirectsException) && $e->hasResponse()
71
                ) {
72
                    $response = $e->getResponse();
73
                } else {
74
                    throw $e;
75
                }
76
            }
77
            return $response;
78
        } else {
79
            return false;
80
        }
81
    }
82
83
    public function callEndpoint($interface, $payload = [ ], $sentargs = [ ])
84
    {
85
        $endpoint = Router::PAYSTACK_API_ROOT . $interface[RouteInterface::ENDPOINT_KEY];
86
        $method = $interface[RouteInterface::METHOD_KEY];
87
88
        $this->moveArgsToSentargs($interface, $payload, $sentargs);
89
        $this->putArgsIntoEndpoint($endpoint, $sentargs);
90
 
91
        $headers = ["Authorization"=>"Bearer " . $this->secret_key ];
92
        $body = '';
93
        if (($method === RouteInterface::POST_METHOD)
94
            || ($method === RouteInterface::PUT_METHOD)
95
        ) {
96
            $headers["Content-Type"] = "application/json";
97
            $body = json_encode($payload);
98
        } elseif ($method === RouteInterface::GET_METHOD) {
99
            $endpoint = $endpoint . '?' . http_build_query($payload);
100
        }
101
        // Use Guzzle if found, else use Curl
102
        $guzzleResponse = $this->attemptGuzzle($method, $endpoint, $headers, $body);
103
        if ($guzzleResponse !== false) {
104
            return $guzzleResponse;
105
        }
106
        
107
        return $this->attemptCurl($method, $endpoint, $headers, $body);
108
    }
109
110
    private function attemptCurl($method, $endpoint, $headers, $body)
111
    {
112
        //open connection
113
        $ch = \curl_init();
114
        \curl_setopt($ch, \CURLOPT_URL, $endpoint);
115
116
        if ($method === RouteInterface::POST_METHOD || $method === RouteInterface::PUT_METHOD) {
117
            ($method === RouteInterface:: POST_METHOD) && \curl_setopt($ch, \CURLOPT_POST, true);
118
            ($method === RouteInterface ::PUT_METHOD) && \curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, "PUT");
119
120
            \curl_setopt($ch, \CURLOPT_POSTFIELDS, $body);
121
        }
122
        //flatten the headers
123
        $flattened_headers = [];
124
        while (list($key, $value) = each($headers)) {
125
            $flattened_headers[] = $key . ": " . $value;
126
        }
127
        \curl_setopt($ch, \CURLOPT_HTTPHEADER, $flattened_headers);
128
        \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, 1);
129
        \curl_setopt($ch, \CURLOPT_SSLVERSION, 6);
130
131
        $response = \curl_exec($ch);
132
        
133
        if (\curl_errno($ch)) {   // should be 0
134
            // curl ended with an error
135
            $cerr = \curl_error($ch);
136
            \curl_close($ch);
137
            throw new \Exception("Curl failed with response: '" . $cerr . "'.");
138
        }
139
140
        // Decode JSON from Paystack:
141
        $resp = \json_decode($response);
142
        \curl_close($ch);
143
144
        if (json_last_error() !== JSON_ERROR_NONE || !$resp->status) {
145
            throw new \Exception(
146
                "Paystack Request failed with response: '" .
147
                ((json_last_error() === JSON_ERROR_NONE) ? $resp->message : $response) . "'."
148
            );
149
        }
150
151
        return $resp;
152
    }
153
}
154