Laravel not calling report method in custom exception - laravel

Trying to use custom exception:
namespace App\Exceptions\Cloud;
use Exception;
class CantConfirmOrder extends Exception
{
public function report()
{
info('test exception');
}
}
But when I throwing it in tinker - nothing writes to log:
>>> throw new CantConfirmOrder('test');
[!] Aliasing 'CantConfirmOrder' to 'App\Exceptions\Cloud\CantConfirmOrder' for this Tinker session.
App\Exceptions\Cloud\CantConfirmOrder with message 'test'
Handler.php:
public function report(Throwable $exception)
{
parent::report($exception);
}
Does I need to call report() manually with try catch? I thinked it will be called automatically when I throwing.

In the context of an HTTP-request, the routing will pick this up and call the report method. So if you throw the error in a Controller method or another route action it should be called. You can try it in a test like this :
Route::get('x', fn() => throw new CantConfirmOrder('test)); $this->get('x');
The Route class/method that invokes the method is
https://laravel.com/api/9.x/Illuminate/Routing/Pipeline.html#method_handleException

Related

Method render() is not being called when custom Laravel exception is thrown from view composer

Edited:
I have a custom exception with render method which is being called when I throw it e.g. from controller, but not being called when I throw it in View composer.
So when I do something like that
public function compose(View $view)
{
throw new CustomException();
}
and put dd() to exception render method
public function render()
{
dd('render is called');
}
I get no result.
If I log my exception directly, finds out that first the CustomException being thrown, then as the result I see ErrorException.
I found a place where it being thrown.
\Illuminate\View\Engines\CompilerEngine::handleViewException
protected function handleViewException(Exception $e, $obLevel)
{
$e = new ErrorException($this->getMessage($e), 0, 1, $e->getFile(), $e->getLine(), $e);
parent::handleViewException($e, $obLevel);
}
I didn't found any mentions in Laravel docs about that case.
I found a tread on github with the same issue: https://github.com/laravel/framework/issues/24658
So the question is, is this expected? Is there any adequate way to avoid this behaviour?
Edit
So, as you know, any exception during view compilation is intercepted and rethrown as ErrorException or as FatalThrowableError.
What you can do is intercept ErrorException and check if ($e->getPrevious() instanceof \CustomException) if so, you do your code, else, let the handler continue.
So I've found working solution for myself.
I've extended CompilerEngine and added additional processing in order to not throw ErrorException when I don't want to.
The important thing is - your resulting Exception must be inherited from ErrorException. Otherwise you will face multiple calls to \App\View\Engines\CompilerEngine::handleViewException which can break your logic and write multiple log entities to your log file.

(Laravel 5.8) How to get verbosity level inside Exception Handler class

I'm trying to get the verbosity level INSIDE the Exception Handler class so I can print more or less information.
I'm writing a batch script that runs over hundreds of thousands loops and so that my goal is to limit the verbosity of the stack trace, since in this case the only useful traces are the last 3 or 4.
I've tried some other answers given here in SO and have read all around Internet, but none of ther talk about working inside the exception handler.
The one that looks closer is this answer that states $this->getOutput()->getVerbosity();but that doesn't works inside the error handler.
This is my code:
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Mail;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\Debug\ExceptionHandler as SymfonyExceptionHandler;
use App\Mail\ExceptionOccured;
use Illuminate\Support\Facades\Log;
use Symfony\Component\Console\Output\OutputInterface;
class Handler extends ExceptionHandler
...
...
public function render($request, Exception $e)
{
// return parent::render($request, $e);
Log::error($this->buildMessage($e));
}
public function buildMessage($e)
{
$verbosity_level = $this->getOutput()->getVerbosity();
$is_verbose = ($verbosity_level >= OutputInterface::VERBOSITY_DEBUG);
$t0 = $e->getTrace()[0];
$tm = "Error: [{$e->getCode()}] {$e->getMessage()} # {$t0['file']}:{$t0['line']}".PHP_EOL;
// if ($is_verbose){
...(more code) ...
// }
return $tm;
}
}
And this is the output:
PHP Fatal error:
Uncaught Error: Call to undefined method App\Exceptions\Handler::getOutput() in ...\Handler.php:89
I've seen that the output property exists but it is protected and has no getter.
Maybe the cited answer is valid for earlier versions.
I'm really lost as I just have 2 months playing with Laravel.
The Exception handler has a specific method for the console: renderForConsole().
/**
* Render an exception to the console.
*
* #param \Symfony\Component\Console\Output\OutputInterface $output
* #param \Exception $e
* #return void
*/
public function renderForConsole($output, Exception $e)
{
(new ConsoleApplication)->renderException($e, $output);
}
It's a pretty simple wrapper around the Symfony Console component's default exception handler. Customize to your needs!

Phpunit : hadling the validation exception in laravel

I have turned throw Exception in handler.php so that I can catch exceptions and see the errors but what happened is when I try to do the validation checks it throws me an exception which is correct but in my test case instead of catching the exception i'm asserting that the session has errors.
/** #test*/
public function a_thread_requires_a_title(){
$thread = make('App\Thread', ['title'=> null]);
$this->post('threads', $thread->toArray())
->assertSessionHasErrors('title');
}
Since, validation error is an exception so it throws me an exception because I've modified the handler.php file as
if(app()->environment() === "testing") throw $exception;
so, what I'm trying to do is change the env for this one test so that it wont throw me an 'Exception'
There are 2 helper methods which you can write at the top of your test method:
$this->withoutExceptionHandling(); and $this->withExceptionHandling();
They are included in Laravel's 'Illuminate\Foundation\Testing\Concerns\InteractsWithExceptionHandling' trait which is used by the abstract TestCase that you should be extending from your test. (as mentioned here)
/** #test*/
public function a_thread_requires_a_title() {
$this->withExceptionHandling();
$thread = make('App\Thread', ['title'=> null]);
$this->post('threads', $thread->toArray())
->assertSessionHasErrors('title');
}

Cannot throw objects that do not implement Throwable

I am using Laravel 5.5. There is following class
vendor\laravel\framework\src\Illuminate\Routing\Middleware\ThrottleRequests.php
with Method Name: buildException
In, Laravel 5.4, I was able to return JSON in this method like below.
protected function buildException($key, $maxAttempts)
{
$retryAfter = $this->getTimeUntilNextRetry($key);
$headers = $this->getHeaders(
$maxAttempts,
$this->calculateRemainingAttempts($key, $maxAttempts, $retryAfter),
$retryAfter
);
return response()->json('429 Too many requests');
}
When I try to return JSON in above method using Laravel 5.5, it says
Cannot throw objects that do not implement Throwable
Now sure , how could I return JSON in Laravel 5.5 for above method
Well, you cannot do it like this now any more. You need to return exception class. But what you can do is returning some custom exception class and then in app/Exceptions/Handler.php in `render method you can add:
if ($e instanceof YourCustomException) {
return response()->json('429 Too many requests');
}
Of course if you really need, you can add your own implementation of handle method and instead of throwing exception you can return response directly in there but probably throwing custom exception and handling it in Handler class is better choice.

laravel: Argument 1 passed to App\Exceptions\CustomException::report() must be an instance of Exception,

I have created a custom exception class in Laravel 5.2. It works well till laravel 5.4.
When Im trying to use the same custom exception class with laravel 5.5 it is throwing following error.
Type error: Argument 1 passed to App\Utility\Exceptions\CustomException::report() must be an instance of Exception, none given, called in /var/www/html/bubbles/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php on line 102 {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\FatalThrowableError(code: 0): Type error: Argument 1 passed to App\\Utility\\Exceptions\\CustomException::report() must be an instance of Exception, none given, called in /var/www/html/bubbles/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php on line 102 at /var/www/html/bubbles/app/Utility/Exceptions/CustomException.php:39)
Here is the custom exception class I've been using
<?php
namespace App\Utility\Exceptions;
use Illuminate\Support\Facades\Lang;
use Exception;
class CustomException extends Exception
{
private $error_code = NULL;
private $error_info = NULL;
function __construct($type = NULL, $errors = NULL)
{
$this->error_code = $type['error_code'];
$this->error_info = $errors;
$message = Lang::get('exceptions.'.$this->error_code);
parent::__construct($message, $type['code'], NULL);
}
public function report(Exception $exception)
{
parent::report($exception);
}
public function getErrorCode()
{
return $this->error_code;
}
public function getErrorInfo()
{
return $this->error_info;
}
}
// end of class CustomException
// end of file CustomException.php
Could anybody will explain me why it is throwing argument must be instance of exception ? Any help would be greatly appreciated.
My programming environment
PHP 7.0.1
Laravel 5.5
The exception handler in Laravel 5.5 checks if the exception has a report method, and if so, let the exception handle the reporting itself. This means that the handler will see your report method, and call $e->report();, but your report method requires a parameter.
This is done in Handler::report.
You either need to remove the parameter in your report method (it should be reporting itself; $this) if you want to use this functionality, or rename the method if you don't want Laravel to call it (and fail).
Relevant: Laravel 5.5 Adds Support for Custom Exception Reporting

Resources