Can't catch symfony FatalErrorException - php

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

Related

How does Throwable interface work inside catch ()?

Interfaces cant be instantiated , and interfaces do not have methods bodys so how that code works ? (to manage Exception and Error at same time in php 7 we use Throwable cause Exception and Error both implement a new interface )
try {
// Code that may throw an Exception or Error.
} catch (Throwable $t) {
// Handle exception
}
Exception and Error implement the interface Throwable. In fact there is a whole hierarchy for Errors in PHP. You can catch a specific type of error or you can go up the tree and catch more generic errors. Throwable is the base interface and is going to catch all errors in the hierarchy.
When type hinting with OOP you do not need to use the exact type. You can use the base interface or parent type. For example.
interface MyInterface {
}
class A implements MyInterface {
}
function doSomething(MyInterface $obj) {
// something
}
doSomething(new A);
This code works and accepts the object of class A even if the type expected is an interface MyInterface.

How to catch trait not found error PHP 7

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.

Exception not being caught inside object method

This is more just for documentation, since I've already solved the issue, but it was subtle and difficult enough to debug that I thought it would be useful in the public sphere.
The issue was that I had a try/catch block in an object method that just wasn't working. The reduced example is in two files, which look like this:
TestClass.php:
<?php
//TestClass.php
namespace MyTest;
class TestClass {
public function __construct() {
\e("Initializing object");
try {
\e("Trying object exception");
\throwTestException("Failing gracefully in object");
\e("After exception");
} catch (Exception $e) {
\e($e->getMessage());
}
\e("After object init exception");
}
}
?>
Main.php:
<?php
//Main.php
function e($str) { echo "\n$str"; }
function throwTestException($msg) {
throw new RuntimeException($msg);
}
require "TestClass.php";
e("Beginning");
try {
e("First try");
throwTestException("Failing gracefully first");
e("After exception");
} catch (Exception $e) {
e($e->getMessage());
}
e("Ending");
e('');
e('Beginning object test');
new \MyTest\TestClass();
e('Ending object test');
?>
The expected result on loading Main.php was this:
Beginning
First try
Failing gracefully first
Ending
Beginning object test
Initializing object
Trying object exception
Failing gracefully in object
After object init exception
Ending object test
What I actually got was something like this:
Beginning
First try
Failing gracefully first
Ending
Beginning object test
Initializing object
Trying object exception
Fatal Error: Uncaught Exception: Failing gracefully in object......
As you can see, the exception was not being caught. I tried all sorts of things and just couldn't figure out why it wasn't being caught. And then.... (See answer below)
I realized it was a namespace issue. Because I had declared TestClass within the namespace MyTest, and throwTestException in the global namespace, my reference to Exception within the class method was tacitly resolving to \MyTest\Exception and thus NOT matching the actual exception being thrown, \RuntimeException. And since I wasn't actually trying to instantiate the exception from within the namespace, no "Unknown Class" errors emerged to reveal what was happening.
The solution, then, was simply to properly resolve the exception class I was trying to catch:
catch(\Exception $e) { .... }
To be fair, this became obvious as I built my highly reduced example. It wasn't obvious initially because the exception I was expecting to catch was being generated by the class's superclass (which was the SQLite3 class). Thus, I didn't have to worry about the namespace when generating the exception, and all I was thinking about when catching it was to use the most general form of exception, Exception. And again, since I wasn't instantiating that exception -- only matching against it in a catch block --, I didn't get any notices that it was unresolved.

Error and exception handling in php7

Recently moved to php7. The following error occurs:
argument 1 passed to MyClass\Throwable::exceptionHandler() must be an instance of Exception, instance of Error given
And the respective class
namespace MyClass;
class Throwable
{
public function exceptionHandler(\Exception $exception)
{
//logic here
}
}
As stated in docs
most errors are now reported by throwing Error exceptions.
Does it mean that I have to provide an instance of Error or even more general Throwable to the exception handler?
Errors and Exceptions both extend Throwable however Errors are not extended from Exception.
Therefore, your ExceptionHandler must accept an object of Type Throwable in order to accept Errors.
Simplest fix is this, though you may want to rename $exception to make it clear.
namespace MyClass;
class Throwable
{
public function exceptionHandler(\Throwable $exception)
{
//logic here
}
}
Note: The new Error class should not be confussed with an ErrorException which has classicly been used as a device for turning PHP 5 errors into Exception objects with symantic meaning.
http://php.net/manual/en/class.error.php

Calling to static methods for unknown classes doesn't trigger an exception catch in __autoload

For some reason, exceptions thrown in an __autoload function aren't being caught when trying to call a static method on a class that doesn't exist yet (PHP 5.3.9).
See this sample code:
<?php
function __autoload($class)
{
throw new Exception('loaded ' . $class . "\n");
}
try {
echo "Object: ";
$test = new Log();
}
catch (Exception $e)
{
error_log($e->getMessage());
}
// Exception is caught with static var.
try {
echo "Static var: ";
Log::$blah;
}
catch (Exception $e)
{
error_log($e->getMessage());
}
// Fatal error occures with static method.
try {
echo "Static method: ";
Log::blah();
}
catch (Exception $e)
{
error_log($e->getMessage());
}
The exception is caught on the first 2 cases (new object and a static property of an unknown class), but is not caught on calling the static method.
PHP mixes errors and exceptions. In this case, the fatal error is "thrown" before the exception could be caught. It's about priorities. Calling an undefined function and giving the fatal error has a higher priority here than handling the exception, the code already stopped (Demo). Fatal errors itself can not be catched.
As you can not catch fatal errors with a try/catch block, your script stops at the fatal error.
Same would be for an undefined static property of a defined class. It would trigger a fatal error as well you won't be able to catch.
You can however turn errors into exceptions and catch those, however this does not work with fatal errors (Demo). I assume this is the root limitation you're running into: Fatal error brings script execution down, regardless if it appears inside a try/catch block or not. Exception handling is brought down as well.
Additionally, PHP 5.5.0-dev and PHP 5.4.0beta2-dev crashes with a simliar to yours code example. Which makes me believe that in the upcoming PHP version there will be a bug which could be considered a flaw. It might be worth to report this against PHP 5.4.
See also Exception slipping exception handler which illustrates your problem without the overhead of __autoload.
Yes, PHP have a problems with exceptions thrown in an __autoload.
But you can use one interesting trick:
function __autoload($class)
{
eval("class $class {
static function __callStatic(\$name,\$val){
throw new Exception('Loaded ' . $class . PHP_EOL);
}
}");
throw new Exception('Loaded ' . $class . PHP_EOL);
}
And don't forgot to implement work with namespaces in evaled expression.

Categories