In the
previous article I suggested reducing all “error mechanisms” to exceptions, so it would be logical to explain how to work correctly with exceptions in PHP.
First, I will explain why I chose the exceptions as the mechanism for working with errors:
- Exceptions are a flexible, extensible error handling method;
- This is a standardized mechanism - a person who did not work with your code will not need to read the manual in order to understand how to handle errors. He only needs to know how exceptions work;
- With exceptions, it is much easier to find the source of errors, since there is always a stack of calls (trace).
I must say that in this article I do not discover America. We describe the standard principles of working with exceptions, plus some features imposed by PHP. It will be useful to read for beginners, although perhaps experienced developers will find something new for themselves.
1. Never throw an abstract exception (i.e. just an Exception). Declare at least one exception class specifically for your application (module, library)
class baseException extends Exception { }
and replace all the lines in your code
throw new Exception ( ) ;
on
throw new baseException ( ) ;
Thus, all exceptions of your code can be distinguished from exceptions of not your code.
2. Exceptions must be hierarchical . You must have a base exception class from which all exceptions thrown in your code are inherited. For example, you have in your code a module for working with fileModule files, 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 the errors associated with working with files, you want to distinguish the situation when the file is not found, then you need to declare one more exception
class fileNotFoundException extends fileModuleException { }
By observing the hierarchy, you can distinguish exceptions from different modules in your application. I do not call for a bunch of exceptions for each module. Exceptions must be designed not from code, but from situations that you want to handle in a special way.
And the reverse situation, do not be stingy to make various exceptions, if circumstances so require
try {
// ...
} catch ( fileModuleException $ e ) {
switch ( $ e -> getCode ( ) ) { // don't do that
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” the code in the base class
function __construct ( $ message = '' , $ code = 0 ) {
parent :: __construct ( $ message , 0 ) ;
}
3. Do not handle exceptions if in this context it is not clear 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 deduce it, because control is responsible for the logic, and view 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 ;
}
From the method that throws exceptions, you can expect any 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
}
There is a very important point - not to break the chain of exceptions. The third parameter is the initial exception. This code works natively in 5.3 and
with refinement in 5.2 . With this approach, the call stack will be “solid” from the very first exception roll.
')
4. You must have a global exception handler . This can be either try ... catch at the top level or ExceptionHandler. All exceptions that reached the global handler are considered critical because they were not properly processed before. They need to be pledged.
5. The exception is an object, respectively, it can be expanded to fit your needs . Suppose you have a multilingual application and the text of the error in the thrown exception must be displayed to the user. Accordingly, this message needs to be translated. It is not difficult if the message has no variable parts, for example, “Error while performing the 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 separately transfer the template of the error text and separately the variables themselves.
Sample Code Old sample code .
6. Convert all assertion fails and non-fatal errors to exceptions (see
my previous article )
7. Never turn off exceptions without any handling.try {
// ...
} catch ( Exception $ e ) {
// do nothing
}
because otherwise, the error due to such actions will be very difficult to find. You need at least to log:
try {
// ...
} catch ( Exception $ e ) {
exceptionHandlerClass :: exceptionLog ( $ e ) ;
}
8. Document exceptions. Specify in the docblock what exceptions the method throws out (@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. One more interesting fact in the end - you can catch exceptions through the interface:
interface iException { }
class customException extends baseException implements iException { }
try {
// ...
} catch ( iException $ e ) {
// ...
}
UPD corrected comments in comments:
1 ,
2 and
3 (thanks to everyone who participated in the discussion).
Special thanks, to habrayuzer
ckopobapkuh for active participation