Exceptions
PHP has an exception model similar to that of other programming
languages. An exception can be &throw;n, and caught ("&catch;ed") within
PHP. Code may be surrounded in a &try; block, to facilitate the catching
of potential exceptions. Each &try; must have at least one corresponding
&catch; or &finally; block.
If an exception is thrown and its current function scope has no &catch;
block, the exception will "bubble up" the call stack to the calling
function until it finds a matching &catch; block. All &finally; blocks it encounters
along the way will be executed. If the call stack is unwound all the way to the
global scope without encountering a matching &catch; block, the program will
terminate with a fatal error unless a global exception handler has been set.
The thrown object must be an &instanceof; Throwable.
Trying to throw an object that is not will result in a PHP Fatal Error.
As of PHP 8.0.0, the &throw; keyword is an expression and may be used in any expression
context. In prior versions it was a statement and was required to be on its own line.
catch
A &catch; block defines how to respond to a thrown exception. A &catch;
block defines one or more types of exception or error it can handle, and
optionally a variable to which to assign the exception. (The variable was
required prior to PHP 8.0.0.) The first &catch; block a thrown exception
or error encounters that matches the type of the thrown object will handle
the object.
Multiple &catch; blocks can be used to catch different classes of
exceptions. Normal execution (when no exception is thrown within the &try;
block) will continue after that last &catch; block defined in sequence.
Exceptions can be &throw;n (or re-thrown) within a &catch; block. If not,
execution will continue after the &catch; block that was triggered.
When an exception is thrown, code following the statement will not be
executed, and PHP will attempt to find the first matching &catch; block.
If an exception is not caught, a PHP Fatal Error will be issued with an
"Uncaught Exception ..." message, unless a handler has
been defined with set_exception_handler.
As of PHP 7.1.0, a &catch; block may specify multiple exceptions
using the pipe (|) character. This is useful for when
different exceptions from different class hierarchies are handled the
same.
As of PHP 8.0.0, the variable name for a caught exception is optional.
If not specified, the &catch; block will still execute but will not
have access to the thrown object.
finally
A &finally; block may also be specified after or
instead of &catch; blocks. Code within the &finally; block will always be
executed after the &try; and &catch; blocks, regardless of whether an
exception has been thrown, and before normal execution resumes.
One notable interaction is between the &finally; block and a &return; statement.
If a &return; statement is encountered inside either the &try; or the &catch; blocks,
the &finally; block will still be executed. Moreover, the &return; statement is
evaluated when encountered, but the result will be returned after the &finally; block
is executed. Additionally, if the &finally; block also contains a &return; statement,
the value from the &finally; block is returned.
Another notable interaction is between an exception thrown from within a &try; block,
and an exception thrown from within a &finally; block. If both blocks throw an exception,
then the exception thrown from the &finally; block will be the one that is propagated,
and the exception thrown from the &try; block will be used as its previous exception.
Global exception handler
If an exception is allowed to bubble up to the global scope, it may be caught
by a global exception handler if set. The set_exception_handler
function can set a function that will be called in place of a &catch; block if no
other block is invoked. The effect is essentially the same as if the entire program
were wrapped in a &try;-&catch; block with that function as the &catch;.
&reftitle.notes;
Internal PHP functions mainly use
Error reporting, only modern
Object-oriented
extensions use exceptions. However, errors can be easily translated to
exceptions with ErrorException.
This technique only works with non-fatal errors, however.
Converting error reporting to exceptions
]]>
The Standard PHP Library (SPL) provides
a good number of built-in
exceptions.
&reftitle.examples;
Throwing an Exception
getMessage(), "\n";
}
// Continue execution
echo "Hello World\n";
?>
]]>
&example.outputs;
Exception handling with a &finally; block
getMessage(), "\n";
} finally {
echo "First finally.\n";
}
try {
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
} finally {
echo "Second finally.\n";
}
// Continue execution
echo "Hello World\n";
?>
]]>
&example.outputs;
Interaction between the &finally; block and &return;
]]>
&example.outputs;
Nested Exception
getMessage());
}
}
}
$foo = new Test;
$foo->testing();
?>
]]>
&example.outputs;
Multi catch exception handling
testing();
?>
]]>
&example.outputs;
Omitting the caught variableOnly permitted in PHP 8.0.0 and later.
]]>
&example.outputs;
Throw as an expressionOnly permitted in PHP 8.0.0 and later.
getMessage();
}
?>
]]>
&example.outputs;
Exception in try and in finally
getMessage(),
$e->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getPrevious()->getMessage(),
);
}
]]>
&example.outputs;
Extending Exceptions
A User defined Exception class can be defined by extending the built-in
Exception class. The members and properties below, show what is accessible
within the child class that derives from the built-in Exception class.
The Built in Exception class
]]>
If a class extends the built-in Exception class and re-defines the constructor, it is highly recommended
that it also call parent::__construct()
to ensure all available data has been properly assigned. The __toString() method can be overridden
to provide a custom output when the object is presented as a string.
Exceptions cannot be cloned. Attempting to clone an Exception will result in a
fatal E_ERROR error.
Extending the Exception class
code}]: {$this->message}\n";
}
public function customFunction() {
echo "A custom function for this type of exception\n";
}
}
/**
* Create a class to test the exception
*/
class TestException
{
public $var;
const THROW_NONE = 0;
const THROW_CUSTOM = 1;
const THROW_DEFAULT = 2;
function __construct($avalue = self::THROW_NONE) {
switch ($avalue) {
case self::THROW_CUSTOM:
// throw custom exception
throw new MyException('1 is an invalid parameter', 5);
break;
case self::THROW_DEFAULT:
// throw default one.
throw new Exception('2 is not allowed as a parameter', 6);
break;
default:
// No exception, object will be created.
$this->var = $avalue;
break;
}
}
}
// Example 1
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (MyException $e) { // Will be caught
echo "Caught my exception\n", $e;
$e->customFunction();
} catch (Exception $e) { // Skipped
echo "Caught Default Exception\n", $e;
}
// Continue execution
var_dump($o); // Null
echo "\n\n";
// Example 2
try {
$o = new TestException(TestException::THROW_DEFAULT);
} catch (MyException $e) { // Doesn't match this type
echo "Caught my exception\n", $e;
$e->customFunction();
} catch (Exception $e) { // Will be caught
echo "Caught Default Exception\n", $e;
}
// Continue execution
var_dump($o); // Null
echo "\n\n";
// Example 3
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (Exception $e) { // Will be caught
echo "Default Exception caught\n", $e;
}
// Continue execution
var_dump($o); // Null
echo "\n\n";
// Example 4
try {
$o = new TestException();
} catch (Exception $e) { // Skipped, no exception
echo "Default Exception caught\n", $e;
}
// Continue execution
var_dump($o); // TestException
echo "\n\n";
?>
]]>