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.
Related
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.
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)
I'm writing a PHPUnit test (v4.6.7) for a new PHP class (v5.3.3) that does stuff with XML. The purpose of the test is to confirm that the class throws an DOMException when handed bad XML.
I want to collect the exception and perform an assertInstanceOf on it, comparing it to DOMException.
No matter what I do, by the time I can see the exception phpunit has already transformed the original exception into an instance of PHPUnit_Framework_Error_Warning.
For example, here is one effort in the code under test:
//...
try {
$this->code_that_causes_exception();
} catch (\Exception $exception) {
$className = get_class($exception);
throw $exception;
}
//...
When run from within phpunit, the string stored in $className is PHPUnit_Framework_Error_Warning, as opposed to whatever exception the code_that_causes_exception actually raised.
Begin Update
Just to clarify my question, with no try/catch around the code under test, the message reported by phpunit is DOMDocument::loadXML(): Start tag expected, '<' not found in Entity, line: 1. I make the perhaps rash assumption that DOMDocument is throwing some sort of exception to produce this message. The DOMDocument says that DOMException is the parent of exceptions thrown from DOMDocument objects.
End Update
I want the original exception so that I can wrap the code under test with an appropriate try/catch to gracefully handle this failure situation.
How do I obtain the original exception from which the PHPUnit_Framework_Error_Warning was instantiated?
Why this is happening
Your exception is not converted to a PHPUnit_Framework_Error_Warning, it is never thrown. PHPUnit automatically converts PHP Warnings into exceptions. This is enabled by the convertWarningsToExceptions property in the XML configuration file (there are also convertNoticesToExceptions and convertErrorsToExceptions respectively). All these settings are true by default.
This means that your original exception is probably never thrown, because a PHP warning is triggered before and then converted into an (instantly-thrown) exception by PHPUnit's error handler.
Have a closer look at your caught exception to see which statement in your code triggers the PHP warning (simply remove the try/catch statement to let PHPUnit present the error).
You can disable this behaviour in your phpunit.xml file:
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.6/phpunit.xsd"
...
convertErrorsToExceptions="false"
convertNoticesToExceptions="false"
convertWarningsToExceptions="false"
...
How you should test exceptions
The probably easiest way to assert that a specific exception is thrown is to use the #expectedException annotation. This is discussed in-depth in the manual. Using this annotation saves you a lof of boilerplate code:
/**
* #expectedException DOMException
* #test
*/
public function myTestCase() {
$this->code_that_causes_exception();
}
This test will pass when a DOMException is thrown and fail when no exception is thrown at all (or any other Exception is thrown).
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.
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)