Disable Laravel's Built-In Error Handling Methods

Disable Laravel's built-in error handling methods

Not without majorly violating the principles of the framework (which I'll tell you how to do below, if you're still interested).

There's a few things that make this difficult to accomplish. It's easy enough to unset the default error and exception handlers

set_error_handler(null);
set_exception_handler(null);

but that leaves you with two major hurdles.

The first is Laravel registers a shutdown handler as part of its bootstrapping, and this shutdown function will look for the last error, and if it was a fatal error, manually call the exception handling code. There's no easy way to un-register a shutdown function.

The second is, the main Laravel Application handler looks like this

#File: vendor/laravel/framework/src/Illuminate/Foundation/Application.php
public function handle(SymfonyRequest $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
try
{
$this->refreshRequest($request = Request::createFromBase($request));

$this->boot();

return $this->dispatch($request);
}
catch (\Exception $e)
{
if ($this->runningUnitTests()) throw $e;

return $this['exception']->handleException($e);
}
}

That is -- if you application code throws an exception, Laravel catches it here and manually calls the exception's handleException method (which triggers the standard Laravel exception handling). There's no way to let PHP handle a fatal exception that happens in your application, Laravel blocks that from ever happening.

The part where I tell you how to do what you want

All this means we need to replace the main Laravel application with our own. In bootstrap/start.php, there's the following line

#File: bootstrap/start.php
$app = new Illuminate\Foundation\Application;

Replace it with the following

ini_set('display_errors','1');
class MyApplication extends Illuminate\Foundation\Application
{
function startExceptionHandling()
{
//do nothing
}

public function handle(Symfony\Component\HttpFoundation\Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
$this->refreshRequest($request = Request::createFromBase($request));

$this->boot();

return $this->dispatch($request);
}
}

$app = new MyApplication;

The first thing we're doing is setting PHP's display errors ini to 1. This makes sure errors are output to the browser.

Next, we're defining a new application class that extends the real application class.

Finally, we replace the real Laravel $app object with an object instantiated by our class.

In our application class itself we blank out startExceptionHandling. This prevents Laravel from setting up custom exception, error, and shutdown callbacks. We also define handle to remove the application boot/dispatch from a try/catch. This is the most fragile part of the process, and may look different depending on your Laravel version.

Final Warnings

If the handle method changes in future version of Laravel this will break.

If custom packages rely on adding custom exception handlers, they may break.

I'd recommend staying away from this as anything other than a temporary debugging technique.

Disable error reporting entirely in Laravel production?

Yes you can change the error reporting. In fact, the framework provides a place to intercept the exceptions: App\Exceptions\Handler. By default the render method will convert the exception thrown to a HTML response. The APP_ENV and APP_DEBUG values will only change how this error response will render (details on the exception stack trace or not, basically).

Try changing the render method to

