FlattenException::flattenArgs()   C
last analyzed

Complexity

Conditions 12
Paths 12

Size

Total Lines 35
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 26
c 1
b 0
f 0
nc 12
nop 3
dl 0
loc 35
rs 6.9666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php 
2
3
/**
4
 * Lenevor Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2021 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
23
namespace Syscodes\Debug\FatalExceptions;
24
25
use Throwable;
26
use ArrayObject;
27
use Syscodes\Core\Http\Exceptions\HttpException;
28
29
/**
30
 * FlattenException wraps a PHP Error or Exception to be able to serialize it.
31
 * Basically, this class removes all objects from the trace.
32
 * 
33
 * @author Alexander Campo <[email protected]>
34
 */
35
class FlattenException
36
{
37
    /**
38
     * Gets the class name.
39
     * 
40
     * @var string $class
41
     */
42
    protected $class;
43
44
    /**
45
     * Gets the code of error.
46
     * 
47
     * @var int $code
48
     */
49
    protected $code;
50
51
    /**
52
     * Gets the file path.
53
     * 
54
     * @var string $file
55
     */
56
    protected $file;
57
58
    /**
59
     * Gets the headers HTTP.
60
     * 
61
     * @var array $headers
62
     */
63
    protected $headers;
64
65
    /**
66
     * Gets the line where specifice the line number and code in happened an error.
67
     *  
68
     * @var int $line
69
     */
70
    protected $line;
71
72
    /**
73
     * Gets the message of exception.
74
     * 
75
     * @var string $message
76
     */
77
    protected $message;
78
79
    /**
80
     * Gets the previous exception.
81
     * 
82
     * @var string $previous
83
     */
84
    protected $previous;
85
86
    /**
87
     * Gets the status code response.
88
     * 
89
     * @var int $statusCode
90
     */
91
    protected $statusCode;
92
93
    /**
94
     * Gets the trace.
95
     * 
96
     * @var array $trace
97
     */
98
    protected $trace;
99
100
    /**
101
     * Load the exception with their respective status code and headers.
102
     * 
103
     * @param  \Throwable  $exception
104
     * @param  int|null    $statusCode
105
     * @param  array       $headers
106
     * 
107
     * @return void
108
     */
109
    public static function make(Throwable $exception, $statusCode = null, array $headers = [])
110
    {
111
        return static::makeFromThrowable($exception, $statusCode, $headers);
0 ignored issues
show
Bug Best Practice introduced by
The expression return static::makeFromT... $statusCode, $headers) returns the type Syscodes\Debug\FatalExceptions\new which is incompatible with the documented return type void.
Loading history...
112
    }
113
114
    /**
115
     * An exception is loaded to change the initial value in set methods.
116
     * 
117
     * @param  \Throwable  $exception
118
     * @param  int|null  $statusCode
119
     * @param  array  $headers
120
     * 
121
     * @return new static
0 ignored issues
show
Bug introduced by
The type Syscodes\Debug\FatalExceptions\new was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
122
     */
123
    public static function makeFromThrowable(Throwable $exception, ?int $statusCode = null, array $headers = [])
124
    {
125
        $e = new static;
126
        $e->setMessage($exception->getMessage());
127
        $e->setCode($exception->getCode());
128
129
        if ($exception instanceof HttpException) {
130
            $statusCode = $exception->getStatusCode();
131
            $headers    = array_merge($headers, $exception->getHeaders());
132
        }
133
134
        if ($statusCode === null) {
135
            $statusCode = 500;
136
        }
137
138
        $e->setStatusCode($statusCode);
139
        $e->setHeaders($headers);
140
        $e->setClass($exception instanceof FatalThrowableError ? $exception->getOriginalClassName() : get_class($exception));
141
        $e->setFile($exception->getFile());
142
        $e->setLine($exception->getLine());
143
        $e->setTraceFromThrowable($exception);
144
145
        $previous = $exception->getPrevious();
146
147
        if ($previous instanceof Throwable) {
0 ignored issues
show
introduced by
$previous is always a sub-type of Throwable.
Loading history...
148
            $e->setPrevious(static::makeFromThrowable($previous));
149
        }
150
151
        return $e;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $e returns the type Syscodes\Debug\FatalExceptions\FlattenException which is incompatible with the documented return type Syscodes\Debug\FatalExceptions\new.
Loading history...
152
    }
153
154
    /*
155
    |-----------------------------------------------------------------
156
    | Getter And Setter Methods
157
    |-----------------------------------------------------------------
158
    */
159
    
160
    /**
161
     * Gets the class name.
162
     * 
163
     * @return void
164
     */
165
    public function getClass()
166
    {
167
        return $this->class;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->class returns the type string which is incompatible with the documented return type void.
Loading history...
168
    }
