Sage-Code Laboratory
index<--

Exceptions & Errors

Exceptions are abnormal bad situations when the program can not continue and should stop execution. PHP 7 has an exception model similar Java. An exception is an object that can be "thrown", and "catch" using a "try" block. At this time I assume you have learned enough about classes and functions to comprehend this model.

PHP: Exceptions

An exception is a special class that you can use to create exception objects. Errors are similar to exceptions. Both Error and Exception classes are implementing a common interface: Throwable. 

try-catch diagram

try-catch workflow

Notes:

Fatal errors: such as running out of memory, will halt immediately the script execution and can not be handled. Also, any uncaught exception is considered a fatal error that will cause the script to stop.

Warning: PHP 5 has a different error model. 

Throwable

To unite the two exception classes, Exception and Error both implement a interface, Throwable.  described better below using code:

interface Throwable {
    public function getMessage(): string;
    public function getCode(): int;
    public function getFile(): string;
    public function getLine(): int;
    public function getTrace(): array;
    public function getTraceAsString(): string;
    public function getPrevious(): Throwable;
    public function __toString(): string;
}

Type hierarchy 

In PHP 7.2 the type hierarchy was updated and it looks like this:

Throwable
 |- Exception
 | |- (user defined)
 | +...
 |
 |- Error
 |- TypeError
 |- ParseError
 |- ArithmeticError
 | +- DivisionByZeroError
 |
 +- AssertionError

Pattern:

Throwable may be used in try/catch blocks to catch both Exception and Error objects: 

try {
    // Code that may throw an Exception or Error.
} catch (Throwable $t) {
    // Handle exception
}

Note: Since Error objects should not be handled at runtime, catching Error  objects should be uncommon. In general, Error objects should only be caught for logging, performing any necessary cleanup, and display an error message to the user.

Catching Exceptions

In practice exceptions objects are anonymous and thrown immediately after creation. It is uncommon to create a variable to hold exception object. Because of this the "catch" must give a name to the caught exception, that is called exception handler:

Example:

<?php
try {
    throw new Exception("oops!");
}
catch (Exception $e) {
    echo $e->getMessage();
}
?>

Note: Handler $e do not exist until the exception is caught.

Ensure execution

Sometimes our purpose is not to catch the exception but to ensure a piece of code is executed after specific code protected by try block. For this we use "finally" keyword associate with "try" statement.

Pattern:

<?php
try {
    //protected code
}
finally {
    //mandatory code
}
?>

Note: 

Peculiar behavior

One of most bizarre feature of PHP is the execution of code after return statement. Using finally you can enforce a piece of code to be executed even after you have prepare a result using return statement:

Example:

<?php
function test() {
    try {
        throw new Exception("oops!");
    }
    catch (Exception $e) {
        echo $e->getMessage(),'<br>';
    }
    finally {
        return "done.";
    }
} //end function
echo test(); // oops!<br>done.
?>

Using exit() and die() 

These two functions can be used to stop script execution and create unrecoverable error. Function die() is the older version. Function exit() is a language construct so it can be called without brackets.

Example:

<?php
function test() {
    exit ("error!");
    return "done.";
} //end function
echo test();
?>

In previous code, function test() is interrupted before it return "done." So instead of printing: "done." it will exit and print "error!" instead. 

Output:

error!

Syntax for exit is overloaded. You can return an error code or a message like in the following example:

exit ( string $status ) : void
exit ( int $status ) : void

Pattern:

Because "or" is a short-circuit operator, the following design pattern is possible and for some reason preferred by PHP developers: 

<?php
$filename = '/path/to/data-file';
$file = fopen($filename, 'r') or exit("unable to open file ($filename)");
fclose($file);
?>

A better approach is to use a try block and catch the exception:

Example:

<?php
$filename = '/path/to/data-file';
try {
    //proactive check if file exist
    if (!file_exists($filename) ) {
        throw new Exception('File not found:'.$filename);
    }
    $file = fopen($filename, 'r');
    fclose($file);
}   
catch (Exception $e) {
    echo $e->getMessage();
}
?>

Note: proactive checking is considered good practice. catching errors must be done only when is necessary. However if you do not, you will get a warning message:

Error Reporting

By default all errors and warning messages are printed out into your output document. Even if you use a try-catch block, you can not suppress all messages that came out from a function call.

Example

<?php
$filename = '/path/to/data-file';
try {
    $file = fopen($filename, 'r');
    //reactive checking
    if ( !$file ) {
        throw new Exception('File open failed.');
    }
    fclose($file);
} 
catch (Exception $e) {
    echo $e->getMessage();
};
?>

Output:


PHP Warning: fopen(/path/to/data-file): failed to open stream: 
No such file or directory in index.php on line 5 File open failed.

The output message do not look right. I have expected: "File open failed." But instead I got a long message starting with "PHP Warning:". To suppress warnings you must use directive error_reporting.

error_reporting ( int $level ) : int 

Parameter $level is a bit-mask. It can be created using bitwise operators and predefined constants:

Example:

<?php
error_reporting(E_ERROR | E_PARSE);
$filename = '/path/to/data-file';
try {
    //proactive check if file exist
    $file = fopen($filename, 'r');
    
    //file exist but for some reason fail to open
    if ( !$file ) {
        throw new Exception('File open failed.');
    }
    fclose($file);
} 
catch (Exception $e) {
    echo $e->getMessage();
};
?>

Output:

The warning message is gone:

File open failed.

Predefined constants:

Examples:

// Turn off all error reporting
error_reporting(0);
// Report simple running errors
error_reporting(E_ERROR | E_WARNING | E_PARSE);
/* Reporting E_NOTICE can be good too (to report uninitialized
 variables or catch variable name misspellings ...) */
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
// Report all errors except E_WARNING
error_reporting(E_ALL & ~E_WARNING);

Read next: Files