Completed
Pull Request — master (#58)
by Patrick
05:05 queued 02:04
created

class.FlipREST.php (11 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
require_once('class.FlipSession.php');
3
require_once('libs/Slim/Slim/Slim.php');
4
require_once('Autoload.php');
5
\Slim\Slim::registerAutoloader();
6
7
const SUCCESS = 0;
8
const UNRECOGNIZED_METHOD = 1;
9
const INVALID_PARAM = 2;
10
const ALREADY_LOGGED_IN = 3;
11
const INVALID_LOGIN = 4;
12
const ACCESS_DENIED = 5;
13
const INTERNAL_ERROR = 6;
14
15
const UNKNOWN_ERROR = 255;
16
17
class OAuth2Auth extends \Slim\Middleware
18
{
19
    protected $headers = array();
20
21
    public function __construct($headers)
22
    {
23
        $this->headers = array_change_key_case($headers);
24
    }
25
26
    private function getUserFromSession()
27
    {
28
        if(FlipSession::isLoggedIn())
29
        {
30
            return FlipSession::getUser();
31
        }
32
        return false;
33
    }
34
35
    /*
36
     * @SuppressWarnings("Superglobals")
37
     * @SuppressWarnings("StaticAccess")
38
     */
39 View Code Duplication
    private function getUserFromBasicAuth($header)
0 ignored issues
show
This method seems to be duplicated in 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...
40
    {
41
        $auth = \AuthProvider::getInstance();
42
        $auth->login($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
43
        $user = FlipSession::getUser();
44
        if($user === false)
45
        {
46
            $data = substr($header, 6);
47
            $userpass = explode(':', base64_decode($data));
48
            $user = $auth->getUserByLogin($userpass[0], $userpass[1]);
49
        }
50
        return $user;
51
    }
52
53
    /*
54
     * @SuppressWarnings("StaticAccess")
55
     */
56
    private function getUserFromToken($header)
57
    {
58
        $auth = \AuthProvider::getInstance();
59
        $key = substr($header, 7);
60
        return $auth->getUserByAccessCode($key);
61
    }
62
63 View Code Duplication
    private function getUserFromHeader($header)
0 ignored issues
show
This method seems to be duplicated in 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...
64
    {
65
        if(strncmp($header, 'Basic', 5) == 0)
66
        {
67
            return $this->getUserFromBasicAuth($header);
68
        }
69
        return $this->getUserFromToken($header);
70
    }
71
72
    public function call()
73
    {
74
        // no auth header
75
        if(!isset($this->headers['Authorization']))
76
        {
77
            $this->app->user = $this->getUserFromSession();
78
        }
79
        else
80
        {
81
            $header = $this->headers['Authorization'];
82
            $this->app->user = $this->getUserFromHeader($header);
83
        }
84
85
        if($this->app->user === false)
86
        {
87
            $this->app->getLog()->error("No user found for call");
88
        }
89
90
        // this line is required for the application to proceed
91
        $this->next->call();
92
    }
93
}
94
95
class FlipRESTFormat extends \Slim\Middleware
96
{
97
    private function fix_encoded_element($key, $value, &$array, $prefix = '')
98
    {
99
        if(is_array($value))
100
        {
101
            $array[$key] = implode(';', $value);
102
        }
103
        else if($key === '_id' && is_object($value))
104
        {
105
            $array[$key] = $value->{'$id'};
106
        }
107
        else if(is_object($value))
108
        {
109
            $array[$key] = $this->app->request->getUrl().$this->app->request->getPath().$prefix.'/'.$key;
110
        }
111
        else if(strncmp($value, 'data:', 5) === 0)
112
        {
113
            $array[$key] = $this->app->request->getUrl().$this->app->request->getPath().$prefix.'/'.$key;
114
        }
115
    }
116
117
    private function createCSV(&$array)
118
    {
119
        if(count($array) == 0)
120
        {
121
            return null;
122
        }
123
        ob_start();
124
        $df = fopen("php://output", 'w');
125
        if(is_array($array))
126
        {
127
            $first = reset($array);
128
            $keys = FALSE;
129 View Code Duplication
            if(is_array($first))
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.

Loading history...
130
            {
131
                $keys = array_keys($first);
132
            }
133
            else if(is_object($first))
134
            {
135
                $keys = array_keys(get_object_vars($first));
136
            }
137
            fputcsv($df, $keys);
138
            foreach($array as $row)
139
            {
140
                if(is_array($row))
141
                {
142
                    $id = $row[$keys[0]];
143
                    foreach($row as $key=>$value)
144
                    {
145
                        $this->fix_encoded_element($key, $value, $row, '/'.$id);
146
                    }
147
                    fputcsv($df, $row);
148
                }
149
                else if(is_object($row))
150
                {
151
                    $keyName = $keys[0];
152
                    $id = $row->$keyName;
153
                    if(is_object($id))
154
                    {
155
                        $id = $id->{'$id'};
156
                    }
157
                    $values = get_object_vars($row);
158
                    foreach($values as $key=>$value)
159
                    {
160
                        $this->fix_encoded_element($key, $value, $values, '/'.$id);
161
                    }
162
                    fputcsv($df, $values);
163
                }
164
            }
165
        }
166
        else
167
        {
168
            $array = get_object_vars($array);
169
            fputcsv($df, array_keys($array));
170
            foreach($array as $key=>$value)
171
            {
172
                $this->fix_encoded_element($key, $value, $array);
173
            }
174
            fputcsv($df, $array);
175
        }
176
        fclose($df);
177
        return ob_get_clean();
178
    }
179
180
    private function collapseEntityToArray($entity, &$array, $keyPrefix='')
181
    {
182
        if(is_object($entity))
183
        {
184
            $entity = (array)$entity;
185
        }
186
        if(array_keys($entity) === range(0, count($entity) - 1))
187
        {
188
            $tmpCount = count($entity);
189 View Code Duplication
            for($i = 0; $i < $tmpCount; $i++)
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.

Loading history...
190
            {
191
                $tmpKey = $keyPrefix.'['.$i.']';
192
                if(is_object($entity[$i]) || is_array($entity[$i]))
193
                {
194
                    $this->collapseEntityToArray($entity[$i], $array, $tmpKey);
195
                }
196
                else
197
                {
198
                    $array[$tmpKey] = $entity[$i];
199
                }
200
            }
201
            return;
202
        }
203
204
        foreach($entity as $key=>$data)
205
        {
206
            if(is_object($data))
207
            {
208
               $data = get_object_vars($data);
209
            }
210
211
            if(is_array($data))
212
            {
213
                if(array_keys($data) !== range(0, count($data) - 1))
214
                {
215
                    //Key array
216
                    foreach($data as $childKey=>$childData)
217
                    {
218
                        $tmpKey = $keyPrefix.$key.'.'.$childKey;
219
                        if(is_object($childData) || is_array($childData))
220
                        {
221
                            $this->collapseEntityToArray($childData, $array, $tmpKey);
222
                        }
223
                        else
224
                        {
225
                            $array[$tmpKey] = $childData;
226
                        }
227
                    }
228
                }
229
                else
230
                {
231
                    //Numeric array
232
                    $tmpCount = count($data);
233 View Code Duplication
                    for($j = 0; $j < $tmpCount; $j++)
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.

Loading history...
234
                    {
235
                        $tmpKey = $keyPrefix.$key.'['.$j.']';
236
                        if(is_object($data[$j]) || is_array($data[$j]))
237
                        {
238
                            $this->collapseEntityToArray($data[$j], $array, $tmpKey);
239
                        }
240
                        else
241
                        {
242
                            $array[$tmpKey] = $data[$j];
243
                        }
244
                    }
245
                }
246
            }
247
            else
248
            {
249
                $array[$keyPrefix.$key] = $data;
250
            }
251
        }
252
    }
253
254
    private function createCSV2(&$array)
255
    {
256
        if(count($array) == 0)
257
        {
258
            return null;
259
        }
260
        if(is_object($array))
261
        {
262
            $array = get_object_vars($array);
263
        }
264
        $rowCount = count($array);
265
        $keys = array();
266
        for($i = 0; $i < $rowCount; $i++)
267
        {
268
            $row = $array[$i];
269
            if(is_object($row))
270
            {
271
                $row = get_object_vars($row);
272
                $array[$i] = $row;
273
            }
274
            if(isset($row['_empty_']))
275
            {
276
                unset($row['_empty_']);
277
                $array[$i] = $row;
278
            }
279
            if(isset($row['_id']))
280
            {
281
                $row['_id'] = $row['_id']->{'$id'};
282
                $array[$i] = $row;
283
            }
284
            $newRow = array();
285
            $this->collapseEntityToArray($row, $newRow);
286
            $row = $newRow;
287
            $array[$i] = $row;
288
            $keys = array_merge($keys, $row);
289
        }
290
        $keys = array_keys($keys);
291
        asort($keys);
292
        ob_start();
293
        $df = fopen("php://output", 'w');
294
        fputcsv($df, $keys);
295
        for($i = 0; $i < $rowCount; $i++)
296
        {
297
            $tmp = array_fill_keys($keys, '');
298
            $row = $array[$i];
299
            $tmp = array_merge($tmp, $row);
300
            ksort($tmp);
301
            fputcsv($df, $tmp);
302
        }
303
        fclose($df);
304
        return ob_get_clean();
305
    }
306
307
    private function create_excel(&$array)
0 ignored issues
show
This method is not used, and could be removed.
Loading history...
308
    {
309
        require_once dirname(__FILE__).'/libs/PHPExcel/Classes/PHPExcel.php';
310
        $ssheat = new PHPExcel();
311
        $sheat = $ssheat->setActiveSheetIndex(0);
312
        if(is_array($array))
313
        {
314
            $first = reset($array);
315
            $keys = false;
316 View Code Duplication
            if(is_array($first))
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.

Loading history...
317
            {
318
                $keys = array_keys($first);
319
            }
320
            else if(is_object($first))
321
            {
322
                $keys = array_keys(get_object_vars($first));
323
            }
324
            $col_count = count($keys);
325
            for($i = 0; $i < $col_count; $i++)
326
            {
327
                $sheat->setCellValueByColumnAndRow($i, 1, $keys[$i]);
328
            }
329
            $row_count = count($array);
330
            for($i = 0; $i < $row_count; $i++)
331
            {
332
                $row = $array[$i];
333
                if(is_object($row))
334
                {
335
                    $row = get_object_vars($row);
336
                }
337
                for($j = 0; $j < $col_count; $j++)
338
                {
339
                    $colName = $keys[$j];
340
                    if(isset($row[$colName]))
341
                    {
342
                        $value = $row[$colName];
343 View Code Duplication
                        if(is_object($value))
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.

Loading history...
344
                        {
345
                            switch($colName)
346
                            {
347
                                case '_id':
348
                                    $value = $value->{'$id'};
349
                                default:
350
                                    $value = json_encode($value);
351
                                    break;
352
                            }
353
                        }
354
                        else if(is_array($value))
355
                        {
356
                            $value = implode(',', $value);
357
                        }
358
                        $sheat->setCellValueByColumnAndRow($j, 2 + $i, $value);
359
                    }
360
                }
361
            }
362
        }
363
        $writer = PHPExcel_IOFactory::createWriter($ssheat, 'Excel2007');
364
        ob_start();
365
        $writer->save('php://output');
366
        return ob_get_clean();
367
    }
368
369
    private function createXML(&$array)
370
    {
371
        $obj = new SerializableObject($array);
372
        return $obj->xmlSerialize();
373
    }
374
375
    private function serializeData()
376
    {
377
        $data = json_decode($this->app->response->getBody());
378
        switch($this->app->fmt)
379
        {
380
            case 'data-table':
381
                $this->app->response->headers->set('Content-Type', 'application/json');
382
                return json_encode(array('data'=>$data));
383 View Code Duplication
            case 'csv':
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.

Loading history...
384
                $this->app->response->headers->set('Content-Type', 'text/csv');
385
                $path = $this->app->request->getPathInfo();
386
                $path = strrchr($path, '/');
387
                $path = substr($path, 1);
388
                $this->app->response->headers->set('Content-Disposition', 'attachment; filename='.$path.'.csv');
389
                return $this->createCSV($data);
390 View Code Duplication
            case 'csv2':
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.

Loading history...
391
                $this->app->response->headers->set('Content-Type', 'text/csv');
392
                $path = $this->app->request->getPathInfo();
393
                $path = strrchr($path, '/');
394
                $path = substr($path, 1);
395
                $this->app->response->headers->set('Content-Disposition', 'attachment; filename='.$path.'.csv');
396
                return $this->createCSV2($data);
397
            case 'xml':
398
                $this->app->response->headers->set('Content-Type', 'application/xml');
399
                return $this->createXML($data);
400
            case 'passthru':
401
                return $this->app->response->getBody();
402
            default:
403
                return 'Unknown fmt '.$this->app->fmt;
404
        }
405
    }
406
407
    private function getFormatFromHeader()
408
    {
409
        $mimeType = $this->app->request->headers->get('Accept');
410
        if(strstr($mimeType, 'odata.streaming=true'))
411
        {
412
            $this->app->response->setStatus(406);
413
            return 'json';
414
        }
415
        switch($mimeType)
416
        {
417
            case 'text/csv':
418
                return 'csv';
419
            case 'text/x-vCard':
420
                return 'vcard';
421
            default:
422
                return 'json';
423
        }
424
    }
425
426
    private function getFormat($params)
427
    {
428
        $fmt = null;
429
        if(isset($params['fmt']))
430
        {
431
            $fmt = $params['fmt'];
432
        }
433
        if($fmt === null && isset($params['$format']))
434
        {
435
            $fmt = $params['$format'];
436
            if(strstr($fmt, 'odata.streaming=true'))
437
            {
438
                $this->app->response->setStatus(406);
439
                return false;
440
            }
441
        }
442
        if($fmt === null)
443
        {
444
            $fmt = $this->getFormatFromHeader();
445
        }
446
        return $fmt;
447
    }
448
449
    public function call()
450
    {
451
        if($this->app->request->isOptions())
452
        {
453
            return;
454
        }
455
        $params = $this->app->request->params();
456
        $fmt = $this->getFormat($params);
457
        if($fmt === false)
458
        {
459
            return;
460
        }
461
462
        $this->app->fmt     = $fmt;
463
        $this->app->odata   = new ODataParams($params);
464
465
        $this->app->isLocal = false;
466
        if($_SERVER['SERVER_ADDR'] === $_SERVER['REMOTE_ADDR'])
467
        {
468
            $this->app->isLocal = true;
469
        }
470
471
        $this->next->call();
472
473
        if($this->app->response->getStatus() == 200 && $this->app->fmt !== 'json')
474
        {
475
            $text = $this->serializeData();
476
            $this->app->response->setBody($text);
477
        }
478
        else if($this->app->response->getStatus() == 200)
479
        {
480
            $this->app->response->headers->set('Content-Type', 'application/json;odata.metadata=none');
481
        }
482
    }
483
}
484
485
class FlipREST extends \Slim\Slim
486
{
487
    public function __construct()
488
    {
489
        parent::__construct();
490
        $this->config('debug', false);
491
        $headers = array();
492
        if(php_sapi_name() !== "cli")
493
        {
494
            $headers = apache_request_headers();
495
        }
496
        $this->add(new OAuth2Auth($headers));
497
        $this->add(new FlipRESTFormat());
498
        $errorHandler = array($this, 'errorHandler');
499
        $this->error($errorHandler);
500
    }
501
502
    public function get_json_body($array = false)
503
    {
504
        return $this->getJsonBody($array);
505
    }
506
507
    public function getJsonBody($array = false)
508
    {
509
        $body = $this->request->getBody();
510
        return json_decode($body, $array);
511
    }
512
513
    public function errorHandler($exception)
514
    {
515
        $error = array(
516
            'code' => $exception->getCode(),
517
            'message' => $exception->getMessage(),
518
            'file' => $exception->getFile(),
519
            'line' => $exception->getLine(),
520
        );
521
        $this->response->headers->set('Content-Type', 'application/json');
522
        error_log(print_r($error, true));
523
        echo json_encode($error);
524
    }
525
526
    function not_found_handler()
0 ignored issues
show
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...
527
    {
528
        $accept = $this->request->headers->get('Accept');
529
        if(strcmp($accept, 'application/json') == 0)
530
        {
531
            $error = array(
532
                'code' => 404,
533
                'message' => 'Not Found'
534
            );
535
            $this->response->headers->set('Content-Type', 'application/json');
536
            $this->response->setStatus(404);
537
            echo json_encode($error);
538
        }
539
        else
540
        {
541
            $this->defaultNotFound();
542
        }
543
    }
544
}
545
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
546