Completed
Push — master ( 179edc...7cecad )
by Ibrahim
14:30
created

Router::__call()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
rs 9.2
cc 4
eloc 6
nc 4
nop 2
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 $use_guzzle=false;
29
30
    const ID_KEY = 'id';
31
    const PAYSTACK_API_ROOT = 'https://api.paystack.co';
32
    const HEADER_KEY = 'header';
33
    const BODY_KEY = 'body';
34
35
 /**
36
 * moveArgsToSentargs
37
 * Insert description here
38
 *
39
 * @param $interface
40
 * @param $payload
41
 * @param $sentargs
42
 *
43
 * @return
44
 *
45
 * @access
46
 * @static
47
 * @see
48
 * @since
49
 */
50
    private function moveArgsToSentargs(
51
        $interface,
52
        &$payload,
53
        &$sentargs
54
    ) {
55
56
57
58
    // check if interface supports args
59
        if (array_key_exists(RouteInterface:: ARGS_KEY, $interface)) {
60
        // to allow args to be specified in the payload, filter them out and put them in sentargs
61
            $sentargs = (!$sentargs) ? [ ] : $sentargs; // Make sure $sentargs is not null
62
            $args = $interface[RouteInterface::ARGS_KEY];
63
            while (list($key, $value) = each($payload)) {
64
            // check that a value was specified
65
            // with a key that was expected as an arg
66
                if (in_array($key, $args)) {
67
                    $sentargs[$key] = $value;
68
                    unset($payload[$key]);
69
                }
70
            }
71
        }
72
    }
73
74
 /**
75
 * putArgsIntoEndpoint
76
 * Insert description here
77
 *
78
 * @param $endpoint
79
 * @param $sentargs
80
 *
81
 * @return
82
 *
83
 * @access
84
 * @static
85
 * @see
86
 * @since
87
 */
88
    private function putArgsIntoEndpoint(&$endpoint, $sentargs)
89
    {
90
    // substitute sentargs in endpoint
91
        while (list($key, $value) = each($sentargs)) {
92
            $endpoint = str_replace('{' . $key . '}', $value, $endpoint);
93
        }
94
    }
95
96
 /**
97
 * callViaCurl
98
 * Insert description here
99
 *
100
 * @param $interface
101
 * @param $payload
102
 * @param $sentargs
103
 *
104
 * @return
105
 *
106
 * @access
107
 * @static
108
 * @see
109
 * @since
110
 */
111
    private function callViaCurl($interface, $payload = [ ], $sentargs = [ ])
112
    {
113
 
114
115
        $endpoint = Router::PAYSTACK_API_ROOT . $interface[RouteInterface::ENDPOINT_KEY];
116
        $method = $interface[RouteInterface::METHOD_KEY];
117
118
        $this->moveArgsToSentargs($interface, $payload, $sentargs);
119
        $this->putArgsIntoEndpoint($endpoint, $sentargs);
120
 
121
        $headers = ["Authorization"=>"Bearer " . $this->secret_key ];
122
        if (($method === RouteInterface::POST_METHOD)||
123
        ($method === RouteInterface::PUT_METHOD)) {
124
            $headers["Content-Type"] = "application/json";
125
            $body = json_encode($payload);
126
        } elseif ($method === RouteInterface::GET_METHOD) {
127
            $endpoint = $endpoint . '?' . http_build_query($payload);
128
            $body='';
129
        }
130
    // Use Guzzle if found, else use Curl
131
        if ($this->use_guzzle && class_exists('\\GuzzleHttp\\Client') && class_exists('\\GuzzleHttp\\Psr7\\Request')) {
132
 
133
            $request = new \GuzzleHttp\Psr7\Request(strtoupper($method), $endpoint, $headers, $body);
0 ignored issues
show
Bug introduced by
The variable $body does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
134
            $client = new \GuzzleHttp\Client(['http_errors' => false]);
135
            $response = $client->send($request);
136
            return $response;
137
            
138
        } else {
139
        //open connection
140
        
141
            $ch = \curl_init();
142
        // set url
143
            \curl_setopt($ch, \CURLOPT_URL, $endpoint);
144
 
145
            if ($method === RouteInterface::POST_METHOD || $method === RouteInterface::PUT_METHOD) {
146
                ($method === RouteInterface:: POST_METHOD) && \curl_setopt($ch, \CURLOPT_POST, true);
147
                ($method === RouteInterface ::PUT_METHOD) && \curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
148
149
                \curl_setopt($ch, \CURLOPT_POSTFIELDS, $body);
150
            }
151
        //flatten the headers
152
            $flattened_headers = [];
153
            while (list($key, $value) = each($headers)) {
154
                $flattened_headers[] = $key . ": " . $value;
155
            }
156
            \curl_setopt($ch, \CURLOPT_HTTPHEADER, $flattened_headers);
157
            \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, 1);
158
            \curl_setopt($ch, \CURLOPT_HEADER, 1);
159
160
            $response = \curl_exec($ch);
161
162
        // Then, after your \curl_exec call:
163
            $header_size = \ curl_getinfo($ch, CURLINFO_HEADER_SIZE);
164
            $header = substr($response, 0, $header_size);
165
            $header = $this->headers_from_lines(explode("\n", trim($header)));
166
            $body = substr($response, $header_size);
167
            $body = json_decode($body, true);
168
            
169
170
        //close connection
171
            \curl_close($ch);
172
173
            return [
174
            Router ::HEADER_KEY => $header, Router::BODY_KEY => $body ];
175
        }
176
177
    }
178
    
179
    function headers_from_lines($lines)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
180
    {
181
        $headers = [];
182
        foreach ($lines as $line) {
183
            $parts = explode(':', $line, 2);
184
            $headers[trim($parts[0])][] = isset($parts[1])
185
            ? trim($parts[1])
186
            : null;
187
        }
188
        return $headers;
189
    }
190
191
 /**
192
 * __call
193
 * Insert description here
194
 *
195
 * @param $methd
196
 * @param $sentargs
197
 *
198
 * @return
199
 *
200
 * @access
201
 * @static
202
 * @see
203
 * @since
204
 */
205
    public function __call($methd, $sentargs)
206
    {
207
        $method = ($methd === 'list' ? 'getList' : $methd );
208
        if (array_key_exists($method, $this->methods) && is_callable($this->methods[$method])) {
209
            return call_user_func_array($this->methods[$method], $sentargs);
0 ignored issues
show
Bug introduced by
The property methods 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...
210
        } else {
211
        // User attempted to call a function that does not exist
212
            throw new \Exception('Function "' . $method . '" does not exist for "' . $this->route . "'.");
213
        }
214
    }
215
216
 /**
217
 * __construct
218
 * Insert description here
219
 *
220
 * @param $route
221
 * @param $secret_key
222
 *
223
 * @return
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
224
 *
225
 * @access
226
 * @static
227
 * @see
228
 * @since
229
 */
230
    public function __construct($route, $paystackObj)
231
    {
232
        $this->route = strtolower($route);
233
        $this->route_class = 'YabaCon\\Paystack\\Routes\\' . ucwords($route);
234
        $this->secret_key = $paystackObj->secret_key;
235
        $this->use_guzzle = $paystackObj->use_guzzle;
236
237
        $mets = get_class_methods($this->route_class);
238
    // add methods to this object per method, except root
239
        foreach ($mets as $mtd) {
240
            if ($mtd === 'root') {
241
            // skip root method
242
                continue;
243
            }
244
        /**
245
 * array
246
 * Insert description here
247
 *
248
 * @param $params
249
 * @param array
250
 * @param $sentargs
251
 *
252
 * @return
253
 *
254
 * @access
255
 * @static
256
 * @see
257
 * @since
258
 */
259
            $mtdFunc = function (
260
                array $params = [ ],
261
                array $sentargs = [ ]
262
            ) use ($mtd) {
263
                $interface = call_user_func($this->route_class . '::' . $mtd);
264
            // TODO: validate params and sentargs against definitions
265
                return $this->callViaCurl($interface, $params, $sentargs);
266
            };
267
            $this->methods[$mtd] = \Closure::bind($mtdFunc, $this, get_class());
268
        }
269
    }
270
}
271