PHP (or other): Strategy to deal with exceptions that "cannot occur" - php

Consider the following code.
class C {}
/**
* #throws \InvalidArgumentException
*/
function classCreateInstance($class) {
if (!is_string($class)) {
throw new \InvalidArgumentException("Class name must be a string.");
}
if (!class_exists($class)) {
throw new \InvalidArgumentException("Class '$class' does not exist.");
}
return new $class();
}
/**
* #return C
*/
function foo() {
return classCreateInstance(C::class);
}
There is one function that may throw an exception, because it does not know anything about the $class argument.
On the other hand, the calling code knows that 'C' is a valid class name, so it would like to assume that the "InvalidArgumentException" will never occur. It would like to avoid verbose try/catch, and it would like to avoid having its own #throws tag. Especially if it is not "allowed" to have one, because it is implementing an interface that does not annotate the exception.
But, from an IDE / automatic code validation perspective, ignoring this exception is not safe.
So.. what is the correct way to deal with exceptions that are "almost impossible" from a calling code perspective?

In Java. there is a distinction between "checked" exception classes and "unchecked" exception classes. Only the checked exceptions are part of the interface contract, whereas throwing an "unchecked" exception is allowed even if the interface does not declare it.
In Java, in the "should never occur" case, one would throw an unchecked exception - e.g. a "RuntimeException".
In PHP, this is all convention-based. Methods can throw whichever exceptions they want. Adding a #throws tag in the doc comment is nice, but it is not enforced by the language.
However, an IDE, or possibly other code reviewing tools, can be configured to analyse code based on the Java model of checked vs unchecked exceptions.
E.g. PhpStorm has options to not require a #throws doc tag for RuntimeException and LogicException. So this would allow to treat these as "unchecked", but then write custom exception classes and treat them like "checked" exceptions in Java.
Native exception classes in PHP: http://php.net/manual/en/spl.exceptions.php#spl.exceptions.tree
Not that these all inherit from LogicException or RuntimeException, so they would all be considered as "unchecked". Only the root class, Exception (and custom child classes) would be considered as "checked".
This distinction also means that if you call a method/function with no declared/annotated exception, you still need to consider that an exception could be thrown nevertheless. This could be covered e.g. by a try/catch at the top level of the application. (e.g. an index.php)

Related

When should exceptions be documentated?

Assume the following snippet:
public function foo()
{
return $this->dependency->bar();
}
If dependency::bar() is known to throw an exception, but foo() is not expected to handle it. Should foo() have an #throws entry in it's docblock? Or should this be implicit? The phpdoc documentation is somewhat vague about this.
Assuming that dependency is injected based on an interface; should the interface document that the method can throw the specified exception.
Does it matter if the exception should be handled by the calling code or is that irrelevant to the situation?
Semantics all the way :)
You have created public function (everyone can use it) which in some case can throw exception - hence you definitely have to have #throws entry in your docblock.
If exception throws from your dependency - it's your internal implementation and it's encapsulated - hence someone who will use your function will have no idea what expect from your function.
In case of interface injection - interface must describe all exceptions because exceptions it's part of behavior of class and interface must describe all possible behaviors.
In case you have try-catch block and handle all exceptions from dependencies - you have to have only your own exceptions in #throws entry in your docblock.

OOP: how to add automatic context to exceptions

