Completed
Push — master ( a95610...d81c99 )
by Ibrahim
13:32 queued 02:28
created

Router::moveArgsToSentargs()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

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