php-doc-en/language/exceptions.xml
Christoph Michael Becker 1d58f72646 Exception updates
* Empty catch statements
* Throw as an expression
* Various other cleanup while I was there to remove comments

I also deleted a crapload of comments. It's so satisfying.

Closes GH-238.


git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@351652 c90b9560-bf6c-de11-be94-00142212c4b1
2020-11-25 23:03:02 +00: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 extends 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 = null, $code = 0, Exception $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, Exception $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
-->