As of PHP 7 it's possible to catch Fatal Errors by catching either Error class or Throwable interface, but for some reason I can't manage to do that when "Fatal Error: trait not found" is triggered.
try {
class Cars {
use Price;
}
} catch (Error $e) {
echo $e->getMessage(); // Output : Fatal error: Trait 'Price' not found in [..] on line [..]
}
The error not caught!! So I came up with a solution
try {
if (trait_exists('Price')) {
class Cars{
use Price;
}
} else {
throw new Error('Trait Price not found');
}
} catch (Error $e) {
echo $e->getMessage(); // Output : Trait Price not found
}
Why that Fatal Error in the first example wasn't caught?
Is my approach in the second example is the only way to do it?
Short answer: not all errors are catchable, some are still being upgraded to the new error/exception model.
PHP 7.0 let you catch creating a missing class:
$foo = new NotAClass;
PHP 7.3 will let you catch errors about parent classes not existing (see bug #75765):
class Foo extends NotAClass {}
However, you still can't catch a missing trait (there's a note on the Github issue for the above bug about this being harder to fix):
class Foo { use NotATrait; }
Note: HHVM is apparently fine catching any and all of these, since it doesn't care what you think about the rules (and partly because this sort of thing is much easier in a fully-compiled environment).
See https://3v4l.org/L0fPA for a demo.
And yes, as was mentioned in the comments, please try and not rely on catching missing classes / traits at run-time. You should know your class hierarchy way earlier that that.
Related
I am trying to catch a custom exception, but keep getting an Exception 'CustomException' is never thrown in the corresponding try block inspection error for the following scenario:
class CustomException extends Exception { }
class Test {
/**
* #throws CustomException
*/
public function willFail()
{
throw new CustomException('I failed');
}
}
$test = new Test;
try {
$test->willFail();
} catch (CustomException $e) {
echo $e->getMessage();
}
The above example works exactly as expected, aside from PhpStorm giving me the inspection error.
I've searched everywhere and can't find out how to fix this without disabling the inspection entirely. Any help would be greatly appreciated!
Just in case anyone else runs into this problem themselves in the future, I'm posting the solution to this problem.
This was fixed by updating "Call tree analysis depth" to "1" in Preferences > Languages & Frameworks > PHP. Click on the "Analysis" tab and update the setting there.
I have code like this:
try {
$var = $object->getCollection()->first()->getItem()->getName();
} catch(\Exception $e) {
$var = null;
}
Of course i have communicative variable and method names. This is just demonstration.
So if my collection is empty the Collection::first() will return false. Then the getItem call will throw a Symfony\Component\Debug\Exception\FatalErrorException which won't be catched by the code above.
My question is that how can i catch this exception? I have long chains like this with many getters that can return null. So i prefer this way rather than checking every value for null.
Use Throwable class instead Exception class:
try {
$var = $object->getCollection()->first()->getItem()->getName();
} catch(\Throwable $e) {
$var = null;
$msg = $e->getMessage();
}
Since PHP 7.0 exceptions thrown from fatal and recoverable errors are instances of a new and separate exception class: Error. This new Error class implements Throwable interface, which specifies methods nearly identical to those of Exception. Because Throwable is higher in hierarchy you can catch with it both, \Error and \Exception.
interface Throwable
|- Exception implements Throwable
|- ...
|- Error implements Throwable
|- TypeError extends Error
|- ParseError extends Error
|- ArithmeticError extends Error
|- DivisionByZeroError extends ArithmeticError
|- AssertionError extends Error
As you can see here, FatalErrorException extends ErrorException (PHP) that extends itself php Exception class.
Now that you have all this elements, you're ready for next step: as the name of exception says, this is a FatalError (a concept related to PHP and not with Symfony2; in that case they built a wrapper class for this error, maybe for interface purposes).
A PHP fatal error isn't a catchable one so is pretty useless to keep the code that could cause the FatalError, inside a try ... catch block
You should check, as a common and good rule, whenever is possible for returned values before try to access them.
Update
Since I've seen an upvote to my answer after PHP7 was released, I would like to caveat that since PHP7 is possible to catch fatal errors so this answer is still valid but only for PHP versions < 7.
Ok. I've found a workaround. I use the property accessor component which throws simple exceptions, not fatal errors.
$pa = \Symfony\Component\PropertyAccess\PropertyAccess::createPropertyAccessor();
try {
$var = $pa->getValue($object, 'collection[0].item.name');
} catch(\Exception $e) {
$var = null;
}
Works for me (PHP 7.0, Symfony 3.0.9):
use Symfony\Component\Debug\Exception\FatalThrowableError;
...
try {
throw new FatalErrorException("something happened!", 0, 1, __FILE__, __LINE__);
} catch (FatalErrorException $e) {
echo "Caught exception of class: " . get_class($e) . PHP_EOL;
}
Output:
Caught exception of class: Symfony\Component\Debug\Exception\FatalErrorException
My exception does not seem to be bubbling up from the parent constructor.
Here's a simplified version:
class importer {
public function __construct()
{
$this->getFile(); // fills out $file with name or false on failure
if (!$this->file) {
throw new Exception("Sorry no file, so no can do!");
}
}
}
class childImporter extends importer {
pubclic function __construct()
{
parent::__construct();
}
}
try {
new childImporter();
nextFunction();
} catch (Exception $e) {
echo $e->getMessage();
}
nextFunction never gets called, and the catch block doesn't get run either, the whole thing throws an uncaught exception fault and stops.
I thought exceptions were supposed to bubble up?
Or is the code above supposed to work and something else is going wrong?
So for anyone else having a similar issue the problem was, as cbuckley pointed out, an issue with namespacing.
I changed both occurrences of Exception to \Exception which caught it nicely.
I wasn't getting any other namespace errors in my ide as PHPStorm had very nicely auto-imported an exception class of Symfony\Component\Config\Definition\Exception\Exception from who knows where (as I am not using any Symfony components anywhere else!).
Hope this helps someone who has also had a really long day!
I want to catch an exception that is thrown by the Google API PHP library, but for some reason it generates a 'Fatal error: uncaught exception' before reaching my catch block.
In my app I have something like this:
try {
$google_client->authenticate($auth_code);
} catch (Exception $e) {
// do something
}
This is Google_Client's authenticate():
public function authenticate($code)
{
$this->authenticated = true;
return $this->getAuth()->authenticate($code);
}
The authenticate($code) above is Google_Auth_OAuth2::authenticate(), which at some point throws the exception:
throw new Google_Auth_Exception(
sprintf(
"Error fetching OAuth2 access token, message: '%s'",
$decodedResponse
),
$response->getResponseHttpCode()
);
If I put a try/catch block in the Google_Client's authenticate, it catches the exception, but without it the program just dies instead of reaching the main try/catch block from my app.
As far as I know this shouldn't be happening. Any ideas?
The problem was that the try/catch block was in a namespaced file and PHP requires you to use "\Exception". More info: PHP 5.3 namespace/exception gotcha
Example (taken from the link above):
<?php
namespace test;
class Foo {
public function test() {
try {
something_that_might_break();
} catch (\Exception $e) { // <<<<<<<<<<< You must use the backslash
// something
}
}
}
?>
I'm not sure how the structure of Google's API is, and I'm not a real fluent PHP programmer, but you're catching a specific exception type of Exception, with which Google's Google_Auth_Exception may not inherit from.
Therefore, since your try-catch block is looking for an exception that is a member of Exception and the Google_Auth_Exception is perhaps not a member of Exception, then your try catch block will miss it.
Try catching the specific exception. This has happened to me before in many different languages.
Edit
The link you posted inherits its exception from: Google/Auth/Exception
Google/Auth/Exception inherits its exception from: Google/Exception
Google/Exception extends Exception, which may, in this context be the Exception that your class is referring to.
It seems my justification for your try-catch block not catching an exception is completely wrong, but the wisdom could still be true. Try catching the specific exception, then use instanceof to see if PHP recognizes Google_Auth_Exception as a member of Exception.
I have some code that looks like this
# Try to import file
try
{
DataManager::fileImport($_FILES['datafile']['tmp_name'],
$_POST['zones'], $_POST['statuses']);
}
catch(Exception $e)
{
print 'Herp.';
$response->body = Helpers::getVarDump($e);
}
DataManager::fileImport is literally a one-line function that throws a normal Exception:
static function fileImport($filepath, $zones, $statuses)
{
throw new Exception('SOME EXCEPTION');
}
And yet I get
Fatal error: Uncaught exception 'Exception' with message 'SOME EXCEPTION'...
From the try block. Also 'Herp.' is never printed. Why doesn't the Exception trigger the catch block?
EDIT: I should mention I'm using Tonic and PHP 5.3.9
EDIT AGAIN: Here's DataManager (with names replaced with ... for anonymity) http://pastebin.com/daHWBJDC
Solution
I neglected to specify use \Exception; in the file containing the try/catch.
Pondering
I know it's intentional that each namespace in PHP should define its own Exception for many reasons, but I still find it odd that catch(Exception e) didn't cause any errors when Exception in that context wasn't defined. If I were to write new Exception() I would get an error.
Oh well, at least I learned something.
Strange. If i run this code i get the "Herp."
<?php
class DataManagerTest {
static function fileImport($filepath, $zones, $statuses)
{
throw new Exception('SOME EXCEPTION');
}
}
# Try to import file
try
{
DataManagerTest::fileImport("param1","param2","param3");
}
catch(Exception $e)
{
print 'Herp.';
}
?>
You might have an issue with your DataManager class because i copied your code, adapted it to run and i get the exception handled... You problem is elsewhere...
class DataManager {
static function fileImport($filepath, $zones, $statuses){
throw new Exception('SOME EXCEPTION');
}
}
try{
DataManager::fileImport('', '', '');
}catch(Exception $e){
print 'Herp.';
}
Results in
Herp.
4 years later...
#Hubro, thank you for saving me with that namespace fix!
It does seem counterintuitive at first that it's necessary when throwing a root-level Exception, even though it ultimately makes sense in the general context of namespaces.
For anyone who doesn't want to utilize #Hubro's file-level fix:
use \Exception;
You could instead add the backslash in front of Exception in the higher level catch block:
} catch (\Exception $e) {
We could all benefit from someone smarter than me providing suggestions on best practices around defining a custom Exception for each namespace. Any takers?