Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Spatie\FlareClient;
4
 
5
use Closure;
6
use Error;
7
use ErrorException;
8
use Exception;
9
use Illuminate\Contracts\Container\Container;
10
use Illuminate\Pipeline\Pipeline;
11
use Spatie\FlareClient\Concerns\HasContext;
12
use Spatie\FlareClient\Context\BaseContextProviderDetector;
13
use Spatie\FlareClient\Context\ContextProviderDetector;
14
use Spatie\FlareClient\Enums\MessageLevels;
15
use Spatie\FlareClient\FlareMiddleware\AddEnvironmentInformation;
16
use Spatie\FlareClient\FlareMiddleware\AddGlows;
17
use Spatie\FlareClient\FlareMiddleware\CensorRequestBodyFields;
18
use Spatie\FlareClient\FlareMiddleware\FlareMiddleware;
19
use Spatie\FlareClient\FlareMiddleware\RemoveRequestIp;
20
use Spatie\FlareClient\Glows\Glow;
21
use Spatie\FlareClient\Glows\GlowRecorder;
22
use Spatie\FlareClient\Http\Client;
23
use Throwable;
24
 
25
class Flare
26
{
27
    use HasContext;
28
 
29
    protected Client $client;
30
 
31
    protected Api $api;
32
 
33
    /** @var array<int, FlareMiddleware|class-string<FlareMiddleware>> */
34
    protected array $middleware = [];
35
 
36
    protected GlowRecorder $recorder;
37
 
38
    protected ?string $applicationPath = null;
39
 
40
    protected ContextProviderDetector $contextDetector;
41
 
42
    protected ?Closure $previousExceptionHandler = null;
43
 
44
    /** @var null|callable */
45
    protected $previousErrorHandler = null;
46
 
47
    /** @var null|callable */
48
    protected $determineVersionCallable = null;
49
 
50
    protected ?int $reportErrorLevels = null;
51
 
52
    /** @var null|callable */
53
    protected $filterExceptionsCallable = null;
54
 
55
    /** @var null|callable */
56
    protected $filterReportsCallable = null;
57
 
58
    protected ?string $stage = null;
59
 
60
    protected ?string $requestId = null;
61
 
62
    protected ?Container $container = null;
63
 
64
    public static function make(
65
        string $apiKey = null,
66
        ContextProviderDetector $contextDetector = null
67
    ): self {
68
        $client = new Client($apiKey);
69
 
70
        return new self($client, $contextDetector);
71
    }
72
 
73
    public function setApiToken(string $apiToken): self
74
    {
75
        $this->client->setApiToken($apiToken);
76
 
77
        return $this;
78
    }
79
 
80
    public function apiTokenSet(): bool
81
    {
82
        return $this->client->apiTokenSet();
83
    }
84
 
85
    public function setBaseUrl(string $baseUrl): self
86
    {
87
        $this->client->setBaseUrl($baseUrl);
88
 
89
        return $this;
90
    }
91
 
92
    public function setStage(?string $stage): self
93
    {
94
        $this->stage = $stage;
95
 
96
        return $this;
97
    }
98
 
99
    public function sendReportsImmediately(): self
100
    {
101
        $this->api->sendReportsImmediately();
102
 
103
        return $this;
104
    }
105
 
106
    public function determineVersionUsing(callable $determineVersionCallable): self
107
    {
108
        $this->determineVersionCallable = $determineVersionCallable;
109
 
110
        return $this;
111
    }
112
 
113
    public function reportErrorLevels(int $reportErrorLevels): self
114
    {
115
        $this->reportErrorLevels = $reportErrorLevels;
116
 
117
        return $this;
118
    }
119
 
120
    public function filterExceptionsUsing(callable $filterExceptionsCallable): self
121
    {
122
        $this->filterExceptionsCallable = $filterExceptionsCallable;
123
 
124
        return $this;
125
    }
126
 
127
    public function filterReportsUsing(callable $filterReportsCallable): self
128
    {
129
        $this->filterReportsCallable = $filterReportsCallable;
130
 
131
        return $this;
132
    }
133
 
134
    public function version(): ?string
135
    {
136
        if (! $this->determineVersionCallable) {
137
            return null;
138
        }
139
 
140
        return ($this->determineVersionCallable)();
141
    }
142
 
143
    /**
144
     * @param \Spatie\FlareClient\Http\Client $client
145
     * @param \Spatie\FlareClient\Context\ContextProviderDetector|null $contextDetector
146
     * @param array<int, FlareMiddleware> $middleware
147
     */
148
    public function __construct(
149
        Client $client,
150
        ContextProviderDetector $contextDetector = null,
151
        array $middleware = []
152
    ) {
153
        $this->client = $client;
154
        $this->recorder = new GlowRecorder();
155
        $this->contextDetector = $contextDetector ?? new BaseContextProviderDetector();
156
        $this->middleware = $middleware;
157
        $this->api = new Api($this->client);
158
 
159
        $this->registerDefaultMiddleware();
160
    }
161
 
162
    /** @return array<int, FlareMiddleware|class-string<FlareMiddleware>> */
163
    public function getMiddleware(): array
164
    {
165
        return $this->middleware;
166
    }
167
 
168
    public function setContextProviderDetector(ContextProviderDetector $contextDetector): self
169
    {
170
        $this->contextDetector = $contextDetector;
171
 
172
        return $this;
173
    }
174
 
175
    public function setContainer(Container $container): self
176
    {
177
        $this->container = $container;
178
 
179
        return $this;
180
    }
181
 
182
    public function registerFlareHandlers(): self
183
    {
184
        $this->registerExceptionHandler();
185
 
186
        $this->registerErrorHandler();
187
 
188
        return $this;
189
    }
190
 
191
    public function registerExceptionHandler(): self
192
    {
193
        /** @phpstan-ignore-next-line */
194
        $this->previousExceptionHandler = set_exception_handler([$this, 'handleException']);
195
 
196
        return $this;
197
    }
198
 
199
    public function registerErrorHandler(): self
200
    {
201
        $this->previousErrorHandler = set_error_handler([$this, 'handleError']);
202
 
203
        return $this;
204
    }
205
 
206
    protected function registerDefaultMiddleware(): self
207
    {
208
        return $this->registerMiddleware([
209
            new AddGlows($this->recorder),
210
            new AddEnvironmentInformation(),
211
        ]);
212
    }
213
 
214
    /**
1294 lars 215
     * @param FlareMiddleware|array<FlareMiddleware>|class-string<FlareMiddleware>|callable $middleware
148 lars 216
     *
217
     * @return $this
218
     */
219
    public function registerMiddleware($middleware): self
220
    {
221
        if (! is_array($middleware)) {
222
            $middleware = [$middleware];
223
        }
224
 
225
        $this->middleware = array_merge($this->middleware, $middleware);
226
 
227
        return $this;
228
    }
229
 
230
    /**
231
     * @return array<int,FlareMiddleware|class-string<FlareMiddleware>>
232
     */
233
    public function getMiddlewares(): array
234
    {
235
        return $this->middleware;
236
    }
237
 
238
    /**
239
     * @param string $name
240
     * @param string $messageLevel
241
     * @param array<int, mixed> $metaData
242
     *
243
     * @return $this
244
     */
245
    public function glow(
246
        string $name,
247
        string $messageLevel = MessageLevels::INFO,
248
        array $metaData = []
249
    ): self {
250
        $this->recorder->record(new Glow($name, $messageLevel, $metaData));
251
 
252
        return $this;
253
    }
254
 
255
    public function handleException(Throwable $throwable): void
256
    {
257
        $this->report($throwable);
258
 
259
        if ($this->previousExceptionHandler) {
260
            call_user_func($this->previousExceptionHandler, $throwable);
261
        }
262
    }
263
 
264
    /**
265
     * @return mixed
266
     */
267
    public function handleError(mixed $code, string $message, string $file = '', int $line = 0)
268
    {
269
        $exception = new ErrorException($message, 0, $code, $file, $line);
270
 
271
        $this->report($exception);
272
 
273
        if ($this->previousErrorHandler) {
274
            return call_user_func(
275
                $this->previousErrorHandler,
276
                $message,
277
                $code,
278
                $file,
279
                $line
280
            );
281
        }
282
    }
283
 
284
    public function applicationPath(string $applicationPath): self
285
    {
286
        $this->applicationPath = $applicationPath;
287
 
288
        return $this;
289
    }
290
 
291
    public function report(Throwable $throwable, callable $callback = null, Report $report = null): ?Report
292
    {
293
        if (! $this->shouldSendReport($throwable)) {
294
            return null;
295
        }
296
 
297
        $report ??= $this->createReport($throwable);
298
 
299
        if (! is_null($callback)) {
300
            call_user_func($callback, $report);
301
        }
302
 
303
        $this->recorder->reset();
304
 
305
        $this->sendReportToApi($report);
306
 
307
        return $report;
308
    }
309
 
310
    protected function shouldSendReport(Throwable $throwable): bool
311
    {
312
        if (isset($this->reportErrorLevels) && $throwable instanceof Error) {
313
            return (bool)($this->reportErrorLevels & $throwable->getCode());
314
        }
315
 
316
        if (isset($this->reportErrorLevels) && $throwable instanceof ErrorException) {
317
            return (bool)($this->reportErrorLevels & $throwable->getSeverity());
318
        }
319
 
320
        if ($this->filterExceptionsCallable && $throwable instanceof Exception) {
321
            return (bool)(call_user_func($this->filterExceptionsCallable, $throwable));
322
        }
323
 
324
        return true;
325
    }
326
 
327
    public function reportMessage(string $message, string $logLevel, callable $callback = null): void
328
    {
329
        $report = $this->createReportFromMessage($message, $logLevel);
330
 
331
        if (! is_null($callback)) {
332
            call_user_func($callback, $report);
333
        }
334
 
335
        $this->sendReportToApi($report);
336
    }
337
 
338
    public function sendTestReport(Throwable $throwable): void
339
    {
340
        $this->api->sendTestReport($this->createReport($throwable));
341
    }
342
 
343
    protected function sendReportToApi(Report $report): void
344
    {
345
        if ($this->filterReportsCallable) {
346
            if (! call_user_func($this->filterReportsCallable, $report)) {
347
                return;
348
            }
349
        }
350
 
351
        try {
352
            $this->api->report($report);
353
        } catch (Exception $exception) {
354
        }
355
    }
356
 
357
    public function reset(): void
358
    {
359
        $this->api->sendQueuedReports();
360
 
361
        $this->userProvidedContext = [];
362
 
363
        $this->recorder->reset();
364
    }
365
 
366
    protected function applyAdditionalParameters(Report $report): void
367
    {
368
        $report
369
            ->stage($this->stage)
370
            ->messageLevel($this->messageLevel)
371
            ->setApplicationPath($this->applicationPath)
372
            ->userProvidedContext($this->userProvidedContext);
373
    }
374
 
375
    public function anonymizeIp(): self
376
    {
377
        $this->registerMiddleware(new RemoveRequestIp());
378
 
379
        return $this;
380
    }
381
 
382
    /**
383
     * @param array<int, string> $fieldNames
384
     *
385
     * @return $this
386
     */
387
    public function censorRequestBodyFields(array $fieldNames): self
388
    {
389
        $this->registerMiddleware(new CensorRequestBodyFields($fieldNames));
390
 
391
        return $this;
392
    }
393
 
394
    public function createReport(Throwable $throwable): Report
395
    {
396
        $report = Report::createForThrowable(
397
            $throwable,
398
            $this->contextDetector->detectCurrentContext(),
399
            $this->applicationPath,
400
            $this->version()
401
        );
402
 
403
        return $this->applyMiddlewareToReport($report);
404
    }
405
 
406
    public function createReportFromMessage(string $message, string $logLevel): Report
407
    {
408
        $report = Report::createForMessage(
409
            $message,
410
            $logLevel,
411
            $this->contextDetector->detectCurrentContext(),
412
            $this->applicationPath
413
        );
414
 
415
        return $this->applyMiddlewareToReport($report);
416
    }
417
 
418
    protected function applyMiddlewareToReport(Report $report): Report
419
    {
420
        $this->applyAdditionalParameters($report);
421
        $middleware = array_map(function ($singleMiddleware) {
422
            return is_string($singleMiddleware)
423
                ? new $singleMiddleware
424
                : $singleMiddleware;
425
        }, $this->middleware);
426
 
427
        $report = (new Pipeline())
428
            ->send($report)
429
            ->through($middleware)
430
            ->then(fn ($report) => $report);
431
 
432
        return $report;
433
    }
434
}