Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <fabien@symfony.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Symfony\Component\ErrorHandler\Exception;
13
 
14
use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
15
use Symfony\Component\HttpFoundation\Response;
16
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
17
 
18
/**
19
 * FlattenException wraps a PHP Error or Exception to be able to serialize it.
20
 *
21
 * Basically, this class removes all objects from the trace.
22
 *
23
 * @author Fabien Potencier <fabien@symfony.com>
24
 */
25
class FlattenException
26
{
27
    private string $message;
28
    private string|int $code;
29
    private ?self $previous = null;
30
    private array $trace;
31
    private string $traceAsString;
32
    private string $class;
33
    private int $statusCode;
34
    private string $statusText;
35
    private array $headers;
36
    private string $file;
37
    private int $line;
38
    private ?string $asString = null;
39
 
40
    public static function create(\Exception $exception, int $statusCode = null, array $headers = []): static
41
    {
42
        return static::createFromThrowable($exception, $statusCode, $headers);
43
    }
44
 
45
    public static function createFromThrowable(\Throwable $exception, int $statusCode = null, array $headers = []): static
46
    {
47
        $e = new static();
48
        $e->setMessage($exception->getMessage());
49
        $e->setCode($exception->getCode());
50
 
51
        if ($exception instanceof HttpExceptionInterface) {
52
            $statusCode = $exception->getStatusCode();
53
            $headers = array_merge($headers, $exception->getHeaders());
54
        } elseif ($exception instanceof RequestExceptionInterface) {
55
            $statusCode = 400;
56
        }
57
 
58
        $statusCode ??= 500;
59
 
60
        if (class_exists(Response::class) && isset(Response::$statusTexts[$statusCode])) {
61
            $statusText = Response::$statusTexts[$statusCode];
62
        } else {
63
            $statusText = 'Whoops, looks like something went wrong.';
64
        }
65
 
66
        $e->setStatusText($statusText);
67
        $e->setStatusCode($statusCode);
68
        $e->setHeaders($headers);
69
        $e->setTraceFromThrowable($exception);
70
        $e->setClass(get_debug_type($exception));
71
        $e->setFile($exception->getFile());
72
        $e->setLine($exception->getLine());
73
 
74
        $previous = $exception->getPrevious();
75
 
76
        if ($previous instanceof \Throwable) {
77
            $e->setPrevious(static::createFromThrowable($previous));
78
        }
79
 
80
        return $e;
81
    }
82
 
83
    public function toArray(): array
84
    {
85
        $exceptions = [];
86
        foreach (array_merge([$this], $this->getAllPrevious()) as $exception) {
87
            $exceptions[] = [
88
                'message' => $exception->getMessage(),
89
                'class' => $exception->getClass(),
90
                'trace' => $exception->getTrace(),
91
            ];
92
        }
93
 
94
        return $exceptions;
95
    }
96
 
97
    public function getStatusCode(): int
98
    {
99
        return $this->statusCode;
100
    }
101
 
102
    /**
103
     * @return $this
104
     */
105
    public function setStatusCode(int $code): static
106
    {
107
        $this->statusCode = $code;
108
 
109
        return $this;
110
    }
111
 
112
    public function getHeaders(): array
113
    {
114
        return $this->headers;
115
    }
116
 
117
    /**
118
     * @return $this
119
     */
120
    public function setHeaders(array $headers): static
121
    {
122
        $this->headers = $headers;
123
 
124
        return $this;
125
    }
126
 
127
    public function getClass(): string
128
    {
129
        return $this->class;
130
    }
131
 
132
    /**
133
     * @return $this
134
     */
135
    public function setClass(string $class): static
136
    {
137
        $this->class = str_contains($class, "@anonymous\0") ? (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous' : $class;
138
 
139
        return $this;
140
    }
141
 
142
    public function getFile(): string
143
    {
144
        return $this->file;
145
    }
146
 
147
    /**
148
     * @return $this
149
     */
150
    public function setFile(string $file): static
151
    {
152
        $this->file = $file;
153
 
154
        return $this;
155
    }
156
 
157
    public function getLine(): int
158
    {
159
        return $this->line;
160
    }
161
 
162
    /**
163
     * @return $this
164
     */
165
    public function setLine(int $line): static
166
    {
167
        $this->line = $line;
168
 
169
        return $this;
170
    }
171
 
172
    public function getStatusText(): string
173
    {
174
        return $this->statusText;
175
    }
176
 
177
    /**
178
     * @return $this
179
     */
180
    public function setStatusText(string $statusText): static
181
    {
182
        $this->statusText = $statusText;
183
 
184
        return $this;
185
    }
186
 
187
    public function getMessage(): string
188
    {
189
        return $this->message;
190
    }
191
 
192
    /**
193
     * @return $this
194
     */
195
    public function setMessage(string $message): static
196
    {
197
        if (str_contains($message, "@anonymous\0")) {
198
            $message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) {
199
                return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0];
200
            }, $message);
201
        }
202
 
203
        $this->message = $message;
204
 
205
        return $this;
206
    }
207
 
208
    /**
209
     * @return int|string int most of the time (might be a string with PDOException)
210
     */
211
    public function getCode(): int|string
212
    {
213
        return $this->code;
214
    }
