500 errors are not caught in prod environment - php

I have a strange problem in a symfony 5.3.10 app and i can't wrap my head around it.
If I trigger a 500 error, like so:
/**
* #Route("/", name="pvr_index", methods={"GET"})
*/
public function index(): Response
{
echo $a;
Where $a is not defined, in dev environment I get a nice error page.
I'd expect that in prod I would get a not so nice error page that just tells me that i have a 500 error. And this would happen correctly until some time ago, when something changed, but I don't know what.
Now instead I get no error, not even in the symfony server console, and the script execution would keep on going like nothing happened.
What I have tried:
Adding the supposed exception inside a try/catch:
try {
echo $a;
}
catch (\Exception $e)
{
dump($e);
die();
}
Nothing happens, the catch isn't triggered.
Creating an exception listener:
In services.yaml i have:
exception_listener:
class: App\Event\ExceptionListener
tags:
- { name: kernel.event_listener, event: kernel.exception }
And in ExceptionListener.php i have:
class ExceptionListener
{
public function __construct(Environment $engine) {
$this->engine = $engine;
}
public function onKernelException(ExceptionEvent $event)
{
dd($event->getThrowable()->getMessage());
}
}
Still nothing happens, the listener isn't triggered.
Everything works as expected in dev environment, the problem presents itself only in prod.
Looking at a phpinfo(); page I see that the error reporting is enabled, so I think that the problem lies in the symfony configuration.
I'm using php 7.4 and have no real access to the php.ini file

This is not a Symfony error (nor a PHP error for that matter).
On PHP 7.4, trying to output an undefined variable would only raise a notice, not a fatal error (code 500). Notices are silently swallowed up by the engine unless you configure your server to log them or output them, where you'd get something like:
Notice: Undefined variable: a in file xxx.php line Y
A try/catch won't do anything for you, since no exception is thrown for you to catch.
Even on PHP 8 or 8.1, this will only raise a warning like:
Warning: Undefined variable $a
You can see the result under version < 8 and >= 8 here.

Related

How to make a PHP autoloader throw an error when the autoloaded file has "compile-time" errors (never mind, it already does)

EDIT: It turned out that what I stated in this question was totally wrong. The code was actually turning off error reporting explicitly prior to autoloading, in places where I hadn't found it.
So this question is basically useless. The accepted answer is correct.
In my current configuration, whenever some PHP file has fatal errors such as syntax errors or calling a function that does not exist, I usually get an error message such like:
Parse error: syntax error, unexpected <whatever> in /path/to/file.php on line XXX
or
Fatal error: Call to undefined function whatever() in /path/to/file.php on line YYY
or the like in the very output.
However, I am using third-party libraries which use a third-party autoloader. Whenever there's a fatal error in any of the autoloaded classes (including parse errors or calling unexisting functions - actually not completely sure about the latter but definitely of the parse error case), I just get a blank page, and not only that: no error is even logged in Apache's error_log file, where usually PHP fatal errors would be logged. So debugging becomes impossible.
I can't stress this enough: this only happens when the fatal error is in some autoloaded file. In every other case (including of course errors in files included via require(), include() and the like), the same errors do show up in the output and in the error_log.
I didn't write the autoloader code, but it's basically like this:
// no idea why this line, but I don't think it's relevant:
ini_set('unserialize_callback_func', 'spl_autoload_call');
spl_autoload_register(array('My_Autoloader', 'autoload'), true);
class My_Autoloader {
static function autoload($classname) {
$filename = //.... computes $filename from $classname
require_once($filename);
}
}
There must be a way to have the autoloader throw errors the same way they would be thrown (and handled) if the errors were not in an autoloaded file, right?
How do I get that?
The only way that the code would behave as you suggest is if the third party code is overriding the error reporting. (EDIT by OP: Yep, it turns out it actually was.) That is usually considered good practice for production systems, but it should be logging the error.
That your third party code is causing such errors gives me pause to wonder about its quality, but we'll ignore that for now.
PHP's built in mechanisms will handle the reporting (to the browser) and the logging (to file). Non fatal errors can be managed by your own code after calling set_error_handler() however fatal errors are not handed off via this route. It is possible to trap and handle fatal errors in your own code using register_shutdown_function(). But start by checking your log files.
If, as you say, both error logging and error reporting are disabled, then stop using this third party code - it is toxic.
Syntax Error Check only on Command Line
With php -l somefile.php from PHP
shell_exec('php -l /fullpath/to/somefile.php')
but you have to analyse the respone string for errors.
Normal response No syntax errors detected in somefile.php
In PHP <= 5.0.4 there was php.net/manual/en/function.php-check-syntax.php
Here a fatal error catch that works:
register_shutdown_function(function(){
$err=error_get_last();
if($err['type']===1){
/*you got an fatal error do something, write it to an file*/
#file_put_contents(var_export($err,true),'myfatalerror.log');
}
});
Hope that helps:)
Use php exception so you can call your file into
function inverse($x) {
if (!$x) {
throw new Exception('Division par zéro.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Exception reçue : ', $e->getMessage(), "\n";
}

Why can't I handle fatal errors in CakePHP 2.x with PHP 5.2?

I'm having a strange problem in CakePHP where my AppExceptionRenderer is not being triggered for fatal and parse errors (E_ERROR and E_PARSE) when using PHP 5.2. The exact same code on my development machine (PHP 5.5) works fine.
Any ideas why?
I ended up tracking it down to what appears to be a bug in PHP 5.2 where a call to new SplFileInfo() strangely resets the fatal error information that normally exists inside error_get_last().
My fix was to tweak Cake's default App::shutdown() function by moving the _checkFatalError() call up above the Cache::write() calls.
So the result was this...
public static function shutdown() {
// For some weird reason on PHP 5.2 the SplFileInfo call made in Cache::write
// resets error_get_last() which means we can't trap fatal/parse errors.
// Small workaround is to check for errors *before* doing the caching thing
self::_checkFatalError();
if (self::$_cacheChange) {
Cache::write('file_map', array_filter(self::$_map), '_cake_core_');
}
if (self::$_objectCacheChange) {
Cache::write('object_map', self::$_objects, '_cake_core_');
}
// self::_checkFatalError();
}
Perhaps it may help someone else out there someday. :-)

PHP exceptions thrown in error handler not caught by exception handler

I chose this title because I have the exact same problem as stated in here:
PHP exceptions thrown in error handler are not caught by exception handler
The author accepted the answer which said he obviously was doing something wrong.
My error and exception handler were working fine that last two years, but now im facing the exactly same problem.
I did a code update and also a server update (Plesk 11.5 running, the PHP version should be the same and is 5.3.2). I checked my code for the error, but a test made it clear that this cant be the problem:
I wrote the following testfile:
function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) {
throw new Exception("this was an error");
}
function exceptionHandler($e) {
echo 'exceptionHandler';
}
set_error_handler('errorHandler');
set_exception_handler('exceptionHandler');
// test one:
throw new Exception(); // outputs "exceptionHandler"
// test two - uncomment the first test of course!
$test->blabla();
The second test should also output "exceptionHandler", but it doesn't!
The output is "Fatal error: Call to a member function blabla() on a non-object in ......./exeptiontest.php on line 0"
This problem drives me crazy at the moment. Any suggestions here? Any PHP settings that cause this?
Update (After reading your comment).
After an error handler has been executed program flow would went back to the expression after that where the error occurred. But it is unreliable to pass back program flow to a fatally failed script. That's why error handlers won't get called on fatal errors. The documentation says:
The following error types cannot be handled with a user defined function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the file where set_error_handler() is called.
As a workaround (depending on your needs), you may define a shutdown function using register_shutdown_function().
Original Answer (It turned out that this was not the problem here)
You need to read the documentation of set_exception_handler() carefully, especially the code example:
function exception_handler($exception) {
echo "Uncaught exception: " , $exception->getMessage(), "\n";
}
set_exception_handler('exception_handler');
throw new Exception('Uncaught Exception');
echo "Not Executed\n";
Meaning in your case that $test->blabla() will never gets executed.
You might expect that the exception handler function works like a catch block, but that isn't the case. True is, that if an exception occurs and no catch block is defined, the program flow will take over to the exception handler which may gracefully shutdown the script - but not more. If you want to handle exceptions the right way, use try / catch
Just to make it more clear: exception handlers behave different than error handlers. After returning from an exception handler the program will terminate while the program flow wents back to the expression after the error when returning from an error handler.