169
170
    /**
171
     * Sets the class name.
172
     * 
173
     * @param  string  $class
174
     * 
175
     * @return $this
176
     */
177
    public function setClass($class)
178
    {
179
        $this->class = 'c' === $class[0] && strpos($class, "class@anonymous\0") === 0 ? get_parent_class($class).'@anonymous' : $class;
180
181
        return $this;
182
    }
183
    
184
    /**
185
     * Gets the code of error.
186
     * 
187
     * @return void
188
     */
189
    public function getCode()
190
    {
191
        return $this->code;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->code returns the type integer which is incompatible with the documented return type void.
Loading history...
192
    }
193
194
    /**
195
     * Sets the code of error.
196
     * 
197
     * @param  int  $code
198
     * 
199
     * @return $this
200
     */
201
    public function setCode($code)
202
    {
203
        $this->code = $code;
204
205
        return $this;
206
    }
207
208
    /**
209
     * Gets the file path.
210
     * 
211
     * @return void
212
     */
213
    public function getFile()
214
    {
215
        return $this->file;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->file returns the type string which is incompatible with the documented return type void.
Loading history...
216
    }
217
218
     /**
219
     * Sets the file path.
220
     * 
221
     * @param  string  $file
222
     * 
223
     * @return $this
224
     */
225
    public function setFile($file)
226
    {
227
        $this->file = $file;
228
229
        return $this;
230
    }
231
232
    /**
233
     * Gets the headers HTTP.
234
     * 
235
     * @return void
236
     */
237
    public function getHeaders()
238
    {
239
        return $this->headers;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->headers returns the type array which is incompatible with the documented return type void.
Loading history...
240
    }
241
242
    /**
243
     * Sets the headers HTTP.
244
     * 
245
     * @param  array  $headers
246
     * 
247
     * @return $this
248
     */
249
    public function setHeaders(array $headers)
250
    {
251
        $this->headers = $headers;
252
253
        return $this;
254
    }
255
256
    /**
257
     * Gets the line where specifice the line number and code in happened an error.
258
     * 
259
     * @return void
260
     */
261
    public function getLine()
262
    {
263
        return $this->line;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->line returns the type integer which is incompatible with the documented return type void.
Loading history...
264
    }
265
266
    /**
267
     * Sets the line where specifice the line number and code in happened an error.
268
     * 
269
     * @param  int  $line
270
     * 
271
     * @return $this
272
     */
273
    public function setLine($line)
274
    {
275
        $this->line = $line;
276
277
        return $this;
278
    }
279
280
    /**
281
     * Gets the message of exception.
282
     * 
283
     * @return void
284
     */
285
    public function getMessage()
286
    {
287
        return $this->message;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->message returns the type string which is incompatible with the documented return type void.
Loading history...
288
    }
289
290
    /**
291
     * Sets the message of exception.
292
     * 
293
     * @param  string  $message
294
     * 
295
     * @return $this
296
     */
297
    public function setMessage($message)
298
    {
299
        $this->message = $message;
300
301
        return $this;
302
    }
303
304
    /**
305
     * Gets the previous exception.
306
     * 
307
     * @return void
308
     */
309
    public function getPrevious()
310
    {
311
        return $this->previous;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->previous returns the type string which is incompatible with the documented return type void.
Loading history...
312
    }
313
314
    /**
315
     * Sets the previous exception.
316
     * 
317
     * @param  self  $previous
318
     * 
319
     * @return $this
320
     */
321
    public function setPrevious(self $previous)
322
    {
323
        $this->previous = $previous;
0 ignored issues
show
Documentation Bug introduced by
It seems like $previous of type Syscodes\Debug\FatalExceptions\FlattenException is incompatible with the declared type string of property $previous.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
324
325
        return $this;
326
    }
327
328
    /**
329
     * Gets all previous exceptions.
330
     * 
331
     * @return array
332
     */
333
    public function getAllPrevious()
334
    {
335
        $exceptions = [];
336
        $exception  = $this;
337
338
        while ($exception = $exception->getPrevious()) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $exception is correct as $exception->getPrevious() targeting Syscodes\Debug\FatalExce...xception::getPrevious() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
339
            $exceptions[] = $exception;
340
        }
341
342
        return $exceptions;
343
    }
344
345
    /**
346
     * Gets the status code response.
347
     * 
348
     * @return void
349
     */
350
    public function getStatusCode()
351
    {
352
        return $this->statusCode;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->statusCode returns the type integer which is incompatible with the documented return type void.
Loading history...
353
    }
354
355
    /**
356
     * Sets the status code response.
357
     * 
358
     * @param  int  $code
359
     * 
360
     * @return $this
361
     */
362
    public function setStatusCode($code)
363
    {
364
        $this->statusCode = $code;
365
366
        return $this;
367
    }
368
369
    /**
370
     * Gets the trace.
371
     * 
372
     * @return void
373
     */
