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