diff --git a/appendices/migration70/incompatible/error-handling.xml b/appendices/migration70/incompatible/error-handling.xml index 37e9a31525b8..f12285f9b6cf 100644 --- a/appendices/migration70/incompatible/error-handling.xml +++ b/appendices/migration70/incompatible/error-handling.xml @@ -17,12 +17,12 @@ Error exceptions). - - A fuller description of how errors operate in PHP 7 can be found - on the PHP 7 errors page. This + + A more complete description of how errors operate in PHP 7 can be found + on the PHP 7 errors page. This migration guide will merely enumerate the changes that affect backward compatibility. - + diff --git a/appendices/tokens.xml b/appendices/tokens.xml index 7a4223faba29..30a4521916ca 100644 --- a/appendices/tokens.xml +++ b/appendices/tokens.xml @@ -180,7 +180,7 @@ defined('T_FN') || define('T_FN', 10001); (<type>int</type>) </entry> <entry>catch</entry> - <entry><xref linkend="language.exceptions"/></entry> + <entry><xref linkend="control-structures.catch"/></entry> </row> <row xml:id="constant.t-class"> <entry> @@ -557,7 +557,7 @@ defined('T_FN') || define('T_FN', 10001); (<type>int</type>) </entry> <entry>finally</entry> - <entry><xref linkend="language.exceptions"/></entry> + <entry><xref linkend="control-structures.finally"/></entry> </row> <row xml:id="constant.t-fn"> <entry> @@ -1255,7 +1255,7 @@ defined('T_FN') || define('T_FN', 10001); (<type>int</type>) </entry> <entry>throw</entry> - <entry><xref linkend="language.exceptions"/></entry> + <entry><xref linkend="control-structures.throw"/></entry> </row> <row xml:id="constant.t-trait"> <entry> @@ -1279,7 +1279,7 @@ defined('T_FN') || define('T_FN', 10001); (<type>int</type>) </entry> <entry>try</entry> - <entry><xref linkend="language.exceptions"/></entry> + <entry><xref linkend="control-structures.try"/></entry> </row> <row xml:id="constant.t-unset"> <entry> diff --git a/language/control-structures.xml b/language/control-structures.xml index b9a6b334e9b5..437eb3ea95af 100644 --- a/language/control-structures.xml +++ b/language/control-structures.xml @@ -54,6 +54,9 @@ &language.control-structures.require-once; &language.control-structures.include-once; &language.control-structures.goto; + &language.control-structures.throw; + &language.control-structures.try-catch; + &language.control-structures.finally; </chapter> diff --git a/language/control-structures/finally.xml b/language/control-structures/finally.xml new file mode 100644 index 000000000000..bcf34b5df7e9 --- /dev/null +++ b/language/control-structures/finally.xml @@ -0,0 +1,444 @@ +<?xml version="1.0" encoding="utf-8"?> +<sect1 xml:id="control-structures.finally" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink"> + <title>finally + + + A &finally; block may be specified after or instead of &catch; blocks. + Code within the &finally; block will always be executed after the &try; and + &catch; blocks, even if a &return; or &yield; statement is encountered, + a Throwable error hasn't been caught or is rethrown. + It will be executed prior to resuming the normal execution flow. + The only exception to this rule is when a call to exit + is performed. + + + <exceptionname>Throwable</exceptionname> error handling with a &finally; block + +getMessage(), "\n"; +} finally { + echo "First finally.\n"; +} + +try { + echo inverse(0) . "\n"; +} catch (\DivisionByZeroError $e) { + echo 'Caught error: ', $e->getMessage(), "\n"; +} finally { + echo "Second finally.\n"; +} + +// Continue execution +echo "Hello World\n"; +]]> + + &example.outputs.8; + + + + + + + When the call stack is unwound after a Throwable error + has been thrown, all &finally; blocks it encounters along the way will be executed. + In other words, nested &finally; blocks are executed even if the + Throwable error is not caught by an adjacent + &catch; block. + + + All &finally; block will be executed + + + + &example.outputs; + + + + + + + Interaction between a &finally; block and a &return; statement + + 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. + + + Interaction between &return; in &finally; block and a previous &return; + + + + &example.outputs; + + + + + + Interaction between the &finally; block and &return; + + + + &example.outputs; + + + + + + + + + Behaviour of a &finally; block in a + <link linkend="language.generators">Generator</link> + + + + If during the traversal of a generator a Throwable + error is thrown, the &finally; within the generator will be executed, + as it is the previous element of the call stack. + + + + &finally; block being run when an <exceptionname>Exception</exceptionname> + has been thrown + + + + + &example.outputs.similar; + + + + + + + It is possible to &yield; within a &finally; block but if a + Throwable error occurs and is not caught within + the &foreach; a Cannot yield from finally in a force-closed + generator Error is thrown. + + + + Error when &yield;ing in &finally; block when an <exceptionname>Exception</exceptionname> + has been thrown + + + + + &example.outputs.similar; + + + + + + + + Interaction between a &finally; block and a call to <function>exit</function> + + When exit is called within a &try; or a &catch; block + all &finally; blocks are skipped. + + + Interaction between &finally; block and <function>exit</function> called in &try; block + + + + &example.outputs; + + + + + + Interaction between &finally; block and <function>exit</function> called in &catch; block + + + + &example.outputs; + + + + + + All &finally; blocks are skipped when <function>exit</function> is called + + + + &example.outputs; + + + + + + + When exit is called within a &finally; block, + all other outer &finally; blocks are skipped as per the above behaviour. + + + Skipping outer &finally; block by calling <function>exit</function> + + + + &example.outputs; + + + + + + + Edge case of calling <function>exit</function> in a &foreach; over a generator + + This one edge case behaviour is highly dependent on which version of PHP + the code is executed. + + + + + + + + Output of the above example prior to PHP 7.1.13, in PHP 7.2.0 and 7.2.1, + and as of PHP 8.0.0 + + + + + + Output of the above example as of PHP 7.1.14, and prior to PHP 8.0.0 + + + + + + + + diff --git a/language/control-structures/throw.xml b/language/control-structures/throw.xml new file mode 100644 index 000000000000..f6db10d656a7 --- /dev/null +++ b/language/control-structures/throw.xml @@ -0,0 +1,117 @@ + + + throw + + + The &throw; keyword is an expression, and may be used in any expression context, + which takes an instance of a Throwable object as an + argument. + + + + Prior to PHP 8.0, the &throw; keyword was a statement and not an expression. + + + + Attempting to &throw; a non-Throwable object will + throw an Error. + + + Throwing a non-<exceptionname>Throwable</exceptionname> object + + + + &example.outputs.similar; + + + + + + + After using &throw; the normal flow of execution is halted and the call stack + is unwound until the global scope is reached and terminates the program with + a fatal error containing the stack trace indicating where the + Throwable object has been thrown. + + + Throwing an Exception in the global scope + + + + &example.outputs.similar; + + + + + + Throwing an Exception in a function + + + + &example.outputs.similar; + + + + + + + As &throw; is an expression it can be used to branch out on the result + of another expression if its value is inappropriate. + + + Using throw as an expression + Only permitted in PHP 8.0.0 and later. + + + + + + + It is possible to handle a thrown Throwable by using + a try-catch block. + See the error handling + section for more information. + + diff --git a/language/control-structures/try-catch.xml b/language/control-structures/try-catch.xml new file mode 100644 index 000000000000..92ff872b572e --- /dev/null +++ b/language/control-structures/try-catch.xml @@ -0,0 +1,226 @@ + + + try-catch blocks + + try + + + A &try; block delimits a segment of code which might &throw; + a Throwable error which one wants to possibly handle. + A &try; block must have at least one corresponding + &catch; or &finally; block. + + + + When a Throwable error is thrown in a &try; block + PHP will attempt to find the first matching &catch; block to handle the + error, if none can be found it will unwind the call stack as usual. + + + + catch + + + A &catch; block defines how to respond to a thrown Throwable error. + A &catch; block defines one or more types of Throwable + (most commonly Exception) it can handle, and + optionally a variable to which to assign the exception. + + + + Prior to PHP 8.0.0, the variable in which to assign the + Throwable object was mandatory. + + + + The first &catch; block a thrown Throwable encounters + that matches the class of the thrown object will handle the error before + resuming normal execution after the &catch; block. + + + Catching an Exception + +getMessage(), "\n"; +} + +// Continue execution +echo "Hello World\n"; +?> +]]> + + &example.outputs; + + + + + + + As of PHP 7.1.0, a &catch; block may specify a union of + Throwable subclasses that the block should handle + using the pipe (|) character. This is useful for when + different exceptions from different class hierarchies are handled the same. + + + Catching a union of classes to handle + + testing(); + +?> +]]> + + &example.outputs; + + + + + + + Multiple &catch; blocks can be used to handle different classes of + Throwable errors in different ways. + Normal execution (when no error is thrown within the &try; block) will + continue after that last &catch; block defined in sequence. + + + Multiple catch blocks + + + + + + + Throwables can be &throw;n (or re-thrown) within a &catch; block. + Otherwise, execution will continue after the &catch; block that was triggered. + + + Nested Exception + +getMessage()); + } + } +} + +$foo = new Test; +$foo->testing(); + +?> +]]> + + &example.outputs; + + + + + + + Throwing an object within a &catch; block will not + be caught by an adjacent &catch; block. + + + Sequential catch blocks + + + + &example.outputs.similar; + + + + + + + + Omitting the caught variable + Only permitted in PHP 8.0.0 and later. + + +]]> + + + + diff --git a/language/error-handling.xml b/language/error-handling.xml new file mode 100644 index 000000000000..3007b6fa151a --- /dev/null +++ b/language/error-handling.xml @@ -0,0 +1,325 @@ + + + Error Handling + + Errors are a normal part of the development and life cycle of an application. + As such PHP provides multiple ways to inform and deal with errors. + + PHP has two main mechanisms for reporting errors: + + + + Throwable errors, which are further split into + programming errors via the Error + class hierarchy and recoverable errors via the + Exception class hierarchy. + + + + + Diagnostic errors which are classified into different + severities. + + + + + + <exceptionname>Throwable</exceptionname> errors + + Most errors in PHP are of this type. + A Throwable error, is an execution event which disrupts + the normal flow of instructions. When a Throwable error + is &throw;n, be that by the engine or manually, it will unwind the call stack + all the way up to the global scope and terminate the program with a fatal error, + unless it is intercepted by a &try;-&catch; block or a global exception handler. + All &finally; blocks it encounters along the way will be executed. + + + To throw a Throwable error from within PHP code, one + must use the &throw; keyword followed by an instance of the + Error or Exception class or + a subclass of one of them. + Trying to throw an object that does not implement Throwable will result in an + Error being thrown with the following message: + Cannot throw objects that do not implement Throwable. + + + + The Standard PHP Library (SPL) provides + various built-in exceptions. + And it's possible to create custom exceptions by + extending + Exception. + + + + It is possible to handle a Throwable error within PHP + by surrounding the code susceptible to throw an error in a &try; block. + A &try; block must have at least one corresponding &catch; or &finally; block. + + + + It is not recommended to catch Error + objects as those signal a programming error. + + + + + Throwable objects cannot be cloned. + Attempting to clone such an + object will result in an Error being thrown with + the following message: + Trying to clone an uncloneable object of class Exception. + + + + + <literal>Global exception handler</literal> + + If a Throwable object is allowed to bubble up to the + global scope, it will be caught by a global exception handler if one is set. + The set_exception_handler accepts a callable + which takes an exception as a parameter. It will be triggered when + a Throwable object bubbles up the whole call stack + without being caught. It acts as the ultimate &catch; for the entire program. + The default exception handler will stop the execution by generating a Fatal Error. + You can customise this behaviour by setting your own handler, and end the execution + of your program gracefully. + + + + + Extending Exceptions + + A user-defined exception can be defined by extending 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 used as a string. + + + Extending <exceptionname>Exception</exceptionname> + +code}]: {$this->message}\n"; + } + + public function customFunction() { + echo "A custom function for this type of exception\n"; + } +} + +try { + throw new CustomException("Something happened", 5); +} catch (CustomException $e) { + echo "Caught custom exception\n"; + echo $e; + $e->customFunction(); +} +]]> + + &example.outputs; + + + + + + + + <exceptionname>Throwable</exceptionname> hierarchy + + + + Throwable + + + Error + + + ArithmeticError + + + DivisionByZeroError + + + + + AssertionError + + + CompileError + + + ParseError + + + + + TypeError + + + ArgumentCountError + + + + + ValueError + + + UnhandledMatchError + + + + + Exception + + + ... + + + + + + + + + + + Diagnostic errors + + Diagnostic errors, also known as traditional errors, are used to signal a + number of different conditions, and can be displayed and/or logged as required. + By default PHP will report all diagnostic errors. (Prior to PHP 8.0, + E_NOTICE and E_DEPRECATED + diagnostics were not reported by default.) + + + In contrast to Throwable errors, diagnostic errors do + not disrupt the normal flow of execution by default. + However, they can take control of execution when a handler has been set + with set_error_handler. + + + Which diagnostics are reported and which are ignored is controlled by the + + &php.ini; directive, or at runtime by calling error_reporting. + It is strongly recommended that the configuration directive be set, + as some errors can occur before execution of your script begins. + + + + In a development environment, + + should always be set to E_ALL to be aware of and fix + issues raised by PHP. + However, in production the level of verbosity could be reduced to + E_ALL & ~E_NOTICE & ~E_DEPRECATED, but in many cases + E_ALL is still appropriate, as it may provide early + warning of potential issues. + + + + + What happens to the diagnostics depends on two further &php.ini; directives. + display_errors + controls whether the diagnostic is shown as part of the script's output. + This should always be disabled in a production environment, as it can include + confidential information such as database passwords, but is often useful to + enable in development, as it ensures immediate reporting of issues. + + + + In addition to displaying errors, PHP can log diagnostics when the + log_errors + directive is enabled. This will log any diagnostic to the file or syslog + defined by + error_log. + This can be extremely useful in a production environment, as diagnostics are + logged when they occur and can then generate reports based on those logs. + + + Individual diagnostics can be suppressed using the + @ operator. + + + + + Prior to PHP 8.0.0 it was possible to suppress critical diagnostics that + would terminate the execution of the PHP script. + + + + + + Diagnostics can be added, removed, have their severity altered or be + elevated to a Throwable error in between PHP versions. + + + + + User-defined diagnostic handler + + + If PHP's default diagnostic handling is inadequate, + it is possible to override the default diagnostic handler with a custom one + which is set by using set_error_handler. + Only non-fatal diagnostic can be handled this way, but they can then be + handled in various ways. For example, this can be used to show a custom + error page to the user and then report more directly than via a log, such + as by sending an e-mail. + + + + Another typical usage is to elevate diagnostic errors to exceptions with + ErrorException. + + + Elevating diagnostic errors to exceptions + + +]]> + + + + It is important to verify that the severity is included in the current + list of reportable diagnostics, otherwise the + @ operator + will be broken, as an exception will be thrown for a suppressed diagnostic. + + + + + + + diff --git a/language/oop5/basic.xml b/language/oop5/basic.xml index ea87be554544..264ca8d0290d 100644 --- a/language/oop5/basic.xml +++ b/language/oop5/basic.xml @@ -197,14 +197,14 @@ readonly class Foo new - + To create an instance of a class, the new keyword must be used. An object will always be created unless the object has a constructor defined that throws an - exception on error. Classes + Exception on error. Classes should be defined before instantiation (and in some cases this is a requirement). - + If a variable containing a string with the name of a class is used with new, a new instance of that class will be created. If @@ -213,10 +213,10 @@ readonly class Foo - + If there are no arguments to be passed to the class's constructor, parentheses after the class name may be omitted. - + @@ -831,11 +831,11 @@ if (is_null($repository)) { - + The nullsafe operator is best used when null is considered a valid and expected possible value for a property or method return. For indicating an error, a thrown exception is preferable. - + diff --git a/language/oop5/changelog.xml b/language/oop5/changelog.xml index 00dd1bb97d99..acfd92b6f61e 100644 --- a/language/oop5/changelog.xml +++ b/language/oop5/changelog.xml @@ -197,7 +197,7 @@ 5.5.0 - Added: finally to handle exceptions. + Added: finally to handle exceptions. @@ -255,7 +255,7 @@ Changed: Prior to 5.3.0, exceptions thrown in the __autoload function could not be - caught in the catch block, and + caught in the catch block, and would result in a fatal error. Exceptions now thrown in the __autoload function can be caught in the catch block, with one provision. If throwing a custom exception, then the custom exception class must diff --git a/language/predefined/exceptions.xml b/language/predefined/exceptions.xml index 14a3f914d56c..c963e3d2a0e0 100644 --- a/language/predefined/exceptions.xml +++ b/language/predefined/exceptions.xml @@ -15,7 +15,7 @@ &language.predefined.closedgeneratorexception; + language/error-handling.xml. --> &language.predefined.error; &language.predefined.argumentcounterror; &language.predefined.arithmeticerror; diff --git a/manual.xml b/manual.xml index 84c6986aa404..ba03c76218ab 100644 --- a/manual.xml +++ b/manual.xml @@ -52,6 +52,7 @@ &language.oop5; &language.namespaces; &language.enumerations; + &language.error-handling; &language.errors; &language.exceptions; &language.fibers; diff --git a/reference/dom/domexception.xml b/reference/dom/domexception.xml index bcea4a042ff6..11bae6e5f574 100644 --- a/reference/dom/domexception.xml +++ b/reference/dom/domexception.xml @@ -25,9 +25,7 @@ FIXME: Remove me once you perform substitutions This class is aliased as Dom\Exception in the Dom namespace. - - See also . - + See also . diff --git a/reference/pdo/error-handling.xml b/reference/pdo/error-handling.xml index 83bb4c22d0fd..64e0e72c867c 100644 --- a/reference/pdo/error-handling.xml +++ b/reference/pdo/error-handling.xml @@ -57,10 +57,10 @@ with less code/nesting than by running in silent mode and explicitly checking the return value of each database call. - - See Exceptions for more - information about Exceptions in PHP. - + + See for more + information about handling Exceptions in PHP. + diff --git a/reference/pdo/pdoexception.xml b/reference/pdo/pdoexception.xml index 1d702df4c231..0a31cc05047b 100644 --- a/reference/pdo/pdoexception.xml +++ b/reference/pdo/pdoexception.xml @@ -8,12 +8,9 @@
&reftitle.intro; - - Represents an error raised by PDO. You should not throw a - PDOException from your own code. - See Exceptions for more - information about Exceptions in PHP. - + Represents an error raised by PDO. You should not throw a + PDOException from your own code. + See also .
@@ -58,19 +55,15 @@ errorInfo - - Corresponds to PDO::errorInfo or - PDOStatement::errorInfo - + Corresponds to PDO::errorInfo or + PDOStatement::errorInfo code - - SQLSTATE error code. Use - Exception::getCode to access it. - + SQLSTATE error code. Use + Exception::getCode to access it. diff --git a/reference/snmp/snmpexception.xml b/reference/snmp/snmpexception.xml index 8f91ea641c60..b6bdc283dca4 100644 --- a/reference/snmp/snmpexception.xml +++ b/reference/snmp/snmpexception.xml @@ -11,9 +11,8 @@ Represents an error raised by SNMP. You should not throw a SNMPException from your own code. - See Exceptions for more - information about Exceptions in PHP. + See also .