Completed
Pull Request — master (#35)
by
unknown
01:55
created

Client::callApi()   C

Complexity

Conditions 9
Paths 48

Size

Total Lines 69
Code Lines 38

Duplication

Lines 8
Ratio 11.59 %

Code Coverage

Tests 5
CRAP Score 62.045

Importance

Changes 0
Metric Value
dl 8
loc 69
ccs 5
cts 38
cp 0.1316
rs 6.2192
c 0
b 0
f 0
cc 9
eloc 38
nc 48
nop 4
crap 62.045

How to fix   Long Method   

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 Onesky\Api;
4
5
/**
6
 * Onesky API wrapper PHP5 library
7
 */
8
9
/**
10
 * @method string projectGroups(string $action, array $arrayWithProjectGroupId) Possible actions: list, show, create, delete, languages
11
 * @method string project(string $action, array $arrayWithProjectIdOrProjectGroupId = null) Possible actions: list, show, create, delete, languages
12
 * @method string files(string $action, array $arrayWithProjectId) Possible actions: list, upload, delete
13
 * @method string translations(string $action, array $arrayWithProjectId) Possible actions: export, status
14
 * @method string importTasks(string $action, array $arrayWithProjectIdAndOrImportId) Possible actions: list, show
15
 * @method string quotations(string $action, array $arrayWithProjectId) Possible actions: show
16
 * @method string orders(string $action, array $arrayWithProjectIdAndOrOrderId) Possible actions: list, show, create
17
 * @method string locales(string $action) Possible actions: list
18
 * @method string projectTypes(string $action) Possible actions: list
19
 */
20
21
class Client
22
{
23
    /**
24
     * Onesky API endpoint
25
     */
26
    protected $endpoint = 'https://platform.api.onesky.io/1';
27
28
    /**
29
     * Client authenticate token
30
     */
31
    protected $apiKey = null;
32
33
    /**
34
     * Client authenticate secret
35
     */
36
    protected $secret = null;
37
38
    /**
39
     * Resources with actions
40
     */
41
    protected $resources = array(
42
        'project_groups' => array(
43
            'list'      => '/project-groups',
44
            'show'      => '/project-groups/:project_group_id',
45
            'create'    => '/project-groups',
46
            'delete'    => '/project-groups/:project_group_id',
47
            'languages' => '/project-groups/:project_group_id/languages',
48
        ),
49
        'projects'       => array(
50
            'list'      => '/project-groups/:project_group_id/projects',
51
            'show'      => '/projects/:project_id',
52
            'create'    => '/project-groups/:project_group_id/projects',
53
            'update'    => '/projects/:project_id',
54
            'delete'    => '/projects/:project_id',
55
            'languages' => '/projects/:project_id/languages',
56
        ),
57
        'files'          => array(
58
            'list'   => '/projects/:project_id/files',
59
            'upload' => '/projects/:project_id/files',
60
            'delete' => '/projects/:project_id/files',
61
        ),
62
        'translations'   => array(
63
            'export' => '/projects/:project_id/translations',
64
            'multilingual' => '/projects/:project_id/translations/multilingual',
65
            'status' => '/projects/:project_id/translations/status',
66
        ),
67
        'import_tasks'   => array(
68
            'list' => '/projects/:project_id/import-tasks/',
69
            'show' => '/projects/:project_id/import-tasks/:import_id'
70
        ),
71
        'quotations'     => array(
72
            'show' => '/projects/:project_id/quotations'
73
        ),
74
        'orders'         => array(
75
            'list'   => '/projects/:project_id/orders',
76
            'show'   => '/projects/:project_id/orders/:order_id',
77
            'create' => '/projects/:project_id/orders'
78
        ),
79
        'locales'        => array(
80
            'list' => '/locales'
81
        ),
82
        'project_types'  => array(
83
            'list' => '/project-types'
84
        ),
85
    );
86
87
    /**
88
     * Actions to use multipart to upload file
89
     */
90
    protected $multiPartActions = array(
91
        'files' => array('upload'),
92
    );
93
94
    /**
95
     * Actions to use multipart to upload file
96
     */
97
    protected $exportFileActions = array(
98
        'translations' => array('export'),
99
    );
100
101
    /**
102
     * Methods of actions mapping
103
     */
104
    protected $methods = array(
105
        // 'get'    => array('list', 'show', 'languages', 'export', 'status'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
106
        'post'   => array('create', 'upload'),
107
        'put'    => array('update'),
108
        'delete' => array('delete'),
109
    );
110
111
    /**
112
     * Default curl settings
113
     */
114
    protected $curlSettings = array(
115
        CURLOPT_RETURNTRANSFER => true,
116
        CURLOPT_SSL_VERIFYPEER => true,
117
        CURLOPT_SSL_VERIFYHOST => 2
118
    );
119
120
    protected $httpHeaders = array(
121
        "Onesky-Plugin: php-wrapper",
122
    );
123
124 26
    public function __construct()
125
    {
126 26
        if (!function_exists('curl_init')) {
127
            throw new \Exception('OneSky needs the CURL PHP extension.');
128
        }
129 26
    }
130
131
    /**
132
     * @param string $apiKey
133
     * @return $this
134
     */
135
    public function setApiKey($apiKey)
136
    {
137
        $this->apiKey = $apiKey;
138
        return $this;
139
    }
140
141
    /**
142
     * @param string $secret
143
     * @return $this
144
     */
145
    public function setSecret($secret)
146
    {
147
        $this->secret = $secret;
148
        return $this;
149
    }
150
151
    /**
152
     * Retrieve resources
153
     * @return array
154
     */
155 25
    public function getResources()
156
    {
157 25
        return array_keys($this->resources);
158
    }
159
160
    /**
161
     * Retrieve actions of a resource
162
     * @param  string $resource Resource name
163
     * @return array|null
164
     */
165 25
    public function getActionsByResource($resource)
166
    {
167 25
        if (!isset($this->resources[$resource])) {
168
            return null; // no resource found
169
        }
170
171 25
        $actions = array();
172 25
        foreach ($this->resources[$resource] as $action => $path) {
173 25
            $actions[] = $action;
174 25
        }
175
176 25
        return $actions;
177
    }
178
179
    /**
180
     * Retrieve the corresponding method to use
181
     * @param  string $action Action name
182
     * @return string
183
     */
184 22
    public function getMethodByAction($action)
185
    {
186 22
        foreach ($this->methods as $method => $actions) {
187 22
            if (in_array($action, $actions)) {
188 10
                return $method;
189
            }
190 18
        }
191
192 12
        return 'get';
193
    }
194
195
    /**
196
     * Determine if using mulit-part to upload file
197
     * @param  string  $resource Resource name
198
     * @param  string  $action   Action name
199
     * @return boolean
200
     */
201 11
    public function isMultiPartAction($resource, $action)
202
    {
203 11
        return isset($this->multiPartActions[$resource]) && in_array($action, $this->multiPartActions[$resource]);
204
    }
205
206
    /**
207
     * Determine if it is to export (download) file
208
     * @param  string  $resource Resource name
209
     * @param  string  $action   Action name
210
     * @return boolean
211
     */
212
    public function isExportFileAction($resource, $action)
213
    {
214
        return isset($this->exportFileActions[$resource]) && in_array($action, $this->exportFileActions[$resource]);
215
    }
216
217
    /**
218
     * For developers to initial request to Onesky API
219
     * 
220
     * Example:
221
     *     $onesky = new Onesky_Api();
222
     *     $onesky->setApiKey('<api-key>')->setSecret('<api-secret>');
223
     *     
224
     *     // To list project type
225
     *     $onesky->projectTypes('list');
226
     *
227
     *     // To create project
228
     *     $onesky->projects('create', array('project_group_id' => 999));
229
     *
230
     *     // To upload string file
231
     *     $onesky->files('upload', array('project_id' => 1099, 'file' => 'path/to/file.yml', 'file_format' => 'YAML'));
232
     * 
233
     * @param  string $fn_name Function name acted as resource name
234
     * @param  array  $params  Parameters passed in request
235
     * @return array  Response
236
     */
237 25
    public function __call($fn_name, $params)
238
    {
239
        // is valid resource
240 25
        $resource = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $fn_name)); // camelcase to underscore
241 25
        if (!in_array($resource, $this->getResources())) {
242
            throw new \BadMethodCallException('Invalid resource');
243
        }
244
245
        // is valid action
246 25
        $action = array_shift($params); // action name
247 25
        if (!in_array($action, $this->getActionsByResource($resource))) {
248 3
            throw new \InvalidArgumentException('Invalid resource action');
249
        }
250
251
        // parameters
252 22
        if (count($params) > 0) {
253 11
            $params = $this->normalizeParams(array_shift($params));
254 11
        } else {
255 11
            $params = array();
256
        }
257
258
        // get request method
259 22
        $method = $this->getMethodByAction($action);
260
261
        // get path
262 22
        $path = $this->getRequestPath($resource, $action, $params);
263
264
        // is multi-part
265 11
        $isMultiPart = $this->isMultiPartAction($resource, $action);
266
267
        // return response
268 11
        return $this->callApi($method, $path, $params, $isMultiPart);
269
    }
