Passed
Push — master ( a996f8...998e87 )
by Wilmer
02:42
created

FlattenException   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 277
Duplicated Lines 0 %

Test Coverage

Coverage 91.18%

Importance

Changes 0
Metric Value
eloc 84
dl 0
loc 277
ccs 93
cts 102
cp 0.9118
rs 9.1199
c 0
b 0
f 0
wmc 41

20 Methods

Rating   Name   Duplication   Size   Complexity  
A setLine() 0 3 1
A setPrevious() 0 3 1
A setToString() 0 3 1
A getCode() 0 3 1
A __toString() 0 3 1
A getTrace() 0 3 1
A setClass() 0 3 1
A getPrevious() 0 3 1
A getTraceAsString() 0 8 2
B setTrace() 0 21 9
A __construct() 0 13 2
A getLine() 0 3 1
A getMessage() 0 3 1
A setMessage() 0 3 1
A setCode() 0 3 1
A getFile() 0 3 1
A getClass() 0 3 1
A setFile() 0 3 1
A getClassNameFromIncomplete() 0 5 1
C flattenArgs() 0 34 12

How to fix   Complexity   

Complex Class

Complex classes like FlattenException often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FlattenException, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Debug;
6
7
/**
8
 * FlattenException wraps a PHP Exception to be able to serialize it.
9
 * Implements the Throwable interface
10
 * Basically, this class removes all objects from the trace.
11
 * Ported from Symfony components @link https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Debug/Exception/FlattenException.php
12
 */