Is this an uncatchable fatal error?

I'm writing an application, where I thought all the errors were being handled, including fatal ones.
But now I found one error that results in a white screen, and the error only shows up in the webserver log.
$nonExistentVar + 1; // Notice error, gets caught and pretty error is displayed
$existentVar->nonExistentMethod(); // Fatal error, gets caught and pretty error is displayed
$nonExistentVar->nonExistentMethod(); // White screen, error can be seen in nginx.error.log
Is the last error uncatchable? Or what could the problem be?
I'm using Silex, not sure if that matters.
The way I understand it, exceptions can be caught but fatal errors cannot. I am curious to know how you are 'catching' the fatal error in example #2?
Why not use a php is_a() test to see if $nonExistentVar is of the correct class before attempting to call the method? Or possibly in conjunction with method_exists() if you still don't know if a class has a given method available.
Try putting only the last line:
$nonExistentVar->nonExistentMethod();
That works for me, as Symfony\Component\Debug\ExceptionHandler sends the response immediately upon encountering the first Error:
public function handle(\Exception $exception)
{
if (class_exists('Symfony\Component\HttpFoundation\Response')) {
$this->createResponse($exception)->send();
} else {
$this->sendPhpResponse($exception);
}
}

Problem testing exceptions with PHPUnit and Zend Framework

When a user accesses /user/validate without the correct post parameters, my Zend application throws a zend exception. (I get the standard "An Error Occurred" message, framed within my layout). This is intentional.
I am now trying to test that behaviour with PHPUnit. Here's my test:
/**
* #expectedException Zend_Exception
*/
public function testEmptyUserValidationParametersCauseException() {
$this->dispatch('/user/validate');
}
When I run the test I get a message saying it failed, "Expected exception Zend_Exception". Any ideas?
I have other tests in the file which are working just fine...
Thanks!
The Zend_Controller_Plugin_ErrorHandler plugin handles exceptions for you and the default Error_Controller forces a 500 redirect which may mean the exception you are testing for no longer exists. Try the following unit test and see if it passes:
public function testUnknownUserRedirectsToErrorPage()
{
$this->dispatch('/user/validate');
$this->assertController('error');
$this->assertAction('error');
$this->assertResponseCode('500');
}
If this works then it shows that by the time you are rendering the error view the exception will no longer exist as it is contained in the code before the redirect.
Well, maybe it simply fails because no exception is thrown?
Did you try running the test without "#expectedException Zend_Exception"? Is there an exception at all?

Categories