(No version information available, might only be in Git)
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, even if a return or yield statement is encountered,
or if a Throwable error is not caught or is rethrown.
It will be executed prior to resuming the normal execution flow.
The only exception to this rule is when the script is terminated abruptly,
such as by a call to exit() or by a fatal error
(for example when the maximum execution time is exceeded).
Приклад #1 Throwable error handling with a finally block
<?php
function inverse($x) {
return 1/$x;
}
try {
echo inverse(5) . "\n";
} catch (\DivisionByZeroError $e) {
echo 'Caught error: ', $e->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";В PHP 8 поданий вище приклад виведе:
0.2 First finally. Caught error: Division by zero Second finally. Hello World
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.
Приклад #2 All finally block will be executed
<?php
try {
try {
throw new Exception();
} finally {
echo "Inner finally\n";
}
} catch (Exception $e) {
} finally {
echo "Outer finally\n";
}Поданий вище приклад виведе:
Inner finally Outer finally
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.
Приклад #3 Interaction between return in finally block and a previous return
<?php
function testReturnTry() {
try {
echo 'Within try', "\n";
return 'try';
} finally {
echo 'Within finally', "\n";
return 'finally';
}
}
function testReturnCatch() {
try {
throw new Exception('foo');
} catch (Exception $e) {
echo 'Within catch', "\n";
return 'catch';
} finally {
echo 'Within finally', "\n";
return 'finally';
}
}
echo testReturnTry(), "\n";
echo testReturnCatch(), "\n";Поданий вище приклад виведе:
Within try Within finally finally Within catch Within finally finally
Приклад #4 Interaction between the finally block and return
<?php
function testReturnTry() {
try {
return 'try';
} finally {
echo 'finally ';
}
}
function testReturnCatch() {
try {
throw new Exception('foo');
} catch (Exception $e) {
return 'catch';
} finally {
echo 'finally ';
}
}
echo testReturnTry(), "\n";
echo testReturnCatch(), "\n";Поданий вище приклад виведе:
finally try finally catch
finally block and an exception thrown in a try block
If both the try block and the finally block throw an exception,
the exception thrown from the finally block is the one that is propagated,
and the exception thrown from the try block will be used as its previous exception.
Приклад #5 Exception in try and in finally
<?php
try {
try {
throw new Exception(message: 'Third', previous: new Exception('Fourth'));
} finally {
throw new Exception(message: 'First', previous: new Exception('Second'));
}
} catch (Exception $e) {
var_dump(
$e->getMessage(),
$e->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getPrevious()->getMessage(),
);
}Поданий вище приклад виведе:
string(5) "First" string(6) "Second" string(5) "Third" string(6) "Fourth"
finally block in a
Generator
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.
Приклад #6
finally block being run when an Exception
has been thrown
<?php
function generate()
{
try {
yield 1;
yield 2;
} catch(\Exception $e) {
echo 'catch';
} finally {
echo "finally\n";
}
}
try {
foreach (generate() as $i) {
echo $i, "\n";
throw new Exception();
}
} catch(Exception $e) {
echo 'outer catch';
}Поданий вище приклад виведе щось схоже на:
1 finally outer catch
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.
Приклад #7
Error when yielding in finally block when an Exception
has been thrown
<?php
function generate()
{
try {
yield 1;
yield 2;
} catch(\Exception $e) {
echo 'catch';
} finally {
echo "finally\n";
yield 3;
}
}
try {
foreach (generate() as $i) {
echo $i, "\n";
throw new Exception();
}
} catch(Exception $e) {
echo 'outer catch';
}Поданий вище приклад виведе щось схоже на:
1
finally
Fatal error: Uncaught Exception in /in/GbFmu:19
Stack trace:
#0 {main}
Next Error: Cannot yield from finally in a force-closed generator in /in/GbFmu:12
Stack trace:
#0 /in/GbFmu(19): generate()
#1 {main}
thrown in /in/GbFmu on line 12
finally block and a call to exit()
When exit() is called within a try or a catch block
all finally blocks are skipped.
Приклад #8 Interaction between finally block and exit() called in try block
<?php
try {
echo 'quit';
exit(0);
} finally {
echo 'finally';
}Поданий вище приклад виведе:
quit
Приклад #9 Interaction between finally block and exit() called in catch block
<?php
try {
throw new Exception('foo');
} catch (Exception $e) {
echo 'catch';
exit(0);
} finally {
echo 'finally ';
}Поданий вище приклад виведе:
catch
Приклад #10 All finally blocks are skipped when exit() is called
<?php
try {
try {
echo 'quit';
exit(0);
} finally {
echo 'inner finally ';
}
} finally {
echo 'outer finally';
}Поданий вище приклад виведе:
quit
When exit() is called within a finally block,
all other outer finally blocks are skipped.
Приклад #11 Skipping outer finally block by calling exit()
<?php
try {
try {
echo 'try', "\n";
} finally {
echo 'inner finally ';
exit(0);
}
} finally {
echo 'outer finally';
}Поданий вище приклад виведе:
try inner finally
This one edge case behaviour is highly dependent on which version of PHP the code is executed.
<?php
function generate()
{
try {
yield 1;
yield 2;
} catch(\Exception $e) {
echo 'catch';
} finally {
echo "inner finally\n";
}
}
try {
foreach (generate() as $i) {
echo $i, "\n";
exit(0);
}
} finally {
echo "outer finally\n";
}Output of the above example prior to PHP 7.1.14, in PHP 7.2.0 and 7.2.1, and as of PHP 8.0.0
1 inner finally
Output of the above example as of PHP 7.1.14 and 7.2.2, and prior to PHP 8.0.0
1