public function render($request, Exception $exception)
{
if ($exception instanceof ErrorException) {
error_reporting(0);

$kernel = app(\Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle($request)->send();
return $kernel->terminate($request, $response);
}

return parent::render($request, $exception);
}

This basically turns reporting off and then attempts to re-handle the request.
In the if clause you may check for any condition you want (the class of the exception, the severity, etc.). Catching ErrorException will probably cover your needs, but notice that you may not be able to recover from a fatal error this way.

Anyway, you should take that as a "proof of concept"... For non-idempotent requests, this "re-handle" approach is not good. Instead, just create a Middleware with

public function handle($request, Closure $next)
{
error_reporting(0);
return $next($request);
}

Same as before, fatal errors can't be recovered this way. But you can show a custom error message combining this middleware with the exception handler approach from before:

public function render($request, Exception $exception)
{
if ($exception instanceof FatalErrorException) {
return view('fatal-error', ['exception' => $exception]);
}

return parent::render($request, $exception);
}

Remove stack trace from Laravel error page

Simply override the render() method in your app's exception handler. Per comments in the base class, this method must return a response object.

public function render($request, \Throwable $e)
{
return response()->view("exception", ["exception" => $e]);
}

Then make your blade view look however you want.

<!doctype html>
<title>Exception</title>
<body>
<p>
Exception of type <code>{{ get_class($exception) }}</code>
thrown in <code>{{ $exception->getFile() }}</code>
on line <code>{{ $exception->getLine() }}</code>.
</p>
</body>

Is try catch block necessary inside controller in laravel?

Don't have a broad Exception catch block. Only catch exceptions you expect to be thrown in that block that way you can properly log unexpected exceptions and fix any other bugs in your code that may have caused those, instead of hiding them from yourself.

If you must do this then it might be in the context of something like:

public function store(Request $request)
{
try
{
$comment = new TicketComment();
$comment->content = $request['comment'];
$comment->user_id = Auth::user()->id;
$comment->ticket_id = $request['ticketId'];
$comment->save();
$ticket = Ticket::where('id', '=', $comment->ticket_id)->first();
$ticket->updated_at = $comment->created_at;
$ticket->update();
}
catch(Exception $e)
{
if (!($e instanceof SQLException)) {
app()->make(\App\Exceptions\Handler::class)->report($e); // Report the exception if you don't know what actually caused it
}
request()->session()->flash('unsuccessMessage', 'Failed to add comment !!!');
return redirect()->back();

}
request()->session()->flash('successMessage', 'Comment has been successfully added !!!');
return redirect()->back();
}

This way any unexpected exceptions will still be reported and you can go through your logs later to fix any bugs that are causing them.

As a reminder since PHP 7.1 you can have a union of exceptions in a catch block (Reference) e.g.

try { } 
catch (ExceptionType1|ExceptionType2 $e) {

}

This way you can handle the exceptions you know you can handle and let Laravel handle the ones you are not sure how to handle.

Note that you generally don't have to have try-catch blocks in controller code, you can always use the exception handler to do the flash/redirect back combination for all unhandled exceptions if that is your preferred way of handling them.

Laravel error handling for APIs

An approach would be to know where the request is coming from. If it comes from the mobile (API request), then return JSON, else, return a view.

if ($request->expectsJson()) {
return response()->json(['message' => 'success']); // No need to put 200 here.
} else {
return view('view.path');
}

You can learn more about the request api here: https://laravel.com/api/5.5/Illuminate/Http/Request.html

How do I deal with Exceptions in Laravel 5.4?

Let's learn about namespace and type-hinting in PHP. If you code it like this:

namespace App\Http\Controllers;

class ItemController extends Controller
{
public function postCheckout(Request $request, Exception $e)
{
...
}
}

That means your postCheckout method expect the second argument to be an instance of class App\Http\Controllers\Exception. That's why you got an error like this if this exception class does not exist:

Class App\Http\Controllers\Exception does not exist

I'm not sure in about your case because I don't know how this postCheckout method being called from another part of your code. But if you're expecting the second parameter to be the built-in PHP Exception class, you can do it with full-path namespace like this:

namespace App\Http\Controllers;

class ItemController extends Controller
{
public function postCheckout(Request $request, \Exception $e)
{
...
}
}

Or you can also import the intended class namespace above:

use Exception;
namespace App\Http\Controllers;

class ItemController extends Controller
{
public function postCheckout(Request $request, Exception $e)
{
...
}
}

And the second error happened because of Laravel's container unable to resolve the dependencies required by the postCheckout method.

(1/1) BindingResolutionException
Unresolvable dependency resolving...

From this error and the name of your class, I'm assuming you use this postCheckout method as route handler?

Route::post('/foo', 'ItemController@postCheckout');

If this is the case, then that's not how you catch the exception with Laravel. To handle exception in Laravel, you can do it within the app/Exceptions/Handler.php file. There are two methods there:

  1. report: If you want to log the exception with custom format.
  2. render: If you want to return a custom HTTP response to that exception.

In your case, you want a custom response:

public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Session\TokenMismatchException) {
// Perform the exception handler here
}

return parent::render($request, $exception);
}

The same thing also applied to your Stripe related exception, you can handle it within this Handler class. Read more about The render Method.

Hope this gives you some ideas.



Related Topics



Leave a reply



Submit