215
 
216
    /**
217
     * @return $this
218
     */
219
    public function setCode(int|string $code): static
220
    {
221
        $this->code = $code;
222
 
223
        return $this;
224
    }
225
 
226
    public function getPrevious(): ?self
227
    {
228
        return $this->previous;
229
    }
230
 
231
    /**
232
     * @return $this
233
     */
234
    public function setPrevious(?self $previous): static
235
    {
236
        $this->previous = $previous;
237
 
238
        return $this;
239
    }
240
 
241
    /**
242
     * @return self[]
243
     */
244
    public function getAllPrevious(): array
245
    {
246
        $exceptions = [];
247
        $e = $this;
248
        while ($e = $e->getPrevious()) {
249
            $exceptions[] = $e;
250
        }
251
 
252
        return $exceptions;
253
    }
254
 
255
    public function getTrace(): array
256
    {
257
        return $this->trace;
258
    }
259
 
260
    /**
261
     * @return $this
262
     */
263
    public function setTraceFromThrowable(\Throwable $throwable): static
264
    {
265
        $this->traceAsString = $throwable->getTraceAsString();
266
 
267
        return $this->setTrace($throwable->getTrace(), $throwable->getFile(), $throwable->getLine());
268
    }
269
 
270
    /**
271
     * @return $this
272
     */
273
    public function setTrace(array $trace, ?string $file, ?int $line): static
274
    {
275
        $this->trace = [];
276
        $this->trace[] = [
277
            'namespace' => '',
278
            'short_class' => '',
279
            'class' => '',
280
            'type' => '',
281
            'function' => '',
282
            'file' => $file,
283
            'line' => $line,
284
            'args' => [],
285
        ];
286
        foreach ($trace as $entry) {
287
            $class = '';
288
            $namespace = '';
289
            if (isset($entry['class'])) {
290
                $parts = explode('\\', $entry['class']);
291
                $class = array_pop($parts);
292
                $namespace = implode('\\', $parts);
293
            }
294
 
295
            $this->trace[] = [
296
                'namespace' => $namespace,
297
                'short_class' => $class,
298
                'class' => $entry['class'] ?? '',
299
                'type' => $entry['type'] ?? '',
300
                'function' => $entry['function'] ?? null,
301
                'file' => $entry['file'] ?? null,
302
                'line' => $entry['line'] ?? null,
303
                'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : [],
304
            ];
305
        }
306
 
307
        return $this;
308
    }
309
 
310
    private function flattenArgs(array $args, int $level = 0, int &$count = 0): array
311
    {
312
        $result = [];
313
        foreach ($args as $key => $value) {
314
            if (++$count > 1e4) {
315
                return ['array', '*SKIPPED over 10000 entries*'];
316
            }
317
            if ($value instanceof \__PHP_Incomplete_Class) {
318
                $result[$key] = ['incomplete-object', $this->getClassNameFromIncomplete($value)];
319
            } elseif (\is_object($value)) {
320
                $result[$key] = ['object', get_debug_type($value)];
321
            } elseif (\is_array($value)) {
322
                if ($level > 10) {
323
                    $result[$key] = ['array', '*DEEP NESTED ARRAY*'];
324
                } else {
325
                    $result[$key] = ['array', $this->flattenArgs($value, $level + 1, $count)];
326
                }
327
            } elseif (null === $value) {
328
                $result[$key] = ['null', null];
329
            } elseif (\is_bool($value)) {
330
                $result[$key] = ['boolean', $value];
331
            } elseif (\is_int($value)) {
332
                $result[$key] = ['integer', $value];
333
            } elseif (\is_float($value)) {
334
                $result[$key] = ['float', $value];
335
            } elseif (\is_resource($value)) {
336
                $result[$key] = ['resource', get_resource_type($value)];
337
            } else {
338
                $result[$key] = ['string', (string) $value];
339
            }
340
        }
341
 
342
        return $result;
343
    }
344
 
345
    private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value): string
346
    {
347
        $array = new \ArrayObject($value);
348
 
349
        return $array['__PHP_Incomplete_Class_Name'];
350
    }
351
 
352
    public function getTraceAsString(): string
353
    {
354
        return $this->traceAsString;
355
    }
356
 
357
    /**
358
     * @return $this
359
     */
360
    public function setAsString(?string $asString): static
361
    {
362
        $this->asString = $asString;
363
 
364
        return $this;
365
    }
366
 
367
    public function getAsString(): string
368
    {
369
        if (null !== $this->asString) {
370
            return $this->asString;
371
        }
372
 
373
        $message = '';
374
        $next = false;
375
 
376
        foreach (array_reverse(array_merge([$this], $this->getAllPrevious())) as $exception) {
377
            if ($next) {
378
                $message .= 'Next ';
379
            } else {
380
                $next = true;
381
            }
382
            $message .= $exception->getClass();
383
 
384
            if ('' != $exception->getMessage()) {
385
                $message .= ': '.$exception->getMessage();
386
            }
387
 
388
            $message .= ' in '.$exception->getFile().':'.$exception->getLine().
389
                "\nStack trace:\n".$exception->getTraceAsString()."\n\n";
390
        }
391
 
392
        return rtrim($message);
393
    }
394
}