Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Symfony\Component\Debug\Exception;
|
Chris@0
|
13
|
Chris@0
|
14 use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
Chris@0
|
15
|
Chris@0
|
16 /**
|
Chris@0
|
17 * FlattenException wraps a PHP Exception to be able to serialize it.
|
Chris@0
|
18 *
|
Chris@0
|
19 * Basically, this class removes all objects from the trace.
|
Chris@0
|
20 *
|
Chris@0
|
21 * @author Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
22 */
|
Chris@0
|
23 class FlattenException
|
Chris@0
|
24 {
|
Chris@0
|
25 private $message;
|
Chris@0
|
26 private $code;
|
Chris@0
|
27 private $previous;
|
Chris@0
|
28 private $trace;
|
Chris@0
|
29 private $class;
|
Chris@0
|
30 private $statusCode;
|
Chris@0
|
31 private $headers;
|
Chris@0
|
32 private $file;
|
Chris@0
|
33 private $line;
|
Chris@0
|
34
|
Chris@0
|
35 public static function create(\Exception $exception, $statusCode = null, array $headers = array())
|
Chris@0
|
36 {
|
Chris@0
|
37 $e = new static();
|
Chris@0
|
38 $e->setMessage($exception->getMessage());
|
Chris@0
|
39 $e->setCode($exception->getCode());
|
Chris@0
|
40
|
Chris@0
|
41 if ($exception instanceof HttpExceptionInterface) {
|
Chris@0
|
42 $statusCode = $exception->getStatusCode();
|
Chris@0
|
43 $headers = array_merge($headers, $exception->getHeaders());
|
Chris@0
|
44 }
|
Chris@0
|
45
|
Chris@0
|
46 if (null === $statusCode) {
|
Chris@0
|
47 $statusCode = 500;
|
Chris@0
|
48 }
|
Chris@0
|
49
|
Chris@0
|
50 $e->setStatusCode($statusCode);
|
Chris@0
|
51 $e->setHeaders($headers);
|
Chris@0
|
52 $e->setTraceFromException($exception);
|
Chris@0
|
53 $e->setClass(get_class($exception));
|
Chris@0
|
54 $e->setFile($exception->getFile());
|
Chris@0
|
55 $e->setLine($exception->getLine());
|
Chris@0
|
56
|
Chris@0
|
57 $previous = $exception->getPrevious();
|
Chris@0
|
58
|
Chris@0
|
59 if ($previous instanceof \Exception) {
|
Chris@0
|
60 $e->setPrevious(static::create($previous));
|
Chris@0
|
61 } elseif ($previous instanceof \Throwable) {
|
Chris@0
|
62 $e->setPrevious(static::create(new FatalThrowableError($previous)));
|
Chris@0
|
63 }
|
Chris@0
|
64
|
Chris@0
|
65 return $e;
|
Chris@0
|
66 }
|
Chris@0
|
67
|
Chris@0
|
68 public function toArray()
|
Chris@0
|
69 {
|
Chris@0
|
70 $exceptions = array();
|
Chris@0
|
71 foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) {
|
Chris@0
|
72 $exceptions[] = array(
|
Chris@0
|
73 'message' => $exception->getMessage(),
|
Chris@0
|
74 'class' => $exception->getClass(),
|
Chris@0
|
75 'trace' => $exception->getTrace(),
|
Chris@0
|
76 );
|
Chris@0
|
77 }
|
Chris@0
|
78
|
Chris@0
|
79 return $exceptions;
|
Chris@0
|
80 }
|
Chris@0
|
81
|
Chris@0
|
82 public function getStatusCode()
|
Chris@0
|
83 {
|
Chris@0
|
84 return $this->statusCode;
|
Chris@0
|
85 }
|
Chris@0
|
86
|
Chris@0
|
87 public function setStatusCode($code)
|
Chris@0
|
88 {
|
Chris@0
|
89 $this->statusCode = $code;
|
Chris@0
|
90 }
|
Chris@0
|
91
|
Chris@0
|
92 public function getHeaders()
|
Chris@0
|
93 {
|
Chris@0
|
94 return $this->headers;
|
Chris@0
|
95 }
|
Chris@0
|
96
|
Chris@0
|
97 public function setHeaders(array $headers)
|
Chris@0
|
98 {
|
Chris@0
|
99 $this->headers = $headers;
|
Chris@0
|
100 }
|
Chris@0
|
101
|
Chris@0
|
102 public function getClass()
|
Chris@0
|
103 {
|
Chris@0
|
104 return $this->class;
|
Chris@0
|
105 }
|
Chris@0
|
106
|
Chris@0
|
107 public function setClass($class)
|
Chris@0
|
108 {
|
Chris@0
|
109 $this->class = $class;
|
Chris@0
|
110 }
|
Chris@0
|
111
|
Chris@0
|
112 public function getFile()
|
Chris@0
|
113 {
|
Chris@0
|
114 return $this->file;
|
Chris@0
|
115 }
|
Chris@0
|
116
|
Chris@0
|
117 public function setFile($file)
|
Chris@0
|
118 {
|
Chris@0
|
119 $this->file = $file;
|
Chris@0
|
120 }
|
Chris@0
|
121
|
Chris@0
|
122 public function getLine()
|
Chris@0
|
123 {
|
Chris@0
|
124 return $this->line;
|
Chris@0
|
125 }
|
Chris@0
|
126
|
Chris@0
|
127 public function setLine($line)
|
Chris@0
|
128 {
|
Chris@0
|
129 $this->line = $line;
|
Chris@0
|
130 }
|
Chris@0
|
131
|
Chris@0
|
132 public function getMessage()
|
Chris@0
|
133 {
|
Chris@0
|
134 return $this->message;
|
Chris@0
|
135 }
|
Chris@0
|
136
|
Chris@0
|
137 public function setMessage($message)
|
Chris@0
|
138 {
|
Chris@0
|
139 $this->message = $message;
|
Chris@0
|
140 }
|
Chris@0
|
141
|
Chris@0
|
142 public function getCode()
|
Chris@0
|
143 {
|
Chris@0
|
144 return $this->code;
|
Chris@0
|
145 }
|
Chris@0
|
146
|
Chris@0
|
147 public function setCode($code)
|
Chris@0
|
148 {
|
Chris@0
|
149 $this->code = $code;
|
Chris@0
|
150 }
|
Chris@0
|
151
|
Chris@0
|
152 public function getPrevious()
|
Chris@0
|
153 {
|
Chris@0
|
154 return $this->previous;
|
Chris@0
|
155 }
|
Chris@0
|
156
|
Chris@0
|
157 public function setPrevious(FlattenException $previous)
|
Chris@0
|
158 {
|
Chris@0
|
159 $this->previous = $previous;
|
Chris@0
|
160 }
|
Chris@0
|
161
|
Chris@0
|
162 public function getAllPrevious()
|
Chris@0
|
163 {
|
Chris@0
|
164 $exceptions = array();
|
Chris@0
|
165 $e = $this;
|
Chris@0
|
166 while ($e = $e->getPrevious()) {
|
Chris@0
|
167 $exceptions[] = $e;
|
Chris@0
|
168 }
|
Chris@0
|
169
|
Chris@0
|
170 return $exceptions;
|
Chris@0
|
171 }
|
Chris@0
|
172
|
Chris@0
|
173 public function getTrace()
|
Chris@0
|
174 {
|
Chris@0
|
175 return $this->trace;
|
Chris@0
|
176 }
|
Chris@0
|
177
|
Chris@0
|
178 public function setTraceFromException(\Exception $exception)
|
Chris@0
|
179 {
|
Chris@0
|
180 $this->setTrace($exception->getTrace(), $exception->getFile(), $exception->getLine());
|
Chris@0
|
181 }
|
Chris@0
|
182
|
Chris@0
|
183 public function setTrace($trace, $file, $line)
|
Chris@0
|
184 {
|
Chris@0
|
185 $this->trace = array();
|
Chris@0
|
186 $this->trace[] = array(
|
Chris@0
|
187 'namespace' => '',
|
Chris@0
|
188 'short_class' => '',
|
Chris@0
|
189 'class' => '',
|
Chris@0
|
190 'type' => '',
|
Chris@0
|
191 'function' => '',
|
Chris@0
|
192 'file' => $file,
|
Chris@0
|
193 'line' => $line,
|
Chris@0
|
194 'args' => array(),
|
Chris@0
|
195 );
|
Chris@0
|
196 foreach ($trace as $entry) {
|
Chris@0
|
197 $class = '';
|
Chris@0
|
198 $namespace = '';
|
Chris@0
|
199 if (isset($entry['class'])) {
|
Chris@0
|
200 $parts = explode('\\', $entry['class']);
|
Chris@0
|
201 $class = array_pop($parts);
|
Chris@0
|
202 $namespace = implode('\\', $parts);
|
Chris@0
|
203 }
|
Chris@0
|
204
|
Chris@0
|
205 $this->trace[] = array(
|
Chris@0
|
206 'namespace' => $namespace,
|
Chris@0
|
207 'short_class' => $class,
|
Chris@0
|
208 'class' => isset($entry['class']) ? $entry['class'] : '',
|
Chris@0
|
209 'type' => isset($entry['type']) ? $entry['type'] : '',
|
Chris@0
|
210 'function' => isset($entry['function']) ? $entry['function'] : null,
|
Chris@0
|
211 'file' => isset($entry['file']) ? $entry['file'] : null,
|
Chris@0
|
212 'line' => isset($entry['line']) ? $entry['line'] : null,
|
Chris@0
|
213 'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(),
|
Chris@0
|
214 );
|
Chris@0
|
215 }
|
Chris@0
|
216 }
|
Chris@0
|
217
|
Chris@0
|
218 private function flattenArgs($args, $level = 0, &$count = 0)
|
Chris@0
|
219 {
|
Chris@0
|
220 $result = array();
|
Chris@0
|
221 foreach ($args as $key => $value) {
|
Chris@0
|
222 if (++$count > 1e4) {
|
Chris@0
|
223 return array('array', '*SKIPPED over 10000 entries*');
|
Chris@0
|
224 }
|
Chris@0
|
225 if ($value instanceof \__PHP_Incomplete_Class) {
|
Chris@0
|
226 // is_object() returns false on PHP<=7.1
|
Chris@0
|
227 $result[$key] = array('incomplete-object', $this->getClassNameFromIncomplete($value));
|
Chris@0
|
228 } elseif (is_object($value)) {
|
Chris@0
|
229 $result[$key] = array('object', get_class($value));
|
Chris@0
|
230 } elseif (is_array($value)) {
|
Chris@0
|
231 if ($level > 10) {
|
Chris@0
|
232 $result[$key] = array('array', '*DEEP NESTED ARRAY*');
|
Chris@0
|
233 } else {
|
Chris@0
|
234 $result[$key] = array('array', $this->flattenArgs($value, $level + 1, $count));
|
Chris@0
|
235 }
|
Chris@0
|
236 } elseif (null === $value) {
|
Chris@0
|
237 $result[$key] = array('null', null);
|
Chris@0
|
238 } elseif (is_bool($value)) {
|
Chris@0
|
239 $result[$key] = array('boolean', $value);
|
Chris@0
|
240 } elseif (is_int($value)) {
|
Chris@0
|
241 $result[$key] = array('integer', $value);
|
Chris@0
|
242 } elseif (is_float($value)) {
|
Chris@0
|
243 $result[$key] = array('float', $value);
|
Chris@0
|
244 } elseif (is_resource($value)) {
|
Chris@0
|
245 $result[$key] = array('resource', get_resource_type($value));
|
Chris@0
|
246 } else {
|
Chris@0
|
247 $result[$key] = array('string', (string) $value);
|
Chris@0
|
248 }
|
Chris@0
|
249 }
|
Chris@0
|
250
|
Chris@0
|
251 return $result;
|
Chris@0
|
252 }
|
Chris@0
|
253
|
Chris@0
|
254 private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value)
|
Chris@0
|
255 {
|
Chris@0
|
256 $array = new \ArrayObject($value);
|
Chris@0
|
257
|
Chris@0
|
258 return $array['__PHP_Incomplete_Class_Name'];
|
Chris@0
|
259 }
|
Chris@0
|
260 }
|