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 workflow
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.
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;
}
In PHP 7.2 the type hierarchy was updated and it looks like this:
Throwable
|- Exception
| |- (user defined)
| +...
|
|- Error
|- TypeError
|- ParseError
|- ArithmeticError
| +- DivisionByZeroError
|
+- AssertionError
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.
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:
<?php
try {
throw new Exception("oops!");
}
catch (Exception $e) {
echo $e->getMessage();
}
?>
Note: Handler $e do not exist until the exception is caught.
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:
<?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.
?>
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.
<?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
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:
<?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:
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:
<?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:
// 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