270
271
    /**
272
     * Retrieve request path and replace variables with values
273
     * @param  string $resource Resource name
274
     * @param  string $action   Action name
275
     * @param  array  $params   Parameters
276
     * @return string Request path
277
     */
278 22
    private function getRequestPath($resource, $action, &$params)
279
    {
280 22
        if (!isset($this->resources[$resource]) || !isset($this->resources[$resource][$action])) {
281
            throw new \UnexpectedValueException('Resource path not found');
282
        }
283
284
        // get path
285 22
        $path = $this->resources[$resource][$action];
286
287
        // replace variables
288 22
        $matchCount = preg_match_all("/:(\w*)/", $path, $variables);
289 22
        if ($matchCount) {
290 22
            foreach ($variables[0] as $index => $placeholder) {
291 22
                if (!isset($params[$variables[1][$index]])) {
292 11
                    throw new \InvalidArgumentException('Missing parameter: ' . $variables[1][$index]);
293
                }
294
295 11
                $path = str_replace($placeholder, $params[$variables[1][$index]], $path);
296 11
                unset($params[$variables[1][$index]]); // remove parameter from $params
297 11
            }
298 11
        }
299
300 11
        return $path;
301
    }
302
303 11
    protected function verifyTokenAndSecret()
304
    {
305 11
        if (empty($this->apiKey) || empty($this->secret)) {
306 11
            throw new \UnexpectedValueException('Invalid authenticate data of api key or secret');
307
        }
308
    }
