Skip to content

Commit

Permalink
[11.x] Improves errors (#51261)
Browse files Browse the repository at this point in the history
* Adds initial work

* Apply fixes from StyleCI

* No need for register anonymous components path

* Dont map line and file

* Fixes access to blade exception

* Apply fixes from StyleCI

* Adds context

* Adds body and headers

* Adds database queries

* Fixes displaying Laravel Error Exceptions trace

* Improves queries over 100

* Refactors style and scripts

* refactors

* chore: coding style

* Reverts

* Reverts

* Adds work in progress regarding design

* Style changes

* Adds hover

* Continues to style things

* Renders tailwindcss first

* Uses night mode by default

* Ui change

* Fixes displaying body values

* Improves styling

* More style changes

* Fix rendering within Livewire

* Apply fixes from StyleCI

* More styling

* Improves bundling

* Uses dark mode by default

* Apply fixes from StyleCI

* Applies styling

* Removes pint file

* Reverts BC

* coding style

* More styling

* Apply fixes from StyleCI

* Adjusts style

* Apply fixes from StyleCI

* Fixes style

* Styles again

* Simplifies code

* Adjusts style

* style

* More styling

* Inline favicon

* Minor style issues

* coding style

* Small changes

* Renames

* Removes non used packages

* Rebuilds

* Removes code

* Fixes Octane

* Styles back

* Improves code

* Refactor

* Removes computer

* Removes non used bundled package

* var hint

* Reduces css size

* Removes non used method

* Apply fixes from StyleCI

* wording

* No need for rescue

* Refactors

* Only registers exceptions pages, if needed

* adds bindings

* defaults

* Reworks icons

* Revert "Removes computer"

This reverts commit 4598691.

* Adds system mode

* Allows to open on editor

* Apply fixes from StyleCI

* Adds context about routing

* Rebuilds scripts

* Rebuild asset

* Fixes undefined line

* wip

* fixes

* Minor fixes

* Styling

* minor

* wip

* Adjusts

* formatting

* formatting

* Formatting

* Fixes method typo

* Adjusts coding style

---------

Co-authored-by: StyleCI Bot <[email protected]>
Co-authored-by: Taylor Otwell <[email protected]>
  • Loading branch information
3 people authored May 27, 2024
1 parent 88a7b47 commit 092e3cb
Show file tree
Hide file tree
Showing 36 changed files with 3,942 additions and 4 deletions.
7 changes: 6 additions & 1 deletion .styleci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ php:
- bad-syntax-strategy.php
js:
finder:
exclude:
- src/Illuminate/Foundation/resources/exceptions/renderer/dist
not-name:
- webpack.mix.js
css: true
css:
finder:
exclude:
- src/Illuminate/Foundation/resources/exceptions/renderer/dist
11 changes: 8 additions & 3 deletions src/Illuminate/Foundation/Exceptions/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\MultipleRecordsFoundException;
use Illuminate\Database\RecordsNotFoundException;
use Illuminate\Foundation\Exceptions\Renderer\Renderer;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
Expand Down Expand Up @@ -831,9 +832,13 @@ protected function convertExceptionToResponse(Throwable $e)
protected function renderExceptionContent(Throwable $e)
{
try {
return config('app.debug') && app()->has(ExceptionRenderer::class)
? $this->renderExceptionWithCustomRenderer($e)
: $this->renderExceptionWithSymfony($e, config('app.debug'));
if (config('app.debug')) {
return app()->has(ExceptionRenderer::class)
? $this->renderExceptionWithCustomRenderer($e)
: $this->container->make(Renderer::class)->render(request(), $e);
}

return $this->renderExceptionWithSymfony($e, config('app.debug'));
} catch (Throwable $e) {
return $this->renderExceptionWithSymfony($e, config('app.debug'));
}
Expand Down
220 changes: 220 additions & 0 deletions src/Illuminate/Foundation/Exceptions/Renderer/Exception.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
<?php

namespace Illuminate\Foundation\Exceptions\Renderer;

use Composer\Autoload\ClassLoader;
use Illuminate\Foundation\Bootstrap\HandleExceptions;
use Illuminate\Http\Request;
use Symfony\Component\ErrorHandler\Exception\FlattenException;

class Exception
{
/**
* The "flattened" exception instance.
*
* @var \Symfony\Component\ErrorHandler\Exception\FlattenException
*/
protected $exception;

/**
* The current request instance.
*
* @var \Illuminate\Http\Request
*/
protected $request;

/**
* The exception listener instance.
*
* @var \Illuminate\Foundation\Exceptions\Renderer\Listener
*/
protected $listener;

/**
* The application's base path.
*
* @var string
*/
protected $basePath;

/**
* Creates a new exception renderer instance.
*
* @param \Symfony\Component\ErrorHandler\Exception\FlattenException $exception
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Foundation\Exceptions\Renderer\Listener $listener
* @param string $basePath
* @return void
*/
public function __construct(FlattenException $exception, Request $request, Listener $listener, string $basePath)
{
$this->exception = $exception;
$this->request = $request;
$this->listener = $listener;
$this->basePath = $basePath;
}

/**
* Get the exception title.
*
* @return string
*/
public function title()
{
return $this->exception->getStatusText();
}

/**
* Get the exception message.
*
* @return string
*/
public function message()
{
return $this->exception->getMessage();
}

/**
* Get the exception class name.
*
* @return string
*/
public function class()
{
return $this->exception->getClass();
}

/**
* Get the first "non-vendor" frame index.
*
* @return int
*/
public function defaultFrame()
{
$key = array_search(false, array_map(function (Frame $frame) {
return $frame->isFromVendor();
}, $this->frames()->all()));

return $key === false ? 0 : $key;
}

/**
* Get the exception's frames.
*
* @return \Illuminate\Support\Collection<int, Frame>
*/
public function frames()
{
$classMap = once(fn () => array_map(function ($path) {
return (string) realpath($path);
}, array_values(ClassLoader::getRegisteredLoaders())[0]->getClassMap()));

$trace = array_values(array_filter(
$this->exception->getTrace(), fn ($trace) => isset($trace['file']),
));

if (($trace[1]['class'] ?? '') === HandleExceptions::class) {
array_shift($trace);
array_shift($trace);
}

return collect(array_map(
fn (array $trace) => new Frame($this->exception, $classMap, $trace, $this->basePath), $trace,
));
}

/**
* Get the exception's request instance.
*
* @return \Illuminate\Http\Request
*/
public function request()
{
return $this->request;
}

/**
* Get the request's headers.
*
* @return array<string, string>
*/
public function requestHeaders()
{
return array_map(function (array $header) {
return implode(', ', $header);
}, $this->request()->headers->all());
}

/**
* Get the request's body parameters.
*
* @return string|null
*/
public function requestBody()
{
if (empty($payload = $this->request()->all())) {
return null;
}

$json = (string) json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

return str_replace('\\', '', $json);
}

/**
* Get the application's route context.
*
* @return array<string, string>|null
*/
public function applicationRouteContext()
{
$route = $this->request()->route();

return $route ? array_filter([
'controller' => $route->getActionName(),
'route name' => $route->getName() ?: null,
'middleware' => implode(', ', $route->gatherMiddleware()),
]) : null;
}

/**
* Get the application's route parameters context.
*
* @return array<string, mixed>|null
*/
public function applicationRouteParametersContext()
{
$parameters = $this->request()->route()->parameters();

return $parameters ? json_encode(array_map(
fn ($value) => $value instanceof Model ? $value->withoutRelations() : $value,
$parameters
), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : null;
}

/**
* Get the application's SQL queries.
*
* @return array<int, array{connectionName: string, time: float, sql: string}>
*/
public function applicationQueries()
{
return array_map(function (array $query) {
$sql = $query['sql'];

foreach ($query['bindings'] as $binding) {
$sql = match (gettype($binding)) {
'integer', 'double' => preg_replace('/\?/', $binding, $sql, 1),
'NULL' => preg_replace('/\?/', 'NULL', $sql, 1),
default => preg_replace('/\?/', "'$binding'", $sql, 1),
};
}

return [
'connectionName' => $query['connectionName'],
'time' => $query['time'],
'sql' => $sql,
];
}, $this->listener->queries());
}
}
Loading

0 comments on commit 092e3cb

Please sign in to comment.