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?
Related
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.
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 guess this might be a silly question, but I cant wrap my head over it.
So I have some code which looks like so:
$process = new ProcessTree();
$process->start();
and the class processTree is:
class ProcessTree()
{
public function __construct()
{} //dummy constructor...
}
public function start()
{
$this->init();
$this->doThis();
$this->doThat();
//etc...
}
So I guess my question is about the dummy constructor. According to good design, should the constructor not do something? Or even, shouldn't the body of start() be inside __construct() instead?
You don't need to use constructor if you don't really need it. I don't think it's a best practice just to put an empty constructor.
You should also don't move code from start() to constructor. Constructor should only help to initialize object (for example set some properties) and if you want to do some actions you should simple run those methods or create wrapper method (as you did) to run multiple methods at once.
Of course we don't know what you do in init(), doThis() and doThat() methods. If they do actions that are always necessary to use other object's method, you can move them into contructor, but as I said we don't know what's code in them.
In general the constructor should not do "too much" because that would violate the Single Responsibility Principle: the constructor's responsibility is to prepare the object for use, so that's its actions should be limited to.
Of course there will be exceptions to all kinds of rules. For example, the constructor of PDO initializes a database connection, which it's not really required to do. But in the context of PDO this makes sense because:
You will always want to make that connection (and sooner rather than later)
There is always a real possibility that the connection will not be successful (so by having the constructor connect and throw on failure you can check for this conveniently)
If the PDO constructor didn't connect or throw then all the other methods would have to be ready to do the same. But PDO's error reporting strategy can be switched to return codes; in this scenario you would be forced to sprinkle "connection failed" logic throughout the code. That would be a really unfortunate outcome, but it's prevented once and for all by connecting in the constructor.
But as a rule of thumb, whenever in doubt don't go off doing things in the constructor.
I reading the following article and got very confused on the "Dynamic Class Instantiation" part. Specifically this code snippet:
$obj = new $className();
if (!$obj instanceof SomeBaseType) {
throw new \InvalidTypeException();
}
I don't understand what this is actually doing for you. If its not an instance of a base class it errors out. How is that helpful? It doesn't get an instance of the correct type, or perform a different action based on the current type, it just throws an exception. I'm having trouble conceptualizing the purpose of this code and the article didn't really clear it up for me.
---------Complete section from the article----------
"Dynamic Class Instantiation
Generally speaking, the following code, while legal, should be used very seldom, and only when other possible instantiation patterns have been exhausted:
$obj = new $className();
if (!$obj instanceof SomeBaseType) {
throw new \InvalidTypeException();
}
Why is this a bad pattern? First, it makes the assumption up front that the constructor signature is free from any required parameters. While this is good for object types that are already known to this factory, it might not always be true of a consumers subtype of the base object in question. This patten should never be used on objects that have dependencies, or in situations where it is conceivable that a subtype might have dependencies because this takes away the possibility for a subtype to practice constructor injection.
Another problem is that instead of managing an object, or a list of objects, you are now managing a class name, or list of class names in addition to an object or list of objects. Instead, one could simply manage the objects.
If, on the other hand, you know this particular object type is no more than a value object (or similar), with no chance of it needing dependencies in subtypes, you can then cautiously use this instantiation pattern."
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.