Skip to content

Commit

Permalink
use whoops for errors
Browse files Browse the repository at this point in the history
  • Loading branch information
taylorotwell committed Jun 1, 2017
1 parent 1229b7f commit b697272
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 53 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"ext-openssl": "*",
"doctrine/inflector": "~1.0",
"erusev/parsedown": "~1.6",
"filp/whoops": "~2.0",

This comment has been minimized.

Copy link
@arcanedev-maroc

arcanedev-maroc Jun 1, 2017

Contributor

Can you extract the "filp/whoops" dependency and put it in the "dev-dependencies" in "laravel/laravel" repo ?

Optionnal: Add it as a "suggest" dependency for this repo ?

This comment has been minimized.

Copy link
@ankurk91

ankurk91 Jun 2, 2017

Contributor

@arcanedev-maroc
One can enable debugging in production and then whoops will not be available if it is moved to dev-dependency

This comment has been minimized.

Copy link
@garygreen

garygreen Jun 3, 2017

Contributor

@ankurk91 enabling whoops in production is suicide - you will expose all your database passwords, env secrets and all sorts. Installing whoops in production at all is a pretty big security risk imo, it should definitely be only require-dev.

"league/flysystem": "~1.0",
"monolog/monolog": "~1.11",
"mtdowling/cron-expression": "~1.0",
Expand Down
123 changes: 83 additions & 40 deletions src/Illuminate/Foundation/Exceptions/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
namespace Illuminate\Foundation\Exceptions;

use Exception;
use Whoops\Run as Whoops;
use Psr\Log\LoggerInterface;
use Illuminate\Http\Response;
use Illuminate\Routing\Router;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\RedirectResponse;
use Whoops\Handler\PrettyPageHandler;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Container\Container;
use Illuminate\Session\TokenMismatchException;
Expand Down Expand Up @@ -220,7 +222,9 @@ protected function convertValidationExceptionToResponse(ValidationException $e,
protected function prepareResponse($request, Exception $e)
{
if (! $this->isHttpException($e) && config('app.debug')) {
return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e);
return $this->toIlluminateResponse(
$this->convertExceptionToResponse($e), $e
);
}

if (! $this->isHttpException($e)) {
Expand All @@ -233,39 +237,52 @@ protected function prepareResponse($request, Exception $e)
}

/**
* Prepare a JSON response for the given exception.
* Create a Symfony response for the given exception.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Illuminate\Http\JsonResponse
* @param \Exception $e
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function prepareJsonResponse($request, Exception $e)
protected function convertExceptionToResponse(Exception $e)
{
$status = $this->isHttpException($e) ? $e->getStatusCode() : 500;

$headers = $this->isHttpException($e) ? $e->getHeaders() : [];
if (config('app.debug')) {
return SymfonyResponse::create(
$this->renderExceptionWithWhoops($e), $e->getStatusCode(), $e->getHeaders()
);
} else {
$e = FlattenException::create($e);

return new JsonResponse(
$this->convertExceptionToArray($e), $status, $headers, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
);
return SymfonyResponse::create(
(new SymfonyExceptionHandler(false))->getHtml($e),
$e->getStatusCode(),
$e->getHeaders()
);
}
}

/**
* Convert the given exception to an array.
* Render an exception to a string using "Whoops".
*
* @param \Exception $e
* @return array
* @return string
*/
protected function convertExceptionToArray(Exception $e)
protected function renderExceptionWithWhoops(Exception $e)
{
return config('app.debug') ? [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTrace(),
] : [
'message' => $this->isHttpException($e) ? $e->getMessage() : 'Server Error',
];
return tap(
(new Whoops)->pushHandler($this->whoopsHandler())
)->handleException($e);
}

/**
* Get the Whoops handler for the application.
*
* @return \Whoops\Handler\Handler
*/
protected function whoopsHandler()
{
return tap(new PrettyPageHandler)->setApplicationPaths([
app_path(), base_path('bootstrap'), config_path(), database_path(),
public_path(), resource_path(), base_path('routes'), storage_path(),
]);
}

/**
Expand All @@ -290,21 +307,6 @@ protected function renderHttpException(HttpException $e)
}
}

/**
* Create a Symfony response for the given exception.
*
* @param \Exception $e
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function convertExceptionToResponse(Exception $e)
{
$e = FlattenException::create($e);

$handler = new SymfonyExceptionHandler(config('app.debug', false));

return SymfonyResponse::create($handler->getHtml($e), $e->getStatusCode(), $e->getHeaders());
}

/**
* Map the given exception into an Illuminate response.
*
Expand All @@ -315,14 +317,55 @@ protected function convertExceptionToResponse(Exception $e)
protected function toIlluminateResponse($response, Exception $e)
{
if ($response instanceof SymfonyRedirectResponse) {
$response = new RedirectResponse($response->getTargetUrl(), $response->getStatusCode(), $response->headers->all());
$response = new RedirectResponse(
$response->getTargetUrl(), $response->getStatusCode(), $response->headers->all()
);
} else {
$response = new Response($response->getContent(), $response->getStatusCode(), $response->headers->all());
$response = new Response(
$response->getContent(), $response->getStatusCode(), $response->headers->all()
);
}

return $response->withException($e);
}

/**
* Prepare a JSON response for the given exception.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Illuminate\Http\JsonResponse
*/
protected function prepareJsonResponse($request, Exception $e)
{
$status = $this->isHttpException($e) ? $e->getStatusCode() : 500;

$headers = $this->isHttpException($e) ? $e->getHeaders() : [];

return new JsonResponse(
$this->convertExceptionToArray($e), $status, $headers,
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
);
}

/**
* Convert the given exception to an array.
*
* @param \Exception $e
* @return array
*/
protected function convertExceptionToArray(Exception $e)
{
return config('app.debug') ? [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTrace(),
] : [
'message' => $this->isHttpException($e) ? $e->getMessage() : 'Server Error',
];
}

/**
* Render an exception to the console.
*
Expand Down
13 changes: 0 additions & 13 deletions tests/Foundation/FoundationExceptionsHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,6 @@ public function tearDown()
m::close();
}

public function testReturnsHtmlPageWithStackTraceWhenHtmlRequestAndDebugTrue()
{
$this->config->shouldReceive('get')->with('app.debug', null)->twice()->andReturn(true);
$this->request->shouldReceive('expectsJson')->once()->andReturn(false);

$response = $this->handler->render($this->request, new Exception('My custom error message'))->getContent();

$this->assertContains('<!DOCTYPE html>', $response);
$this->assertContains('Whoops, looks like something went wrong.', $response);
$this->assertContains('My custom error message', $response);
$this->assertNotContains('"message":', $response);
}

public function testReturnsJsonWithStackTraceWhenAjaxRequestAndDebugTrue()
{
$this->config->shouldReceive('get')->with('app.debug', null)->once()->andReturn(true);
Expand Down

0 comments on commit b697272

Please sign in to comment.