374
    public function getTrace()
375
    {
376
        return $this->trace;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->trace returns the type array which is incompatible with the documented return type void.
Loading history...
377
    }
378
379
    /**
380
     * Converts the collection to an array.
381
     * 
382
     * @return array
383
     */
384
    public function toArray()
385
    {
386
        $exceptions = [];
387
388
        foreach (array_merge([$this], $this->getAllPrevious()) as $exception) {
389
            $exceptions[] = [
390
                'message' => $exception->getMessage(),
391
                'class'   => $exception->getClass(),
392
                'trace'   => $exception->getTrace(),
393
            ];
394
        }
395
396
        return $exceptions;
397
    }
398
    
399
    /**
400
     * Sets the trace from throwable.
401
     * 
402
     * @param  \Throwable  $throwable 
403
     * 
404
     * @return void
405
     */
406
    public function setTraceFromThrowable(Throwable $throwable)
407
    {
408
        return $this->setTrace($throwable->getTrace(), $throwable->getFile(), $throwable->getLine());
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->setTrace($... $throwable->getLine()) returns the type Syscodes\Debug\FatalExceptions\FlattenException which is incompatible with the documented return type void.
Loading history...
409
    }
410
411
    /**
412
     * Sets the trace.
413
     * 
414
     * @param  array   $trace
415
     * @param  string  $file
416
     * @param  int     $line
417
     * 
418
     * @return $this
419
     */
420
    public function setTrace($trace, $file, $line)
421
    {
422
        $this->trace   = [];
423
        $this->trace[] = [
424
            'namespace'   => '',
425
            'short_class' => '',
426
            'class'       => '',
427
            'type'        => '',
428
            'function'    => '',
429
            'file'        => $file,
430
            'line'        => $line,
431
            'args'        => [],
432
        ];
433
434
        foreach ($trace as $item) {
435
            $class     = '';
436
            $namespace = '';
437
438
            if (isset($item['class'])) {
439
                $parts     = explode('\\', $item['class']);
440
                $class     = array_pop($parts);
441
                $namespace = implode('\\', $parts);
442
            }
443
444
            $this->trace[] = [
445
                'namespace'   => $namespace,
446
                'short_class' => $class,
447
                'class'       => $item['class'] ?? '',
448
                'type'        => $item['type'] ?? '',
449
                'function'    => $item['function'] ?? null,
450
                'file'        => $item['file'] ?? null,
451
                'line'        => $item['line'] ?? null,
452
                'args'        => isset($item['args']) ? $this->flattenArgs($item['args']) : [],
453
            ];
454
        }
455
456
        return $this;
457
    }
458
459
    /**
460
     * Flatten a multi-dimensional array into a many levels.
461
     * 
462
     * @param  array  $args
463
     * @param  int    $level  Default value is 0
464
     * @param  int    $count  Default value is 0
465
     * 
466
     * @return array
467
     */
468
    private function flattenArgs($args, $level = 0, &$count = 0)
469
    {   
470
        $result = [];
471
472
        foreach ($args as $key => $value) {
473
            if (++$count > 1e4) {
474
                return ['array', '*SKIPPED over 10000 entries*'];
475
            }
476
477
            if ($value instanceof \__PHP_Incomplete_Class) {
478
                $result[$key] = ['incomplete-object', $this->getClassNameFromIncomplete($value)];
479
            } elseif (is_object($value)) {
480
                $result[$key] = ['object', get_class($value)];
481
            } elseif (is_array($value)) {
482
                if ($level > 10) {
483
                    $result[$key] = ['array', '*DEEP NESTED ARRAY*'];
484
                } else {
485
                    $result[$key] = ['array', $this->flattenArgs($value, $level + 1, $count)];
486
                }
487
            } elseif ($value === null) {
488
                $result[$key] = ['null', null];
489
            } elseif (is_bool($value)) {
490
                $result[$key] = ['boolean', $value];
491
            } elseif (is_int($value)) {
492
                $result[$key] = ['integer', $value];
493
            } elseif (is_float($value)) {
494
                $result[$key] = ['float', $value];
495
            } elseif (is_resource($value)) {
496
                $result[$key] = ['resource', get_resource_type($value)];
497
            } else {
498
                $result[$key] = ['string', (string) $value];
499
            }
500
        }
501
502
        return $result;
503
    }
504
505
    /**
506
     * Gets class name PHP incomplete.
507
     * 
508
     * @param  object  $value
509
     * 
510
     * @return array
511
     */
512
    private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value)
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

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

512
    private function getClassNameFromIncomplete(/** @scrutinizer ignore-unused */ \__PHP_Incomplete_Class $value)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
513
    {
514
        $array = new ArrayObject;
515
        
516
        return $array['__PHP_Incomplete_Class_Name'];
517
    }
518
}