Passed
Push — 0.7.0 ( ba8797...2cebcc )
by Alexander
03:45 queued 11s
created

Handler::getHttpExceptionView()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php 
2
3
/**
4
 * Lenevor Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2021 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
23
namespace Syscodes\Core\Exceptions;
24
25
use Exception;
26
use Throwable;
27
use Syscodes\Console\Cli;
28
use Syscodes\Debug\GDebug;
29
use Syscodes\Http\Response;
30
use Psr\Log\LoggerInterface;
31
use Syscodes\Collections\Arr;
32
use Syscodes\Debug\ExceptionHandler;
33
use Syscodes\Contracts\Container\Container;
34
use Syscodes\Core\Http\Exceptions\HttpException;
35
use Syscodes\Http\Exceptions\HttpResponseException;
36
use Syscodes\Debug\FatalExceptions\FlattenException;
37
use Syscodes\Core\Http\Exceptions\NotFoundHttpException;
38
use Syscodes\Database\Exceptions\ModelNotFoundException;
39
use Syscodes\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract;
40
41
/**
42
 * The system's main exception class is loaded for activate the render method of debugging.
43
 * 
44
 * @author Alexander Campo <[email protected]>
45
 */
46
class Handler implements ExceptionHandlerContract
47
{
48
    /**
49
     * The container implementation.
50
     * 
51
     * @var \Syscodes\Contracts\Container\Container $container
52
     */
53
    protected $container;
54
55
    /**
56
     * A list of the exception types that should not be reported.
57
     * 
58
     * @var array $dontReport
59
     */
60
    protected $dontReport = [];
61
62
    /**
63
     * A list of the Core exception types that should not be reported.
64
     * 
65
     * @var array $coreDontReport
66
     */
67
    protected $coreDontReport = [
68
        HttpException::class,
69
        HttpResponseException::class,
70
        ModelNotFoundException::class,
71
    ];
72
73
    /**
74
     * Constructor. Create a new exception handler instance.
75
     * 
76
     * @param  \Syscodes\Contracts\Container\Container  $container
77
     * 
78
     * @return void
79
     */
80
    public function __construct(Container $container)
81
    {
82
        $this->container = $container;
83
    }
84
    
85
    /**
86
     * Report or log an exception.
87
     * 
88
     * @param  \Exception  $e
89
     * 
90
     * @return mixed
91
     * 
92
     * @throws \Exception
93
     */
94
    public function report(Throwable $e)
95
    {
96
        if ($this->shouldntReport($e)) {
97
            return;
98
        }
99
100
        if (method_exists($e, 'report')) {
101
            return $e->report($e);
102
        }
103
104
        try {
105
            $logger = $this->container->make(LoggerInterface::class);
106
        } catch (Exception $e) {
107
            throw $e;
108
        }
109
        
110
        $logger->error($e->getMessage());
111
    }
112
113
    /**
114
     * Determine if the exception should be reported.
115
     * 
116
     * @param  \Throwable  $e
117
     * 
118
     * @return bool
119
     */
120
    public function shouldReport(Throwable $e)
121
    {
122
        return ! $this->shouldntReport($e);
123
    }
124
125
    /**
126
     * Determine if the exception is in the "do not report" list.
127
     * 
128
     * @param  \Throwable  $e
129
     * 
130
     * @return bool
131
     */
132
    public function shouldntReport(Throwable $e)
133
    {
134
        $dontReport = array_merge($this->dontReport, $this->coreDontReport);
135
136
        foreach ($dontReport as $type) {
137
            if ($e instanceof $type) {
138
                return true;
139
            }
140
        }
141
142
        return false;
143
    }
144
145
    /**
146
     * Render an exception into a response.
147
     *
148
     * @param  \Syscodes\Http\Request  $request
149
     * @param  \Throwable  $e
150
     * 
151
     * @return \Syscodes\Http\Response
152
     */
153
    public function render($request, Throwable $e)
154
    {
155
        $e = $this->prepareException($e);
156
157
        if ($e instanceof HttpResponseException) {
158
            $e->getResponse();
159
        }
160
161
        return $this->prepareResponse($request, $e);
162
    }
163
164
    /**
165
     * Prepare exception for rendering.
166
     * 
167
     * @param  \Throwable  $e
168
     * 
169
     * @return \Throwable
170
     */
171
    protected function prepareException(Throwable $e)
172
    {
173
        if ($e instanceof ModelNotFoundException) {
174
            $e = new NotFoundHttpException($e->getMessage(), $e);
175
        }
176
177
        return $e;
178
    }
179
     
180
    /**
181
     * Prepare a response for the given exception.
182
     * 
183
     * @param  \Syscodes\Http\Request  $request
184
     * @param  \Throwable  $e
185
     * 
186
     * @return \Syscodes\Http\Response
187
     * 
188
     * @uses   \Syscodes\Core\Http\Exceptions\HttpException
189
     */
190
    protected function prepareResponse($request, Throwable $e)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

190
    protected function prepareResponse(/** @scrutinizer ignore-unused */ $request, Throwable $e)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
191
    {
192
        if ( ! $this->isHttpException($e) && config('app.debug')) {
193
            return $this->toSyscodesResponse($this->convertExceptionToResponse($e), $e);
194
        }
195
196
        // When the debug is not active, the HTTP 500 code view is throw
197
        if ( ! $this->isHttpException($e)) {
198
            $e = new HttpException(500, $e->getMessage());
199
        }
200
201
        return $this->toSyscodesResponse($this->renderHttpException($e), $e);
202
    }
203
204
    /**
205
     * Render the given HttpException.
206
     * 
207
     * @param  \Syscodes\Core\Http\Exceptions\HttpException  $e
208
     * 
209
     * @return \Syscodes\Http\Response
210
     */
211
    protected function renderHttpException(HttpException $e)
212
    {
213
        $this->registerViewErrorPaths();
214
215
        if (view()->viewExists($view = $this->getHttpExceptionView($e))) {
0 ignored issues
show
Bug introduced by
The method viewExists() does not exist on Syscodes\View\View. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

215
        if (view()->/** @scrutinizer ignore-call */ viewExists($view = $this->getHttpExceptionView($e))) {
Loading history...
216
            return response()->view(
0 ignored issues
show
Bug introduced by
The method view() does not exist on Syscodes\Http\Response. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

216
            return response()->/** @scrutinizer ignore-call */ view(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
217
                $view, 
218
                ['exception' => $e],
219
                $e->getStatusCode(),
220
                $e->getHeaders()
221
            );
222
        }
223
224
        return $this->convertExceptionToResponse($e);
225
    }
226
227
    /**
228
     * Register the error view paths.
229
     * 
230
     * @return void
231
     */
232
    protected function registerViewErrorPaths()
233
    {
234
        (new RegisterErrorViewPaths)();
235
    }
236
237
    /**
238
     * Get the view used to render HTTP exceptions.
239
     * 
240
     * @param  \Syscodes\Core\Http\Exceptions\HttpException  $e
241
     * 
242
     * @return string
243
     */
244
    protected function getHttpExceptionView(HttpException $e)
245
    {
246
        return "errors::{$e->getStatusCode()}";
247
    }
248
249
    /**
250
     * Create a response for the given exception.
251
     * 
252
     * @param  \Exception  $e
253
     * 
254
     * @return \Syscodes\Http\Response
255
     */
256
    protected function convertExceptionToResponse(Throwable $e)
257
    {
258
        return Response::render(
259
            $this->renderExceptionContent($e),
260
            $this->isHttpException($e) ? $e->getStatusCode() : 500,
0 ignored issues
show
Bug introduced by
The method getStatusCode() does not exist on Throwable. It seems like you code against a sub-type of Throwable such as Syscodes\Core\Http\Exceptions\HttpException. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

260
            $this->isHttpException($e) ? $e->/** @scrutinizer ignore-call */ getStatusCode() : 500,
Loading history...
261
            $this->isHttpException($e) ? $e->getHeaders() : []
0 ignored issues
show
Bug introduced by
The method getHeaders() does not exist on Throwable. It seems like you code against a sub-type of Throwable such as Syscodes\Core\Http\Exceptions\HttpException. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

261
            $this->isHttpException($e) ? $e->/** @scrutinizer ignore-call */ getHeaders() : []
Loading history...
262
        );
263
    }
264
265
    /**
266
     * Gets the response content for the given exception.
267
     * 
268
     * @param  \Throwable  $e
269
     * 
270
     * @return string
271
     */
272
    protected function renderExceptionContent(Throwable $e)
273
    {
274
        try {
275
            return config('app.debug') && class_exists(GDebug::class)
276
                        ? $this->renderExceptionWithGDebug($e) 
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->renderExceptionWithGDebug($e) targeting Syscodes\Core\Exceptions...erExceptionWithGDebug() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
277
                        : $this->renderExceptionWithFlatDesignDebug($e, config('app.debug'));
0 ignored issues
show
Bug introduced by
It seems like config('app.debug') can also be of type Syscodes\Config\Configure; however, parameter $debug of Syscodes\Core\Exceptions...onWithFlatDesignDebug() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

277
                        : $this->renderExceptionWithFlatDesignDebug($e, /** @scrutinizer ignore-type */ config('app.debug'));
Loading history...
278
        } catch (Exception $e) {
279
            $this->renderExceptionWithFlatDesignDebug($e, config('app.debug'));
280
        }
281
    }
282
283
    /**
284
     * Render an exception to a string using "GDebug".
285
     * 
286
     * @param  \Throwable  $e
287
     * 
288
     * @return void
289
     * 
290
     * @uses   \Syscodes\Debug\GDebug
291
     */
292
    protected function renderExceptionWithGDebug(Throwable $e)
293
    {
294
        return take(new GDebug, function ($debug) {
0 ignored issues
show
Bug introduced by
new Syscodes\Debug\GDebug() of type Syscodes\Debug\GDebug is incompatible with the type string expected by parameter $value of take(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

294
        return take(/** @scrutinizer ignore-type */ new GDebug, function ($debug) {
Loading history...
Bug Best Practice introduced by
The expression return take(new Syscodes...})->handleException($e) returns the type string which is incompatible with the documented return type void.
Loading history...
295
            
296
            $debug->pushHandler($this->DebugHandler());
297
298
            $debug->writeToOutput(false);
299
300
            $debug->allowQuit(false);
301
302
        })->handleException($e);
303
    }
304
305
    /**
306
     * Get the Debug handler for the application.
307
     *
308
     * @return \Syscodes\Debug\Handlers\MainHandler
309
     */
310
    protected function DebugHandler()
311
    {
312
        return (new DebugHandler)->initDebug();
313
    }
314
315
    /**
316
     * Render an exception to a string using Flat Design Debug.
317
     * 
318
     * @param  \Throwable  $e
319
     * @param  bool  $debug
320
     * 
321
     * @return string
322
     */
323
    protected function renderExceptionWithFlatDesignDebug(Throwable $e, $debug)
324
    {
325
        return (new ExceptionHandler($debug))->getHtmlResponse(
326
            FlattenException::make($e)
0 ignored issues
show
Bug introduced by
Syscodes\Debug\FatalExce...ttenException::make($e) of type void is incompatible with the type Exception|Syscodes\Debug...ptions\FlattenException expected by parameter $exception of Syscodes\Debug\ExceptionHandler::getHtmlResponse(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

326
            /** @scrutinizer ignore-type */ FlattenException::make($e)
Loading history...
Bug introduced by
Are you sure the usage of Syscodes\Debug\FatalExce...ttenException::make($e) targeting Syscodes\Debug\FatalExce...lattenException::make() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
327
        );
328
    }
329
330
    /**
331
     * Map the given exception into an Syscodes response.
332
     * 
333
     * @param  \Syscodes\Http\Response  $response
334
     * @param  \Exception  $e 
335
     * 
336
     * @return \Syscodes\Http\Response
337
     */
338
    protected function toSyscodesResponse($response, Throwable $e)
339
    {
340
        if ($response instanceof RedirectResponse) {
0 ignored issues
show
Bug introduced by
The type Syscodes\Core\Exceptions\RedirectResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
341
            $response = new RedirectResponse(
342
                $response->getTargetUrl(), $response->status(), $response->headers->all()
0 ignored issues
show
Bug introduced by
The method getTargetUrl() does not exist on Syscodes\Http\Response. It seems like you code against a sub-type of Syscodes\Http\Response such as Syscodes\Http\RedirectResponse. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

342
                $response->/** @scrutinizer ignore-call */ 
343
                           getTargetUrl(), $response->status(), $response->headers->all()
Loading history...
343
            );
344
        } else {
345
            $response = new Response(
346
                $response->content(), $response->status(), $response->headers->all()
347
            );
348
        }
349
350
        return $response->withException($e);
351
    }
352
353
    /**
354
     * Render an exception to the console.
355
     * 
356
     * @param  \Throwable  $e
357
     * 
358
     * @return void
359
     */
360
    public function renderForConsole(Throwable $e)
361
    {
362
        $message = sprintf(
363
            Cli::color("%s: ", 'light_red').
364
            Cli::color("%s in file %s on line %d", 'light_cyan')."\n\n%s\n",
365
            getClass($e, true),
0 ignored issues
show
Bug introduced by
getClass($e, true) of type array is incompatible with the type double|integer|string expected by parameter $values of sprintf(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

365
            /** @scrutinizer ignore-type */ getClass($e, true),
Loading history...
366
            $e->getMessage(),
367
            $e->getFile(),
368
            $e->getLine(),
369
            $e->getTraceAsString()
370
        );
371
372
        echo $message;
373
    }
374
375
    /**
376
     * Determine if the given exception is an HTTP exception.
377
     * 
378
     * @param  \Throwable  $e
379
     * 
380
     * @return bool
381
     */
382
    protected function isHttpException(Throwable $e)
383
    {
384
        return $e instanceof HttpException;
385
    }
386
}