When should exceptions be documentated? - php

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.

Related

The proper way to do exception handling

Now what I generally do when writing code is something like this
function changeBookAuthor(int $id, string $newName){
if(!$newName){
throw new MyAppException('No author name was provided');
}
$book = Books::find($id);
if(!$book){
throw new MyAppException('The provided book id could not be found');
}
}
in the laravel doc we see:
https://laravel.com/docs/5.4/errors
public function report(Exception $exception)
{
if ($exception instanceof CustomException) {
//
}
return parent::report($exception);
}
Now how to I properly handle the exception? they are all the same exception and they have no code neither. Should I provide an error code?
the problem with php exception is that they use integers. Is quite annoying imho. Better would be 'changeauthor_bookid_notfound' as code instead of a random number. Should I create an exception class for each single exception? e.g. not reuse MyAppException that seems a bit tedious. I would have a trillion classes.
Now if for a special exception I want special handling, with my code, I cannot easily do it. I have no code to check for (e.g. $exception->code == 3331 then do special) and I don't have custom exception classes neither
what is a proven good solid way to handle this case?
code, new class on each error, something else all together?
and if provide a code, what is a nice way to do it?
The "proper" way to do it would be to define either a custom Exception class for each exception, or to define custom exceptions based on the type of error being thrown, however realize that Laravel already has many built in exceptions and mechanics for handling the use cases you outlined.
For instance, in the case of the "Book Not Found" exception, rather than manually triggering an exception yourself, you could use Books::findOrFail($id); which throws an instance of ModelNotFoundException when appropriate.
Also, in PHP there is no need to handle exceptions for unprovided arguments. Unless expressly denoted as optional, all method arguments are required, and Laravel will throw a PHP exception if an argument is missing.
Additionally, Laravel provides the abort() magic method which throws a HTTP error along with a custom error message and can be used like so:
abort(418, "I'm a teapot...")
So, if you must reinvent the wheel, the proper way is to define custom exception classes and define the custom handlers for those classes, but realize that Laravel already has many built in tools for managing exceptions without needing to do so.

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?

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

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)

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.

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