I have a class that receives some initialization values in its constructor and uses them to implement an interface. My code creates several objects from this class, with different initialization values.
Whenever an exception occurs in one of the methods, the stacktrace shows which class and method threw the exception, but not which object did.
Therefore, I would like to wrap every exception that leaves my class with some additional information, including the original initialization values that object received in its constructor. This should happen both to exceptions that I throw myself (that's trivial) and to ones thrown by the runtime (such as NPEs) and by libraries used by my class.
Of course, I could wrap every public method in a try/catch:
public function whatever(...)
{
try {
// ...
} catch (\Throwable $e) {
throw $this->wrapException($e);
}
}
But adding those 4 lines to every public method and indenting the body of every method one level, just to get more readable stacktraces, violates the DRY principle and is plain ugly.
Is there a better way? A generic OOP design pattern or maybe some PHP-specific trick that can address this issue, such as magic method names?

Exception annotations for propagated exceptions

Suppose I have a function a that throws an exception $e. Hence, according to phpdoc I should have an annotation #throws over the definition of a.
When I have another function b calling a
function b() {
a();
}
is it good practice/bad practice/correct/wrong to have a #throw annotation over the definition of b indicating that b could throw that kind of exception?
#throws annotation is to indicate for the developer if the function() can throw an exception
First, you have to ask the question : why don't catch the exception in b() method, is there a valid reason for that ?
Yes ? so you must add #throws annotation, it will indicate you, or others developers that using function() b() IS NOT SAFE and they will decide if they will
catch or propagate the exception
Also, since PHP doesn't force you to catch an exception thrown by another function, the #throws annotation became a must/mandatory practice
As a matter of fact, b() throws exceptions. Whether that happens directly or indirectly is irrelevant to the caller. Now, the annotations are not supposed to document internal implementation details that may change or even vary with different derived classes. Rather, the annotations document the visible behaviour for the caller, so the effective exceptions should also be part of the annotations.

suggestions for unit testing exceptions

Consider a method which might throw an exception with some descriptive text:
if ($someCondition) {
throw new \Whatever\Exception('dilithium exhausted');
}
And elsewhere in the method is another block that might throw the same exception, but with different text:
if ($anotherCondition) {
throw new \Whatever\Exception('differentialator exploded');
}
While writing unit tests for this class, you create failure cases so that you can verify that these two exceptions get thrown properly. In these failure cases, do you prefer to:
A) Use #exceptionExpected in the test method's docblock to trap the generic \Whatever\Exception class and subsequently ignore the getMessage() text, assuming you got the right one? (Seems like a bad idea.)
or:
B) Use try/catch and then assert that the caught exception's getMessage() text equals the exact descriptive string you're expecting? (More resilient but it means changing your tests whenever you change your error wording.)
or:
C) Create a separate exception for each error case (e.g., \Whatever\DilithiumException and \Whatever\DifferentialatorException) and then use #exceptionExpected for each one.
I'm currently using B but tending toward C. I'm curious what others are doing in this same scenario. Do you have any guidelines that help you determine, "At what point does an error deserve its own exception class versus a more generic shared one?"
All of the above.
A is great, and I use as much as possible because it is simplest. There is another case when A does not work:
/**
* #exceptionExpected FooException
*/
test() {
// code that could throw FooException
...
// purpose of the test that throws of FooException
}
In this case, the test could pass when it should have failed because it didn't even get to what I was testing. A good way to deal with this is to use $this->setExpectedException()
B is great when you might actually use information from the exception. Rather than using the text of the exception message I would prefer to use the code. I have a form validation exception that packages up all the problems encountered in the data into one exception. By extending the exception class it becomes easy to transmit a good deal of information from the internal error state to the external handling code.
C accomplishes the same thing as B, but allows for simplifying the code by relying on more classes. The difference between these two is subtle and I tend to rely on design aesthetic to make the decision.
TL; DR: Use exception codes rather than messages, and design to the use case rather than the unit tests.
PHPUnit also provides #expectedExceptionCode and #expectedExceptionMessage when you need this level of detail. Warning: The latter requires the former.
BTW, I also tend toward A. If I need to express more meaning in the exception, I prefer to create a new exception class. I find the message to be too volatile to be worth testing in most applications.

Ensuring all exceptions are considered when using PHP

I've used exceptions in Java and like the way it won't let you call a method unless you catch or throw the exceptions that it might throw.
I'm looking for something similar in PHP. I realise PHP is more dynamic than Java, and doesn't even let you define the exceptions that it throws, but what is the closest I can get?
We document our methods using PHP Doc, so something that triggered an E_WARNING if you called a method without the correct try/catch block, or threw an exception without the correct #thows comment, would be perfect.
There's no way to do it in PHP itself. You will have to parse PHP and figure it out yourself. Try writing phc plugin for this.
I don't think you can reasonably get very close at all, because the language core provides just nothing whatsoever for you to work with. At best, you'd wind up creating some kind of entirely user-space funcall/exception validation mechanism that would have an absolutely horrific impact on performance.
I'm not sure that you can accomplish your stated goal. The PHP environment doesn't analyze what a function might or might not do, which would generally be a compile-time operation for other languages (I would think). I don't think you can even find that sort of thing via Reflection.
You are wrong however when you say that you can't define the exceptions that get thrown, as the Exception base class is fully extendable. PHP doesn't throw any exceptions by default though, it triggers errors. There is a fundamental difference between triggered errors and Exceptions, the latter being a user-land construct for the most part.
This isn't your question, but I'd put out a suggestion that if you wanted to move to a fully Exception-oriented environment, you could write your own error handler using set_error_handler() and manage the PHP triggered errors, and have it throw out an Exception.
I think you can simply reproduce this behavior in PHP using exception handlers and reflection.
class YourException extends Exception {
public function __toString() {
return __CLASS__;
}
}
class MyObject {
public function __construct(){}
/**
* #throws MyException
*/
public function myMethod() {
return 'foo';
}
}
try {
$o = new MyObject();
$o->myMethod();
}
catch(YourException $e) {
$method = new ReflectionMethod('MyObject', 'myMethod');
$method->getDocComment();
$throws = // Get the #throws comment (foreach, regexp, whatever);
if($e === $throws) {
// do something
}
}
Setting your own exception handler.
Grab and analyse the comments with Reflection mechanism (see getDocComment)

Categories