39

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.

2
  • have you tried catch (\FatalErrorException) Commented Feb 9, 2015 at 9:37
  • i've tried catch(Symfony\Component\Debug\Exception\FatalErrorException) and catch(\ErrorException). Commented Feb 9, 2015 at 11:58

4 Answers 4

80

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
Sign up to request clarification or add additional context in comments.

2 Comments

I can confirm this solves the issue, but I don't understand why. Symfony's FatalErrorException extends \ErrorException, which extends \Exception. The fact that PHP now has Error as a base class doesn't seem relevant since it's not in the inheritance hierarchy for FatalErrorException. Can anybody explain?
@ScottBuchanan I figured out something interesting today. My issue was with Symfony's FatalThrowableError (which also extends ErrorException). I noticed that if you manually throw a FatalThrowableError in an Exception try/catch, it's caught. But I was seeing unhandled errors of this type in the application originating within an Exception try/catch. I'm using the Laravel framework, and I've deduced that the framework caught the unhandled TypeError (an Error, not an Exception) after my handler didn't catch it and wrapped it in a FatalThrowableError.
12

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.

5 Comments

Do you know about a tool that has similar features to the default twig filter?
@Jumi: what do you mean?
@Jumi: I know about "default twig filter". What I can't understand here is what do you mean with "a tool"? What tool? A PHP one?
Yes. A library, a function, something that keeps me away from writing conditions for all getters. Like the symfony property accessor would return an object containing which call fails in the chain.
I'm not aware of nothing like what you're asing. BTW as I told you, isn't a good practice to skip controls because, eventually, big issues could be found and your code will became shortly unstable
0

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;
}

2 Comments

Using exceptions for logic/control flow is a bad idea. I hope you won't ship code blocks like the one above into any production environment.
@RaduMurzea Actually its a very common pattern in some languages like Python, where they express it as "It is Easier to Ask for Forgiveness than Permission." (see en.wikipedia.org/wiki/Python_syntax_and_semantics#Exceptions)
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.