![](http://habrastorage.org/getpro/habr/avatars/806/0d0/e27/8060d0e272f3141cdb8a7b90d04d7af4.jpg)
Correct work with exceptions in PHP
In a previous article, I suggested reducing all “error mechanisms” to exceptions, so it would be logical to explain how to work with exceptions in PHP correctly.
First, I will explain why I chose exceptions as the mechanism for working with errors:
I must say right away that in this article I do not discover America. The standard principles for working with exceptions, plus some of the features imposed by PHP, are described. It will be useful to read for beginners, although maybe experienced developers will find something new for themselves.
1. Never throw an abstract exception (ie just an Exception). Declare at least one exception class specifically for your application (module, library)
and replace all lines in your code
on the
Thus, all exceptions of your code can be distinguished from exceptions of your code.
2. Exceptions should be hierarchical . You must have a base exception class from which all exceptions thrown in your code are inherited. For example, if you have a module for working with fileModule files in your code, declare an exception that will be thrown only by this module
If you need even more distinguishability of errors, for example, among all errors associated with working with files, you want to distinguish between situations when the file is not found, then you need to declare one more exception
Observing hierarchy, you can distinguish exceptions from different modules in your application. I do not call for a bunch of exceptions for each module. Exceptions should not be designed from code, but from situations that you want to handle in a special way.
And the reverse situation, do not skimp on making various exceptions, if circumstances require
So that such situations are not possible in principle, you can "drown out" code in the base class
3. Do not handle exceptions if it is not clear in this context how to handle it . For example, if you follow the MVC pattern, then in the model method it may not be clear how to handle the error - how to output it, because control is responsible for the logic, and view is responsible for the output. If it is not clear what to do with the exception, then "forward" it further.
Any exceptions can be expected from a method that throws exceptions. You can narrow the number of exceptions thrown by the method by converting the exception:
This is a very important point - do not break the chain of exceptions. The third parameter passes the initial exception. This code natively works in 5.3 and with refinement in 5.2 . With this approach, the call stack will be “solid” from the very first throw of an exception.
4. You must have a global exception handler . It can be either try ... catch at the highest level or an ExceptionHandler. All exceptions that reach the global handler are considered critical, since they were not correctly handled earlier. They must be secured.
5. An exception is an object; accordingly, it can be expanded to fit your needs. Let's say you have a multilingual application and the error text in the throwing exception needs to be displayed to the user. Accordingly, this message needs to be translated. It is not difficult if the message is without variable parts, for example, "Error during operation." But what to do if the message includes variable parts, for example, “You do not have enough money on the balance sheet (1000). Need 2000. " Then you can pass the error text template separately and the variables themselves separately. Sample Code Anold code example .
6. Convert all assertion fail and non-fatal errors to exceptions (see my previous article )
7. Never suppress exceptions without any processing
because otherwise it would be very difficult to find an error due to such actions. You need to at least log in:
8. Document the exceptions. Indicate in the docblock what exceptions the method throws (@throws tag, you can specify more than one). It will make life easier for everyone.
That's basically all you need to know about exceptions. Finally, one more interesting fact - exceptions can be caught on the interface:
UPD fixed comments in comments: 1 , 2 and 3 (thanks to everyone who participated in the discussion).
Special thanks to habrayuzer ckopobapkuh for active participation
First, I will explain why I chose exceptions as the mechanism for working with errors:
- Exceptions are a flexible, extensible error handling method;
- This is a standardized mechanism - a person who has not worked with your code will not need to read the manual to understand how to handle errors. It is enough for him to know how exceptions work;
- With exceptions, it is much easier to find the source of errors, since there is always a call stack (trace).
I must say right away that in this article I do not discover America. The standard principles for working with exceptions, plus some of the features imposed by PHP, are described. It will be useful to read for beginners, although maybe experienced developers will find something new for themselves.
1. Never throw an abstract exception (ie just an Exception). Declare at least one exception class specifically for your application (module, library)
class baseException extends Exception {}
and replace all lines in your code
throw new Exception ();
on the
throw new baseException ();
Thus, all exceptions of your code can be distinguished from exceptions of your code.
2. Exceptions should be hierarchical . You must have a base exception class from which all exceptions thrown in your code are inherited. For example, if you have a module for working with fileModule files in your code, declare an exception that will be thrown only by this module
class fileModuleException extends baseException {}
If you need even more distinguishability of errors, for example, among all errors associated with working with files, you want to distinguish between situations when the file is not found, then you need to declare one more exception
class fileNotFoundException extends fileModuleException {}
Observing hierarchy, you can distinguish exceptions from different modules in your application. I do not call for a bunch of exceptions for each module. Exceptions should not be designed from code, but from situations that you want to handle in a special way.
And the reverse situation, do not skimp on making various exceptions, if circumstances require
try {
// ...
} catch (fileModuleException $ e) {
switch ($ e-> getCode ()) {// no need to do this
case 1: echo 'file not found';
case 2: echo 'file not readable';
// ...
}
}
So that such situations are not possible in principle, you can "drown out" code in the base class
function __construct ($ message = '', $ code = 0) {
parent :: __ construct ($ message, 0);
}
3. Do not handle exceptions if it is not clear in this context how to handle it . For example, if you follow the MVC pattern, then in the model method it may not be clear how to handle the error - how to output it, because control is responsible for the logic, and view is responsible for the output. If it is not clear what to do with the exception, then "forward" it further.
try {
$ db-> begin ();
// ...
$ db-> commit ();
} catch (Exception $ e) {
$ db-> rollback ();
throw $ e;
}
Any exceptions can be expected from a method that throws exceptions. You can narrow the number of exceptions thrown by the method by converting the exception:
try {
// ...
} catch (Exception $ e) {
throw new baseException ($ message, 0, $ e); // do not break the chain
}
This is a very important point - do not break the chain of exceptions. The third parameter passes the initial exception. This code natively works in 5.3 and with refinement in 5.2 . With this approach, the call stack will be “solid” from the very first throw of an exception.
4. You must have a global exception handler . It can be either try ... catch at the highest level or an ExceptionHandler. All exceptions that reach the global handler are considered critical, since they were not correctly handled earlier. They must be secured.
5. An exception is an object; accordingly, it can be expanded to fit your needs. Let's say you have a multilingual application and the error text in the throwing exception needs to be displayed to the user. Accordingly, this message needs to be translated. It is not difficult if the message is without variable parts, for example, "Error during operation." But what to do if the message includes variable parts, for example, “You do not have enough money on the balance sheet (1000). Need 2000. " Then you can pass the error text template separately and the variables themselves separately. Sample Code An
6. Convert all assertion fail and non-fatal errors to exceptions (see my previous article )
7. Never suppress exceptions without any processing
try {
// ...
} catch (Exception $ e) {
// do nothing
}
because otherwise it would be very difficult to find an error due to such actions. You need to at least log in:
try {
// ...
} catch (Exception $ e) {
exceptionHandlerClass :: exceptionLog ($ e);
}
8. Document the exceptions. Indicate in the docblock what exceptions the method throws (@throws tag, you can specify more than one). It will make life easier for everyone.
That's basically all you need to know about exceptions. Finally, one more interesting fact - exceptions can be caught on the interface:
interface iException {}
class customException extends baseException implements iException {}
try {
// ...
} catch (iException $ e) {
// ...
}
UPD fixed comments in comments: 1 , 2 and 3 (thanks to everyone who participated in the discussion).
Special thanks to habrayuzer ckopobapkuh for active participation