GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( ae9f57...8a0092 )
by Steeven
02:24
created

Restful::unpublish()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 13
c 1
b 0
f 0
nc 9
nop 0
dl 0
loc 21
rs 9.5222
1
<?php
2
/**
3
 * This file is part of the O2System Framework package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @author         Steeve Andrian Salim
9
 * @copyright      Copyright (c) Steeve Andrian Salim
10
 */
11
12
// ------------------------------------------------------------------------
13
14
namespace O2System\Framework\Http\Controllers;
15
16
// ------------------------------------------------------------------------
17
18
use O2System\Cache\Item;
19
use O2System\Framework\Http\Controller;
20
use O2System\Framework\Models\Sql\Model;
21
use O2System\Psr\Http\Header\ResponseFieldInterface;
22
use O2System\Security\Filters\Rules;
23
use O2System\Spl\Exceptions\Logic\OutOfRangeException;
24
25
/**
26
 * Class Restful
27
 *
28
 * @package O2System\Framework\Http\Controllers
29
 */
30
class Restful extends Controller
31
{
32
    /**
33
     * Push headers flag
34
     *
35
     * Used for push default headers by controller.
36
     * Set to FALSE if you want to set default headers on the web-server configuration.
37
     *
38
     * APACHE via .htaccess
39
     * IIS via .webconfig
40
     * Nginx via config
41
     *
42
     * @type string
43
     */
44
    protected $pushDefaultHeaders = false;
45
46
    /**
47
     * Access-Control-Allow-Origin
48
     *
49
     * Used for indicates whether a resource can be shared based by
50
     * returning the value of the Origin request header, "*", or "null" in the response.
51
     *
52
     * @type string
53
     */
54
    protected $accessControlAllowOrigin = '*';
55
56
    /**
57
     * Access-Control-Allow-Credentials
58
     *
59
     * Used for indicates whether the response to request can be exposed when the omit credentials flag is unset.
60
     * When part of the response to a preflight request it indicates that the actual request can include user
61
     * credentials.
62
     *
63
     * @type bool
64
     */
65
    protected $accessControlAllowCredentials = true;
66
67
    /**
68
     * Access-Control-Method
69
     *
70
     * @var string
71
     */
72
    protected $accessControlMethod = 'GET';
73
74
    /**
75
     * Access-Control-Params
76
     *
77
     * @var array
78
     */
79
    protected $accessControlParams = [];
80
81
    /**
82
     * Access-Control-Allow-Methods
83
     *
84
     * Used for indicates, as part of the response to a preflight request,
85
     * which methods can be used during the actual request.
86
     *
87
     * @type array
88
     */
89
    protected $accessControlAllowMethods = [
90
        'GET', // common request
91
        'POST', // used for create, update request
92
        'PUT', // used for upload files request
93
        'DELETE', // used for delete request
94
        'OPTIONS', // used for preflight request
95
    ];
96
97
    /**
98
     * Access-Control-Allow-Headers
99
     *
100
     * Used for indicates, as part of the response to a preflight request,
101
     * which header field names can be used during the actual request.
102
     *
103
     * @type array
104
     */
105
    protected $accessControlAllowHeaders = [
106
        'Origin',
107
        'Access-Control-Request-Method',
108
        'Access-Control-Request-Headers',
109
        'X-Api-Authenticate', // API-Authenticate: api_key="xxx", api_secret="xxx", api_signature="xxx"
110
        'X-Api-Token',
111
        'X-Web-Token', // X-Web-Token: xxx (json-web-token)
112
        'X-Csrf-Token',
113
        'X-Xss-Token',
114
        'X-Request-ID',
115
        'X-Requested-With',
116
        'X-Requested-Result',
117
    ];
118
119
    /**
120
     * Access-Control-Allow-Headers
121
     *
122
     * Used for indicates, as part of the response to a preflight request,
123
     * which header field names can be used during the actual request.
124
     *
125
     * @type array
126
     */
127
    protected $accessControlAllowContentTypes = [
128
        'text/html',
129
        'application/json',
130
        'application/xml',
131
    ];
132
133
    /**
134
     * Access-Control-Max-Age
135
     *
136
     * Used for indicates how long the results of a preflight request can be cached in a preflight result cache
137
     *
138
     * @type int
139
     */
140
    protected $accessControlMaxAge = 86400;
141
142
    /**
143
     * Restful::$ajaxOnly
144
     *
145
     * @var bool
146
     */
147
    protected $ajaxOnly = false;
148
149
    /**
150
     * Restful::$model
151
     *
152
     * @var \O2System\Framework\Models\Sql\Model|\O2System\Framework\Models\NoSql\Model|\O2System\Framework\Models\Files\Model
153
     */
154
    public $model;
155
156
    /**
157
     * Restful::$params
158
     *
159
     * @var array
160
     */
161
    public $params = [];
162
163
    /**
164
     * Restful::$paramsWithRules
165
     *
166
     * @var array
167
     */
168
    public $paramsWithRules = [];
169
170
    /**
171
     * Restful::$fillableColumns
172
     *
173
     * @var array
174
     */
175
    public $fillableColumns = [];
176
177
    /**
178
     * Restful::$fillableColumnsWithRules
179
     *
180
     * @var array
181
     */
182
    public $fillableColumnsWithRules = [];
183
184
    /**
185
     * Restful::$dataTableColumns
186
     *
187
     * @var array
188
     */
189
    public $dataTableColumns = [];
190
191
    // ------------------------------------------------------------------------
192
193
    /**
194
     * Restful::__construct
195
     */
196
    public function __construct()
197
    {
198
        if (services()->has('presenter')) {
199
            presenter()->setTheme(false);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $theme of O2System\Framework\Http\Presenter::setTheme(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

199
            presenter()->setTheme(/** @scrutinizer ignore-type */ false);
Loading history...
200
        }
201
202
        if (is_ajax()) {
203
            output()->setContentType('application/json');
0 ignored issues
show
Bug introduced by
The method setContentType() does not exist on O2System\Kernel\Cli\Output. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

203
            output()->/** @scrutinizer ignore-call */ setContentType('application/json');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
204
        } elseif ($this->ajaxOnly === false) {
205
            output()->setContentType('application/json');
206
        } else {
207
            output()->setContentType('text/html');
208
        }
209
210
        if ($contentType = input()->server('HTTP_X_REQUESTED_CONTENT_TYPE')) {
211
            if (in_array($contentType, $this->accessControlAllowContentTypes)) {
212
                output()->setContentType($contentType);
213
            }
214
        }
215
216
        if ($this->pushDefaultHeaders) {
217
218
            $origin = input()->server('HTTP_ORIGIN');
219
220
            /**
221
             * Prepare for preflight modern browser request
222
             *
223
             * Since some server cannot use 'Access-Control-Allow-Origin: *'
224
             * the Access-Control-Allow-Origin will be defined based on requested origin
225
             */
226
            if ($this->accessControlAllowOrigin === '*') {
227
                output()->addHeader(ResponseFieldInterface::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN, $origin);
0 ignored issues
show
Bug introduced by
The method addHeader() does not exist on O2System\Kernel\Cli\Output. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

227
                output()->/** @scrutinizer ignore-call */ addHeader(ResponseFieldInterface::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN, $origin);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
228
            }
229
230
            // Set response access control allowed credentials
231
            if ($this->accessControlAllowCredentials === false) {
232
                output()->addHeader(ResponseFieldInterface::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS, 'false');
233
            }
234
235
            // Set response access control allowed methods header
236
            if (count($this->accessControlAllowMethods)) {
237
                output()->addHeader(
238
                    ResponseFieldInterface::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS,
239
                    implode(', ', $this->accessControlAllowMethods)
240
                );
241
            }
242
243
            // Set response access control allowed headers header
244
            if (count($this->accessControlAllowHeaders)) {
245
                output()->addHeader(
246
                    ResponseFieldInterface::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS,
247
                    implode(', ', $this->accessControlAllowHeaders)
248
                );
249
            }
250
251
            // Set response access control allowed content types header
252
            if (count($this->accessControlAllowContentTypes)) {
253
                output()->addHeader(
254
                    ResponseFieldInterface::RESPONSE_ACCESS_CONTROL_ALLOW_CONTENT_TYPES,
255
                    implode(', ', $this->accessControlAllowContentTypes)
256
                );
257
            }
258
259
            // Set response access control max age header
260
            if ($this->accessControlMaxAge > 0) {
261
                output()->addHeader(ResponseFieldInterface::RESPONSE_ACCESS_CONTROL_MAX_AGE,
262
                    $this->accessControlMaxAge);
263
            }
264
        }
265
266
        if (input()->server('REQUEST_METHOD') === 'OPTIONS') {
267
            exit(EXIT_SUCCESS);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
268
        } elseif ( ! in_array(input()->server('REQUEST_METHOD'), $this->accessControlAllowMethods)) {
269
            $this->sendError(405);
270
        } elseif (count($this->accessControlParams)) {
271
            if ($this->accessControlMethod === 'GET') {
272
                if (empty($_GET)) {
273
                    $this->sendError(400);
274
                }
275
            } elseif ($this->accessControlMethod === 'POST') {
276
                if (empty($_POST)) {
277
                    $this->sendError(400);
278
                }
279
            } elseif (in_array($this->accessControlMethod, ['GETPOST', 'POSTGET'])) {
280
                if (empty($_REQUEST)) {
281
                    $this->sendError(400);
282
                }
283
            }
284
        }
285
286
        if (empty($this->model)) {
287
            $controllerClassName = get_called_class();
288
            $modelClassName = str_replace('Controllers', 'Models', $controllerClassName);
289
290
            if (class_exists($modelClassName)) {
291
                $this->model = new $modelClassName();
292
            }
293
        } elseif (class_exists($this->model)) {
0 ignored issues
show
Bug introduced by
$this->model of type O2System\Framework\Model...mework\Models\Sql\Model is incompatible with the type string expected by parameter $class_name of class_exists(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

293
        } elseif (class_exists(/** @scrutinizer ignore-type */ $this->model)) {
Loading history...
294
            $this->model = new $this->model();
295
        }
296
    }
297
298
    // ------------------------------------------------------------------------
299
300
    /**
301
     * Restful::index
302
     */
303
    public function index()
304
    {
305
        if (empty($this->model)) {
306
            output()->sendError(204);
307
        } else {
308
            if ( ! $this->model instanceof Model) {
309
                $this->sendError(503, 'Model is not exists!');
310
            }
311
312
            if ( ! $this->model instanceof Model) {
0 ignored issues
show
introduced by
$this->model is always a sub-type of O2System\Framework\Models\Sql\Model.
Loading history...
313
                $this->sendError(503, 'Model is not exists!');
314
            }
315
316
            if (count($this->params)) {
317
                if ($get = input()->get()) {
318
                    if (false !== ($result = $this->model->withPaging()->findWhere($get->getArrayCopy()))) {
319
                        if ($result->count()) {
320
                            $this->sendPayload($result);
321
                        } else {
322
                            $this->sendError(204);
323
                        }
324
                    } else {
325
                        $this->sendError(204);
326
                    }
327
                } else {
328
                    $this->sendError(400, 'Get parameters cannot be empty!');
329
                }
330
            } elseif (count($this->paramsWithRules)) {
331
                if ($get = input()->get()) {
332
                    $rules = new Rules($get);
0 ignored issues
show
Bug introduced by
$get of type O2System\Spl\DataStructures\SplArrayObject is incompatible with the type array expected by parameter $sourceVars of O2System\Security\Filters\Rules::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

332
                    $rules = new Rules(/** @scrutinizer ignore-type */ $get);
Loading history...
333
                    $rules->sets($this->paramsWithRules);
334
335
                    if ( ! $rules->validate()) {
336
                        $this->sendError(400, implode(', ', $rules->displayErrors(true)));
0 ignored issues
show
Unused Code introduced by
The call to O2System\Security\Filters\Rules::displayErrors() has too many arguments starting with true. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

336
                        $this->sendError(400, implode(', ', $rules->/** @scrutinizer ignore-call */ displayErrors(true)));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
It seems like $rules->displayErrors(true) can also be of type string; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

336
                        $this->sendError(400, implode(', ', /** @scrutinizer ignore-type */ $rules->displayErrors(true)));
Loading history...
337
                    } else {
338
                        $conditions = [];
339
340
                        foreach ($this->paramsWithRules as $param) {
341
                            if ($get->offsetExists($param[ 'field' ])) {
342
                                $conditions[ $param[ 'field' ] ] = $get->offsetGet($param[ 'field' ]);
343
                            }
344
                        }
345
346
                        if (false !== ($result = $this->model->withPaging()->findWhere($conditions))) {
347
                            if ($result->count()) {
348
                                $this->sendPayload($result);
349
                            } else {
350
                                $this->sendError(204);
351
                            }
352
                        } else {
353
                            $this->sendError(204);
354
                        }
355
                    }
356
                } else {
357
                    $this->sendError(400, 'Get parameters cannot be empty!');
358
                }
359
            } elseif ($get = input()->get()) {
360
                if (false !== ($result = $this->model->withPaging()->findWhere($get->getArrayCopy()))) {
361
                    if ($result->count()) {
362
                        $this->sendPayload($result);
363
                    } else {
364
                        $this->sendError(204);
365
                    }
366
                } else {
367
                    $this->sendError(204);
368
                }
369
            } else {
370
                if (false !== ($result = $this->model->allWithPaging())) {
371
                    $this->sendPayload($result);
372
                } else {
373
                    $this->sendError(204);
374
                }
375
            }
376
        }
377
    }
378
379
    // ------------------------------------------------------------------------
380
381
    /**
382
     * Restful::datatable
383
     */
384
    public function datatable()
385
    {
386
        if (empty($this->model)) {
387
            output()->sendError(204);
388
        } else {
389
            if ( ! $this->model instanceof Model) {
390
                $this->sendError(503, 'Model is not exists!');
391
            }
392
393
            if ( ! $this->model instanceof Model) {
0 ignored issues
show
introduced by
$this->model is always a sub-type of O2System\Framework\Models\Sql\Model.
Loading history...
394
                $this->sendError(503, 'Model is not exists!');
395
            }
396
397
            // Start as limit
398
            if ($start = input()->request('start')) {
399
                $this->model->qb->limit($start);
0 ignored issues
show
Bug introduced by
It seems like $start can also be of type O2System\Spl\DataStructures\SplArrayObject; however, parameter $limit of O2System\Database\Sql\Ab...ctQueryBuilder::limit() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

399
                $this->model->qb->limit(/** @scrutinizer ignore-type */ $start);
Loading history...
400
            }
401
402
            // Length as offset
403
            if (($length = input()->request('length')) != -1) {
404
                $this->model->qb->offset($length);
0 ignored issues
show
Bug introduced by
It seems like $length can also be of type O2System\Spl\DataStructures\SplArrayObject; however, parameter $offset of O2System\Database\Sql\Ab...tQueryBuilder::offset() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

404
                $this->model->qb->offset(/** @scrutinizer ignore-type */ $length);
Loading history...
405
            }
406
407
            /**
408
             * @example
409
             * $dataTableColumns = [
410
             *     'first_name',
411
             *     'start_date' => [
412
             *         'formatter' => function($row) {
413
             *              $row->date = date('jS M y', strtotime($row->date));
414
             *         }
415
             *     ]
416
             * ];
417
             */
418
            if (count($this->dataTableColumns)) {
419
                $i = 0;
420
                foreach ($this->dataTableColumns as $number => $column) {
421
                    if (is_array($column)) {
422
                        $field = $number;
423
                        $number = $i;
424
                    } else {
425
                        $field = $column;
426
                    }
427
428
                    if ($columns = input()->request('columns')) {
429
                        if (isset($columns[ $number ])) {
430
                            if (isset($columns[ $number ][ 'searchable' ])) {
431
                                if ($columns[ $number ][ 'searchable' ] === 'true') {
432
                                    if ($search = input()->request('search')) {
433
                                        if (isset($search[ 'value' ])) {
434
                                            $this->model->qb->orLike($field, $search[ 'value' ]);
435
                                        }
436
                                    }
437
                                }
438
                            }
439
440
                            if (isset($columns[ $number ][ 'orderable' ])) {
441
                                if ($columns[ $number ][ 'orderable' ] === 'true') {
442
                                    if ($order = input()->request('order')) {
443
                                        if (isset($order[ $number ][ 'dir' ])) {
444
                                            $this->model->orderBy($field, strtoupper($order[ $number ][ 'dir' ]));
0 ignored issues
show
Bug introduced by
The method orderBy() does not exist on O2System\Framework\Models\Sql\Model. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

444
                                            $this->model->/** @scrutinizer ignore-call */ 
445
                                                          orderBy($field, strtoupper($order[ $number ][ 'dir' ]));
Loading history...
445
                                        }
446
                                    }
447
                                }
448
                            }
449
                        }
450
                    }
451
452
                    $i++;
453
                }
454
455
                $this->model->rebuildRowCallback(function ($row) {
456
                    $row->DT_RowId = 'datatable-row-' . $row->id;
457
458
                    foreach ($this->dataTableColumns as $number => $column) {
459
                        if (is_array($column)) {
460
                            if (isset($column[ 'formatter' ])) {
461
                                call_user_func($column[ 'formatter' ], $row);
462
                            }
463
                        }
464
                    }
465
                });
466
467
            } elseif (count($this->model->visibleColumns)) {
468
                // Search value
469
                foreach ($this->model->visibleColumns as $number => $field) {
470
                    if ($columns = input()->request('columns')) {
471
                        if (isset($columns[ $number ])) {
472
                            if (isset($columns[ $number ][ 'searchable' ])) {
473
                                if ($columns[ $number ][ 'searchable' ] === 'true') {
474
                                    if ($search = input()->request('search')) {
475
                                        if (isset($search[ 'value' ])) {
476
                                            $this->model->qb->orLike($field, $search[ 'value' ]);
477
                                        }
478
                                    }
479
                                }
480
                            }
481
482
                            if (isset($columns[ $number ][ 'orderable' ])) {
483
                                if ($columns[ $number ][ 'orderable' ] === 'true') {
484
                                    if ($order = input()->request('order')) {
485
                                        if (isset($order[ $number ][ 'dir' ])) {
486
                                            $this->model->orderBy($field, strtoupper($order[ $number ][ 'dir' ]));
487
                                        }
488
                                    }
489
                                }
490
                            }
491
                        }
492
                    }
493
                }
494
495
                $this->model->rebuildRowCallback(function ($row) {
496
                    $row->DT_RowId = 'datatable-row-' . $row->id;
497
                });
498
            }
499
500
            if (count($this->params)) {
501
                if ($get = input()->get()) {
502
                    if (false !== ($result = $this->model->withPaging()->findWhere($get->getArrayCopy()))) {
503
                        if ($result->count()) {
504
                            $this->sendPayload([
505
                                'draw'            => input()->request('draw'),
506
                                'recordsTotal'    => $result->info->num_total,
0 ignored issues
show
Bug Best Practice introduced by
The property $info is declared protected in O2System\Framework\Models\Sql\DataObjects\Result. Since you implement __get, consider adding a @property or @property-read.
Loading history...
507
                                'recordsFiltered' => $result->info->num_founds,
508
                                'data'            => $result->toArray(),
509
                            ]);
510
                        } else {
511
                            $this->sendError(204);
512
                        }
513
                    } else {
514
                        $this->sendError(204);
515
                    }
516
                } else {
517
                    $this->sendError(400, 'Get parameters cannot be empty!');
518
                }
519
            } elseif (count($this->paramsWithRules)) {
520
                if ($get = input()->get()) {
521
                    $rules = new Rules($get);
0 ignored issues
show
Bug introduced by
$get of type O2System\Spl\DataStructures\SplArrayObject is incompatible with the type array expected by parameter $sourceVars of O2System\Security\Filters\Rules::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

521
                    $rules = new Rules(/** @scrutinizer ignore-type */ $get);
Loading history...
522
                    $rules->sets($this->paramsWithRules);
523
524
                    if ( ! $rules->validate()) {
525
                        $this->sendError(400, implode(', ', $rules->displayErrors(true)));
0 ignored issues
show
Unused Code introduced by
The call to O2System\Security\Filters\Rules::displayErrors() has too many arguments starting with true. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

525
                        $this->sendError(400, implode(', ', $rules->/** @scrutinizer ignore-call */ displayErrors(true)));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
It seems like $rules->displayErrors(true) can also be of type string; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

525
                        $this->sendError(400, implode(', ', /** @scrutinizer ignore-type */ $rules->displayErrors(true)));
Loading history...
526
                    } else {
527
                        $conditions = [];
528
529
                        foreach ($this->paramsWithRules as $param) {
530
                            if ($get->offsetExists($param[ 'field' ])) {
531
                                $conditions[ $param[ 'field' ] ] = $get->offsetGet($param[ 'field' ]);
532
                            }
533
                        }
534
535
                        if (false !== ($result = $this->model->withPaging()->findWhere($conditions))) {
536
                            if ($result->count()) {
537
                                $this->sendPayload([
538
                                    'draw'            => input()->request('draw'),
539
                                    'recordsTotal'    => $result->info->num_total,
540
                                    'recordsFiltered' => $result->info->num_founds,
541
                                    'data'            => $result->toArray(),
542
                                ]);
543
                            } else {
544
                                $this->sendError(204);
545
                            }
546
                        } else {
547
                            $this->sendError(204);
548
                        }
549
                    }
550
                } else {
551
                    $this->sendError(400, 'Get parameters cannot be empty!');
552
                }
553
            } elseif ($get = input()->get()) {
554
                if (false !== ($result = $this->model->withPaging()->findWhere($get->getArrayCopy()))) {
555
                    if ($result->count()) {
556
                        $this->sendPayload([
557
                            'draw'            => input()->request('draw'),
558
                            'recordsTotal'    => $result->info->num_total,
559
                            'recordsFiltered' => $result->info->num_founds,
560
                            'data'            => $result->toArray(),
561
                        ]);
562
                    } else {
563
                        $this->sendError(204);
564
                    }
565
                } else {
566
                    $this->sendError(204);
567
                }
568
            } else {
569
                if (false !== ($result = $this->model->allWithPaging())) {
570
                    $this->sendPayload([
571
                        'draw'            => input()->request('draw'),
572
                        'recordsTotal'    => $result->info->num_total,
573
                        'recordsFiltered' => $result->info->num_founds,
574
                        'data'            => $result->toArray(),
575
                    ]);
576
                } else {
577
                    $this->sendError(204);
578
                }
579
            }
580
        }
581
    }
582
583
    // ------------------------------------------------------------------------
584
585
    /**
586
     * Controller::create
587
     *
588
     * @throws \O2System\Spl\Exceptions\Logic\BadFunctionCall\BadDependencyCallException
589
     * @throws \O2System\Spl\Exceptions\Logic\OutOfRangeException
590
     */
591
    public function create()
592
    {
593
        if ($post = input()->post()) {
594
            if (count($this->fillableColumnsWithRules)) {
595
                $rules = new Rules($post);
0 ignored issues
show
Bug introduced by
$post of type O2System\Spl\DataStructures\SplArrayObject is incompatible with the type array expected by parameter $sourceVars of O2System\Security\Filters\Rules::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

595
                $rules = new Rules(/** @scrutinizer ignore-type */ $post);
Loading history...
596
                $rules->sets($this->fillableColumnsWithRules);
597
                if ( ! $rules->validate()) {
598
                    $this->sendError(400, $rules->displayErrors(true));
0 ignored issues
show
Unused Code introduced by
The call to O2System\Security\Filters\Rules::displayErrors() has too many arguments starting with true. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

598
                    $this->sendError(400, $rules->/** @scrutinizer ignore-call */ displayErrors(true));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
It seems like $rules->displayErrors(true) can also be of type array; however, parameter $message of O2System\Framework\Http\...rs\Restful::sendError() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

598
                    $this->sendError(400, /** @scrutinizer ignore-type */ $rules->displayErrors(true));
Loading history...
599
                }
600
            }
601
602
            if ( ! $this->model instanceof Model) {
603
                $this->sendError(503, 'Model is not ready');
604
            }
605
606
            $data = [];
607
608
            if (count($this->fillableColumnsWithRules)) {
609
                foreach ($this->fillableColumnsWithRules as $column) {
610
                    if ($post->offsetExists($column[ 'field' ])) {
611
                        $data[ $column[ 'field' ] ] = $post->offsetGet($column[ 'field' ]);
612
                    }
613
                }
614
            } elseif (count($this->fillableColumns)) {
615
                foreach ($this->fillableColumns as $column) {
616
                    if ($post->offsetExists($column[ 'field' ])) {
617
                        $data[ $column[ 'field' ] ] = $post->offsetGet($column[ 'field' ]);
618
                    }
619
                }
620
            } else {
621
                $data = $post->getArrayCopy();
622
            }
623
624
            if (count($data)) {
625
                $data[ 'record_create_timestamp' ] = $data[ 'record_update_timestamp' ] = timestamp();
626
                $data[ 'record_create_user' ] = $data[ 'record_update_user' ] = globals()->account->id;
0 ignored issues
show
Bug Best Practice introduced by
The property account does not exist on O2System\Framework\Containers\Globals. Since you implemented __get, consider adding a @property annotation.
Loading history...
627
628
                if ($this->model->insert($data)) {
629
                    $data[ 'id' ] = $this->model->db->getLastInsertId();
630
                    $this->sendPayload([
631
                        'code' => 201,
632
                        'Successful insert request',
633
                        'data' => $data,
634
                    ]);
635
                } else {
636
                    $this->sendError(501, 'Failed update request');
637
                }
638
            } else {
639
                $this->sendError(400, 'Post parameters cannot be empty!');
640
            }
641
        } else {
642
            $this->sendError(400);
643
        }
644
    }
645
646
    // ------------------------------------------------------------------------
647
648
    /**
649
     * Restful::update
650
     *
651
     * @throws \O2System\Spl\Exceptions\Logic\BadFunctionCall\BadDependencyCallException
652
     * @throws \O2System\Spl\Exceptions\Logic\OutOfRangeException
653
     */
654
    public function update()
655
    {
656
        if ($post = input()->post()) {
657
            $conditions = [];
658
659
            if (count($this->fillableColumnsWithRules)) {
660
                $rules = new Rules($post);
0 ignored issues
show
Bug introduced by
$post of type O2System\Spl\DataStructures\SplArrayObject is incompatible with the type array expected by parameter $sourceVars of O2System\Security\Filters\Rules::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

660
                $rules = new Rules(/** @scrutinizer ignore-type */ $post);
Loading history...
661
                $rules->sets($this->fillableColumnsWithRules);
662
663
                if (count($this->model->primaryKeys)) {
0 ignored issues
show
Bug Best Practice introduced by
The property primaryKeys does not exist on O2System\Framework\Models\Files\Model. Since you implemented __get, consider adding a @property annotation.
Loading history...
664
                    foreach ($this->model->primaryKeys as $primaryKey) {
665
                        $rules->add($primaryKey, language('LABEL_' . strtoupper($primaryKey)), 'required',
0 ignored issues
show
Bug introduced by
language('LABEL_' . strtoupper($primaryKey)) of type O2System\Framework\Servi...ernel\Services\Language is incompatible with the type string expected by parameter $label of O2System\Security\Filters\Rules::add(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

665
                        $rules->add($primaryKey, /** @scrutinizer ignore-type */ language('LABEL_' . strtoupper($primaryKey)), 'required',
Loading history...
666
                            'this field cannot be empty!');
0 ignored issues
show
Bug introduced by
'this field cannot be empty!' of type string is incompatible with the type array expected by parameter $messages of O2System\Security\Filters\Rules::add(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

666
                            /** @scrutinizer ignore-type */ 'this field cannot be empty!');
Loading history...
667
                    }
668
                } else {
669
                    $primaryKey = empty($this->model->primaryKey) ? 'id' : $this->model->primaryKey;
670
                    $conditions = [$primaryKey => $post->offsetGet($primaryKey)];
671
                    $rules->add($primaryKey, language('LABEL_' . strtoupper($primaryKey)), 'required',
672
                        'this field cannot be empty!');
673
                }
674
675
                if ( ! $rules->validate()) {
676
                    $this->sendError(400, implode(', ', $rules->displayErrors(true)));
0 ignored issues
show
Bug introduced by
It seems like $rules->displayErrors(true) can also be of type string; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

676
                    $this->sendError(400, implode(', ', /** @scrutinizer ignore-type */ $rules->displayErrors(true)));
Loading history...
Unused Code introduced by
The call to O2System\Security\Filters\Rules::displayErrors() has too many arguments starting with true. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

676
                    $this->sendError(400, implode(', ', $rules->/** @scrutinizer ignore-call */ displayErrors(true)));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
677
                }
678
            }
679
680
            if ( ! $this->model instanceof Model) {
681
                $this->sendError(503, 'Model is not ready');
682
            }
683
684
            $data = [];
685
686
            if (count($this->fillableColumnsWithRules)) {
687
                foreach ($this->fillableColumnsWithRules as $column) {
688
                    if ($post->offsetExists($column[ 'field' ])) {
689
                        $data[ $column[ 'field' ] ] = $post->offsetGet($column[ 'field' ]);
690
                    }
691
                }
692
            } elseif (count($this->fillableColumns)) {
693
                foreach ($this->fillableColumns as $column) {
694
                    if ($post->offsetExists($column[ 'field' ])) {
695
                        $data[ $column[ 'field' ] ] = $post->offsetGet($column[ 'field' ]);
696
                    }
697
                }
698
            } else {
699
                $data = $post->getArrayCopy();
700
            }
701
702
            if (count($data)) {
703
                $data[ 'record_update_timestamp' ] = timestamp();
704
                $data[ 'record_update_user' ] = globals()->account->id;
0 ignored issues
show
Bug Best Practice introduced by
The property account does not exist on O2System\Framework\Containers\Globals. Since you implemented __get, consider adding a @property annotation.
Loading history...
705
706
707
                if ($this->model->update($data, $conditions)) {
708
                    $this->sendError(201, 'Successful update request');
709
                } else {
710
                    $this->sendError(501, 'Failed update request');
711
                }
712
            } else {
713
                $this->sendError(400, 'Post parameters cannot be empty!');
714
            }
715
        } else {
716
            $this->sendError(400);
717
        }
718
    }
719
720
    // ------------------------------------------------------------------------
721
722
    /**
723
     * Restful::delete
724
     *
725
     * @throws \O2System\Spl\Exceptions\Logic\OutOfRangeException
726
     * @throws \O2System\Spl\Exceptions\RuntimeException
727
     * @throws \Psr\Cache\InvalidArgumentException
728
     */
729
    public function delete()
730
    {
731
        if ($post = input()->post()) {
732
            $rules = new Rules($post);
0 ignored issues
show
Bug introduced by
$post of type O2System\Spl\DataStructures\SplArrayObject is incompatible with the type array expected by parameter $sourceVars of O2System\Security\Filters\Rules::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

732
            $rules = new Rules(/** @scrutinizer ignore-type */ $post);
Loading history...
733
            $rules->add('id', 'ID', 'required', 'ID field cannot be empty!');
0 ignored issues
show
Bug introduced by
'ID field cannot be empty!' of type string is incompatible with the type array expected by parameter $messages of O2System\Security\Filters\Rules::add(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

733
            $rules->add('id', 'ID', 'required', /** @scrutinizer ignore-type */ 'ID field cannot be empty!');
Loading history...
734
735
            if ( ! $rules->validate()) {
736
                $this->sendError(400, implode(', ', $rules->getErrors()));
737
            }
738
739
            if ( ! $this->model instanceof Model) {
740
                $this->sendError(503, 'Model is not ready');
741
            }
742
743
            if ($this->model->delete($post->id)) {
744
                $this->sendError(201, 'Successful delete request');
745
            } else {
746
                $this->sendError(501, 'Failed delete request');
747
            }
748
        } else {
749
            $this->sendError(400);
750
        }
751
    }
752
753
    // ------------------------------------------------------------------------
754
755
    /**
756
     * Restful::publish
757
     *
758
     * @throws OutOfRangeException
759
     */
760
    public function publish()
761
    {
762
        if ($post = input()->post()) {
763
            $rules = new Rules($post);
0 ignored issues
show
Bug introduced by
$post of type O2System\Spl\DataStructures\SplArrayObject is incompatible with the type array expected by parameter $sourceVars of O2System\Security\Filters\Rules::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

763
            $rules = new Rules(/** @scrutinizer ignore-type */ $post);
Loading history...
764
            $rules->add('id', 'ID', 'required', 'ID field cannot be empty!');
0 ignored issues
show
Bug introduced by
'ID field cannot be empty!' of type string is incompatible with the type array expected by parameter $messages of O2System\Security\Filters\Rules::add(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

764
            $rules->add('id', 'ID', 'required', /** @scrutinizer ignore-type */ 'ID field cannot be empty!');
Loading history...
765
766
            if ( ! $rules->validate()) {
767
                $this->sendError(400, implode(', ', $rules->getErrors()));
768
            }
769
770
            if ( ! $this->model instanceof Model) {
771
                $this->sendError(503, 'Model is not ready');
772
            }
773
774
            if ($this->model->publish($post->id)) {
775
                $this->sendError(201, 'Successful publish request');
776
            } else {
777
                $this->sendError(501, 'Failed publish request');
778
            }
779
        } else {
780
            $this->sendError(400);
781
        }
782
    }
783
784
    // ------------------------------------------------------------------------
785
786
    /**
787
     * Restful::unpublish
788
     *
789
     * @throws OutOfRangeException
790
     */
791
    public function unpublish()
792
    {
793
        if ($post = input()->post()) {
794
            $rules = new Rules($post);
0 ignored issues
show
Bug introduced by
$post of type O2System\Spl\DataStructures\SplArrayObject is incompatible with the type array expected by parameter $sourceVars of O2System\Security\Filters\Rules::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

794
            $rules = new Rules(/** @scrutinizer ignore-type */ $post);
Loading history...
795
            $rules->add('id', 'ID', 'required', 'ID field cannot be empty!');
0 ignored issues
show
Bug introduced by
'ID field cannot be empty!' of type string is incompatible with the type array expected by parameter $messages of O2System\Security\Filters\Rules::add(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

795
            $rules->add('id', 'ID', 'required', /** @scrutinizer ignore-type */ 'ID field cannot be empty!');
Loading history...
796
797
            if ( ! $rules->validate()) {
798
                $this->sendError(400, implode(', ', $rules->getErrors()));
799
            }
800
801
            if ( ! $this->model instanceof Model) {
802
                $this->sendError(503, 'Model is not ready');
803
            }
804
805
            if ($this->model->unpublish($post->id)) {
806
                $this->sendError(201, 'Successful unpublish request');
807
            } else {
808
                $this->sendError(501, 'Failed unpublish request');
809
            }
810
        } else {
811
            $this->sendError(400);
812
        }
813
    }
814
815
    // ------------------------------------------------------------------------
816
817
    /**
818
     * Restful::archive
819
     *
820
     * @throws OutOfRangeException
821
     */
822
    public function archive()
823
    {
824
        if ($post = input()->post()) {
825
            $rules = new Rules($post);
0 ignored issues
show
Bug introduced by
$post of type O2System\Spl\DataStructures\SplArrayObject is incompatible with the type array expected by parameter $sourceVars of O2System\Security\Filters\Rules::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

825
            $rules = new Rules(/** @scrutinizer ignore-type */ $post);
Loading history...
826
            $rules->add('id', 'ID', 'required', 'ID field cannot be empty!');
0 ignored issues
show
Bug introduced by
'ID field cannot be empty!' of type string is incompatible with the type array expected by parameter $messages of O2System\Security\Filters\Rules::add(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

826
            $rules->add('id', 'ID', 'required', /** @scrutinizer ignore-type */ 'ID field cannot be empty!');
Loading history...
827
828
            if ( ! $rules->validate()) {
829
                $this->sendError(400, implode(', ', $rules->getErrors()));
830
            }
831
832
            if ( ! $this->model instanceof Model) {
833
                $this->sendError(503, 'Model is not ready');
834
            }
835
836
            if ($this->model->unpublish($post->id)) {
837
                $this->sendError(201, 'Successful archived request');
838
            } else {
839
                $this->sendError(501, 'Failed archived request');
840
            }
841
        } else {
842
            $this->sendError(400);
843
        }
844
    }
845
846
    // ------------------------------------------------------------------------
847
848
    /**
849
     * Restful::sendError
850
     *
851
     * @param int         $code
852
     * @param string|null $message
853
     */
854
    public function sendError($code, $message = null)
855
    {
856
        if ($this->ajaxOnly === false) {
857
            output()->setContentType('application/json');
858
        }
859
860
        if (is_array($code)) {
0 ignored issues
show
introduced by
The condition is_array($code) is always false.
Loading history...
861
            if (is_numeric(key($code))) {
862
                $message = reset($code);
863
                $code = key($code);
864
            } elseif (isset($code[ 'code' ])) {
865
                $code = $code[ 'code' ];
866
                $message = $code[ 'message' ];
867
            }
868
        }
869
870
        output()->sendError($code, $message);
871
    }
872
873
    // ------------------------------------------------------------------------
874
875
    /**
876
     * Restful::sendPayload
877
     *
878
     * @param mixed $data        The payload data to-be send.
879
     * @param bool  $longPooling Long pooling flag mode.
880
     *
881
     * @throws \Exception
882
     */
883
    public function sendPayload($data, $longPooling = false)
884
    {
885
        if ($longPooling === false) {
886
            if ($this->ajaxOnly) {
887
                if (is_ajax()) {
888
                    output()->send($data);
0 ignored issues
show
Bug introduced by
The method send() does not exist on O2System\Kernel\Cli\Output. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

888
                    output()->/** @scrutinizer ignore-call */ send($data);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
889
                } else {
890
                    output()->sendError(403);
891
                }
892
            } else {
893
                output()->send($data);
894
            }
895
        } elseif (is_ajax()) {
896
            /**
897
             * Server-side file.
898
             * This file is an infinitive loop. Seriously.
899
             * It gets the cache created timestamp, checks if this is larger than the timestamp of the
900
             * AJAX-submitted timestamp (time of last ajax request), and if so, it sends back a JSON with the data from
901
             * data.txt (and a timestamp). If not, it waits for one seconds and then start the next while step.
902
             *
903
             * Note: This returns a JSON, containing the content of data.txt and the timestamp of the last data.txt change.
904
             * This timestamp is used by the client's JavaScript for the next request, so THIS server-side script here only
905
             * serves new content after the last file change. Sounds weird, but try it out, you'll get into it really fast!
906
             */
907
908
            // set php runtime to unlimited
909
            set_time_limit(0);
910
911
            $longPoolingCacheKey = 'long-pooling-' . session()->get('id');
0 ignored issues
show
Bug introduced by
Are you sure session()->get('id') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

911
            $longPoolingCacheKey = 'long-pooling-' . /** @scrutinizer ignore-type */ session()->get('id');
Loading history...
912
            $longPoolingCacheData = null;
913
914
            if ( ! cache()->hasItem($longPoolingCacheKey)) {
915
                cache()->save(new Item($longPoolingCacheKey, $data));
916
            }
917
918
            // main loop
919
            while (true) {
920
                // if ajax request has send a timestamp, then $lastCallTimestamp = timestamp, else $last_call = null
921
                $lastCallTimestamp = (int)input()->getPost('last_call_timestamp');
0 ignored issues
show
Bug introduced by
The method getPost() does not exist on O2System\Kernel\Cli\Input. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

921
                $lastCallTimestamp = (int)input()->/** @scrutinizer ignore-call */ getPost('last_call_timestamp');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
922
923
                // PHP caches file data, like requesting the size of a file, by default. clearstatcache() clears that cache
924
                clearstatcache();
925
926
                if (cache()->hasItem($longPoolingCacheKey)) {
927
                    $longPoolingCacheData = cache()->getItem($longPoolingCacheKey);
928
                }
929
930
                // get timestamp of when file has been changed the last time
931
                $longPoolingCacheMetadata = $longPoolingCacheData->getMetadata();
932
933
                // if no timestamp delivered via ajax or data.txt has been changed SINCE last ajax timestamp
934
                if ($lastCallTimestamp == null || $longPoolingCacheMetadata[ 'ctime' ] > $lastCallTimestamp) {
935
                    output()->send([
936
                        'timestamp' => $longPoolingCacheMetadata,
937
                        'data'      => $data,
938
                    ]);
939
                } else {
940
                    // wait for 1 sec (not very sexy as this blocks the PHP/Apache process, but that's how it goes)
941
                    sleep(1);
942
                    continue;
943
                }
944
            }
945
        } else {
946
            output()->sendError(501);
947
        }
948
    }
949
}