309
310
    /**
311
     * Initial request to Onesky API
312
     * @param  string  $method
313
     * @param  string  $path
314
     * @param  array   $params
315
     * @param  boolean $isMultiPart
316
     * @return array
317
     */
318 11
    private function callApi($method, $path, $params, $isMultiPart)
319
    {
320
        // init session
321 11
        $ch = curl_init();
322
323
        // request settings
324 11
        curl_setopt_array($ch, $this->curlSettings); // basic settings
325
326
        // url
327 11
        $url = $this->endpoint . $path;
328 11
        $url .= $method == 'get' ? $this->getAuthQueryStringWithParams($params) : $this->getAuthQueryString();
329
330
        curl_setopt($ch, CURLOPT_URL, $url);
331
        curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/certs/AmazonRootCA1.pem');
332
333
        // http header
334
        $requestHeaders = $this->httpHeaders;
335
        if (!$isMultiPart) {
336
            $requestHeaders[] = "Content-Type: application/json";
337
        }
338
        curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
339
340
        // method specific settings
341
        switch ($method) {
342
            case 'post':
343
                curl_setopt($ch, CURLOPT_POST, 1);
344
345
                // request body
346
                if ($isMultiPart) {
347
                    if (version_compare(PHP_VERSION, '5.5.0') === -1) {
348
                        // fallback to old method
349
                        $params['file'] = '@' . $params['file'];
350
                    } else {
351
                        // make use of CURLFile
352
                        curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true);
353
                        $params['file'] = new \CURLFile($params['file']);
354
                    }
355
                    $postBody = $params;
356
                } else {
357
                    $postBody = json_encode($params);
358
                }
359
                curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody);
360
                break;
361
362 View Code Duplication
            case 'put':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
363
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
364
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
365
                break;
366
367 View Code Duplication
            case 'delete':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
368
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
369
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
370
                break;
371
        }
372
373
        // execute request
374
        $response = curl_exec($ch);
375
376
        // error handling
377
        if ($response === false) {
378
            throw new \UnexpectedValueException(curl_error($ch));
379
        }
380
381
        // close connection
382
        curl_close($ch);
383
384
        // return response
385
        return $response;
386
    }
387
388
    /**
389
     * @param $params
390
     * @return string
391
     */
392 6
    private function getAuthQueryStringWithParams($params)
393
    {
394 6
        $queryString = $this->getAuthQueryString();
395
396
        if (count($params) > 0) {
397
            $queryString .= '&' . http_build_query($params);
398
        }
399
400
        return $queryString;
401
    }
402
403
    /**
404
     * @return string
405
     */
406 11
    private function getAuthQueryString()
407
    {
408 11
        $this->verifyTokenAndSecret();
409
410
        $timestamp = time();
411
        $devHash = md5($timestamp . $this->secret);
412
413
        $queryString  = '?api_key=' . $this->apiKey;
414
        $queryString .= '&timestamp=' . $timestamp;
415
        $queryString .= '&dev_hash=' . $devHash;
416
417
        return $queryString;
418
    }
419
420
    /**
421
     * @param array $params
422
     * @return array
423
     */
424 11
    private function normalizeParams(array $params)
425
    {
426
        // change boolean value to integer for curl
427 11
        foreach ($params as $key => $value) {
428 11
            if (is_bool($value)) {
429
                $params[$key] = (int)$value;
430
            }
431 11
        }
432
433 11
        return $params;
434
    }
435
}
436