Just realized these aren't exceptions but php fatal and parse errors so an exception handler won't handle this. Yet I never saw these kind of errors in Laravel. How is it that Laravel converts these into pretty exception handler type pages?
Lumen: debug mode not showing stack trace
This may be an intentional part of lightening the framework but I was wondering.
Testing out Lumen for the first time I get the "page not found" and "whoops something went wrong" pages when debug is false but when I set debug to true I just get the old fashioned php error which I haven't seen in years.
For example, a intentional call to an undefined method simply gives me this
Fatal error: Call to undefined method App\Services\Database\Crud::all() in D:\projects\lumen-api\app\Models\User\User.php on line 11
Whereas with Laravel I would get a full stack trace.
Wondering if I should install filp/whoops.
Placing the following in a class takes care of getting an exception for bad method call
public function __call($method, $args)
{
if(! method_exists($this, $method)) throw new \BadMethodCallException("Call to undefined method {$method}()");
return $method($args);
}
But still getting parse errors. How can I catch those and throw an exception instead?
Ok now I'm getting somewhere with this. It shows the symfony exception page but also shows the raw php error above it so I think the only issue now is that it's not handling the error soon enough in the request.
I did some copying from Laravel's Foundation\Bootstrap\HandleExceptions class into Lumens Laravel\Lumen\Application class where it handles exceptions. The main thing is the missing "register_shutdown_function" followed by a few other protected functions it calls.
/**
* Set the error handling for the application.
*
* @return void
*/
protected function registerErrorHandling()
{
error_reporting(-1);
set_error_handler(function ($level, $message, $file = '', $line = 0) {
throw new ErrorException($message, 0, $level, $file, $line);
});
set_exception_handler(function ($e) {
$this->handleUncaughtException($e);
});
/// starts here by adding register_shutdown_function
register_shutdown_function(function(){
if ( ! is_null($error = error_get_last()) && $this->isFatal($error['type'])) {
$this->handleException($this->fatalExceptionFromError($error));
}
});
}
protected function isFatal($type)
{
return in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]);
}
public function handleException($e)
{
$this->getExceptionHandler()->report($e);
if ($this->app->runningInConsole())
{
$this->renderForConsole($e);
}
else
{
$this->renderHttpResponse($e);
}
}
protected function fatalExceptionFromError(array $error)
{
return new FatalErrorException(
$error['message'], $error['type'], 0, $error['file'], $error['line']
);
}
protected function getExceptionHandler()
{
return $this->app->make('Illuminate\Contracts\Debug\ExceptionHandler');
}
protected function renderHttpResponse($e)
{
$this->getExceptionHandler()->render($this->app['request'], $e)->send();
}
/// the above gets me symfony exception pages for parse errors but still shows raw php error above it
I'm getting a blank page, no errors at all? I have debug set to TRUE in my .env as well. I'm trying to figure out what is going on. I'm using Homestead and HHVM, so I might try and remove HHVM and see if it's related to that...
@isimmons thanks for pointing me in the right direction...
First, make sure you have these settings in your php.ini:
/etc/hhvm/php.ini
hhvm.server.implicit_flush = true
hhvm.error_handling.call_user_handler_on_fatals = true
Then I just stuck all the code (with some fixes from this hhvm github issue) into a file in my App/Exceptions folder:
app/Exceptions/HandleExceptionsFix.php
<?php namespace App\Exceptions;
use ErrorException;
use Illuminate\Contracts\Foundation\Application;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Debug\Exception\FatalErrorException;
class HandleExceptionsFix {
protected $app;
protected static $fatalError = null;
public function bootstrap(Application $app)
{
$this->app = $app;
error_reporting(-1);
set_error_handler([$this, 'handleError']);
set_exception_handler([$this, 'handleException']);
register_shutdown_function([$this, 'handleShutdown']);
if ( ! $app->environment('testing'))
{
ini_set('display_errors', 'Off');
}
}
public function handleError($level, $message, $file = '', $line = 0, $context = array())
{
// **** Add this, loads fatal error
if ($level & (1 << 24)) {
self::$fatalError = array(
'message' => $message,
'type' => $level,
'file' => $file,
'line' => $line
);
}
if (error_reporting() & $level)
{
throw new ErrorException($message, 0, $level, $file, $line);
}
}
public function handleException($e)
{
$this->getExceptionHandler()->report($e);
if ($this->app->runningInConsole())
{
$this->renderForConsole($e);
}
else
{
$this->renderHttpResponse($e);
}
}
protected function renderForConsole($e)
{
$this->getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
}
protected function renderHttpResponse($e)
{
$this->getExceptionHandler()->render($this->app['request'], $e)->send();
}
// Updated this to check for the fatal error
public function handleShutdown()
{
$error = error_get_last();
if(self::$fatalError){
$error = self::$fatalError;
}
if ( ! is_null($error) && $this->isFatal($error['type']))
{
$this->handleException($this->fatalExceptionFromError($error, 0));
}
}
protected function fatalExceptionFromError(array $error, $traceOffset = null)
{
return new FatalErrorException(
$error['message'], $error['type'], 0, $error['file'], $error['line'], $traceOffset
);
}
protected function isFatal($type)
{
// *** Add type 16777217 that HVVM returns for fatal
return in_array($type, [16777217, E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]);
}
protected function getExceptionHandler()
{
// return $this->app->make('Illuminate\Contracts\Debug\ExceptionHandler');
return new \App\Exceptions\Handler(); // <-- call our app's handler instead
}
}
And in bootstrap/app.php I call it right after $app is set:
bootstrap/app.php
$hhvmfix = new App\Exceptions\HandleExceptionsFix;
$hhvmfix->bootstrap($app);
(and make sure you have Dotenv::load(__DIR__.'/../'); uncommented in the file so you can see the full information available depending on environment settings)
I decided to post the Laravel issue(with a fix) here: https://github.com/laravel/framework/issues/8744
And my stackoverflow answer here for Laravel: http://stackoverflow.com/questions/29264326/laravel-5-show-blank-page-on-server-error-and-no-laravel-log-running-with-hhv
Please or to participate in this conversation.