How to set custom exception handler in Laravel 5? - php

Hi I'm new to laravel and working with custom exception handling.
I have caught all exceptions known to my knowledge and it is working fine. As per my understanding, set_exception_handler is used for handling uncaught exceptions. Now I have two questions:
1) I have to know whether my understanding for set_exception_handler is correct or not.
2) How to implement it in laravel 5 to handle uncaught exceptions
This is how I have implemented set_exception_handler in my controller
class SearchController extends BaseController{
public function getTitleMessage($exc){
var_dump("set exception handler".$exc);
return json_encode("Error");
}
public function genericSearch(){
//Bussiness logic goes here
set_exception_handler('getTitleMessage');
throw new Exception("Search Failed");
}
This is showing an error that set_exception_handler is not a valid callback. So I have changed my code to
set_exception_handler(array($this,'getTitleMessage'));
But also not working for me. Someone guide me how to implement it in laravel controller. Thanks in advance

Laravel already uses a global exception handler
Take a look at the vendor\laravel\framework\src\Illuminate\Foundation\Bootstrap\HandleExceptions.php file; as you see in the bootstrap method, Laravel already uses set_exception_handler to set the handleException method as the global exception handler
That method will ultimately call App\Exceptions\Handler::render when an uncaught exception is raised.
So, if you want to handle in some way an exception that you're not catching manually, all you have to do is add your code to the render method:
app\Exceptions\Handler.php
public function render($request, Exception $e)
{
//DO WATHEVER YOU WANT WITH $e
return parent::render($request, $e);
}

You've to implement your custom exception handler logic in the app\Exceptions\Handler.php render method:
public function render($request, Exception $exception) {
if (method_exists($e, 'render') && $response = $e->render($request)){
return Router::prepareResponse($request, $response);
} elseif ($e instanceof Responsable) {
return $e->toResponse($request);
}
$e = $this->prepareException($e);
/* Your custom logic */
if ($e instanceof HttpResponseException) {
return $e->getResponse();
} elseif ($e instanceof AuthenticationException) {
return $this->unauthenticated($request, $e);
} elseif ($e instanceof ValidationException) {
return $this->convertValidationExceptionToResponse($e, $request);
}
return parent::render($request, $exception);
}

Related

Laravel not reporting all exceptions to report method of Handler.php

I added a send email function to the report method of app/Exceptions/Handler.php
But some exceptions the method is called and others not, the $dontReport array is empty
This error above, for example, is not being reported.
Here is the handler
class Handler extends ExceptionHandler
{
protected $dontReport = [
//
];
public function report(Throwable $exception)
{
if ($this->shouldReport($exception)) {
$this->sendEmail($exception);
}
parent::report($exception);
}
public function sendEmail(Throwable $exception)
{
try {
$e = FlattenException::create($exception);
$handler = new HtmlErrorRenderer(true);
$css = $handler->getStylesheet();
$content = $handler->getBody($e);
\Mail::send('emails.exception', compact('css', 'content'), function ($message) {
$message->to(['email#myemail.com'])
->subject('Exception: ' . \Request::fullUrl());
});
} catch (Throwable $exception) {
Log::error($exception);
}
}
}
Laravel automatically filters out certain types of exceptions automatically.
Behind the scenes, Laravel already ignores some types of errors for you, such as exceptions resulting from 404 HTTP "not found" errors or 419 HTTP responses generated by invalid CSRF tokens
Try updating your report method to this, this should send an email for every exception, regardless of the type.
Link to the docs for reporting exceptions.
public function report(Throwable $exception)
{
$this->sendEmail($exception);
parent::report($exception);
}

How do I get the HTTP Status Code from an Exception thrown in Laravel?

I am currently integrating some logic in App/Exceptions/Handler.php. I would like to be able to access the HTTP Status Code on the $exception variable:
public function report(Throwable $exception)
{
dd($exception->statusCode);
parent::report($exception);
}
However I get the following error:
ErrorException Undefined property: ErrorException::$statusCode
When I dd($exception) I get the following:
Symfony\Component\HttpKernel\Exception\NotFoundHttpException {#1214 ▼
-statusCode: 404
-headers: []
#message: ""
#code: 0
#file: "C:\Users\CEX\Documents\GitHub\unified\vendor\laravel\framework\src\Illuminate\Routing\AbstractRouteCollection.php"
#line: 43
trace: {▶}
}
How do I access the statusCode?
If you look at the source code of Symfony\Component\HttpKernel\Exception\NotFoundHttpException you will find that it extends Symfony\Component\HttpKernel\Exception\HttpException if you look at the declaration of the class you will see that $statusCode is private but it has a getter method
class HttpException extends \RuntimeException implements HttpExceptionInterface
{
private $statusCode;
private $headers;
public function __construct(int $statusCode, string $message = null, \Throwable $previous = null, array $headers = [], ?int $code = 0)
{
$this->statusCode = $statusCode;
$this->headers = $headers;
parent::__construct($message, $code, $previous);
}
public function getStatusCode()
{
return $this->statusCode;
}
//...
}
As such you simply need to do $exception->getStatusCode() to retrieve the status code (404 in your case) though you should do a check to make sure your throwable implements the HttpExceptionInterface because that might not always be the case and so the method would not exist and you would get a fatal error
if ($exception instanceof \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface) {
$code = $exception->getStatusCode();
}
follow #Tofandel answer, my practice is as follows,
Since this question is relatively new I presume you are suing Laravel version 7 or 8. According to the documentary of Error Handling, the correct page rendering method has now been as
public function render($request, Throwable $exception)
{
if ($exception instanceof CustomException) {
return response()->view('errors.custom', [], 500);
}
return parent::render($request, $exception);
}
In order to use customised error view, you would have to tell Laravel what kind of Exception instance your thrown $exception is. To overwrite the internal error handling you would want to catch the thrown as a HTTP exception. For these versions the correct Class is as Symfony\Component\HttpKernel\Exception\. Therefore,
bring in the handling Class by
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
on the top of your app\Exceptions\Handler.php
modify the default render() method to something like follows in accordance to your scenario,
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
//
//
public function render($request, Throwable $exception)
{
// return parent::render($request, $exception);
if ($exception instanceof HttpExceptionInterface) {
if (env('APP_ENV') === 'production' && $exception->getStatusCode() == 404) {
return response()->view('errors.404', [], 404);
}
if (env('APP_ENV') === 'production' && $exception->getStatusCode() == 500) {
return response()->view('errors.500', [], 500);
}
}
return parent::render($request, $exception);
}
Check the docs here https://www.php.net/manual/en/exception.getcode.php
Try $exception->getCode()

Laravel : How to handle Exception in View composer

In my laravel 5.5 project, view composers are used for passing data to the views.
In the view composer's constructor() a try catch block is used to catch the exceptions and a custom exception is rethrown from the catch method.
In the default exception handler of the application, custom exception is handled to display my custom error view.
Problem : The custom exception is not working properly when thrown from the view composer. Laravel's default exception error page is shown instead of my custom error page.
ProductComponentComposer.php
namespace App\Http\ViewComposers;
use Illuminate\View\View;
use App\Repositories\ProductRepository;
use Exception;
use App\Exceptions\AppCustomException;
class ProductComponentComposer
{
protected $products;
/**
* Create a new product partial composer.
*
* #param ProductRepository $productRepo
* #return void
*/
public function __construct(ProductRepository $productRepo)
{
try {
$this->products = $productRepo->getProducts();
} catch (Exception $e) {
throw new AppCustomException("CustomError", 1001);
}
}
/**
* Bind data to the view.
*
* #param View $view
* #return void
*/
public function compose(View $view)
{
$view->with(['productsCombo' => $this->products]);
}
}
Handler.php
public function render($request, Exception $exception)
{
if($exception instanceof AppCustomException) {
//custom error page when custom exception is thrown
return response()->view('errors.app-custom-exception', compact('exception'));
}
return parent::render($request, $exception);
}
Note : The custom exception is handled properly if thrown from the controller.
I also tried throwing the exception from the compose() method of the ProductComponentComposer instead of the __constructor(). But that also not working.
How to fix this to get my custom exception view if any exception is occured in the view composer?
Thanks in advance..
I had the same issue where a custom exception was thrown within a method in my view composer class, yet \ErrorException is what I got displayed.
There's a handler on a framework's level (\laravel\framework\src\Illuminate\View\Engines\PhpEngine.php:45) I believe is causing this.
Fix I've applied:
App\Exceptions\Handler.php
public function render($request, Exception $exception)
{
if ($exception instanceof AppCustomException ||
$exception instanceof \ErrorException &&
$exception->getPrevious() instanceof AppCustomException
) {
//custom error page when custom exception is thrown
return response()->view('errors.app-custom-exception', compact('exception'));
}
// default
return parent::render($request, $exception);
}
Make sure that what you're getting really is an instance of \ErrorException

Convert to Laravel exception?

Currently in a package, it has HttpException exception
namespace DigitalOceanV2\Exception;
class HttpException extends \RuntimeException implements ExceptionInterface
{
}
Is there a way to to convert it Laravel HttpResponseException uses without touching that exception from the package?
You can catch that exception and rethrow it.
In your app/Exceptions/Handler.php file.
public function render($request, Exception $exception)
{
if ($exception instanceof \DigitalOceanV2\Exception) {
throw new HttpResponseException;
}
return parent::render($request, $exception);
}
Edit: I haven't tested this but according to the exception class. You can pass a response as a parameter to the constructor. So you should be able to do this:
$response = response($exception->getMessage());
throw new HttpResponseException($response);
Maybe this is late, but at least on Laravel 8+ there is a \App\Exceptions\Handler::report method that I think would be more appropriate to override and put such logic - e.g. converting the exception to a different/custom class.
In fact render suggests how to render it, not what to do with it.
public function report(Throwable $e)
{
if ($e instanceof \DigitalOceanV2\Exception) {
throw new HttpResponseException;
}
return parent::report($e);
}

Laravel Exceptions Handler not work

Environment: Laravel 5.1, PHP 5.6.10
I tried to implement App\Exceptions\Handler::render() to response error message in JSON format.
The app/Exceptions/Handler.php as follows:
// ignore..
public function render($request, Exception $e)
{
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
} elseif ($e instanceof AbstractException) {
return response()->apiJsonError(
$e->getMessage(),
$e->getErrors(),
$e->statusCode());
}
// ignore...
}
In controller, it also throws an exception:
if (ArrayUtil::isIndexExceed($list, $maxIndex)) {
// Index exceeds
throw new App\Exceptions\ExceedingIndexException;
}
However, when the error occurs, the Handler::render() is not invoked. The response is the ExceedingIndexException stack.
The following part is Exception class
My custom exception class, ExceedingIndexException:
namespace App\Exceptions;
use App\Http\Responses\Error;
use App\Exceptions\AbstractException;
class ExceedingIndexException extends AbstractException
{
public function __construct()
{
$message = 'Unable to execute';
$error = new Error('exceeding_index_value');
$statusCode = 400;
parent::__construct($statusCode, $error, $message);
}
}
The ExceedingIndexException class inherits AbstractException:
namespace App\Exceptions;
abstract class AbstractException extends \Exception
{
protected $statusCode;
protected $errors;
public function __construct(
$statusCode, $errors, $message, $code = 0, \Exception $previous = null) {
parent::__construct($message, $code, $previous);
$this->statusCode = $statusCode;
$this->errors = $errors;
}
public function getStatusCode()
{
return $this->statusCode;
}
public function getErrors()
{
return $this->errors;
}
}
Solution
I found my project depends on Dingo API for RESTful API. Because, Dingo also supports and registers its own exception handler, App\Exceptions\Handler doesn't be invoked.
I tried to use Custom Exception Responses in Dingo API as my Exception Handler to response error in JSON format. And it works for me.
Actually, you can replace the Dingo API error handler by a custom error handler. Get a copy of https://github.com/KIVagant/api/blob/develop/src/Exception/Handler.php
and save it to YourApp\Exceptions\Api\V1\Handler.php. Add interface Dingo\Api\Contract\Debug\ExceptionHandler to it,
then follow the instruction in Exceptions now can return any additional data.
use Dingo\Api\Contract\Debug\ExceptionHandler as DingoExceptionHandler;
class Handler implements ExceptionHandler, DingoExceptionHandler {
Replace the error handler, eg in boot ().
// Resolve YourApp\Exceptions\Api\V1\Handler ifself
$this->app->alias('api.exception', 'YourApp\Exceptions\Api\V1\Handler');
$this->app->singleton('api.exception', function ($app) {
return new \YourApp\Exceptions\Api\V1\Handler($app['Illuminate\Contracts\Debug\ExceptionHandler'],
$app['config']['api.errorFormat'], $app['config']['api.debug']);
});
Don't forget set error format. You can set up error format in boot(), eg:
// Set up error format
$this->app['api.exception']->setErrorFormat(...)
Indeed, Dingo API took over the Exception handling.
You you face similar problems where Lumen Exception handler isn't handling anything, probably some package took over the handling. In this case it was Dingo API.
Register your custom response for the exception for Dingo API:
https://github.com/dingo/api/wiki/Errors-And-Error-Responses#custom-exception-responses

Categories