Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Spatie\Ignition;
4
 
5
use ArrayObject;
6
use ErrorException;
7
use Spatie\FlareClient\Context\BaseContextProviderDetector;
8
use Spatie\FlareClient\Context\ContextProviderDetector;
9
use Spatie\FlareClient\Enums\MessageLevels;
10
use Spatie\FlareClient\Flare;
11
use Spatie\FlareClient\FlareMiddleware\AddDocumentationLinks;
12
use Spatie\FlareClient\FlareMiddleware\AddSolutions;
13
use Spatie\FlareClient\FlareMiddleware\FlareMiddleware;
14
use Spatie\FlareClient\Report;
15
use Spatie\Ignition\Config\IgnitionConfig;
16
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
17
use Spatie\Ignition\Contracts\SolutionProviderRepository as SolutionProviderRepositoryContract;
18
use Spatie\Ignition\ErrorPage\ErrorPageViewModel;
19
use Spatie\Ignition\ErrorPage\Renderer;
20
use Spatie\Ignition\Solutions\SolutionProviders\BadMethodCallSolutionProvider;
21
use Spatie\Ignition\Solutions\SolutionProviders\MergeConflictSolutionProvider;
22
use Spatie\Ignition\Solutions\SolutionProviders\SolutionProviderRepository;
23
use Spatie\Ignition\Solutions\SolutionProviders\UndefinedPropertySolutionProvider;
24
use Throwable;
25
 
