Handling Errors and Exceptions in PHP

    This “small” article is a development of the topic covered in this article.
    As you know, PHP was born a long time ago and even then the question arose of what to do with the errors that arise. Perl, which is the undoubted progenitor of PHP, by default did not have any error handling system. If any error occurred, the server threw the 500th error and that was all. Therefore, Warnings, Fatal Errors and Notices were a real breakthrough in facilitating the already difficult work of a programmer. However, time passed, PHP mechanisms did not change, and technology, as you know, do not like to stand still.

    And now in PHP 5.0, finally, in the arsenal of programmers there was such a powerful tool as an exception or Exception. Exception has many advantages, I will describe only a few (maybe I express myself inaccurately or even illiterate, but I was just too lazy to look for scientific terms to describe the advantages, because they are described “in my own words”):
    • End-to-end generation. This means that the occurrence of an exception somewhere in the code will lead to a sequential exit from the control structures and functions to the first catch block or to the main function (with the corresponding error in the stream) of the main script
    • Ability to override core Exception class through inheritance
    • Ability to handle multiple types of exceptions simultaneously

    The possibilities for handling standard PHP errors are extremely limited:
    • Can be blocked with @
    • You can set your handler using set_error_handler
    • You can generate your own error with trigger_error

    It is clear that the standard error handling mechanism is outdated and is present in the language only for compatibility reasons.
    In this short article I will try to highlight how universal error handling can be made by translating it to using the exception mechanism.
    The main idea: we set our handler for standard errors and throw an exception in it:
    1. class MyException extends Exception {
    2.    public function __construct ($ message, $ errorLevel = 0, $ errorFile = '', $ errorLine = 0) {
    3.       parent :: __ construct ($ message, $ errorLevel);
    4.       $ this-> file = $ errorFile;
    5.       $ this-> line = $ errorLine;
    6.    }
    7. }
    8. set_error_handler (create_function ('$ c, $ m, $ f, $ l', 'throw new MyException ($ m, $ c, $ f, $ l);'), E_ALL);
    9. ?>
    * This source code was highlighted with Source Code Highlighter .

    This code needs to be taken out in a separate file and to connect it only once. The MyException class extends the standard Exception class by adding two additional parameters to the constructor: the file and the line number with the error.
    The set_error_handler function sets a dynamically created lambda function (callback) as an error handler, which throws an exception in case of an error. I especially ask you to pay attention to the second parameter of the set_error_handler function. This parameter is very important, since it determines for which types of errors the user-defined (that is, our) handler will be called, and for which the standard one. In this example, I set the value to E_ALL, which means that the handler will be called for all types of errors.
    If we do not want to handle some types of errors, for example, Notice, then we can easily indicate this:
    set_error_handler (create_function ('$ c, $ m, $ f, $ l', 'throw new MyException ($ m, $ c, $ f, $ l);'), E_ALL & ~ E_NOTICE); * This source code was highlighted with Source Code Highlighter .

    However, the ideal approach, as it seems to me, is to still handle all errors, but for some types, in particular, notice, it would be advisable not to throw an exception, but simply to display information on the screen:
    set_error_handler (create_function ('$ c, $ m, $ f, $ l', 'if ($ c === E_NOTICE) {echo' This is notice: '. $ m} else {throw new MyException ($ m, $ c, $ f, $ l);} '), E_ALL); * This source code was highlighted with Source Code Highlighter .

    Now consider an example closer to life. Objective:
    There is a registration form on the site, it is necessary to implement using exceptions the processing of validation errors and the issuance of appropriate warnings for the user.
    There are actually two difficulties here:
    1. Print all errors at once, not one at a time
    2. Separate handling of validation errors from handling other exceptions

    Decision:
    The main difficulty here for us will be the very notorious advantage of Exception, which consists in the fact that when an exception is thrown, the control structures exit to the first catch block (or to the end of the script). In order to get around this pitfall, we will define a new descendant class FormFieldsListException, in which we implement the error accumulation mechanism, and we will throw an exception only after all fields have been validated. In the FormFieldsListException class, we define the protected member $ _list in which we will store the data. To simplify working with the $ _list array, we indicate that the class will implement two interfaces: ArrayAccess for accessing the array elements and Iterator for working in a loop. When initializing the validation validation method, create a FormFieldsListException object,
    1. class FormFieldsListException extends Exception implements ArrayAccess, Iterator {
    2.   protected $ _list = array ();
    3.   
    4.   public function __construct () {
    5.   }
    6.   
    7.   public function offsetExists ($ index) {
    8.     return isset ($ this -> _ list [$ index]);
    9.   }
    10.   
    11.   public function offsetGet ($ index) {
    12.     return $ this -> _ list [$ index];
    13.   }
    14.   
    15.   public function offsetSet ($ index, $ value) {
    16.     if (isset ($ index)) {
    17.       $ this -> _ list [$ index] = $ value;
    18.     }
    19.     else {
    20.       $ this -> _ list [] = $ value;
    21.     }
    22.   }
    23.   
    24.   public function offsetUnset ($ index) {
    25.     unset ($ this -> _ list [$ index]);
    26.   }
    27.   
    28.   public function current () {
    29.     return current ($ this -> _ list);
    30.   }
    31.   
    32.   public function key () {
    33.     return key ($ this -> _ list);
    34.   }
    35.   
    36.   public function next () {
    37.     return next ($ this -> _ list);
    38.   }
    39.   
    40.   public function rewind () {
    41.     return reset ($ this -> _ list);
    42.   }
    43.   
    44.   public function valid () {
    45.     return (bool) $ this-> current ();
    46.   }
    47. }
    48. ?>
    * This source code was highlighted with Source Code Highlighter .

    After the validation procedure is completed, we check whether any error messages have been entered. If so, then “throw” the prepared object of exception.
    To catch an exception, we use two catch blocks: for FormFieldsListException and for all other exceptions. This allows you to specify different types of actions when various types of exceptions occur.
    1. function validateForm () {
    2.    $ e = new FormFieldsListException ();
    3.    if ($ errorInFirstField) {
    4.       $ e [] = 'Error in first field';
    5.    }
    6.    if ($ errorInSecondField) {
    7.       $ e [] = 'Error in second field';
    8.    }
    9.    if ((bool) $ e-> current ()) {
    10.       throw $ e;
    11.    }
    12. }
    13.  
    14. try {
    15.    validateForm ();
    16. }
    17. catch (FormFieldsListException $ error) {
    18.    echo ' Errors in the fields :
      ';
    19.    foreach ($ error as $ e) {
    20.       echo $ e. '
      ';
    21.    }
    22. }
    23. catch (Exception $ error) {
    24.    echo 'Not validation error! '. $ error-> getMessage ();
    25. }
    26. ?>
    * This source code was highlighted with Source Code Highlighter .

    Here it is! :)
    A properly designed exception system can seriously simplify the life of a programmer, especially when developing applications using the MVC pattern. As this small study showed, the exception handling system in PHP5 contains considerable reserves for modernization and use in specific situations.
    PS : Some of the programmers to whom I showed this article consider using exceptions to validate forms, to put it mildly, not the best option (by the way, I would ask readers who are “in the subject” to comment on this), so I ask you to consider this example just a case study, not a guide to action.
    PPS : Many thanks to fellow ashofthedream, the dispute with which prompted me to study the exceptions in more detail.

    UPD : Moved to PHP Blog

    Also popular now: