php-doc-en/language/exceptions.xml
Barney Laurance 14cd35a23d
Exception implements Throwable + fix message param type
We were getting an Error trying to pass null to construct an exception, and found that "" works but null doesn't.

Also correcting the inheritance keyword since Throwable is an interface.

This brings these docs more into line with the PHPStorm stubs.

Closes GH-668.
2021-06-07 19:06:00 +02:00

557 lines
15 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<chapter xml:id="language.exceptions" xmlns="http://docbook.org/ns/docbook">
<title>Exceptions</title>
<sect1 xml:id="language.exceptions.extending">
<title>Extending Exceptions</title>
<para>
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.
</para>
<example>
<title>The Built in Exception class</title>
<programlisting role="php">
<![CDATA[
<?php
class Exception implements Throwable
{
protected $message = 'Unknown exception'; // exception message
private $string; // __toString cache
protected $code = 0; // user defined exception code
protected $file; // source filename of exception
protected $line; // source line of exception
private $trace; // backtrace
private $previous; // previous exception if nested exception
public function __construct($message = '', $code = 0, Throwable $previous = null);
final private function __clone(); // Inhibits cloning of exceptions.
final public function getMessage(); // message of exception
final public function getCode(); // code of exception
final public function getFile(); // source filename
final public function getLine(); // source line
final public function getTrace(); // an array of the backtrace()
final public function getPrevious(); // previous exception
final public function getTraceAsString(); // formatted string of trace
// Overrideable
public function __toString(); // formatted string for display
}
?>
]]>
</programlisting>
</example>
<para>
If a class extends the built-in Exception class and re-defines the <link
linkend="language.oop5.decon">constructor</link>, it is highly recommended
that it also call <link
linkend="language.oop5.paamayim-nekudotayim">parent::__construct()</link>
to ensure all available data has been properly assigned. The <link
linkend="language.oop5.magic">__toString()</link> method can be overridden
to provide a custom output when the object is presented as a string.
</para>
<note>
<para>
Exceptions cannot be cloned. Attempting to <link
linkend="language.oop5.cloning">clone</link> an Exception will result in a
fatal <constant>E_ERROR</constant> error.
</para>
</note>
<example>
<title>Extending the Exception class</title>
<programlisting role="php">
<![CDATA[
<?php
/**
* Define a custom exception class
*/
class MyException extends Exception
{
// Redefine the exception so message isn't optional
public function __construct($message, $code = 0, Throwable $previous = null) {
// some code
// make sure everything is assigned properly
parent::__construct($message, $code, $previous);
}
// custom string representation of object
public function __toString() {
return __CLASS__ . ": [{$this->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";
?>
]]>
</programlisting>
</example>
</sect1>
<simplesect xml:id="language.exceptions.introduction">
<para>
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.
</para>
<para>
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.
</para>
<para>
The thrown object must be an instance of the
<classname>Exception</classname> class or a subclass of
<classname>Exception</classname>. Trying to throw an object that is not
will result in a PHP Fatal Error.
</para>
<para>
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.
</para>
</simplesect>
<simplesect xml:id="language.exceptions.catch">
<title><literal>catch</literal></title>
<para>
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.
</para>
<para>
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.
</para>
<para>
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
"<literal>Uncaught Exception ...</literal>" message, unless a handler has
been defined with <function>set_exception_handler</function>.
</para>
<para>
As of PHP 7.1.0, a &catch; block may specify multiple exceptions
using the pipe (<literal>|</literal>) character. This is useful for when
different exceptions from different class hierarchies are handled the
same.
</para>
<para>
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.
</para>
</simplesect>
<simplesect xml:id="language.exceptions.finally">
<title><literal>finally</literal></title>
<para>
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.
</para>
<para>
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.
</para>
</simplesect>
<simplesect xml:id="language.exceptions.exception-handler">
<title><literal>Global exception handler</literal></title>
<para>
If an exception is allowed to bubble up to the global scope, it may be caught
by a global exception handler if set. The <function>set_exception_handler</function>
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;.
</para>
</simplesect>
<simplesect xml:id="language.exceptions.notes">
&reftitle.notes;
<note>
<para>
Internal PHP functions mainly use
<link linkend="ini.error-reporting">Error reporting</link>, only modern
<link linkend="language.oop5">Object oriented</link>
extensions use exceptions. However, errors can be easily translated to
exceptions with <link linkend="class.errorexception">ErrorException</link>.
This technique only works with non-fatal errors, however.
</para>
<example>
<title>Converting error reporting to exceptions</title>
<programlisting role="php">
<![CDATA[
<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler('exceptions_error_handler');
?>
]]>
</programlisting>
</example>
</note>
<tip>
<para>
The <link linkend="intro.spl">Standard PHP Library (SPL)</link> provides
a good number of <link linkend="spl.exceptions">built-in
exceptions</link>.
</para>
</tip>
</simplesect>
<simplesect xml:id="language.exceptions.examples">
&reftitle.examples;
<example>
<title>Throwing an Exception</title>
<programlisting role="php">
<![CDATA[
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
// Continue execution
echo "Hello World\n";
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
0.2
Caught exception: Division by zero.
Hello World
]]>
</screen>
</example>
<example>
<title>Exception handling with a &finally; block</title>
<programlisting role="php">
<![CDATA[
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->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";
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
0.2
First finally.
Caught exception: Division by zero.
Second finally.
Hello World
]]>
</screen>
</example>
<example>
<title>Interaction between the &finally; block and &return;</title>
<programlisting role="php">
<![CDATA[
<?php
function test() {
try {
throw new Exception('foo');
} catch (Exception $e) {
return 'catch';
} finally {
return 'finally';
}
}
echo test();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
finally
]]>
</screen>
</example>
<example>
<title>Nested Exception</title>
<programlisting role="php">
<![CDATA[
<?php
class MyException extends Exception { }
class Test {
public function testing() {
try {
try {
throw new MyException('foo!');
} catch (MyException $e) {
// rethrow it
throw $e;
}
} catch (Exception $e) {
var_dump($e->getMessage());
}
}
}
$foo = new Test;
$foo->testing();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
string(4) "foo!"
]]>
</screen>
</example>
<example>
<title>Multi catch exception handling</title>
<programlisting role="php">
<![CDATA[
<?php
class MyException extends Exception { }
class MyOtherException extends Exception { }
class Test {
public function testing() {
try {
throw new MyException();
} catch (MyException | MyOtherException $e) {
var_dump(get_class($e));
}
}
}
$foo = new Test;
$foo->testing();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
string(11) "MyException"
]]>
</screen>
</example>
<example>
<title>Omitting the caught variable</title>
<para>Only permitted in PHP 8.0.0 and later.</para>
<programlisting role="php">
<![CDATA[
<?php
class SpecificException extends Exception {}
function test() {
throw new SpecificException('Oopsie');
}
try {
test();
} catch (SpecificException) {
print "A SpecificException was thrown, but we don't care about the details.";
}
?>
]]>
</programlisting>
</example>
<example>
<title>Throw as an expression</title>
<para>Only permitted in PHP 8.0.0 and later.</para>
<programlisting role="php">
<![CDATA[
<?php
class SpecificException extends Exception {}
function test() {
do_something_risky() or throw new Exception('It did not work');
}
try {
test();
} catch (Exception $e) {
print $e->getMessage();
}
?>
]]>
</programlisting>
</example>
</simplesect>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->