php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #80009 Fatal Error is not caught with Throwable
Submitted: 2020-08-22 18:04 UTC Modified: 2020-08-24 14:47 UTC
From: brucekwells at gmail dot com Assigned: cmb (profile)
Status: Not a bug Package: Compile Failure
PHP Version: 7.4.9 OS: Windows 10.0 build 19041 AMD64
Private report: No CVE-ID: None
 [2020-08-22 18:04 UTC] brucekwells at gmail dot com
Description:
------------
Some Fatal Errors are caught in this script and some are not.

Comment out the "extends TestBase" of the TestExtended class, and this example will catch the error: Return value of TestExtended::returnString() must be of the type int, string returned

Or change the class in the new statement to an undefined class.  Or change the method call.  Both are fatal errors, both caught.

But as written, this script does not catch the fatal error.

Test script:
---------------
<?php

try
	{
	class TestBase
		{

		public function returnString() : string
			{
			return __METHOD__;
			}

		}

	class TestExtended extends TestBase
		{

		public function returnString() : int
			{
			return __METHOD__;
			}

		}

	$test = new TestExtended();
	echo $test->returnString();
	}
catch (\Throwable $e)
	{
	echo 'Caught error: ' . $e->getMessage();
	}
phpinfo();



Expected result:
----------------
'Caught error: ' followed by PHP error message is printed, followed by phpinfo() output.

Actual result:
--------------
Fatal error message is printed the standard PHP error handler and is not prefixed by 'Caught error: '.  phpinfo() is also not printed.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-08-22 20:49 UTC] cmb@php.net
-Status: Open +Status: Not a bug -Assigned To: +Assigned To: cmb
 [2020-08-22 20:49 UTC] cmb@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

There is a difference between E_(*_)ERROR and Throwable.
Just try without the try-catch-statement.
 [2020-08-23 15:34 UTC] brucekwells at gmail dot com
So I am confused. Here is the manual entry for PHP 7 errors which states they are handled by throwing exceptions of type Error, which extends Throwable.

https://www.php.net/manual/en/language.errors.php7.php

So I can catch the following fatal errors by catching Throwable:
 - Return value of TestExtended::returnString() must be of the type int, string returned 
 - Class 'TestExtendedd' not found 
 - Call to undefined method TestExtended::returnStringx() 

But apparently I can not catch 
 -  Declaration of TestExtended::returnString(): int must be compatible with TestBase::returnString(): string

Now the manual does say "most errors" are handled by exceptions. Apparently this one is not.  I would consider this a bug.

My use case (that I found in the wild) is some Google code that does exactly what I have in the test case.  I noticed this when I tried to run the ReflectorClass on it.  The error was thrown but not caught.  I want to write unit tests that will load every class using the ReflectionClass.  The reason to do this is I can not new every possible class due to required parameters in the constructor. But the ReflectionClass does not construct the object, it merely loads the class, and in so doing, causes PHP to generate this error.

I also have used the nikic/PhpParser to analyze the syntax of each PHP file, but it can not detect this problem, as it does not know the parent class declared the return value differently, as it is just parsing the file based on syntax rules, and it is syntactically correctly.

So my unit tests can now do a syntax check on every single file in my source tree with PhpParser, and if this worked as documented, I would use ReflectionClass to load every class (including all it's parents, which may not be my code) and check for errors.  I would not be executing any code, but simply doing a complete syntax and essentially a lint / linker type check as well. Too often people use unit test code coverage to try to find all these types of errors, but that is a very manual and error prone process.  My solution would make PHP much more like a compiled language where all code must pass a basic syntax check, meaning it was loaded and parsed by PHP itself.  This would be a HUGE improvement for insuring PHP code in your project is syntactically correct.

Now it is possible this error is intended to not be catchable.  Is there a list of PHP errors that are documented as not being catchable?

And thanks for the prompt reply over the weekend! I think if we could get PHP to throw exceptions on errors in all cases, we would have a much better language in the long run.  Thanks for your consideration.
 [2020-08-23 17:08 UTC] cmb@php.net
The nomenclature isn't optimal, or at least there is the subtle
difference between Error (an exception), and "real" errors
(E_ERROR etc.).  See set_exception_handler() vs. set_error_handler().

Anyhow, that doesn't really matter, since either kind of error
isn't really meant to be caught (even if it's possible for Error
exceptions). Instead they signal programming errors which should
be resolved.

Finally, it is simply not possible to continue execution for all
kinds of error conditions, because the engine is in an invalid
state.  This is why there are still errors which are not
Exceptions.
 [2020-08-24 07:42 UTC] nikic@php.net
> Now it is possible this error is intended to not be catchable.  Is there a list of PHP errors that are documented as not being catchable?

As a very rough rule: Any error thrown during compilation rather than run-time cannot be caught.

We'd love to make everything throw, but it would take too much work for some of them.

If you're just validating files, the easiest way to do so would probably be to fork/exec a new process that can freely fatal.
 [2020-08-24 14:47 UTC] brucekwells at gmail dot com
Thanks for the explanation.  I was hoping it was going to be some sort of "opps, missed that case, easy fix."

I'll look into your suggestion of a separate failable process.

And thanks for all the hard work on PHP, it just keeps getting better and better. Looking forward to 8 and beyond.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sun Apr 28 07:01:30 2024 UTC