13
class FlattenException
14
{
15
    /**
16
     * @var string
17
     */
18
    protected string $message;
19
    /**
20
     * @var mixed|int
21
     */
22
    protected $code;
23
    /**
24
     * @var string
25
     */
26
    protected string $file;
27
    /**
28
     * @var int
29
     */
30
    protected int $line;
31
32
    /**
33
     * @var FlattenException|null
34
     */
35
    private ?FlattenException $_previous;
36
    /**
37
     * @var array
38
     */
39
    private array $_trace;
40
    /**
41
     * @var string
42
     */
43
    private string $_toString;
44
    /**
45
     * @var string
46
     */
47
    private string $_class;
48
49
    /**
50
     * FlattenException constructor.
51
     * @param \Throwable $exception
52
     */
53 10
    public function __construct(\Throwable $exception)
54
    {
55 10
        $this->setMessage($exception->getMessage());
56 10
        $this->setCode($exception->getCode());
57 10
        $this->setFile($exception->getFile());
58 10
        $this->setLine($exception->getLine());
59 10
        $this->setTrace($exception->getTrace());
60 10
        $this->setToString($exception->__toString());
61 10
        $this->setClass(get_class($exception));
62
63 10
        $previous = $exception->getPrevious();
64 10
        if ($previous instanceof \Exception) {
65 1
            $this->setPrevious(new self($previous));
66
        }
67 10
    }
68
69
    /**
70
     * Gets the Exception message
71
     * @return string the Exception message as a string.
72
     */
73 1
    public function getMessage(): string
74
    {
75 1
        return $this->message;
76
    }
77
78
    /**
79
     * Gets the Exception code
80
     * @return mixed|int the exception code as integer.
81
     */
82 1
    public function getCode()
83
    {
84 1
        return $this->code;
85
    }
86
87
    /**
88
     * Gets the file in which the exception occurred
89
     * @return string the filename in which the exception was created.
90
     */
91 1
    public function getFile(): string
92
    {
93 1
        return $this->file;
94
    }
95
96
    /**
97
     * Gets the line in which the exception occurred
98
     * @return int the line number where the exception was created.
99
     */
100 1
    public function getLine(): int
101
    {
102 1
        return $this->line;
103
    }
104
105
    /**
106
     * Gets the stack trace
107
     * @return array the Exception stack trace as an array.
108
     */
109 2
    public function getTrace(): array
110
    {
111 2
        return $this->_trace;
112
    }
113
114
    /**
115
     * Returns previous Exception
116
     * @return FlattenException|null the previous `FlattenException` if available or null otherwise.
117
     */
118 1
    public function getPrevious(): ?FlattenException
119
    {
120 1
        return $this->_previous;
121
    }
122
123
    /**
124
     * Gets the stack trace as a string
125
     * @return string the Exception stack trace as a string.
126
     */
127 1
    public function getTraceAsString(): string
128
    {
129 1
        $remove = "Stack trace:\n";
130 1
        $len = strpos($this->_toString, $remove);
131 1
        if ($len === false) {
132
            return '';
133
        }
134 1
        return substr($this->_toString, $len + strlen($remove));
135
    }
136
137
    /**
138
     * String representation of the exception
139
     * @return string the string representation of the exception.
140
     */
141 1
    public function __toString()
142
    {
143 1
        return $this->_toString;
144
    }
145
146
    /**
147
     * @return string the name of the class in which the exception was created.
148
     */
149 1
    public function getClass(): string
150
    {
151 1
        return $this->_class;
152
    }
153
154
    /**
155
     * @param string $message the Exception message as a string.
156
     */
157 10
    protected function setMessage($message): void
158
    {
159 10
        $this->message = $message;
160 10
    }
161
162
    /**
163
     * @param mixed|int $code the exception code as integer.
164
     */
165 10
    protected function setCode($code): void
166
    {
167 10
        $this->code = $code;
168 10
    }
169
170
    /**
171
     * @param string $file the filename in which the exception was created.
172
     */
173 10
    protected function setFile($file): void
174
    {
175 10
        $this->file = $file;
176 10
    }
177
178
    /**
179
     * @param int $line the line number where the exception was created.
180
     */
181 10
    protected function setLine($line): void
182
    {
183 10
        $this->line = $line;
184 10
    }
185
186
    /**
187
     * @param array $trace the Exception stack trace as an array.
188
     */
189 10
    protected function setTrace($trace): void
190
    {
191 10
        $this->_trace = [];
192 10
        foreach ($trace as $entry) {
193 10
            $class = '';
194 10
            $namespace = '';
195 10
            if (isset($entry['class'])) {
196 10
                $parts = explode('\\', $entry['class']);
197 10
                $class = array_pop($parts);
198 10
                $namespace = implode('\\', $parts);
199
            }
200
201 10
            $this->_trace[] = [
202 10
                'namespace' => $namespace,
203 10
                'short_class' => $class,
204 10
                'class' => isset($entry['class']) ? $entry['class'] : '',
205 10
                'type' => isset($entry['type']) ? $entry['type'] : '',
206 10
                'function' => isset($entry['function']) ? $entry['function'] : null,
207 10
                'file' => isset($entry['file']) ? $entry['file'] : null,
208 10
                'line' => isset($entry['line']) ? $entry['line'] : null,
209 10
                'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : [],
210
            ];
211
        }
212 10
    }
213
214
    /**
215
     * @param string $string the string representation of the thrown object.
216
     */
217 10
    protected function setToString($string): void
218
    {
219 10
        $this->_toString = $string;
220 10
    }
221
222
    /**
223
     * @param FlattenException $previous previous Exception.
224
     */
225 1
    protected function setPrevious(FlattenException $previous): void
226
    {
227 1
        $this->_previous = $previous;
228 1
    }
229
230
    /**
231
     * @param string $class the name of the class in which the exception was created.
232
     */
233 10
    protected function setClass($class): void
234
    {
235 10
        $this->_class = $class;
236 10
    }
237
238
    /**
239
     * Allows you to sterilize the Exception trace arguments
240
     * @param array $args
241
     * @param int $level recursion level
242
     * @param int $count number of records counter
243
     * @return array arguments tracing.
244
     */
245 10
    private function flattenArgs($args, $level = 0, &$count = 0): array
246
    {
247 10
        $result = [];
248 10
        foreach ($args as $key => $value) {
249 10
            if (++$count > 10000) {
250
                return ['array', '*SKIPPED over 10000 entries*'];
251
            }
252 10
            if ($value instanceof \__PHP_Incomplete_Class) {
253
                // is_object() returns false on PHP<=7.1
254
                $result[$key] = ['incomplete-object', $this->getClassNameFromIncomplete($value)];
255 10
            } elseif (is_object($value)) {
256 10
                $result[$key] = ['object', get_class($value)];
257 10
            } elseif (is_array($value)) {
258 10
                if ($level > 10) {
259
                    $result[$key] = ['array', '*DEEP NESTED ARRAY*'];
260
                } else {
261 10
                    $result[$key] = ['array', $this->flattenArgs($value, $level + 1, $count)];
262
                }
263 10
            } elseif (null === $value) {
264 10
                $result[$key] = ['null', null];
265 10
            } elseif (is_bool($value)) {
266 10
                $result[$key] = ['boolean', $value];
267 10
            } elseif (is_int($value)) {
268 10
                $result[$key] = ['integer', $value];
269 10
            } elseif (is_float($value)) {
270
                $result[$key] = ['float', $value];
271 10
            } elseif (is_resource($value)) {
272
                $result[$key] = ['resource', get_resource_type($value)];
273
            } else {
274 10
                $result[$key] = ['string', (string)$value];
275
            }
276
        }
277
278 10
        return $result;
279
    }
280
281
    /**
282
     * @param \__PHP_Incomplete_Class $value
283
     * @return string the real class name of an incomplete class
284
     */
285
    private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value): string
286
    {
287
        $array = new \ArrayObject($value);
288
289
        return $array['__PHP_Incomplete_Class_Name'];
290
    }
291
}
292