26
class Ignition
27
{
28
    protected Flare $flare;
29
 
30
    protected bool $shouldDisplayException = true;
31
 
32
    protected string $flareApiKey = '';
33
 
34
    protected string $applicationPath = '';
35
 
36
    /** @var array<int, FlareMiddleware> */
37
    protected array $middleware = [];
38
 
39
    protected IgnitionConfig $ignitionConfig;
40
 
41
    protected ContextProviderDetector $contextProviderDetector;
42
 
43
    protected SolutionProviderRepositoryContract $solutionProviderRepository;
44
 
45
    protected ?bool $inProductionEnvironment = null;
46
 
47
    protected ?string $solutionTransformerClass = null;
48
 
49
    /** @var ArrayObject<int, callable(Throwable): mixed> */
50
    protected ArrayObject $documentationLinkResolvers;
51
 
52
    protected string $customHtmlHead = '';
53
 
54
    protected string $customHtmlBody = '';
55
 
56
    public static function make(): self
57
    {
58
        return new self();
59
    }
60
 
61
    public function __construct()
62
    {
63
        $this->flare = Flare::make();
64
 
65
        $this->ignitionConfig = IgnitionConfig::loadFromConfigFile();
66
 
67
        $this->solutionProviderRepository = new SolutionProviderRepository($this->getDefaultSolutionProviders());
68
 
69
        $this->documentationLinkResolvers = new ArrayObject();
70
 
71
        $this->contextProviderDetector = new BaseContextProviderDetector();
72
 
73
        $this->middleware[] = new AddSolutions($this->solutionProviderRepository);
74
        $this->middleware[] = new AddDocumentationLinks($this->documentationLinkResolvers);
75
    }
76
 
77
    public function setSolutionTransformerClass(string $solutionTransformerClass): self
78
    {
79
        $this->solutionTransformerClass = $solutionTransformerClass;
80
 
81
        return $this;
82
    }
83
 
84
    /** @param callable(Throwable): mixed $callable */
85
    public function resolveDocumentationLink(callable $callable): self
86
    {
87
        $this->documentationLinkResolvers[] = $callable;
88
 
89
        return $this;
90
    }
91
 
92
    public function setConfig(IgnitionConfig $ignitionConfig): self
93
    {
94
        $this->ignitionConfig = $ignitionConfig;
95
 
96
        return $this;
97
    }
98
 
99
    public function runningInProductionEnvironment(bool $boolean = true): self
100
    {
101
        $this->inProductionEnvironment = $boolean;
102
 
103
        return $this;
104
    }
105
 
106
    public function getFlare(): Flare
107
    {
108
        return $this->flare;
109
    }
110
 
111
    public function setFlare(Flare $flare): self
112
    {
113
        $this->flare = $flare;
114
 
115
        return $this;
116
    }
117
 
118
    public function setSolutionProviderRepository(SolutionProviderRepositoryContract $solutionProviderRepository): self
119
    {
120
        $this->solutionProviderRepository = $solutionProviderRepository;
121
 
122
        return $this;
123
    }
124
 
125
    public function shouldDisplayException(bool $shouldDisplayException): self
126
    {
127
        $this->shouldDisplayException = $shouldDisplayException;
128
 
129
        return $this;
130
    }
131
 
132
    public function applicationPath(string $applicationPath): self
133
    {
134
        $this->applicationPath = $applicationPath;
135
 
136
        return $this;
137
    }
138
 
139
    /**
140
     * @param string $name
141
     * @param string $messageLevel
142
     * @param array<int, mixed> $metaData
143
     *
144
     * @return $this
145
     */
146
    public function glow(
147
        string $name,
148
        string $messageLevel = MessageLevels::INFO,
149
        array $metaData = []
150
    ): self {
151
        $this->flare->glow($name, $messageLevel, $metaData);
152
 
153
        return $this;
154
    }
155
 
156
    /**
157
     * @param array<int, HasSolutionsForThrowable|class-string<HasSolutionsForThrowable>> $solutionProviders
158
     *
159
     * @return $this
160
     */
161
    public function addSolutionProviders(array $solutionProviders): self
162
    {
163
        $this->solutionProviderRepository->registerSolutionProviders($solutionProviders);
164
 
165
        return $this;
166
    }
167
 
168
    /** @deprecated Use `setTheme('dark')` instead */
169
    public function useDarkMode(): self
170
    {
171
        return $this->setTheme('dark');
172
    }
173
 
174
    /** @deprecated Use `setTheme($theme)` instead */
175
    public function theme(string $theme): self
176
    {
177
        return $this->setTheme($theme);
178
    }
179
 
180
    public function setTheme(string $theme): self
181
    {
182
        $this->ignitionConfig->setOption('theme', $theme);
183
 
184
        return $this;
185
    }
186
 
187
    public function setEditor(string $editor): self
188
    {
189
        $this->ignitionConfig->setOption('editor', $editor);
190
 
191
        return $this;
192
    }
193
 
194
    public function sendToFlare(?string $apiKey): self
195
    {
196
        $this->flareApiKey = $apiKey ?? '';
197
 
198
        return $this;
199
    }
200
 
201
    public function configureFlare(callable $callable): self
202
    {
203
        ($callable)($this->flare);
204
 
205
        return $this;
206
    }
207
 
208
    /**
209
     * @param FlareMiddleware|array<int, FlareMiddleware> $middleware
210
     *
211
     * @return $this
212
     */
213
    public function registerMiddleware(array|FlareMiddleware $middleware): self
214
    {
215
        if (! is_array($middleware)) {
216
            $middleware = [$middleware];
217
        }
218
 
219
        foreach ($middleware as $singleMiddleware) {
220
            $this->middleware = array_merge($this->middleware, $middleware);
221
        }
222
 
223
        return $this;
224
    }
225
 
226
    public function setContextProviderDetector(ContextProviderDetector $contextProviderDetector): self
227
    {
228
        $this->contextProviderDetector = $contextProviderDetector;
229
 
230
        return $this;
231
    }
232
 
233
    public function reset(): self
234
    {
235
        $this->flare->reset();
236
 
237
        return $this;
238
    }
239
 
240
    public function register(): self
241
    {
242
        error_reporting(-1);
243
 
244
        /** @phpstan-ignore-next-line  */
245
        set_error_handler([$this, 'renderError']);
246
 
247
        /** @phpstan-ignore-next-line  */
248
        set_exception_handler([$this, 'handleException']);
249
 
250
        return $this;
251
    }
252
 
253
    /**
254
     * @param int $level
255
     * @param string $message
256
     * @param string $file
257
     * @param int $line
258
     * @param array<int, mixed> $context
259
     *
260
     * @return void
261
     * @throws \ErrorException
262
     */
263
    public function renderError(
264
        int $level,
265
        string $message,
266
        string $file = '',
267
        int $line = 0,
268
        array $context = []
269
    ): void {
270
        throw new ErrorException($message, 0, $level, $file, $line);
271
    }
272
 
273
    /**
274
     * This is the main entry point for the framework agnostic Ignition package.
275
     * Displays the Ignition page and optionally sends a report to Flare.
276
     */
277
    public function handleException(Throwable $throwable): Report
278
    {
279
        $this->setUpFlare();
280
 
281
        $report = $this->createReport($throwable);
282
 
283
        if ($this->shouldDisplayException && $this->inProductionEnvironment !== true) {
284
            $this->renderException($throwable, $report);
285
        }
286
 
287
        if ($this->flare->apiTokenSet() && $this->inProductionEnvironment !== false) {
288
            $this->flare->report($throwable, report: $report);
289
        }
290
 
291
        return $report;
292
    }
293
 
294
    /**
295
     * This is the main entrypoint for laravel-ignition. It only renders the exception.
296
     * Sending the report to Flare is handled in the laravel-ignition log handler.
297
     */
298
    public function renderException(Throwable $throwable, ?Report $report = null): void
299
    {
300
        $this->setUpFlare();
301
 
302
        $report ??= $this->createReport($throwable);
303
 
304
        $viewModel = new ErrorPageViewModel(
305
            $throwable,
306
            $this->ignitionConfig,
307
            $report,
308
            $this->solutionProviderRepository->getSolutionsForThrowable($throwable),
309
            $this->solutionTransformerClass,
310
            $this->customHtmlHead,
311
            $this->customHtmlBody
312
        );
313
 
314
        (new Renderer())->render(['viewModel' => $viewModel]);
315
    }
316
 
317
    /**
318
     * Add custom HTML which will be added to the head tag of the error page.
319
     */
320
    public function addCustomHtmlToHead(string $html): self
321
    {
322
        $this->customHtmlHead .= $html;
323
 
324
        return $this;
325
    }
326
 
327
    /**
328
     * Add custom HTML which will be added to the body tag of the error page.
329
     */
330
    public function addCustomHtmlToBody(string $html): self
331
    {
332
        $this->customHtmlBody .= $html;
333
 
334
        return $this;
335
    }
336
 
337
    protected function setUpFlare(): self
338
    {
339
        if (! $this->flare->apiTokenSet()) {
340
            $this->flare->setApiToken($this->flareApiKey ?? '');
341
        }
342
 
343
        $this->flare->setContextProviderDetector($this->contextProviderDetector);
344
 
345
        foreach ($this->middleware as $singleMiddleware) {
346
            $this->flare->registerMiddleware($singleMiddleware);
347
        }
348
 
349
        if ($this->applicationPath !== '') {
350
            $this->flare->applicationPath($this->applicationPath);
351
        }
352
 
353
        return $this;
354
    }
355
 
356
    /** @return array<class-string<HasSolutionsForThrowable>> */
357
    protected function getDefaultSolutionProviders(): array
358
    {
359
        return [
360
            BadMethodCallSolutionProvider::class,
361
            MergeConflictSolutionProvider::class,
362
            UndefinedPropertySolutionProvider::class,
363
        ];
364
    }
365
 
366
    protected function createReport(Throwable $throwable): Report
367
    {
368
        return $this->flare->createReport($throwable);
369
    }
370
}