How does Throwable interface work inside catch ()? - php

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.

Related

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.

Exceptions don't bubble through instances in PHP?

I was getting an error about an exception thrown in a __toString() method in a class of mine, even though there was a try...catch() in there. I did some tracking down and it turns out an object within my instance was throwing an exception in its __toString(), which meant that it wasn't being caught by the containing class' catch!
I wrote some test code as a demonstration:
class test {
public function __toString() {
try {
$b = new b();
echo (string)$b;
} catch (exception $e) {
return (string)$e;
}
}
}
class b {
public function __toString() {
throw new Exception ("test");
}
}
$test = new test();
echo $test;
I had thought that exceptions always "bubbled up" through the code until they were caught or made it all the way out.
Is there any workaround for this? The instance within my class is from a library; I don't know if I can maintainably modify it to have a catch in its own __toString().
Per PHP's __toString() section in the magic methods docs:
Warning
You cannot throw an exception from within a __toString() method. Doing
so will result in a fatal error.
When executing the code snippet you gave you get a message that says the same thing:
PHP Fatal error: Method b::__toString() must not throw an exception
There's a feature request here to support this, which states that in current state of things it would be hard to implement.
Having said all that, the proper way to fix this is to submit a patch to your upstream library to not throw exceptions from that method.
If that's not possible, the workaround for you is instead of:
echo (string)$b;
write
echo $b->__toString();
In which case you can catch the exception as you expect.

How to structure a simple PHP Exception class

Hi devs
I have a simple PHP OO project. I have an exception who appears 2 times.
Something like :
if (!$x) {
throw new Exception("Don't use this class here.");
}
I want to dev a class in order to edit this code like that :
if (!$x) {
throw new ClassUsageException();
}
How to dev the Excpetion class with default Exception message ?
Thanks
I'd advise creating new exception classes sparsly. It is no fun to check a multitude of exceptions left and right. And if you really feel the need, check what kinds of exceptions are already defined and where in that hierarchy your exception will fit and then extend that class, i.e. give the developers a chance to catch a (meaningful) range of exceptions without having to explicitly write one catch-block after the other.
I'm really not sure what you're trying to achieve here with Don't use this class here. but it could be an InvalidArgumentException (or something derived from that exception, if you really must). There are other mechanisms to prevent an instance of a certain class at a specific place though.
You can extend the Exception class
<?php
Class ClassUsageException extends Exception {
public function __construct($msg = "Don't use this class here.", $code = 0) {
parent::__construct($msg, $code); //Construct the parent thus Exception.
}
}
try {
throw new ClassUsageException();
} catch(ClassUsageException $e) {
echo $e->getMessage(); //Returns Don't use this class here.
}

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

Why use multiple PHP Exception classes

I admit I do not use Exceptions a whole lot and they are at time hard for me to grasp 100% in PHP, this could be partially because PHP does not have the best error => Exceptions support but none the less I do not know much about them.
Take for example this code below, it has 4 different Classes defined that do nothing but extend a base Exception class. I am just curious why one would not just call an Exception and why they have all these separate classes.
I assume there is a good reason?
class OptimizeImageException extends Exception {};
class FileNotFoundException extends OptimizeImageException {};
class FileNotImageException extends OptimizeImageException {};
class ModuleNotFoundException extends OptimizeImageException {};
By having multiple Exception classes, you can pick out which one you're interested in when catching them.
<?php
class OptimizeImageException extends Exception {};
class FileNotFoundException extends OptimizeImageException {};
class FileNotImageException extends OptimizeImageException {};
class ModuleNotFoundException extends OptimizeImageException {};
try {
throw new FileNotImageException();
} catch (FileNotFoundException $x) {
echo "NOT FOUND!";
// do something about it
} catch (FileNotImageException $x) {
echo "NOT IMAGE!";
// do something about it
} catch (Exception $x) {
echo "UNKNOWN EXCEPTION!";
// do something else about it
}
This is a trivial example, but say you have a function loadImage() which is supposed to load an image. If the function fails, you can handle different failure scenarios differently. If you always throw a basic Exception, you only know something went wrong. You don't know what went wrong, so you can't have different recovery responses based on different scenarios, not without using another mechanism (which then makes exceptions rather weak).
You need to have different exception classes to be able to find out what actually happened in case that some particular exception could be handled.
Like:
try {
// do something
} catch (OptimizeImageException $e) {
// image cannot be optimized. left it as is and log the error message
}
In the code above you're handling one particular exception case, that can be processed right here and right now to move your application flow as expected. Other possible exceptions will go upper.
If you had only one exception class like MyAppException then all you could do is just catch it, check the message (which is weird) to realize what actually happened and if you cannot handle it - rethrow exception. As you can see this way your code would be a bit hackish and unmaintainable (you cannot change exception message)

Categories